decred.org/dcrwallet/v3@v3.1.0/wallet/internal/snacl/snacl.go (about)

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