-
Notifications
You must be signed in to change notification settings - Fork 342
/
encryption.go
103 lines (86 loc) · 2.31 KB
/
encryption.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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package crypt
import (
"crypto/rand"
"errors"
"io"
"golang.org/x/crypto/nacl/secretbox"
"golang.org/x/crypto/scrypt"
)
const (
KeySaltBytes = 8
KeySizeBytes = 32
NonceSizeBytes = 24
)
type SecretStore interface {
SharedSecret() []byte
Encrypt(data []byte) ([]byte, error)
Decrypt(encrypted []byte) ([]byte, error)
}
type NaclSecretStore struct {
secret []byte
}
var (
ErrFailDecrypt = errors.New("could not decrypt value")
)
func NewSecretStore(secret []byte) *NaclSecretStore {
return &NaclSecretStore{secret: secret}
}
func (a *NaclSecretStore) SharedSecret() []byte {
return a.secret
}
func (a *NaclSecretStore) kdf(storedSalt []byte) (key [KeySizeBytes]byte, salt [KeySaltBytes]byte, err error) {
if storedSalt != nil {
copy(salt[:], storedSalt)
} else if _, err = io.ReadFull(rand.Reader, salt[:]); err != nil {
return
}
// scrypt's N, r & p, benchmarked to run at about 1ms, since it's in the critical path.
// fair trade-off for a high throughput low latency system
const (
N = 512
r = 8
p = 1
keyLen = 32
)
keySlice, err := scrypt.Key(a.secret, salt[:], N, r, p, keyLen)
if err != nil {
return
}
copy(key[:], keySlice)
return
}
func (a *NaclSecretStore) Encrypt(data []byte) ([]byte, error) {
// generate a random nonce
var nonce [NonceSizeBytes]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
return nil, err
}
// derive a key from the stored secret, generating a new random salt
key, salt, err := a.kdf(nil)
if err != nil {
return nil, err
}
// use nonce and derived key to encrypt data
encrypted := secretbox.Seal(nonce[:], data, &nonce, &key)
// kdf salt (8) + nonce (24) + encrypted data (rest)
return append(salt[:], encrypted...), nil
}
func (a *NaclSecretStore) Decrypt(encrypted []byte) ([]byte, error) {
// extract salt
var salt [KeySaltBytes]byte
copy(salt[:], encrypted[:KeySaltBytes])
// derive encryption key from salt and stored secret
key, _, err := a.kdf(salt[:])
if err != nil {
return nil, err
}
// extract nonce
var decryptNonce [NonceSizeBytes]byte
copy(decryptNonce[:], encrypted[KeySaltBytes:KeySaltBytes+NonceSizeBytes])
// decrypt the rest
decrypted, ok := secretbox.Open(nil, encrypted[KeySaltBytes+NonceSizeBytes:], &decryptNonce, &key)
if !ok {
return nil, ErrFailDecrypt
}
return decrypted, nil
}