/
symmetric.go
68 lines (62 loc) · 2.5 KB
/
symmetric.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package grant
import (
"crypto/rand"
"fmt"
"github.com/monax/hoard/encryption"
"github.com/monax/hoard/reference"
"golang.org/x/crypto/scrypt"
)
// We bump it a little from the 100ms for interactive logins rule: https://blog.filippo.io/the-scrypt-parameters/
const scryptSecurityWorkExponent = 16
const minSecretSize = 8
// SymmetricGrant encrypts the given reference based on a secret read from the provider store
func SymmetricGrant(ref *reference.Ref, secret []byte) ([]byte, error) {
if len(secret) < minSecretSize {
return nil, fmt.Errorf("SymmetricGrant cannot encrypt with a secret of size < %d", minSecretSize)
}
// Generate scrypt salt
salt := make([]byte, encryption.NonceSize)
_, err := rand.Read(salt)
if err != nil {
return nil, fmt.Errorf("SymmetricGrant failed to generate random salt: %v", err)
}
// Derive key
secretKey, err := DeriveSecretKey(secret, salt)
if err != nil {
return nil, fmt.Errorf("SymmetricGrant failed to derive secret key: %v", err)
}
// Generate AES nonce
nonce := make([]byte, encryption.NonceSize)
_, err = rand.Read(nonce)
if err != nil {
return nil, fmt.Errorf("SymmetricGrant failed to generate random nonce: %v", err)
}
// Encrypt reference with key and nonce
blob, err := encryption.Encrypt([]byte(ref.Plaintext(nil)), nonce, secretKey)
if err != nil {
return nil, fmt.Errorf("SymmetricGrant failed to encyrpt: %v", err)
}
// Store salt and nonce so we can re-derive key/decrypt later
return encryption.Salinate(blob.EncryptedData, append(nonce, salt...)), nil
}
// SymmetricReference decrypts the given grant based on a secret read from the provider store
func SymmetricReference(ciphertext, secret []byte) (*reference.Ref, error) {
// Extract nonce and salt stored with ciphertext
encryptedData, nonceAndSalt := encryption.Desalinate(ciphertext, encryption.NonceSize+encryption.NonceSize)
nonce, salt := nonceAndSalt[:encryption.NonceSize], nonceAndSalt[encryption.NonceSize:]
// Re-derive key based on these
secretKey, err := DeriveSecretKey(secret, salt)
if err != nil {
return nil, fmt.Errorf("SymmetricReference failed to derive secret key: %v", err)
}
// Decrypt
data, err := encryption.Decrypt(encryptedData, nonce, secretKey)
if err != nil {
return nil, fmt.Errorf("SymmetricReference failed to decrypt: %v", err)
}
// Deserialise reference
return reference.FromPlaintext(string(data)), nil
}
func DeriveSecretKey(secret, salt []byte) ([]byte, error) {
return scrypt.Key(secret, salt, 1<<scryptSecurityWorkExponent, 8, 1, encryption.KeySize)
}