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