]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/internal/fips/ecdsa: add HMAC_DRBG
authorFilippo Valsorda <filippo@golang.org>
Sat, 16 Nov 2024 18:36:30 +0000 (19:36 +0100)
committerGopher Robot <gobot@golang.org>
Tue, 19 Nov 2024 23:02:24 +0000 (23:02 +0000)
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>

src/crypto/internal/fips/ecdsa/cast.go
src/crypto/internal/fips/ecdsa/hmacdrbg.go [new file with mode: 0644]

index ed6d632c1c1c5082f7f7805f646c705faf2bb684..9982e1abc3ac2fa31ec34feaf542570af7fb7b40 100644 (file)
@@ -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
+       })
+}
diff --git a/src/crypto/internal/fips/ecdsa/hmacdrbg.go b/src/crypto/internal/fips/ecdsa/hmacdrbg.go
new file mode 100644 (file)
index 0000000..dcb9cf6
--- /dev/null
@@ -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])
+}