1// This package is used to implement Key Derivation Functions (KDF) 2// based on the recommendations of NIST SP 800-108. These are useful 3// for generating unique-per-transaction keys, or situations in which 4// a key hierarchy may be useful. 5package kdf 6 7import ( 8 "crypto/hmac" 9 "crypto/sha256" 10 "encoding/binary" 11 "fmt" 12) 13 14// PRF is a pseudo-random function that takes a key or seed, 15// as well as additional binary data and generates output that is 16// indistinguishable from random. Examples are cryptographic hash 17// functions or block ciphers. 18type PRF func([]byte, []byte) ([]byte, error) 19 20// CounterMode implements the counter mode KDF that uses a pseudo-random-function (PRF) 21// along with a counter to generate derived keys. The KDF takes a base key 22// a derivation context, and the required number of output bits. 23func CounterMode(prf PRF, prfLen uint32, key []byte, context []byte, bits uint32) ([]byte, error) { 24 // Ensure the PRF is byte aligned 25 if prfLen%8 != 0 { 26 return nil, fmt.Errorf("PRF must be byte aligned") 27 } 28 29 // Ensure the bits required are byte aligned 30 if bits%8 != 0 { 31 return nil, fmt.Errorf("bits required must be byte aligned") 32 } 33 34 // Determine the number of rounds required 35 rounds := bits / prfLen 36 if bits%prfLen != 0 { 37 rounds++ 38 } 39 40 // Allocate and setup the input 41 input := make([]byte, 4+len(context)+4) 42 copy(input[4:], context) 43 binary.BigEndian.PutUint32(input[4+len(context):], bits) 44 45 // Iteratively generate more key material 46 var out []byte 47 var i uint32 48 for i = 0; i < rounds; i++ { 49 // Update the counter in the input string 50 binary.BigEndian.PutUint32(input[:4], i) 51 52 // Compute more key material 53 part, err := prf(key, input) 54 if err != nil { 55 return nil, err 56 } 57 if uint32(len(part)*8) != prfLen { 58 return nil, fmt.Errorf("PRF length mis-match (%d vs %d)", len(part)*8, prfLen) 59 } 60 out = append(out, part...) 61 } 62 63 // Return the desired number of output bytes 64 return out[:bits/8], nil 65} 66 67const ( 68 // HMACSHA256PRFLen is the length of output from HMACSHA256PRF 69 HMACSHA256PRFLen uint32 = 256 70) 71 72// HMACSHA256PRF is a pseudo-random-function (PRF) that uses an HMAC-SHA256 73func HMACSHA256PRF(key []byte, data []byte) ([]byte, error) { 74 hash := hmac.New(sha256.New, key) 75 hash.Write(data) 76 return hash.Sum(nil), nil 77} 78