github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/auth/crypt/encryption.go (about) 1 package crypt 2 3 import ( 4 "crypto/rand" 5 "errors" 6 "io" 7 8 "golang.org/x/crypto/nacl/secretbox" 9 "golang.org/x/crypto/scrypt" 10 ) 11 12 const ( 13 KeySaltBytes = 8 14 KeySizeBytes = 32 15 NonceSizeBytes = 24 16 ) 17 18 type SecretStore interface { 19 SharedSecret() []byte 20 Encrypt(data []byte) ([]byte, error) 21 Decrypt(encrypted []byte) ([]byte, error) 22 } 23 24 type NaclSecretStore struct { 25 secret []byte 26 } 27 28 var ( 29 ErrFailDecrypt = errors.New("could not decrypt value") 30 ) 31 32 func NewSecretStore(secret []byte) *NaclSecretStore { 33 return &NaclSecretStore{secret: secret} 34 } 35 36 func (a *NaclSecretStore) SharedSecret() []byte { 37 return a.secret 38 } 39 40 func (a *NaclSecretStore) kdf(storedSalt []byte) (key [KeySizeBytes]byte, salt [KeySaltBytes]byte, err error) { 41 if storedSalt != nil { 42 copy(salt[:], storedSalt) 43 } else if _, err = io.ReadFull(rand.Reader, salt[:]); err != nil { 44 return 45 } 46 // scrypt's N, r & p, benchmarked to run at about 1ms, since it's in the critical path. 47 // fair trade-off for a high throughput low latency system 48 const ( 49 N = 512 50 r = 8 51 p = 1 52 keyLen = 32 53 ) 54 keySlice, err := scrypt.Key(a.secret, salt[:], N, r, p, keyLen) 55 if err != nil { 56 return 57 } 58 copy(key[:], keySlice) 59 return 60 } 61 62 func (a *NaclSecretStore) Encrypt(data []byte) ([]byte, error) { 63 // generate a random nonce 64 var nonce [NonceSizeBytes]byte 65 if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { 66 return nil, err 67 } 68 69 // derive a key from the stored secret, generating a new random salt 70 key, salt, err := a.kdf(nil) 71 if err != nil { 72 return nil, err 73 } 74 75 // use nonce and derived key to encrypt data 76 encrypted := secretbox.Seal(nonce[:], data, &nonce, &key) 77 78 // kdf salt (8) + nonce (24) + encrypted data (rest) 79 return append(salt[:], encrypted...), nil 80 } 81 82 func (a *NaclSecretStore) Decrypt(encrypted []byte) ([]byte, error) { 83 // extract salt 84 var salt [KeySaltBytes]byte 85 copy(salt[:], encrypted[:KeySaltBytes]) 86 87 // derive encryption key from salt and stored secret 88 key, _, err := a.kdf(salt[:]) 89 if err != nil { 90 return nil, err 91 } 92 93 // extract nonce 94 var decryptNonce [NonceSizeBytes]byte 95 copy(decryptNonce[:], encrypted[KeySaltBytes:KeySaltBytes+NonceSizeBytes]) 96 97 // decrypt the rest 98 decrypted, ok := secretbox.Open(nil, encrypted[KeySaltBytes+NonceSizeBytes:], &decryptNonce, &key) 99 if !ok { 100 return nil, ErrFailDecrypt 101 } 102 return decrypted, nil 103 }