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 }