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