github.com/icodeface/tls@v0.0.0-20230910023335-34df9250cd12/internal/x/crypto/hkdf/hkdf.go (about) 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. 11 package hkdf 12 13 import ( 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. 26 func 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 35 type 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 46 func (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. 83 func 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. 90 func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader { 91 prk := Extract(hash, secret, salt) 92 return Expand(hash, prk, info) 93 }