Skip to content

Commit c93477b

Browse files
FiloSottilegopherbot
authored andcommitted
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 <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Russ Cox <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 3104b6a commit c93477b

File tree

15 files changed

+94
-84
lines changed

15 files changed

+94
-84
lines changed

src/crypto/ecdh/nist.go

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"crypto/internal/boring"
1010
"crypto/internal/fips140/ecdh"
11+
"crypto/internal/fips140only"
1112
"errors"
1213
"io"
1314
)
@@ -43,6 +44,10 @@ func (c *nistCurve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
4344
return k, nil
4445
}
4546

47+
if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) {
48+
return nil, errors.New("crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-only mode")
49+
}
50+
4651
privateKey, err := c.generate(rand)
4752
if err != nil {
4853
return nil, err

src/crypto/ecdsa/ecdsa.go

+7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"crypto/internal/boring"
2222
"crypto/internal/boring/bbig"
2323
"crypto/internal/fips140/ecdsa"
24+
"crypto/internal/fips140only"
2425
"crypto/internal/randutil"
2526
"crypto/sha512"
2627
"crypto/subtle"
@@ -182,6 +183,9 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
182183
}
183184

184185
func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], rand io.Reader) (*PrivateKey, error) {
186+
if fips140only.Enabled && fips140only.ApprovedRandomReader(rand) {
187+
return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
188+
}
185189
privateKey, err := ecdsa.GenerateKey(c, rand)
186190
if err != nil {
187191
return nil, err
@@ -228,6 +232,9 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
228232
}
229233

230234
func signFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey, rand io.Reader, hash []byte) ([]byte, error) {
235+
if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) {
236+
return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
237+
}
231238
// privateKeyToFIPS is very slow in FIPS mode because it performs a
232239
// Sign+Verify cycle per FIPS 140-3 IG 10.3.A. We should find a way to cache
233240
// it or attach it to the PrivateKey.

src/crypto/internal/fips140/drbg/rand.go

+37
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ package drbg
77
import (
88
"crypto/internal/entropy"
99
"crypto/internal/fips140"
10+
"crypto/internal/randutil"
1011
"crypto/internal/sysrand"
12+
"io"
1113
"sync"
1214
)
1315

@@ -56,3 +58,38 @@ func Read(b []byte) {
5658
b = b[size:]
5759
}
5860
}
61+
62+
// DefaultReader is a sentinel type, embedded in the default
63+
// [crypto/rand.Reader], used to recognize it when passed to
64+
// APIs that accept a rand io.Reader.
65+
type DefaultReader interface{ defaultReader() }
66+
67+
// ReadWithReader uses Reader to fill b with cryptographically secure random
68+
// bytes. It is intended for use in APIs that expose a rand io.Reader.
69+
//
70+
// If Reader is not the default Reader from crypto/rand,
71+
// [randutil.MaybeReadByte] and [fips140.RecordNonApproved] are called.
72+
func ReadWithReader(r io.Reader, b []byte) error {
73+
if _, ok := r.(DefaultReader); ok {
74+
Read(b)
75+
return nil
76+
}
77+
78+
fips140.RecordNonApproved()
79+
randutil.MaybeReadByte(r)
80+
_, err := io.ReadFull(r, b)
81+
return err
82+
}
83+
84+
// ReadWithReaderDeterministic is like ReadWithReader, but it doesn't call
85+
// [randutil.MaybeReadByte] on non-default Readers.
86+
func ReadWithReaderDeterministic(r io.Reader, b []byte) error {
87+
if _, ok := r.(DefaultReader); ok {
88+
Read(b)
89+
return nil
90+
}
91+
92+
fips140.RecordNonApproved()
93+
_, err := io.ReadFull(r, b)
94+
return err
95+
}

src/crypto/internal/fips140/ecdh/ecdh.go

+6-14
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"crypto/internal/fips140/drbg"
1111
"crypto/internal/fips140/nistec"
1212
"crypto/internal/fips140deps/byteorder"
13-
"crypto/internal/randutil"
1413
"errors"
1514
"io"
1615
"math/bits"
@@ -137,27 +136,20 @@ var p521Order = []byte{0x01, 0xff,
137136
}
138137

139138
// GenerateKey generates a new ECDSA private key pair for the specified curve.
140-
//
141-
// In FIPS mode, rand is ignored.
142139
func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
143140
fips140.RecordApproved()
144141
// This procedure is equivalent to Key Pair Generation by Testing
145142
// Candidates, specified in NIST SP 800-56A Rev. 3, Section 5.6.1.2.2.
146143

147144
for {
148145
key := make([]byte, len(c.N))
149-
if fips140.Enabled {
150-
drbg.Read(key)
151-
} else {
152-
randutil.MaybeReadByte(rand)
153-
if _, err := io.ReadFull(rand, key); err != nil {
154-
return nil, err
155-
}
156-
// In tests, rand will return all zeros and NewPrivateKey will reject
157-
// the zero key as it generates the identity as a public key. This also
158-
// makes this function consistent with crypto/elliptic.GenerateKey.
159-
key[1] ^= 0x42
146+
if err := drbg.ReadWithReader(rand, key); err != nil {
147+
return nil, err
160148
}
149+
// In tests, rand will return all zeros and NewPrivateKey will reject
150+
// the zero key as it generates the identity as a public key. This also
151+
// makes this function consistent with crypto/elliptic.GenerateKey.
152+
key[1] ^= 0x42
161153

162154
// Mask off any excess bits if the size of the underlying field is not a
163155
// whole number of bytes, which is only the case for P-521.

src/crypto/internal/fips140/ecdsa/cast.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ func testHash() []byte {
5454
func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) error {
5555
return fips140.PCT("ECDSA PCT", func() error {
5656
hash := testHash()
57-
sig, err := Sign(c, sha512.New, k, nil, hash)
57+
drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil)
58+
sig, err := sign(c, k, drbg, hash)
5859
if err != nil {
5960
return err
6061
}

src/crypto/internal/fips140/ecdsa/ecdsa.go

+3-20
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"crypto/internal/fips140/bigmod"
1111
"crypto/internal/fips140/drbg"
1212
"crypto/internal/fips140/nistec"
13-
"crypto/internal/randutil"
1413
"errors"
1514
"io"
1615
"sync"
@@ -187,20 +186,11 @@ func NewPublicKey[P Point[P]](c *Curve[P], Q []byte) (*PublicKey, error) {
187186
}
188187

189188
// GenerateKey generates a new ECDSA private key pair for the specified curve.
190-
//
191-
// In FIPS mode, rand is ignored.
192189
func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
193190
fips140.RecordApproved()
194191

195192
k, Q, err := randomPoint(c, func(b []byte) error {
196-
if fips140.Enabled {
197-
drbg.Read(b)
198-
return nil
199-
} else {
200-
randutil.MaybeReadByte(rand)
201-
_, err := io.ReadFull(rand, b)
202-
return err
203-
}
193+
return drbg.ReadWithReader(rand, b)
204194
})
205195
if err != nil {
206196
return nil, err
@@ -281,8 +271,6 @@ type Signature struct {
281271
// the hash function H) using the private key, priv. If the hash is longer than
282272
// the bit-length of the private key's curve order, the hash will be truncated
283273
// to that length.
284-
//
285-
// The signature is randomized. If FIPS mode is enabled, rand is ignored.
286274
func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey, rand io.Reader, hash []byte) (*Signature, error) {
287275
if priv.pub.curve != c.curve {
288276
return nil, errors.New("ecdsa: private key does not match curve")
@@ -296,13 +284,8 @@ func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey,
296284
// advantage of closely resembling Deterministic ECDSA.
297285

298286
Z := make([]byte, len(priv.d))
299-
if fips140.Enabled {
300-
drbg.Read(Z)
301-
} else {
302-
randutil.MaybeReadByte(rand)
303-
if _, err := io.ReadFull(rand, Z); err != nil {
304-
return nil, err
305-
}
287+
if err := drbg.ReadWithReader(rand, Z); err != nil {
288+
return nil, err
306289
}
307290

308291
// See https://github.com/cfrg/draft-irtf-cfrg-det-sigs-with-noise/issues/6

src/crypto/internal/fips140/ed25519/ed25519.go

+4-15
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"crypto/internal/fips140/edwards25519"
1212
"crypto/internal/fips140/sha512"
1313
"errors"
14-
"io"
1514
"strconv"
1615
)
1716

@@ -61,24 +60,14 @@ func (pub *PublicKey) Bytes() []byte {
6160
}
6261

6362
// GenerateKey generates a new Ed25519 private key pair.
64-
//
65-
// In FIPS mode, rand is ignored. Otherwise, the output of this function is
66-
// deterministic, and equivalent to reading 32 bytes from rand, and passing them
67-
// to [NewKeyFromSeed].
68-
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
63+
func GenerateKey() (*PrivateKey, error) {
6964
priv := &PrivateKey{}
70-
return generateKey(priv, rand)
65+
return generateKey(priv)
7166
}
7267

73-
func generateKey(priv *PrivateKey, rand io.Reader) (*PrivateKey, error) {
68+
func generateKey(priv *PrivateKey) (*PrivateKey, error) {
7469
fips140.RecordApproved()
75-
if fips140.Enabled {
76-
drbg.Read(priv.seed[:])
77-
} else {
78-
if _, err := io.ReadFull(rand, priv.seed[:]); err != nil {
79-
return nil, err
80-
}
81-
}
70+
drbg.Read(priv.seed[:])
8271
precomputePrivateKey(priv)
8372
if err := fipsPCT(priv); err != nil {
8473
// This clearly can't happen, but FIPS 140-3 requires that we check.

src/crypto/internal/fips140/rsa/keygen.go

+3-11
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,12 @@ import (
88
"crypto/internal/fips140"
99
"crypto/internal/fips140/bigmod"
1010
"crypto/internal/fips140/drbg"
11-
"crypto/internal/randutil"
1211
"errors"
1312
"io"
1413
)
1514

1615
// GenerateKey generates a new RSA key pair of the given bit size.
1716
// bits must be at least 128.
18-
//
19-
// When operating in FIPS mode, rand is ignored.
2017
func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) {
2118
if bits < 128 {
2219
return nil, errors.New("rsa: key too small")
@@ -94,21 +91,16 @@ func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) {
9491
}
9592

9693
// randomPrime returns a random prime number of the given bit size following
97-
// the process in FIPS 186-5, Appendix A.1.3. rand is ignored in FIPS mode.
94+
// the process in FIPS 186-5, Appendix A.1.3.
9895
func randomPrime(rand io.Reader, bits int) ([]byte, error) {
9996
if bits < 64 {
10097
return nil, errors.New("rsa: prime size must be at least 32-bit")
10198
}
10299

103100
b := make([]byte, (bits+7)/8)
104101
for {
105-
if fips140.Enabled {
106-
drbg.Read(b)
107-
} else {
108-
randutil.MaybeReadByte(rand)
109-
if _, err := io.ReadFull(rand, b); err != nil {
110-
return nil, err
111-
}
102+
if err := drbg.ReadWithReader(rand, b); err != nil {
103+
return nil, err
112104
}
113105
if excess := len(b)*8 - bits; excess != 0 {
114106
b[0] >>= excess

src/crypto/internal/fips140/rsa/pkcs1v22.go

+4-17
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,6 @@ func PSSMaxSaltLength(pub *PublicKey, hash fips140.Hash) (int, error) {
264264
}
265265

266266
// SignPSS calculates the signature of hashed using RSASSA-PSS.
267-
//
268-
// In FIPS mode, rand is ignored and can be nil.
269267
func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte, saltLength int) ([]byte, error) {
270268
fipsSelfTest()
271269
fips140.RecordApproved()
@@ -286,12 +284,8 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte,
286284
fips140.RecordNonApproved()
287285
}
288286
salt := make([]byte, saltLength)
289-
if fips140.Enabled {
290-
drbg.Read(salt)
291-
} else {
292-
if _, err := io.ReadFull(rand, salt); err != nil {
293-
return nil, err
294-
}
287+
if err := drbg.ReadWithReaderDeterministic(rand, salt); err != nil {
288+
return nil, err
295289
}
296290

297291
emBits := priv.pub.N.BitLen() - 1
@@ -374,8 +368,6 @@ func checkApprovedHash(hash fips140.Hash) {
374368
}
375369

376370
// EncryptOAEP encrypts the given message with RSAES-OAEP.
377-
//
378-
// In FIPS mode, random is ignored and can be nil.
379371
func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) {
380372
// Note that while we don't commit to deterministic execution with respect
381373
// to the random stream, we also don't apply MaybeReadByte, so per Hyrum's
@@ -408,13 +400,8 @@ func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, m
408400
db[len(db)-len(msg)-1] = 1
409401
copy(db[len(db)-len(msg):], msg)
410402

411-
if fips140.Enabled {
412-
drbg.Read(seed)
413-
} else {
414-
_, err := io.ReadFull(random, seed)
415-
if err != nil {
416-
return nil, err
417-
}
403+
if err := drbg.ReadWithReaderDeterministic(random, seed); err != nil {
404+
return nil, err
418405
}
419406

420407
mgf1XOR(db, mgfHash, seed)

src/crypto/internal/fips140only/fips140only.go

+7
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
package fips140only
66

77
import (
8+
"crypto/internal/fips140/drbg"
89
"crypto/internal/fips140/sha256"
910
"crypto/internal/fips140/sha3"
1011
"crypto/internal/fips140/sha512"
1112
"hash"
1213
"internal/godebug"
14+
"io"
1315
)
1416

1517
// Enabled reports whether FIPS 140-only mode is enabled, in which non-approved
@@ -24,3 +26,8 @@ func ApprovedHash(h hash.Hash) bool {
2426
return false
2527
}
2628
}
29+
30+
func ApprovedRandomReader(r io.Reader) bool {
31+
_, ok := r.(drbg.DefaultReader)
32+
return ok
33+
}

src/crypto/internal/fips140test/cast_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func TestConditionals(t *testing.T) {
8585
t.Fatal(err)
8686
}
8787
ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32))
88-
k25519, err := ed25519.GenerateKey(rand.Reader)
88+
k25519, err := ed25519.GenerateKey()
8989
if err != nil {
9090
t.Fatal(err)
9191
}

src/crypto/rand/rand.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ func init() {
3838
Reader = &reader{}
3939
}
4040

41-
type reader struct{}
41+
type reader struct {
42+
drbg.DefaultReader
43+
}
4244

4345
func (r *reader) Read(b []byte) (n int, err error) {
4446
boring.Unreachable()

0 commit comments

Comments
 (0)