1// Copyright 2014 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package hkdf implements the HMAC-based Extract-and-Expand Key Derivation 6// Function (HKDF) as defined in RFC 5869. 7// 8// HKDF is a cryptographic key derivation function (KDF) with the goal of 9// expanding limited input keying material into one or more cryptographically 10// strong secret keys. 11package hkdf // import "golang.org/x/crypto/hkdf" 12 13import ( 14 "crypto/hmac" 15 "errors" 16 "hash" 17 "io" 18) 19 20// Extract generates a pseudorandom key for use with Expand from an input secret 21// and an optional independent salt. 22// 23// Only use this function if you need to reuse the extracted key with multiple 24// Expand invocations and different context values. Most common scenarios, 25// including the generation of multiple keys, should use New instead. 26func Extract(hash func() hash.Hash, secret, salt []byte) []byte { 27 if salt == nil { 28 salt = make([]byte, hash().Size()) 29 } 30 extractor := hmac.New(hash, salt) 31 extractor.Write(secret) 32 return extractor.Sum(nil) 33} 34 35type hkdf struct { 36 expander hash.Hash 37 size int 38 39 info []byte 40 counter byte 41 42 prev []byte 43 buf []byte 44} 45 46func (f *hkdf) Read(p []byte) (int, error) { 47 // Check whether enough data can be generated 48 need := len(p) 49 remains := len(f.buf) + int(255-f.counter+1)*f.size 50 if remains < need { 51 return 0, errors.New("hkdf: entropy limit reached") 52 } 53 // Read any leftover from the buffer 54 n := copy(p, f.buf) 55 p = p[n:] 56 57 // Fill the rest of the buffer 58 for len(p) > 0 { 59 f.expander.Reset() 60 f.expander.Write(f.prev) 61 f.expander.Write(f.info) 62 f.expander.Write([]byte{f.counter}) 63 f.prev = f.expander.Sum(f.prev[:0]) 64 f.counter++ 65 66 // Copy the new batch into p 67 f.buf = f.prev 68 n = copy(p, f.buf) 69 p = p[n:] 70 } 71 // Save leftovers for next run 72 f.buf = f.buf[n:] 73 74 return need, nil 75} 76 77// Expand returns a Reader, from which keys can be read, using the given 78// pseudorandom key and optional context info, skipping the extraction step. 79// 80// The pseudorandomKey should have been generated by Extract, or be a uniformly 81// random or pseudorandom cryptographically strong key. See RFC 5869, Section 82// 3.3. Most common scenarios will want to use New instead. 83func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader { 84 expander := hmac.New(hash, pseudorandomKey) 85 return &hkdf{expander, expander.Size(), info, 1, nil, nil} 86} 87 88// New returns a Reader, from which keys can be read, using the given hash, 89// secret, salt and context info. Salt and info can be nil. 90func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader { 91 prk := Extract(hash, secret, salt) 92 return Expand(hash, prk, info) 93} 94