1 package snacl
2 3 import (
4 "crypto/rand"
5 "crypto/sha256"
6 "crypto/subtle"
7 "encoding/binary"
8 "errors"
9 "io"
10 "runtime/debug"
11 12 "github.com/btcsuite/golangcrypto/nacl/secretbox"
13 "github.com/btcsuite/golangcrypto/scrypt"
14 15 "github.com/p9c/p9/pkg/util/zero"
16 )
17 18 var (
19 prng = rand.Reader
20 21 // ErrInvalidPassword ...
22 ErrInvalidPassword = errors.New("invalid password")
23 // ErrMalformed ...
24 ErrMalformed = errors.New("malformed data")
25 // ErrDecryptFailed ...
26 ErrDecryptFailed = errors.New("unable to decrypt")
27 )
28 29 // Various constants needed for encryption scheme.
30 const (
31 // Overhead const here expose secretbox's for convenience.
32 Overhead = secretbox.Overhead
33 keySize = 32
34 NonceSize = 24
35 DefaultN = 16384 // 2^14
36 DefaultR = 8
37 DefaultP = 1
38 )
39 40 // CryptoKey represents a secret key which can be used to encrypt and decrypt data.
41 type CryptoKey [keySize]byte
42 43 // Encrypt encrypts the passed data.
44 func (ck *CryptoKey) Encrypt(in []byte) ([]byte, error) {
45 var nonce [NonceSize]byte
46 _, e := io.ReadFull(prng, nonce[:])
47 if e != nil {
48 E.Ln(e)
49 return nil, e
50 }
51 blob := secretbox.Seal(nil, in, &nonce, (*[keySize]byte)(ck))
52 return append(nonce[:], blob...), nil
53 }
54 55 // Decrypt decrypts the passed data. The must be the output of the Encrypt function.
56 func (ck *CryptoKey) Decrypt(in []byte) ([]byte, error) {
57 if len(in) < NonceSize {
58 return nil, ErrMalformed
59 }
60 var nonce [NonceSize]byte
61 copy(nonce[:], in[:NonceSize])
62 blob := in[NonceSize:]
63 opened, ok := secretbox.Open(nil, blob, &nonce, (*[keySize]byte)(ck))
64 if !ok {
65 return nil, ErrDecryptFailed
66 }
67 return opened, nil
68 }
69 70 // Zero clears the key by manually zeroing all memory. This is for security conscience application which wish to zero
71 // the memory after they've used it rather than waiting until it's reclaimed by the garbage collector. The key is no
72 // longer usable after this call.
73 func (ck *CryptoKey) Zero() {
74 zero.Bytea32((*[keySize]byte)(ck))
75 }
76 77 // GenerateCryptoKey generates a new crypotgraphically random key.
78 func GenerateCryptoKey() (*CryptoKey, error) {
79 var key CryptoKey
80 _, e := io.ReadFull(prng, key[:])
81 if e != nil {
82 E.Ln(e)
83 return nil, e
84 }
85 return &key, nil
86 }
87 88 // Parameters are not secret and can be stored in plain text.
89 type Parameters struct {
90 Salt [keySize]byte
91 Digest [sha256.Size]byte
92 N int
93 R int
94 P int
95 }
96 97 // SecretKey houses a crypto key and the parameters needed to derive it from a passphrase. It should only be used in
98 // memory.
99 type SecretKey struct {
100 Key *CryptoKey
101 Parameters Parameters
102 }
103 104 // deriveKey fills out the Key field.
105 func (sk *SecretKey) deriveKey(password *[]byte) (e error) {
106 key, e := scrypt.Key(*password, sk.Parameters.Salt[:],
107 sk.Parameters.N,
108 sk.Parameters.R,
109 sk.Parameters.P,
110 len(sk.Key),
111 )
112 if e != nil {
113 E.Ln(e)
114 return e
115 }
116 copy(sk.Key[:], key)
117 zero.Bytes(key)
118 // I'm not a fan of forced garbage collections, but scrypt allocates a ton of memory and calling it back to back
119 // without a GC cycle in between means you end up needing twice the amount of memory.
120 //
121 // For example, if your scrypt parameters are such that you require 1GB and you call it twice in a row, without this
122 // you end up allocating 2GB since the first GB probably hasn't been released yet.
123 debug.FreeOSMemory()
124 return nil
125 }
126 127 // Marshal returns the Parameters field marshalled into a format suitable for storage.
128 //
129 // This result of this can be stored in clear text.
130 func (sk *SecretKey) Marshal() []byte {
131 params := &sk.Parameters
132 // The marshalled format for the the netparams is as follows:
133 //
134 // <salt><digest><N><R><P>
135 //
136 // KeySize + sha256.Size + N (8 bytes) + R (8 bytes) + P (8 bytes)
137 marshalled := make([]byte, keySize+sha256.Size+24)
138 b := marshalled
139 copy(b[:keySize], params.Salt[:])
140 b = b[keySize:]
141 copy(b[:sha256.Size], params.Digest[:])
142 b = b[sha256.Size:]
143 binary.LittleEndian.PutUint64(b[:8], uint64(params.N))
144 b = b[8:]
145 binary.LittleEndian.PutUint64(b[:8], uint64(params.R))
146 b = b[8:]
147 binary.LittleEndian.PutUint64(b[:8], uint64(params.P))
148 return marshalled
149 }
150 151 // Unmarshal unmarshalls the parameters needed to derive the secret key from a passphrase into sk.
152 func (sk *SecretKey) Unmarshal(marshalled []byte) (e error) {
153 if sk.Key == nil {
154 sk.Key = (*CryptoKey)(&[keySize]byte{})
155 }
156 // The marshalled format for the the netparams is as follows:
157 //
158 // <salt><digest><N><R><P>
159 //
160 // KeySize + sha256.Size + N (8 bytes) + R (8 bytes) + P (8 bytes)
161 if len(marshalled) != keySize+sha256.Size+24 {
162 return ErrMalformed
163 }
164 params := &sk.Parameters
165 copy(params.Salt[:], marshalled[:keySize])
166 marshalled = marshalled[keySize:]
167 copy(params.Digest[:], marshalled[:sha256.Size])
168 marshalled = marshalled[sha256.Size:]
169 params.N = int(binary.LittleEndian.Uint64(marshalled[:8]))
170 marshalled = marshalled[8:]
171 params.R = int(binary.LittleEndian.Uint64(marshalled[:8]))
172 marshalled = marshalled[8:]
173 params.P = int(binary.LittleEndian.Uint64(marshalled[:8]))
174 return nil
175 }
176 177 // Zero zeroes the underlying secret key while leaving the parameters intact.
178 //
179 // This effectively makes the key unusable until it is derived again via the DeriveKey function.
180 func (sk *SecretKey) Zero() {
181 sk.Key.Zero()
182 }
183 184 // DeriveKey derives the underlying secret key and ensures it matches the expected digest.
185 //
186 // This should only be called after previously calling the Zero function or on an initial Unmarshal.
187 func (sk *SecretKey) DeriveKey(password *[]byte) (e error) {
188 if e = sk.deriveKey(password); E.Chk(e) {
189 return
190 }
191 // verify password
192 digest := sha256.Sum256(sk.Key[:])
193 if subtle.ConstantTimeCompare(digest[:], sk.Parameters.Digest[:]) != 1 {
194 return ErrInvalidPassword
195 }
196 return nil
197 }
198 199 // Encrypt encrypts in bytes and returns a JSON blob.
200 func (sk *SecretKey) Encrypt(in []byte) ([]byte, error) {
201 return sk.Key.Encrypt(in)
202 }
203 204 // Decrypt takes in a JSON blob and returns it's decrypted form.
205 func (sk *SecretKey) Decrypt(in []byte) ([]byte, error) {
206 return sk.Key.Decrypt(in)
207 }
208 209 // NewSecretKey returns a SecretKey structure based on the passed parameters.
210 func NewSecretKey(password *[]byte, N, r, p int) (sk *SecretKey, e error) {
211 sk = &SecretKey{
212 Key: (*CryptoKey)(&[keySize]byte{}),
213 }
214 // setup parameters
215 sk.Parameters.N = N
216 sk.Parameters.R = r
217 sk.Parameters.P = p
218 _, e = io.ReadFull(prng, sk.Parameters.Salt[:])
219 if e != nil {
220 E.Ln(e)
221 return nil, e
222 }
223 // derive key
224 e = sk.deriveKey(password)
225 if e != nil {
226 E.Ln(e)
227 return nil, e
228 }
229 // store digest
230 sk.Parameters.Digest = sha256.Sum256(sk.Key[:])
231 return sk, nil
232 }
233