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 }