github.com/GGP1/kure@v0.8.4/crypt/crypt.go (about)

     1  package crypt
     2  
     3  import (
     4  	"crypto/aes"
     5  	"crypto/cipher"
     6  	"crypto/rand"
     7  	"io"
     8  
     9  	"github.com/GGP1/kure/config"
    10  
    11  	"github.com/awnumar/memguard"
    12  	"github.com/pkg/errors"
    13  	"golang.org/x/crypto/argon2"
    14  )
    15  
    16  const saltSize = 32
    17  
    18  var (
    19  	// Do not provide the reason of failure to potential attackers
    20  	errEncrypt = errors.New("encryption failed")
    21  	errDecrypt = errors.New("decryption failed")
    22  )
    23  
    24  // Encrypt ciphers data.
    25  func Encrypt(data []byte) ([]byte, error) {
    26  	if data == nil {
    27  		return nil, errEncrypt
    28  	}
    29  
    30  	key, salt, err := deriveKey(nil)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	block, err := aes.NewCipher(key.Bytes())
    36  	if err != nil {
    37  		return nil, errEncrypt
    38  	}
    39  	key.Destroy()
    40  
    41  	gcm, err := cipher.NewGCM(block)
    42  	if err != nil {
    43  		return nil, errEncrypt
    44  	}
    45  
    46  	// make 12 byte long nonce
    47  	nonce := make([]byte, gcm.NonceSize())
    48  
    49  	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
    50  		return nil, errEncrypt
    51  	}
    52  
    53  	dst := make([]byte, gcm.NonceSize())
    54  	copy(dst, nonce)
    55  
    56  	// Encrypt, authenticate and append the salt to the end of it
    57  	ciphertext := gcm.Seal(dst, nonce, data, nil)
    58  	ciphertext = append(ciphertext, salt...)
    59  
    60  	return ciphertext, nil
    61  }
    62  
    63  // Decrypt deciphers data.
    64  func Decrypt(data []byte) ([]byte, error) {
    65  	if data == nil {
    66  		return nil, errDecrypt
    67  	}
    68  
    69  	// Split salt (last 32 bytes) from the data
    70  	salt, data := data[len(data)-saltSize:], data[:len(data)-saltSize]
    71  
    72  	key, _, err := deriveKey(salt)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	block, err := aes.NewCipher(key.Bytes())
    78  	if err != nil {
    79  		return nil, errDecrypt
    80  	}
    81  	key.Destroy()
    82  
    83  	gcm, err := cipher.NewGCM(block)
    84  	if err != nil {
    85  		return nil, errDecrypt
    86  	}
    87  
    88  	// The nonce is 12 bytes long
    89  	nonceSize := gcm.NonceSize()
    90  	if len(data) < nonceSize {
    91  		return nil, errDecrypt
    92  	}
    93  
    94  	// Decrypt and authenticate ciphertext
    95  	plaintext, err := gcm.Open(nil, data[:nonceSize], data[nonceSize:], nil)
    96  	if err != nil {
    97  		return nil, errDecrypt
    98  	}
    99  
   100  	return plaintext, nil
   101  }
   102  
   103  // deriveKey derives the key from the password, salt and other parameters using
   104  // the key derivation function argon2id.
   105  //
   106  // It destroys the buffer of the enclave passed and returns the derived key, and the salt used.
   107  func deriveKey(salt []byte) (*memguard.LockedBuffer, []byte, error) {
   108  	password := config.GetEnclave("auth.password")
   109  	iters := config.GetUint32("auth.iterations")
   110  	memory := config.GetUint32("auth.memory")
   111  	threads := config.GetUint32("auth.threads")
   112  
   113  	// When decrypting the salt is taken from the encrypted data and when encrypting it's randomly generated
   114  	if salt == nil {
   115  		salt = make([]byte, saltSize)
   116  		if _, err := rand.Read(salt); err != nil {
   117  			return nil, nil, errors.New("generating salt")
   118  		}
   119  	}
   120  
   121  	// Decrypt enclave and save its content in a locked buffer
   122  	pwd, err := password.Open()
   123  	if err != nil {
   124  		return nil, nil, errors.New("decrypting key")
   125  	}
   126  
   127  	derivedKey := memguard.NewBufferFromBytes(argon2.IDKey(pwd.Bytes(), salt, iters, memory, uint8(threads), 32))
   128  	pwd.Destroy()
   129  
   130  	return derivedKey, salt, nil
   131  }