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  }