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