github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/crypto/kdf.go (about)

     1  package crypto
     2  
     3  import (
     4  	"crypto/rand"
     5  	"time"
     6  
     7  	"github.com/restic/restic/internal/errors"
     8  
     9  	sscrypt "github.com/elithrar/simple-scrypt"
    10  	"golang.org/x/crypto/scrypt"
    11  )
    12  
    13  const saltLength = 64
    14  
    15  // Params are the default parameters used for the key derivation function KDF().
    16  type Params struct {
    17  	N int
    18  	R int
    19  	P int
    20  }
    21  
    22  // DefaultKDFParams are the default parameters used for Calibrate and KDF().
    23  var DefaultKDFParams = Params{
    24  	N: sscrypt.DefaultParams.N,
    25  	R: sscrypt.DefaultParams.R,
    26  	P: sscrypt.DefaultParams.P,
    27  }
    28  
    29  // Calibrate determines new KDF parameters for the current hardware.
    30  func Calibrate(timeout time.Duration, memory int) (Params, error) {
    31  	defaultParams := sscrypt.Params{
    32  		N:       DefaultKDFParams.N,
    33  		R:       DefaultKDFParams.R,
    34  		P:       DefaultKDFParams.P,
    35  		DKLen:   sscrypt.DefaultParams.DKLen,
    36  		SaltLen: sscrypt.DefaultParams.SaltLen,
    37  	}
    38  
    39  	params, err := sscrypt.Calibrate(timeout, memory, defaultParams)
    40  	if err != nil {
    41  		return DefaultKDFParams, errors.Wrap(err, "scrypt.Calibrate")
    42  	}
    43  
    44  	return Params{
    45  		N: params.N,
    46  		R: params.R,
    47  		P: params.P,
    48  	}, nil
    49  }
    50  
    51  // KDF derives encryption and message authentication keys from the password
    52  // using the supplied parameters N, R and P and the Salt.
    53  func KDF(p Params, salt []byte, password string) (*Key, error) {
    54  	if len(salt) != saltLength {
    55  		return nil, errors.Errorf("scrypt() called with invalid salt bytes (len %d)", len(salt))
    56  	}
    57  
    58  	// make sure we have valid parameters
    59  	params := sscrypt.Params{
    60  		N:       p.N,
    61  		R:       p.R,
    62  		P:       p.P,
    63  		DKLen:   sscrypt.DefaultParams.DKLen,
    64  		SaltLen: len(salt),
    65  	}
    66  
    67  	if err := params.Check(); err != nil {
    68  		return nil, errors.Wrap(err, "Check")
    69  	}
    70  
    71  	derKeys := &Key{}
    72  
    73  	keybytes := macKeySize + aesKeySize
    74  	scryptKeys, err := scrypt.Key([]byte(password), salt, p.N, p.R, p.P, keybytes)
    75  	if err != nil {
    76  		return nil, errors.Wrap(err, "scrypt.Key")
    77  	}
    78  
    79  	if len(scryptKeys) != keybytes {
    80  		return nil, errors.Errorf("invalid numbers of bytes expanded from scrypt(): %d", len(scryptKeys))
    81  	}
    82  
    83  	// first 32 byte of scrypt output is the encryption key
    84  	copy(derKeys.EncryptionKey[:], scryptKeys[:aesKeySize])
    85  
    86  	// next 32 byte of scrypt output is the mac key, in the form k||r
    87  	macKeyFromSlice(&derKeys.MACKey, scryptKeys[aesKeySize:])
    88  
    89  	return derKeys, nil
    90  }
    91  
    92  // NewSalt returns new random salt bytes to use with KDF(). If NewSalt returns
    93  // an error, this is a grave situation and the program must abort and terminate.
    94  func NewSalt() ([]byte, error) {
    95  	buf := make([]byte, saltLength)
    96  	n, err := rand.Read(buf)
    97  	if n != saltLength || err != nil {
    98  		panic("unable to read enough random bytes for new salt")
    99  	}
   100  
   101  	return buf, nil
   102  }