import (
"bytes"
"crypto/internal/fips"
+ "crypto/internal/fips/sha256"
"errors"
"sync"
)
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
+ })
+}
--- /dev/null
+// 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])
+}