1
0
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:
Filippo Valsorda 2024-11-16 19:36:30 +01:00 committed by Gopher Robot
parent 45c6cc3020
commit 5321fc265c
2 changed files with 151 additions and 0 deletions

View 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
})
}

View 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])
}