-
-
Notifications
You must be signed in to change notification settings - Fork 25
/
secretbox.go
76 lines (66 loc) · 2.31 KB
/
secretbox.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
package keys
import (
"bytes"
"github.com/pkg/errors"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/nacl/secretbox"
)
// SecretBoxSeal encrypts using a key.
// It prepends a 24 byte nonce to the the encrypted bytes.
func SecretBoxSeal(b []byte, secretKey *[32]byte) []byte {
nonce := Rand24()
return secretBoxSeal(b, nonce, secretKey)
}
func secretBoxSeal(b []byte, nonce *[24]byte, secretKey *[32]byte) []byte {
encrypted := secretbox.Seal(nil, b, nonce, secretKey)
encrypted = append(nonce[:], encrypted...)
return encrypted
}
// SecretBoxOpen decrypt using a key.
// It assumes a 24 byte nonce before the encrypted bytes.
func SecretBoxOpen(encrypted []byte, secretKey *[32]byte) ([]byte, error) {
return secretBoxOpen(encrypted, secretKey)
}
func secretBoxOpen(encrypted []byte, secretKey *[32]byte) ([]byte, error) {
if len(encrypted) < 24 {
return nil, errors.Errorf("not enough bytes")
}
var nonce [24]byte
copy(nonce[:], encrypted[:24])
encrypted = encrypted[24:]
b, ok := secretbox.Open(nil, encrypted, &nonce, secretKey)
if !ok {
return nil, errors.Errorf("secretbox open failed")
}
return b, nil
}
// EncryptWithPassword encrypts bytes with a password.
// Uses argon2.IDKey(password, salt, 1, 64*1024, 4, 32) with 16 byte salt.
// The salt bytes are prepended to the encrypted bytes.
// This uses nacl.secretbox, so the bytes/message should be small.
// If you need to encrypt large amounts of data, use Saltpack instead
// (TODO: More details here).
func EncryptWithPassword(b []byte, password string) []byte {
salt := Rand16()
key := argon2.IDKey([]byte(password), salt[:], 1, 64*1024, 4, 32)
encrypted := SecretBoxSeal(b, Bytes32(key))
return bytesJoin(salt[:], encrypted)
}
// DecryptWithPassword decrypts bytes using a password.
// It assumes a 16 byte salt before the encrypted bytes.
func DecryptWithPassword(encrypted []byte, password string) ([]byte, error) {
if len(encrypted) < 16 {
return nil, errors.Errorf("failed to decrypt with a password: not enough bytes")
}
salt := encrypted[0:16]
b := encrypted[16:]
key := argon2.IDKey([]byte(password), salt[:], 1, 64*1024, 4, 32)
out, err := SecretBoxOpen(b, Bytes32(key))
if err != nil {
return nil, errors.Wrapf(err, "failed to decrypt with a password")
}
return out, nil
}
func bytesJoin(b ...[]byte) []byte {
return bytes.Join(b, []byte{})
}