github.com/decred/dcrlnd@v0.7.6/internal/snacl/snacl.go (about)

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