mirror of
https://github.com/golang/go
synced 2024-11-26 10:58:16 -07:00
crypto/internal/fips/ecdsa: add HMAC_DRBG
We'll use this for deterministic and hedged ECDSA. For #69536 Change-Id: Ifb3d963a084fb4914536826250589ff8862add9f Reviewed-on: https://go-review.googlesource.com/c/go/+/628680 Reviewed-by: Russ Cox <rsc@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Filippo Valsorda <filippo@golang.org>
This commit is contained in:
parent
45c6cc3020
commit
5321fc265c
@ -7,6 +7,7 @@ package ecdsa
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/internal/fips"
|
||||
"crypto/internal/fips/sha256"
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
@ -67,3 +68,33 @@ var fipsSelfTest = sync.OnceFunc(func() {
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
func init() {
|
||||
fips.CAST("HMAC_DRBG SHA2-256", func() error {
|
||||
entropy := []byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
}
|
||||
nonce := []byte{
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
}
|
||||
personalizationString := []byte{
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||
}
|
||||
want := []byte{
|
||||
0xc7, 0xff, 0x15, 0xb9, 0x68, 0x9a, 0x1d, 0x12,
|
||||
0x67, 0x67, 0x4b, 0xd4, 0x11, 0x27, 0xf3, 0xa6,
|
||||
0xa8, 0x8c, 0x1e, 0xd1, 0x58, 0xee, 0xda, 0x21,
|
||||
0x6e, 0x3c, 0xce, 0x84, 0xce, 0x45, 0x5e, 0xdb,
|
||||
}
|
||||
c := newDRBG(sha256.New, entropy, nonce, personalizationString)
|
||||
got := make([]byte, len(want))
|
||||
c.Generate(got)
|
||||
if !bytes.Equal(got, want) {
|
||||
return errors.New("unexpected result")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
120
src/crypto/internal/fips/ecdsa/hmacdrbg.go
Normal file
120
src/crypto/internal/fips/ecdsa/hmacdrbg.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2024 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 ecdsa
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/internal/fips"
|
||||
"crypto/internal/fips/hmac"
|
||||
)
|
||||
|
||||
// hmacDRBG is an SP 800-90A Rev. 1 HMAC_DRBG.
|
||||
//
|
||||
// It is only intended to be used to generate ECDSA nonces. Since it will be
|
||||
// instantiated ex-novo for each signature, its Generate function will only be
|
||||
// invoked once or twice (only for P-256, with probability 2⁻³²).
|
||||
//
|
||||
// Per Table 2, it has a reseed interval of 2^48 requests, and a maximum request
|
||||
// size of 2^19 bits (2^16 bytes, 64 KiB).
|
||||
type hmacDRBG struct {
|
||||
newHMAC func(key []byte) *hmac.HMAC
|
||||
|
||||
hK *hmac.HMAC
|
||||
V []byte
|
||||
|
||||
reseedCounter uint64
|
||||
}
|
||||
|
||||
const (
|
||||
reseedInterval = 1 << 48
|
||||
maxRequestSize = (1 << 19) / 8
|
||||
)
|
||||
|
||||
func newDRBG[H fips.Hash](hash func() H, entropy, nonce, personalizationString []byte) *hmacDRBG {
|
||||
// HMAC_DRBG_Instantiate_algorithm, per Section 10.1.2.3.
|
||||
fips.RecordApproved()
|
||||
|
||||
d := &hmacDRBG{
|
||||
newHMAC: func(key []byte) *hmac.HMAC {
|
||||
return hmac.New(hash, key)
|
||||
},
|
||||
}
|
||||
size := hash().Size()
|
||||
|
||||
// K = 0x00 0x00 0x00 ... 0x00
|
||||
K := make([]byte, size)
|
||||
|
||||
// V = 0x01 0x01 0x01 ... 0x01
|
||||
d.V = bytes.Repeat([]byte{0x01}, size)
|
||||
|
||||
// HMAC_DRBG_Update, per Section 10.1.2.2.
|
||||
// K = HMAC (K, V || 0x00 || provided_data)
|
||||
h := hmac.New(hash, K)
|
||||
h.Write(d.V)
|
||||
h.Write([]byte{0x00})
|
||||
h.Write(entropy)
|
||||
h.Write(nonce)
|
||||
h.Write(personalizationString)
|
||||
K = h.Sum(K[:0])
|
||||
// V = HMAC (K, V)
|
||||
h = hmac.New(hash, K)
|
||||
h.Write(d.V)
|
||||
d.V = h.Sum(d.V[:0])
|
||||
// K = HMAC (K, V || 0x01 || provided_data).
|
||||
h.Reset()
|
||||
h.Write(d.V)
|
||||
h.Write([]byte{0x01})
|
||||
h.Write(entropy)
|
||||
h.Write(nonce)
|
||||
h.Write(personalizationString)
|
||||
K = h.Sum(K[:0])
|
||||
// V = HMAC (K, V)
|
||||
h = hmac.New(hash, K)
|
||||
h.Write(d.V)
|
||||
d.V = h.Sum(d.V[:0])
|
||||
|
||||
d.hK = h
|
||||
d.reseedCounter = 1
|
||||
return d
|
||||
}
|
||||
|
||||
// Generate produces at most maxRequestSize bytes of random data in out.
|
||||
func (d *hmacDRBG) Generate(out []byte) {
|
||||
// HMAC_DRBG_Generate_algorithm, per Section 10.1.2.5.
|
||||
fips.RecordApproved()
|
||||
|
||||
if len(out) > maxRequestSize {
|
||||
panic("ecdsa: internal error: request size exceeds maximum")
|
||||
}
|
||||
|
||||
if d.reseedCounter > reseedInterval {
|
||||
panic("ecdsa: reseed interval exceeded")
|
||||
}
|
||||
|
||||
tlen := 0
|
||||
for tlen < len(out) {
|
||||
// V = HMAC_K(V)
|
||||
// T = T || V
|
||||
d.hK.Reset()
|
||||
d.hK.Write(d.V)
|
||||
d.V = d.hK.Sum(d.V[:0])
|
||||
tlen += copy(out[tlen:], d.V)
|
||||
}
|
||||
|
||||
// Note that if this function shows up on ECDSA-level profiles, this can be
|
||||
// optimized in the common case by deferring the rest to the next Generate
|
||||
// call, which will never come in nearly all cases.
|
||||
|
||||
// HMAC_DRBG_Update, per Section 10.1.2.2, without provided_data.
|
||||
// K = HMAC (K, V || 0x00)
|
||||
d.hK.Reset()
|
||||
d.hK.Write(d.V)
|
||||
d.hK.Write([]byte{0x00})
|
||||
K := d.hK.Sum(nil)
|
||||
// V = HMAC (K, V)
|
||||
d.hK = d.newHMAC(K)
|
||||
d.hK.Write(d.V)
|
||||
d.V = d.hK.Sum(d.V[:0])
|
||||
}
|
Loading…
Reference in New Issue
Block a user