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 }