github.com/safedep/dry@v0.0.0-20241016050132-a15651f0548b/crypto/aes.go (about) 1 package crypto 2 3 import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/rand" 7 "fmt" 8 "io" 9 10 "golang.org/x/crypto/scrypt" 11 ) 12 13 const ( 14 aesKDFSaltSize = 8 15 aesKeySize = 32 16 aesNonce = 12 17 ) 18 19 // This is a simple encryptor interface that does not support crypto configuration 20 // keys, IVs, etc. It is meant to be used for simple encryption/decryption tasks for 21 // internal data security. This is not meant to be used for secure communication 22 type SimpleEncryptor interface { 23 Encrypt(data []byte) ([]byte, error) 24 Decrypt(data []byte) ([]byte, error) 25 } 26 27 type aesEncryptor struct { 28 key []byte 29 } 30 31 // Based on: https://go.dev/src/crypto/cipher/example_test.go 32 // https://pkg.go.dev/golang.org/x/crypto/scrypt 33 func NewAesEncryptor(salt, key string) (SimpleEncryptor, error) { 34 if len(salt) != aesKDFSaltSize { 35 return nil, fmt.Errorf("salt size must be %d", aesKDFSaltSize) 36 } 37 38 sb := []byte(salt) 39 kb, err := scrypt.Key([]byte(key), sb, 1<<15, 8, 1, aesKeySize) 40 if err != nil { 41 return nil, err 42 } 43 44 if len(kb) != aesKeySize { 45 return nil, fmt.Errorf("key size must be %d", aesKeySize) 46 } 47 48 return &aesEncryptor{key: kb}, nil 49 } 50 51 func (a *aesEncryptor) Encrypt(data []byte) ([]byte, error) { 52 block, err := aes.NewCipher(a.key) 53 if err != nil { 54 return nil, err 55 } 56 57 nonce := make([]byte, aesNonce) 58 _, err = io.ReadFull(rand.Reader, nonce) 59 if err != nil { 60 return nil, err 61 } 62 63 gcm, err := cipher.NewGCM(block) 64 if err != nil { 65 return nil, err 66 } 67 68 ciphertext := gcm.Seal(nil, nonce, data, nil) 69 return append(nonce, ciphertext...), nil 70 } 71 72 func (a *aesEncryptor) Decrypt(data []byte) ([]byte, error) { 73 block, err := aes.NewCipher(a.key) 74 if err != nil { 75 return nil, err 76 } 77 78 if len(data) < aesNonce { 79 return nil, fmt.Errorf("ciphertext too short") 80 } 81 82 nonce := data[:aesNonce] 83 ciphertext := data[aesNonce:] 84 85 gcm, err := cipher.NewGCM(block) 86 if err != nil { 87 return nil, err 88 } 89 90 plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) 91 if err != nil { 92 return nil, err 93 } 94 95 return plaintext, nil 96 }