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  }