mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
crypto: use provided random Reader in FIPS mode
This removes the difference in behavior between FIPS mode on and off. Instead of the sentinel type we could have moved the Reader to the drbg package and checked for equality, but then we would have locked the crypto/rand.Reader implementation to the one in the FIPS module (which we might have to support for years). In internal/ed25519.GenerateKey we remove the random parameter entirely, since that function is not actually used by crypto/ed25519.GenerateKey, which instead commits to being deterministic. Fixes #70772 Change-Id: Ic1c7ca2c1cd59eb9cd090a8b235c0ce218921ac5 Reviewed-on: https://go-review.googlesource.com/c/go/+/635195 Reviewed-by: Roland Shoemaker <roland@golang.org> Auto-Submit: Filippo Valsorda <filippo@golang.org> Reviewed-by: Russ Cox <rsc@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
3104b6adbb
commit
c93477b5e5
15 changed files with 94 additions and 84 deletions
|
|
@ -8,6 +8,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/internal/boring"
|
||||
"crypto/internal/fips140/ecdh"
|
||||
"crypto/internal/fips140only"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
|
@ -43,6 +44,10 @@ func (c *nistCurve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
|||
return k, nil
|
||||
}
|
||||
|
||||
if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) {
|
||||
return nil, errors.New("crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-only mode")
|
||||
}
|
||||
|
||||
privateKey, err := c.generate(rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"crypto/internal/boring"
|
||||
"crypto/internal/boring/bbig"
|
||||
"crypto/internal/fips140/ecdsa"
|
||||
"crypto/internal/fips140only"
|
||||
"crypto/internal/randutil"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
|
|
@ -182,6 +183,9 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
|
|||
}
|
||||
|
||||
func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], rand io.Reader) (*PrivateKey, error) {
|
||||
if fips140only.Enabled && fips140only.ApprovedRandomReader(rand) {
|
||||
return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
|
||||
}
|
||||
privateKey, err := ecdsa.GenerateKey(c, rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -228,6 +232,9 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
func signFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey, rand io.Reader, hash []byte) ([]byte, error) {
|
||||
if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) {
|
||||
return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
|
||||
}
|
||||
// privateKeyToFIPS is very slow in FIPS mode because it performs a
|
||||
// Sign+Verify cycle per FIPS 140-3 IG 10.3.A. We should find a way to cache
|
||||
// it or attach it to the PrivateKey.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ package drbg
|
|||
import (
|
||||
"crypto/internal/entropy"
|
||||
"crypto/internal/fips140"
|
||||
"crypto/internal/randutil"
|
||||
"crypto/internal/sysrand"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
|
@ -56,3 +58,38 @@ func Read(b []byte) {
|
|||
b = b[size:]
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultReader is a sentinel type, embedded in the default
|
||||
// [crypto/rand.Reader], used to recognize it when passed to
|
||||
// APIs that accept a rand io.Reader.
|
||||
type DefaultReader interface{ defaultReader() }
|
||||
|
||||
// ReadWithReader uses Reader to fill b with cryptographically secure random
|
||||
// bytes. It is intended for use in APIs that expose a rand io.Reader.
|
||||
//
|
||||
// If Reader is not the default Reader from crypto/rand,
|
||||
// [randutil.MaybeReadByte] and [fips140.RecordNonApproved] are called.
|
||||
func ReadWithReader(r io.Reader, b []byte) error {
|
||||
if _, ok := r.(DefaultReader); ok {
|
||||
Read(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
fips140.RecordNonApproved()
|
||||
randutil.MaybeReadByte(r)
|
||||
_, err := io.ReadFull(r, b)
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadWithReaderDeterministic is like ReadWithReader, but it doesn't call
|
||||
// [randutil.MaybeReadByte] on non-default Readers.
|
||||
func ReadWithReaderDeterministic(r io.Reader, b []byte) error {
|
||||
if _, ok := r.(DefaultReader); ok {
|
||||
Read(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
fips140.RecordNonApproved()
|
||||
_, err := io.ReadFull(r, b)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"crypto/internal/fips140/drbg"
|
||||
"crypto/internal/fips140/nistec"
|
||||
"crypto/internal/fips140deps/byteorder"
|
||||
"crypto/internal/randutil"
|
||||
"errors"
|
||||
"io"
|
||||
"math/bits"
|
||||
|
|
@ -137,8 +136,6 @@ var p521Order = []byte{0x01, 0xff,
|
|||
}
|
||||
|
||||
// GenerateKey generates a new ECDSA private key pair for the specified curve.
|
||||
//
|
||||
// In FIPS mode, rand is ignored.
|
||||
func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
|
||||
fips140.RecordApproved()
|
||||
// This procedure is equivalent to Key Pair Generation by Testing
|
||||
|
|
@ -146,18 +143,13 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
|
|||
|
||||
for {
|
||||
key := make([]byte, len(c.N))
|
||||
if fips140.Enabled {
|
||||
drbg.Read(key)
|
||||
} else {
|
||||
randutil.MaybeReadByte(rand)
|
||||
if _, err := io.ReadFull(rand, key); err != nil {
|
||||
if err := drbg.ReadWithReader(rand, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// In tests, rand will return all zeros and NewPrivateKey will reject
|
||||
// the zero key as it generates the identity as a public key. This also
|
||||
// makes this function consistent with crypto/elliptic.GenerateKey.
|
||||
key[1] ^= 0x42
|
||||
}
|
||||
|
||||
// Mask off any excess bits if the size of the underlying field is not a
|
||||
// whole number of bytes, which is only the case for P-521.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ func testHash() []byte {
|
|||
func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) error {
|
||||
return fips140.PCT("ECDSA PCT", func() error {
|
||||
hash := testHash()
|
||||
sig, err := Sign(c, sha512.New, k, nil, hash)
|
||||
drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil)
|
||||
sig, err := sign(c, k, drbg, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"crypto/internal/fips140/bigmod"
|
||||
"crypto/internal/fips140/drbg"
|
||||
"crypto/internal/fips140/nistec"
|
||||
"crypto/internal/randutil"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
|
|
@ -187,20 +186,11 @@ func NewPublicKey[P Point[P]](c *Curve[P], Q []byte) (*PublicKey, error) {
|
|||
}
|
||||
|
||||
// GenerateKey generates a new ECDSA private key pair for the specified curve.
|
||||
//
|
||||
// In FIPS mode, rand is ignored.
|
||||
func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
|
||||
fips140.RecordApproved()
|
||||
|
||||
k, Q, err := randomPoint(c, func(b []byte) error {
|
||||
if fips140.Enabled {
|
||||
drbg.Read(b)
|
||||
return nil
|
||||
} else {
|
||||
randutil.MaybeReadByte(rand)
|
||||
_, err := io.ReadFull(rand, b)
|
||||
return err
|
||||
}
|
||||
return drbg.ReadWithReader(rand, b)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -281,8 +271,6 @@ type Signature struct {
|
|||
// the hash function H) using the private key, priv. If the hash is longer than
|
||||
// the bit-length of the private key's curve order, the hash will be truncated
|
||||
// to that length.
|
||||
//
|
||||
// The signature is randomized. If FIPS mode is enabled, rand is ignored.
|
||||
func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey, rand io.Reader, hash []byte) (*Signature, error) {
|
||||
if priv.pub.curve != c.curve {
|
||||
return nil, errors.New("ecdsa: private key does not match curve")
|
||||
|
|
@ -296,14 +284,9 @@ func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey,
|
|||
// advantage of closely resembling Deterministic ECDSA.
|
||||
|
||||
Z := make([]byte, len(priv.d))
|
||||
if fips140.Enabled {
|
||||
drbg.Read(Z)
|
||||
} else {
|
||||
randutil.MaybeReadByte(rand)
|
||||
if _, err := io.ReadFull(rand, Z); err != nil {
|
||||
if err := drbg.ReadWithReader(rand, Z); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/cfrg/draft-irtf-cfrg-det-sigs-with-noise/issues/6
|
||||
// for the FIPS compliance of this method. In short Z is entropy from the
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import (
|
|||
"crypto/internal/fips140/edwards25519"
|
||||
"crypto/internal/fips140/sha512"
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
|
@ -61,24 +60,14 @@ func (pub *PublicKey) Bytes() []byte {
|
|||
}
|
||||
|
||||
// GenerateKey generates a new Ed25519 private key pair.
|
||||
//
|
||||
// In FIPS mode, rand is ignored. Otherwise, the output of this function is
|
||||
// deterministic, and equivalent to reading 32 bytes from rand, and passing them
|
||||
// to [NewKeyFromSeed].
|
||||
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
||||
func GenerateKey() (*PrivateKey, error) {
|
||||
priv := &PrivateKey{}
|
||||
return generateKey(priv, rand)
|
||||
return generateKey(priv)
|
||||
}
|
||||
|
||||
func generateKey(priv *PrivateKey, rand io.Reader) (*PrivateKey, error) {
|
||||
func generateKey(priv *PrivateKey) (*PrivateKey, error) {
|
||||
fips140.RecordApproved()
|
||||
if fips140.Enabled {
|
||||
drbg.Read(priv.seed[:])
|
||||
} else {
|
||||
if _, err := io.ReadFull(rand, priv.seed[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
precomputePrivateKey(priv)
|
||||
if err := fipsPCT(priv); err != nil {
|
||||
// This clearly can't happen, but FIPS 140-3 requires that we check.
|
||||
|
|
|
|||
|
|
@ -8,15 +8,12 @@ import (
|
|||
"crypto/internal/fips140"
|
||||
"crypto/internal/fips140/bigmod"
|
||||
"crypto/internal/fips140/drbg"
|
||||
"crypto/internal/randutil"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// GenerateKey generates a new RSA key pair of the given bit size.
|
||||
// bits must be at least 128.
|
||||
//
|
||||
// When operating in FIPS mode, rand is ignored.
|
||||
func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) {
|
||||
if bits < 128 {
|
||||
return nil, errors.New("rsa: key too small")
|
||||
|
|
@ -94,7 +91,7 @@ func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) {
|
|||
}
|
||||
|
||||
// randomPrime returns a random prime number of the given bit size following
|
||||
// the process in FIPS 186-5, Appendix A.1.3. rand is ignored in FIPS mode.
|
||||
// the process in FIPS 186-5, Appendix A.1.3.
|
||||
func randomPrime(rand io.Reader, bits int) ([]byte, error) {
|
||||
if bits < 64 {
|
||||
return nil, errors.New("rsa: prime size must be at least 32-bit")
|
||||
|
|
@ -102,14 +99,9 @@ func randomPrime(rand io.Reader, bits int) ([]byte, error) {
|
|||
|
||||
b := make([]byte, (bits+7)/8)
|
||||
for {
|
||||
if fips140.Enabled {
|
||||
drbg.Read(b)
|
||||
} else {
|
||||
randutil.MaybeReadByte(rand)
|
||||
if _, err := io.ReadFull(rand, b); err != nil {
|
||||
if err := drbg.ReadWithReader(rand, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if excess := len(b)*8 - bits; excess != 0 {
|
||||
b[0] >>= excess
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,8 +264,6 @@ func PSSMaxSaltLength(pub *PublicKey, hash fips140.Hash) (int, error) {
|
|||
}
|
||||
|
||||
// SignPSS calculates the signature of hashed using RSASSA-PSS.
|
||||
//
|
||||
// In FIPS mode, rand is ignored and can be nil.
|
||||
func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte, saltLength int) ([]byte, error) {
|
||||
fipsSelfTest()
|
||||
fips140.RecordApproved()
|
||||
|
|
@ -286,13 +284,9 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte,
|
|||
fips140.RecordNonApproved()
|
||||
}
|
||||
salt := make([]byte, saltLength)
|
||||
if fips140.Enabled {
|
||||
drbg.Read(salt)
|
||||
} else {
|
||||
if _, err := io.ReadFull(rand, salt); err != nil {
|
||||
if err := drbg.ReadWithReaderDeterministic(rand, salt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
emBits := priv.pub.N.BitLen() - 1
|
||||
em, err := emsaPSSEncode(hashed, emBits, salt, hash)
|
||||
|
|
@ -374,8 +368,6 @@ func checkApprovedHash(hash fips140.Hash) {
|
|||
}
|
||||
|
||||
// EncryptOAEP encrypts the given message with RSAES-OAEP.
|
||||
//
|
||||
// In FIPS mode, random is ignored and can be nil.
|
||||
func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) {
|
||||
// Note that while we don't commit to deterministic execution with respect
|
||||
// to the random stream, we also don't apply MaybeReadByte, so per Hyrum's
|
||||
|
|
@ -408,14 +400,9 @@ func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, m
|
|||
db[len(db)-len(msg)-1] = 1
|
||||
copy(db[len(db)-len(msg):], msg)
|
||||
|
||||
if fips140.Enabled {
|
||||
drbg.Read(seed)
|
||||
} else {
|
||||
_, err := io.ReadFull(random, seed)
|
||||
if err != nil {
|
||||
if err := drbg.ReadWithReaderDeterministic(random, seed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
mgf1XOR(db, mgfHash, seed)
|
||||
mgf1XOR(seed, mgfHash, db)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@
|
|||
package fips140only
|
||||
|
||||
import (
|
||||
"crypto/internal/fips140/drbg"
|
||||
"crypto/internal/fips140/sha256"
|
||||
"crypto/internal/fips140/sha3"
|
||||
"crypto/internal/fips140/sha512"
|
||||
"hash"
|
||||
"internal/godebug"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Enabled reports whether FIPS 140-only mode is enabled, in which non-approved
|
||||
|
|
@ -24,3 +26,8 @@ func ApprovedHash(h hash.Hash) bool {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func ApprovedRandomReader(r io.Reader) bool {
|
||||
_, ok := r.(drbg.DefaultReader)
|
||||
return ok
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ func TestConditionals(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
|
||||
k25519, err := ed25519.GenerateKey(rand.Reader)
|
||||
k25519, err := ed25519.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ func init() {
|
|||
Reader = &reader{}
|
||||
}
|
||||
|
||||
type reader struct{}
|
||||
type reader struct {
|
||||
drbg.DefaultReader
|
||||
}
|
||||
|
||||
func (r *reader) Read(b []byte) (n int, err error) {
|
||||
boring.Unreachable()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ import (
|
|||
const (
|
||||
// PSSSaltLengthAuto causes the salt in a PSS signature to be as large
|
||||
// as possible when signing, and to be auto-detected when verifying.
|
||||
//
|
||||
// When signing in FIPS 140-3 mode, the salt length is capped at the length
|
||||
// of the hash function used in the signature.
|
||||
PSSSaltLengthAuto = 0
|
||||
// PSSSaltLengthEqualsHash causes the salt length to equal the length
|
||||
// of the hash used in the signature.
|
||||
|
|
@ -67,6 +70,9 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte,
|
|||
if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) {
|
||||
return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")
|
||||
}
|
||||
if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) {
|
||||
return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
|
||||
}
|
||||
|
||||
if opts != nil && opts.Hash != 0 {
|
||||
hash = opts.Hash
|
||||
|
|
@ -188,6 +194,9 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l
|
|||
if fips140only.Enabled && !fips140only.ApprovedHash(hash) {
|
||||
return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")
|
||||
}
|
||||
if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) {
|
||||
return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
|
||||
}
|
||||
|
||||
defer hash.Reset()
|
||||
|
||||
|
|
|
|||
|
|
@ -322,6 +322,9 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
|
|||
if fips140only.Enabled && bits%2 == 1 {
|
||||
return nil, errors.New("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode")
|
||||
}
|
||||
if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) {
|
||||
return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
|
||||
}
|
||||
|
||||
k, err := rsa.GenerateKey(random, bits)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"crypto"
|
||||
"crypto/internal/boring"
|
||||
"crypto/internal/cryptotest"
|
||||
"crypto/internal/fips140"
|
||||
"crypto/rand"
|
||||
. "crypto/rsa"
|
||||
"crypto/sha1"
|
||||
|
|
@ -782,9 +781,6 @@ type testEncryptOAEPStruct struct {
|
|||
}
|
||||
|
||||
func TestEncryptOAEP(t *testing.T) {
|
||||
if fips140.Enabled {
|
||||
t.Skip("FIPS mode overrides the deterministic random source")
|
||||
}
|
||||
sha1 := sha1.New()
|
||||
n := new(big.Int)
|
||||
for i, test := range testEncryptOAEPData {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue