github.com/ericwq/aprilsh@v0.0.0-20240517091432-958bc568daa0/encrypt/encrypt.go (about) 1 // Copyright 2022 wangqi. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package encrypt 6 7 import ( 8 "crypto/aes" 9 "crypto/cipher" 10 "crypto/rand" 11 "encoding/base64" 12 "encoding/binary" 13 "fmt" 14 "io" 15 "sync/atomic" 16 "syscall" 17 18 "github.com/ericwq/aprilsh/util" 19 ) 20 21 const ( 22 NONCE_LEN = 12 23 24 RECEIVE_MTU = 2048 25 ADDED_BYTES = 16 /* final OCB block */ 26 ) 27 28 // var logW = log.New(os.Stderr, "WARN: ", log.Ldate|log.Ltime|log.Lshortfile) 29 30 // use sys call to generate random number 31 func PrngFill(size int) (dst []byte) { 32 dst = make([]byte, size) 33 if size == 0 { 34 return dst 35 } 36 37 rand.Read(dst) 38 // _, err := rand.Read(dst) 39 // if err != nil { 40 // panic(fmt.Sprintf("Could not read random number. %s", err)) 41 // } 42 return 43 } 44 45 func PrngUint8() uint8 { 46 var u8 uint8 47 for u8 == 0 { 48 dst := PrngFill(1) 49 u8 = dst[0] 50 } 51 return u8 52 } 53 54 func randomNonce() ([]byte, error) { 55 return _randomNonce() 56 } 57 58 // don't use this function directly, it's for internal purpose only 59 type _randFunc func(io.Reader, []byte) (int, error) 60 61 // don't use this function directly, it's for internal purpose only 62 func _randomNonce(r ..._randFunc) ([]byte, error) { 63 // Never use more than 2^32 random nonces with a given key because of the risk of a repeat. 64 var f _randFunc 65 if len(r) > 0 { 66 f = r[0] 67 } else { 68 f = io.ReadFull 69 } 70 71 nonce := make([]byte, NONCE_LEN) 72 if _, err := f(rand.Reader, nonce); err != nil { 73 // logW.Printf("#randomNonce. %s\n", err) 74 util.Logger.Warn("#randomNonce", "error", err) 75 return nil, err 76 } 77 78 return nonce, nil 79 } 80 81 type Base64Key struct { 82 key []uint8 83 } 84 85 // random key 128bit 86 func NewBase64Key() *Base64Key { 87 b := &Base64Key{} 88 b.key = PrngFill(16) 89 return b 90 } 91 92 func NewBase64Key2(printableKey string) *Base64Key { 93 key, err := base64.StdEncoding.DecodeString(printableKey) 94 if err != nil { 95 // logW.Printf("#Base64Key Key must be well-formed base64. %s\n", err) 96 util.Logger.Warn("key must be well-formed base64", "error", err) 97 return nil 98 } 99 100 if len(key) != 16 { 101 // logW.Println("#Base64Key Key must represent 16 octets.") 102 util.Logger.Warn("key must represent 16 octets.", "key", key) 103 return nil 104 } 105 106 b := &Base64Key{} 107 b.key = key 108 // // to catch changes after the first 128 bits 109 // if printableKey != b.printableKey() { 110 // panic("Base64 key was not encoded 128-bit key.") 111 // } 112 113 return b 114 } 115 116 func (b *Base64Key) printableKey() string { 117 return base64.StdEncoding.EncodeToString(b.key) 118 } 119 120 func (b *Base64Key) data() []uint8 { 121 return b.key 122 } 123 124 func (b *Base64Key) String() string { 125 return b.printableKey() 126 } 127 128 var counter uint64 129 130 func Unique() uint64 { 131 atomic.AddUint64(&counter, 1) 132 return atomic.LoadUint64(&counter) 133 } 134 135 type Message struct { 136 nonce []byte 137 text []byte 138 } 139 140 func NewMessage(seqNonce uint64, payload []byte) (m *Message) { 141 m = &Message{} 142 // fmt.Printf("#Message seqNonce=% x\n", seqNonce) 143 144 b := make([]byte, NONCE_LEN) 145 binary.BigEndian.PutUint32(b[0:], 0) 146 binary.BigEndian.PutUint64(b[4:], seqNonce) 147 148 m.nonce = b 149 m.text = payload 150 151 // fmt.Printf("#Message head=% x, nonce=% x\n", m.nonce[:4], m.nonce[4:]) 152 // fmt.Printf("#Message text=% x\n", m.text) 153 return m 154 } 155 156 func (m *Message) NonceVal() uint64 { 157 seqNonce := binary.BigEndian.Uint64(m.nonce[4:]) 158 159 // fmt.Printf("#NonceVal seqNonce=%x\n", seqNonce) 160 return seqNonce 161 } 162 163 // the first two bytes is timestamp in text field 164 func (m *Message) GetTimestamp() uint16 { 165 // var ts uint16 166 // buf := bytes.NewReader(m.text[:2]) 167 // err := binary.Read(buf, hostEndian, &ts) 168 // if err != nil { 169 // fmt.Printf("#GetTimestamp failed. %s\n", err) 170 // } 171 // 172 // return ts 173 return binary.BigEndian.Uint16(m.text[:2]) 174 } 175 176 // the [2:4] bytes is timestampReply in text field 177 func (m *Message) GetTimestampReply() uint16 { 178 // var tsr uint16 179 // buf := bytes.NewReader(m.text[2:4]) 180 // err := binary.Read(buf, hostEndian, &tsr) 181 // if err != nil { 182 // fmt.Printf("#GetTimestampReply failed. %s\n", err) 183 // } 184 // 185 // return tsr 186 return binary.BigEndian.Uint16(m.text[2:4]) 187 } 188 189 func (m *Message) GetPayload() (payload []byte) { 190 return m.text[4:] 191 } 192 193 type Session struct { 194 base64Key Base64Key 195 aead cipher.AEAD 196 // sync.Mutex 197 } 198 199 func NewSession(key Base64Key) (*Session, error) { 200 s := &Session{base64Key: key} 201 block, err := aes.NewCipher([]byte(s.base64Key.key)) 202 if err != nil { 203 // logW.Printf("#session %s\n", err) 204 util.Logger.Warn("create session from key", "error", err) 205 return nil, err 206 } 207 208 aesgcm, _ := cipher.NewGCM(block) 209 // aesgcm, err := cipher.NewGCM(block) 210 // if err != nil { 211 // return nil, err 212 // } 213 214 s.aead = aesgcm 215 return s, nil 216 } 217 218 // https://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb 219 // https://installmd.com/c/276/go/encrypt-a-string-using-aes-gcm 220 221 // Encrypt with AES-128 GCM 222 func (s *Session) Encrypt(plainText *Message) []byte { 223 // s.Lock() 224 // defer s.Unlock() 225 nonce := plainText.nonce 226 227 cipherText := s.aead.Seal(nonce, nonce, plainText.text, nil) 228 return cipherText 229 } 230 231 // Decrypt with AES-128 GCM 232 func (s *Session) Decrypt(text []byte) (*Message, error) { 233 // s.Lock() 234 // defer s.Unlock() 235 ns := s.aead.NonceSize() 236 nonce, cipherText := text[:ns], text[ns:] 237 // fmt.Printf("#decrypt ciphertext=% x, %p\n", cipherText, cipherText) 238 239 plainText, err := s.aead.Open(nil, nonce, cipherText, nil) 240 if err != nil { 241 // logW.Printf("#decrypt %s\n", err) 242 return nil, err 243 } 244 245 m := Message{} 246 m.nonce = nonce 247 m.text = plainText 248 // fmt.Printf("#decrypt nonce=% x, plaintext=% x\n", nonce, plainText) 249 250 return &m, nil 251 } 252 253 var savedCoreLimit uint64 254 255 // Disable dumping core, as a precaution to avoid saving sensitive data to disk. 256 func DisableDumpingCore() error { 257 // the value argument is provided by last parameter of accessRlimit 258 f := func(rlim *syscall.Rlimit, value uint64) { 259 savedCoreLimit = rlim.Cur 260 rlim.Cur = value 261 } 262 return accessRlimit(syscall.RLIMIT_CORE, f, 0) 263 } 264 265 // restore the dumping core to saved value 266 func ReenableDumpingCore() error { 267 f := func(rlim *syscall.Rlimit, _ uint64) { 268 rlim.Cur = savedCoreLimit 269 } 270 271 // we don't use value parameter, so it's not important the specific value 272 return accessRlimit(syscall.RLIMIT_CORE, f, 0) 273 } 274 275 // get specified resource, then do some action defined by f, finally set the specififed resource 276 func accessRlimit(resource int, f func(rlim *syscall.Rlimit, value uint64), value uint64) error { 277 var rlim syscall.Rlimit 278 if err := syscall.Getrlimit(resource, &rlim); err != nil { 279 return fmt.Errorf("Getrlimit() reports %s", err.Error()) 280 } 281 282 f(&rlim, value) 283 284 if err := syscall.Setrlimit(resource, &rlim); err != nil { 285 return fmt.Errorf("Setrlimit() reports %s", err.Error()) 286 } 287 return nil 288 } 289 290 // https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html 291 // 292 // var hostEndian binary.ByteOrder 293 // 294 // func init() { 295 // buf := [2]byte{} 296 // *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) 297 // 298 // switch buf { 299 // case [2]byte{0xCD, 0xAB}: 300 // hostEndian = binary.LittleEndian 301 // case [2]byte{0xAB, 0xCD}: 302 // hostEndian = binary.BigEndian 303 // default: 304 // panic("Could not determine native endianness.") 305 // } 306 // }