snacl.go raw

   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