pkg.re/essentialkaos/ek.10@v12.41.0+incompatible/secstr/secure_string.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  // Package secstr provides methods and structs for working with protected (secure) strings
     5  package secstr
     6  
     7  // ////////////////////////////////////////////////////////////////////////////////// //
     8  //                                                                                    //
     9  //                         Copyright (c) 2022 ESSENTIAL KAOS                          //
    10  //      Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>     //
    11  //                                                                                    //
    12  // ////////////////////////////////////////////////////////////////////////////////// //
    13  
    14  import (
    15  	"fmt"
    16  	"runtime"
    17  
    18  	"golang.org/x/sys/unix"
    19  )
    20  
    21  // ////////////////////////////////////////////////////////////////////////////////// //
    22  
    23  // String contains protected data
    24  type String struct {
    25  	Data []byte
    26  }
    27  
    28  // ////////////////////////////////////////////////////////////////////////////////// //
    29  
    30  // NewSecureString creates new secure string
    31  func NewSecureString(data interface{}) (*String, error) {
    32  	switch v := data.(type) {
    33  	case []byte:
    34  		return secureStringFromSlice(v)
    35  	case *string:
    36  		return secureStringFromStringPointer(v)
    37  	case string:
    38  		return secureStringFromString(v)
    39  	default:
    40  		return nil, fmt.Errorf("Unsupported data type for secure string: %t", data)
    41  	}
    42  }
    43  
    44  // ////////////////////////////////////////////////////////////////////////////////// //
    45  
    46  // IsEmpty returns false if string is empty
    47  func (s *String) IsEmpty() bool {
    48  	return len(s.Data) == 0
    49  }
    50  
    51  // Destroy destroys data
    52  func (s *String) Destroy() error {
    53  	return destroySecureString(s)
    54  }
    55  
    56  // ////////////////////////////////////////////////////////////////////////////////// //
    57  
    58  // secureStringFromSlice creates secure string from byte slice
    59  func secureStringFromSlice(data []byte) (*String, error) {
    60  	var err error
    61  
    62  	s := &String{}
    63  
    64  	// Destroy source data
    65  	defer clearByteSlice(data)
    66  
    67  	s.Data, err = unix.Mmap(
    68  		-1, 0, len(data),
    69  		unix.PROT_READ|unix.PROT_WRITE, // Pages may be read and written
    70  		unix.MAP_ANON|unix.MAP_PRIVATE, // The mapping is not backed by any file + private copy-on-write
    71  	)
    72  
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	// Lock memory with data
    78  	err = unix.Mlock(s.Data)
    79  
    80  	if err != nil {
    81  		unix.Munmap(s.Data)
    82  		return nil, err
    83  	}
    84  
    85  	// Copy data
    86  	for i := range data {
    87  		s.Data[i] = data[i]
    88  	}
    89  
    90  	// Protect memory region with data
    91  	err = unix.Mprotect(s.Data, unix.PROT_READ) // The memory can be read
    92  
    93  	if err != nil {
    94  		unix.Munmap(s.Data)
    95  		clearByteSlice(s.Data) // Destroy data if memory cannot be protected
    96  		return nil, err
    97  	}
    98  
    99  	// Set finalizer
   100  	runtime.SetFinalizer(s, destroySecureString)
   101  
   102  	return s, nil
   103  }
   104  
   105  // secureStringFromString creates secure string from string
   106  func secureStringFromString(data string) (*String, error) {
   107  	return secureStringFromSlice([]byte(data))
   108  }
   109  
   110  // secureStringFromStringPointer creates secure string from string pointer
   111  func secureStringFromStringPointer(data *string) (*String, error) {
   112  	s, err := secureStringFromSlice([]byte(*data))
   113  
   114  	// Clear source data
   115  	*data = ""
   116  
   117  	return s, err
   118  }
   119  
   120  // ////////////////////////////////////////////////////////////////////////////////// //
   121  
   122  func destroySecureString(s *String) error {
   123  	if s.Data == nil {
   124  		return nil
   125  	}
   126  
   127  	err := unix.Mprotect(s.Data, unix.PROT_READ|unix.PROT_WRITE)
   128  
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	clearByteSlice(s.Data) // Clear data
   134  
   135  	// Unlock memory
   136  	err = unix.Munlock(s.Data)
   137  
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	err = unix.Munmap(s.Data)
   143  
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	s.Data = nil // Mark as nil for GC
   149  
   150  	return nil
   151  }
   152  
   153  // clearByteSlice clears byte slice
   154  func clearByteSlice(s []byte) {
   155  	for i := range s {
   156  		s[i] = 0
   157  	}
   158  }