HKDF is commonly used to read keys that are the the same length (or smaller) than the size of the hash digest, which means the loop inside Read only runs once. In that case, calling Reset is unnecesssary overhead. name old time/op new time/op delta 16ByteMD5Single-8 1.39µs ± 1% 1.22µs ± 0% -11.95% (p=0.000 n=10+9) 20ByteSHA1Single-8 826ns ± 0% 746ns ± 0% -9.70% (p=0.000 n=9+10) 32ByteSHA256Single-8 838ns ± 1% 744ns ± 0% -11.29% (p=0.000 n=10+10) 64ByteSHA512Single-8 5.12µs ± 0% 4.57µs ± 0% -10.78% (p=0.000 n=8+10) 8ByteMD5Stream-8 137ns ± 0% 138ns ± 0% +0.27% (p=0.009 n=9+6) 16ByteMD5Stream-8 264ns ± 0% 265ns ± 0% +0.29% (p=0.000 n=10+10) 8ByteSHA1Stream-8 64.1ns ± 0% 64.4ns ± 0% +0.60% (p=0.000 n=9+9) 20ByteSHA1Stream-8 145ns ± 0% 146ns ± 1% +0.69% (p=0.000 n=9+10) 8ByteSHA256Stream-8 42.9ns ± 1% 43.1ns ± 0% +0.48% (p=0.005 n=10+10) 32ByteSHA256Stream-8 151ns ± 0% 152ns ± 0% +0.35% (p=0.006 n=10+8) 8ByteSHA512Stream-8 139ns ± 0% 139ns ± 0% +0.08% (p=0.035 n=9+10) 64ByteSHA512Stream-8 1.07µs ± 0% 1.07µs ± 0% +0.33% (p=0.000 n=9+10) name old speed new speed delta 16ByteMD5Single-8 11.6MB/s ± 0% 13.1MB/s ± 0% +13.50% (p=0.000 n=9+9) 20ByteSHA1Single-8 24.2MB/s ± 0% 26.8MB/s ± 0% +10.75% (p=0.000 n=9+10) 32ByteSHA256Single-8 38.2MB/s ± 1% 43.0MB/s ± 0% +12.72% (p=0.000 n=10+10) 64ByteSHA512Single-8 12.5MB/s ± 0% 14.0MB/s ± 0% +12.06% (p=0.000 n=8+10) 8ByteMD5Stream-8 58.2MB/s ± 0% 58.1MB/s ± 0% -0.27% (p=0.004 n=9+9) 16ByteMD5Stream-8 60.6MB/s ± 0% 60.5MB/s ± 0% -0.27% (p=0.000 n=9+10) 8ByteSHA1Stream-8 125MB/s ± 0% 124MB/s ± 0% -0.59% (p=0.000 n=9+9) 20ByteSHA1Stream-8 138MB/s ± 0% 137MB/s ± 1% -0.69% (p=0.000 n=9+10) 8ByteSHA256Stream-8 186MB/s ± 1% 185MB/s ± 0% -0.47% (p=0.005 n=10+10) 32ByteSHA256Stream-8 211MB/s ± 0% 211MB/s ± 0% -0.37% (p=0.003 n=10+8) 8ByteSHA512Stream-8 57.4MB/s ± 1% 57.4MB/s ± 0% ~ (p=0.137 n=10+10) 64ByteSHA512Stream-8 59.9MB/s ± 0% 59.7MB/s ± 0% -0.33% (p=0.000 n=9+10) name old alloc/op new alloc/op delta 16ByteMD5Single-8 1.17kB ± 0% 0.98kB ± 0% -16.42% (p=0.000 n=10+10) 20ByteSHA1Single-8 1.25kB ± 0% 1.06kB ± 0% -15.37% (p=0.000 n=10+10) 32ByteSHA256Single-8 1.36kB ± 0% 1.14kB ± 0% -16.46% (p=0.000 n=10+10) 64ByteSHA512Single-8 2.26kB ± 0% 1.84kB ± 0% -18.43% (p=0.000 n=10+10) 8ByteMD5Stream-8 2.00B ± 0% 2.00B ± 0% ~ (all equal) 16ByteMD5Stream-8 5.00B ± 0% 5.00B ± 0% ~ (all equal) 8ByteSHA1Stream-8 2.00B ± 0% 2.00B ± 0% ~ (all equal) 20ByteSHA1Stream-8 5.00B ± 0% 5.00B ± 0% ~ (all equal) 8ByteSHA256Stream-8 1.00B ± 0% 1.00B ± 0% ~ (all equal) 32ByteSHA256Stream-8 6.00B ± 0% 6.00B ± 0% ~ (all equal) 8ByteSHA512Stream-8 1.00B ± 0% 1.00B ± 0% ~ (all equal) 64ByteSHA512Stream-8 9.00B ± 0% 9.00B ± 0% ~ (all equal) name old allocs/op new allocs/op delta 16ByteMD5Single-8 16.0 ± 0% 14.0 ± 0% -12.50% (p=0.000 n=10+10) 20ByteSHA1Single-8 16.0 ± 0% 14.0 ± 0% -12.50% (p=0.000 n=10+10) 32ByteSHA256Single-8 16.0 ± 0% 14.0 ± 0% -12.50% (p=0.000 n=10+10) 64ByteSHA512Single-8 16.0 ± 0% 14.0 ± 0% -12.50% (p=0.000 n=10+10) 8ByteMD5Stream-8 0.00 0.00 ~ (all equal) 16ByteMD5Stream-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) 8ByteSHA1Stream-8 0.00 0.00 ~ (all equal) 20ByteSHA1Stream-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) 8ByteSHA256Stream-8 0.00 0.00 ~ (all equal) 32ByteSHA256Stream-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) 8ByteSHA512Stream-8 0.00 0.00 ~ (all equal) 64ByteSHA512Stream-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) Change-Id: I08e5deb7045ce9aa63428e0ac7ae6140226414b4 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/423414 Reviewed-by: Roland Shoemaker <roland@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
96 lines
2.7 KiB
Go
96 lines
2.7 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package hkdf implements the HMAC-based Extract-and-Expand Key Derivation
|
|
// Function (HKDF) as defined in RFC 5869.
|
|
//
|
|
// HKDF is a cryptographic key derivation function (KDF) with the goal of
|
|
// expanding limited input keying material into one or more cryptographically
|
|
// strong secret keys.
|
|
package hkdf // import "golang.org/x/crypto/hkdf"
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"errors"
|
|
"hash"
|
|
"io"
|
|
)
|
|
|
|
// Extract generates a pseudorandom key for use with Expand from an input secret
|
|
// and an optional independent salt.
|
|
//
|
|
// Only use this function if you need to reuse the extracted key with multiple
|
|
// Expand invocations and different context values. Most common scenarios,
|
|
// including the generation of multiple keys, should use New instead.
|
|
func Extract(hash func() hash.Hash, secret, salt []byte) []byte {
|
|
if salt == nil {
|
|
salt = make([]byte, hash().Size())
|
|
}
|
|
extractor := hmac.New(hash, salt)
|
|
extractor.Write(secret)
|
|
return extractor.Sum(nil)
|
|
}
|
|
|
|
type hkdf struct {
|
|
expander hash.Hash
|
|
size int
|
|
|
|
info []byte
|
|
counter byte
|
|
|
|
prev []byte
|
|
buf []byte
|
|
}
|
|
|
|
func (f *hkdf) Read(p []byte) (int, error) {
|
|
// Check whether enough data can be generated
|
|
need := len(p)
|
|
remains := len(f.buf) + int(255-f.counter+1)*f.size
|
|
if remains < need {
|
|
return 0, errors.New("hkdf: entropy limit reached")
|
|
}
|
|
// Read any leftover from the buffer
|
|
n := copy(p, f.buf)
|
|
p = p[n:]
|
|
|
|
// Fill the rest of the buffer
|
|
for len(p) > 0 {
|
|
if f.counter > 1 {
|
|
f.expander.Reset()
|
|
}
|
|
f.expander.Write(f.prev)
|
|
f.expander.Write(f.info)
|
|
f.expander.Write([]byte{f.counter})
|
|
f.prev = f.expander.Sum(f.prev[:0])
|
|
f.counter++
|
|
|
|
// Copy the new batch into p
|
|
f.buf = f.prev
|
|
n = copy(p, f.buf)
|
|
p = p[n:]
|
|
}
|
|
// Save leftovers for next run
|
|
f.buf = f.buf[n:]
|
|
|
|
return need, nil
|
|
}
|
|
|
|
// Expand returns a Reader, from which keys can be read, using the given
|
|
// pseudorandom key and optional context info, skipping the extraction step.
|
|
//
|
|
// The pseudorandomKey should have been generated by Extract, or be a uniformly
|
|
// random or pseudorandom cryptographically strong key. See RFC 5869, Section
|
|
// 3.3. Most common scenarios will want to use New instead.
|
|
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader {
|
|
expander := hmac.New(hash, pseudorandomKey)
|
|
return &hkdf{expander, expander.Size(), info, 1, nil, nil}
|
|
}
|
|
|
|
// New returns a Reader, from which keys can be read, using the given hash,
|
|
// secret, salt and context info. Salt and info can be nil.
|
|
func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader {
|
|
prk := Extract(hash, secret, salt)
|
|
return Expand(hash, prk, info)
|
|
}
|