crypto,testing/cryptotest: ignore random io.Reader params, add SetGlobalRandom

First, we centralize all random bytes generation through drbg.Read. The
rest of the FIPS 140-3 module can't use external functions anyway, so
drbg.Read needs to have all the logic.

Then, make sure that the crypto/... tree uses drbg.Read (or the new
crypto/internal/rand.Reader wrapper) instead of crypto/rand, so it is
unaffected by applications setting crypto/rand.Reader.

Next, pass all unspecified random io.Reader parameters through the new
crypto/internal/rand.CustomReader, which just redirects to drbg.Read
unless GODEBUG=cryptocustomrand=1 is set. Move all the calls to
MaybeReadByte there, since it's only needed for these custom Readers.

Finally, add testing/cryptotest.SetGlobalRandom which sets
crypto/rand.Reader to a locked deterministic source and overrides
drbg.Read. This way SetGlobalRandom should affect all cryptographic
randomness in the standard library.

Fixes #70942

Co-authored-by: qiulaidongfeng <2645477756@qq.com>
Change-Id: I6a6a69641311d9fac318abcc6d79677f0e406100
Reviewed-on: https://go-review.googlesource.com/c/go/+/724480
Reviewed-by: Nicholas Husin <nsh@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Nicholas Husin <husin@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Filippo Valsorda 2025-09-15 18:58:04 +02:00 committed by Gopher Robot
parent 21ebed0ac0
commit 2b8dbb35b0
36 changed files with 591 additions and 125 deletions

1
api/next/70942.txt Normal file
View file

@ -0,0 +1 @@
pkg testing/cryptotest, func SetGlobalRandom(*testing.T, uint64) #70942

View file

@ -178,6 +178,11 @@ includes these key/value pairs in the goroutine status header of runtime
tracebacks and debug=2 runtime/pprof stack dumps. This format may change in the future.
(see go.dev/issue/76349)
Go 1.26 added a new `cryptocustomrand` setting that controls whether most crypto/...
APIs ignore the random `io.Reader` parameter. For Go 1.26, it defaults
to `cryptocustomrand=0`, ignoring the random parameters. Using `cryptocustomrand=1`
reverts to the pre-Go 1.26 behavior.
### Go 1.25
Go 1.25 added a new `decoratemappings` setting that controls whether the Go

View file

@ -0,0 +1,4 @@
The random parameter to [GenerateKey] is now ignored.
Instead, it now always uses a secure source of cryptographically random bytes.
For deterministic testing, use the new [testing/cryptotest.SetGlobalRandom] function.
The new GODEBUG setting `cryptocustomrand=1` temporarily restores the old behavior.

View file

@ -0,0 +1,4 @@
The random parameter to [Curve.GenerateKey] is now ignored.
Instead, it now always uses a secure source of cryptographically random bytes.
For deterministic testing, use the new [testing/cryptotest.SetGlobalRandom] function.
The new GODEBUG setting `cryptocustomrand=1` temporarily restores the old behavior.

View file

@ -0,0 +1,4 @@
The random parameter to [GenerateKey], [SignASN1], [Sign], and [PrivateKey.Sign] is now ignored.
Instead, they now always use a secure source of cryptographically random bytes.
For deterministic testing, use the new [testing/cryptotest.SetGlobalRandom] function.
The new GODEBUG setting `cryptocustomrand=1` temporarily restores the old behavior.

View file

@ -0,0 +1,4 @@
If the random parameter to [GenerateKey] is nil, GenerateKey now always uses a
secure source of cryptographically random bytes, instead of [crypto/rand.Reader]
(which could have been overridden). The new GODEBUG setting `cryptocustomrand=1`
temporarily restores the old behavior.

View file

@ -0,0 +1,4 @@
The random parameter to [Prime] is now ignored.
Instead, it now always uses a secure source of cryptographically random bytes.
For deterministic testing, use the new [testing/cryptotest.SetGlobalRandom] function.
The new GODEBUG setting `cryptocustomrand=1` temporarily restores the old behavior.

View file

@ -0,0 +1,4 @@
The random parameter to [GenerateKey], [GenerateMultiPrimeKey], and [EncryptPKCS1v15] is now ignored.
Instead, they now always use a secure source of cryptographically random bytes.
For deterministic testing, use the new [testing/cryptotest.SetGlobalRandom] function.
The new GODEBUG setting `cryptocustomrand=1` temporarily restores the old behavior.

View file

@ -0,0 +1,4 @@
The new [SetGlobalRandom] function configures a global, deterministic
cryptographic randomness source for the duration of the test. It affects
crypto/rand, and all implicit sources of cryptographic randomness in the
`crypto/...` packages.

View file

@ -19,7 +19,7 @@ import (
"math/big"
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/internal/rand"
)
// Parameters represents the domain parameters for a key. These parameters can
@ -209,14 +209,18 @@ func fermatInverse(k, P *big.Int) *big.Int {
// to the byte-length of the subgroup. This function does not perform that
// truncation itself.
//
// Since Go 1.26, a secure source of random bytes is always used, and the Reader is
// ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed
// in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].
//
// Be aware that calling Sign with an attacker-controlled [PrivateKey] may
// require an arbitrary amount of CPU.
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
func Sign(random io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
if fips140only.Enforced() {
return nil, nil, errors.New("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode")
}
randutil.MaybeReadByte(rand)
random = rand.CustomReader(random)
// FIPS 186-3, section 4.6
@ -232,7 +236,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
k := new(big.Int)
buf := make([]byte, n)
for {
_, err = io.ReadFull(rand, buf)
_, err = io.ReadFull(random, buf)
if err != nil {
return
}

View file

@ -18,9 +18,9 @@ import (
type Curve interface {
// GenerateKey generates a random PrivateKey.
//
// Most applications should use [crypto/rand.Reader] as rand. Note that the
// returned key does not depend deterministically on the bytes read from rand,
// and may change between calls and/or between versions.
// Since Go 1.26, a secure source of random bytes is always used, and rand
// is ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be
// removed in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].
GenerateKey(rand io.Reader) (*PrivateKey, error)
// NewPrivateKey checks that key is valid and returns a PrivateKey.

View file

@ -9,6 +9,7 @@ import (
"crypto/internal/boring"
"crypto/internal/fips140/ecdh"
"crypto/internal/fips140only"
"crypto/internal/rand"
"errors"
"io"
)
@ -25,8 +26,8 @@ func (c *nistCurve) String() string {
return c.name
}
func (c *nistCurve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
if boring.Enabled && rand == boring.RandReader {
func (c *nistCurve) GenerateKey(r io.Reader) (*PrivateKey, error) {
if boring.Enabled && r == boring.RandReader {
key, bytes, err := boring.GenerateKeyECDH(c.name)
if err != nil {
return nil, err
@ -44,11 +45,13 @@ func (c *nistCurve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
return k, nil
}
if fips140only.Enforced() && !fips140only.ApprovedRandomReader(rand) {
r = rand.CustomReader(r)
if fips140only.Enforced() && !fips140only.ApprovedRandomReader(r) {
return nil, errors.New("crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-only mode")
}
privateKey, err := c.generate(rand)
privateKey, err := c.generate(r)
if err != nil {
return nil, err
}

View file

@ -8,7 +8,7 @@ import (
"bytes"
"crypto/internal/fips140/edwards25519/field"
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/internal/rand"
"errors"
"io"
)
@ -34,13 +34,13 @@ func (c *x25519Curve) String() string {
return "X25519"
}
func (c *x25519Curve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
func (c *x25519Curve) GenerateKey(r io.Reader) (*PrivateKey, error) {
if fips140only.Enforced() {
return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode")
}
r = rand.CustomReader(r)
key := make([]byte, x25519PrivateKeySize)
randutil.MaybeReadByte(rand)
if _, err := io.ReadFull(rand, key); err != nil {
if _, err := io.ReadFull(r, key); err != nil {
return nil, err
}
return c.NewPrivateKey(key)

View file

@ -27,7 +27,7 @@ import (
"crypto/internal/fips140cache"
"crypto/internal/fips140hash"
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/internal/rand"
"crypto/sha512"
"crypto/subtle"
"errors"
@ -310,31 +310,31 @@ func privateKeyBytes[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey) ([]b
// the bit-length of the private key's curve order, the hash will be truncated
// to that length. It returns the ASN.1 encoded signature, like [SignASN1].
//
// If rand is not nil, the signature is randomized. Most applications should use
// [crypto/rand.Reader] as rand. Note that the returned signature does not
// depend deterministically on the bytes read from rand, and may change between
// calls and/or between versions.
// If random is not nil, the signature is randomized. Most applications should use
// [crypto/rand.Reader] as random, but unless GODEBUG=cryptocustomrand=1 is set, a
// secure source of random bytes is always used, and the actual Reader is ignored.
// The GODEBUG setting will be removed in a future Go release. Instead, use
// [testing/cryptotest.SetGlobalRandom].
//
// If rand is nil, Sign will produce a deterministic signature according to RFC
// If random is nil, Sign will produce a deterministic signature according to RFC
// 6979. When producing a deterministic signature, opts.HashFunc() must be the
// function used to produce digest and priv.Curve must be one of
// [elliptic.P224], [elliptic.P256], [elliptic.P384], or [elliptic.P521].
func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
if rand == nil {
func (priv *PrivateKey) Sign(random io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
if random == nil {
return signRFC6979(priv, digest, opts)
}
return SignASN1(rand, priv, digest)
random = rand.CustomReader(random)
return SignASN1(random, priv, digest)
}
// GenerateKey generates a new ECDSA private key for the specified curve.
//
// Most applications should use [crypto/rand.Reader] as rand. Note that the
// returned key does not depend deterministically on the bytes read from rand,
// and may change between calls and/or between versions.
func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
randutil.MaybeReadByte(rand)
if boring.Enabled && rand == boring.RandReader {
// Since Go 1.26, a secure source of random bytes is always used, and the Reader is
// ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed
// in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].
func GenerateKey(c elliptic.Curve, r io.Reader) (*PrivateKey, error) {
if boring.Enabled && r == boring.RandReader {
x, y, d, err := boring.GenerateKeyECDSA(c.Params().Name)
if err != nil {
return nil, err
@ -343,17 +343,19 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
}
boring.UnreachableExceptTests()
r = rand.CustomReader(r)
switch c.Params() {
case elliptic.P224().Params():
return generateFIPS(c, ecdsa.P224(), rand)
return generateFIPS(c, ecdsa.P224(), r)
case elliptic.P256().Params():
return generateFIPS(c, ecdsa.P256(), rand)
return generateFIPS(c, ecdsa.P256(), r)
case elliptic.P384().Params():
return generateFIPS(c, ecdsa.P384(), rand)
return generateFIPS(c, ecdsa.P384(), r)
case elliptic.P521().Params():
return generateFIPS(c, ecdsa.P521(), rand)
return generateFIPS(c, ecdsa.P521(), r)
default:
return generateLegacy(c, rand)
return generateLegacy(c, r)
}
}
@ -373,13 +375,12 @@ func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], ran
// private key's curve order, the hash will be truncated to that length. It
// returns the ASN.1 encoded signature.
//
// The signature is randomized. Most applications should use [crypto/rand.Reader]
// as rand. Note that the returned signature does not depend deterministically on
// the bytes read from rand, and may change between calls and/or between versions.
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
randutil.MaybeReadByte(rand)
if boring.Enabled && rand == boring.RandReader {
// The signature is randomized. Since Go 1.26, a secure source of random bytes
// is always used, and the Reader is ignored unless GODEBUG=cryptocustomrand=1
// is set. This setting will be removed in a future Go release. Instead, use
// [testing/cryptotest.SetGlobalRandom].
func SignASN1(r io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
if boring.Enabled && r == boring.RandReader {
b, err := boringPrivateKey(priv)
if err != nil {
return nil, err
@ -388,17 +389,19 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
}
boring.UnreachableExceptTests()
r = rand.CustomReader(r)
switch priv.Curve.Params() {
case elliptic.P224().Params():
return signFIPS(ecdsa.P224(), priv, rand, hash)
return signFIPS(ecdsa.P224(), priv, r, hash)
case elliptic.P256().Params():
return signFIPS(ecdsa.P256(), priv, rand, hash)
return signFIPS(ecdsa.P256(), priv, r, hash)
case elliptic.P384().Params():
return signFIPS(ecdsa.P384(), priv, rand, hash)
return signFIPS(ecdsa.P384(), priv, r, hash)
case elliptic.P521().Params():
return signFIPS(ecdsa.P521(), priv, rand, hash)
return signFIPS(ecdsa.P521(), priv, r, hash)
default:
return signLegacy(priv, rand, hash)
return signLegacy(priv, r, hash)
}
}

View file

@ -61,6 +61,11 @@ var errZeroParam = errors.New("zero parameter")
// private key's curve order, the hash will be truncated to that length. It
// returns the signature as a pair of integers. Most applications should use
// [SignASN1] instead of dealing directly with r, s.
//
// The signature is randomized. Since Go 1.26, a secure source of random bytes
// is always used, and the Reader is ignored unless GODEBUG=cryptocustomrand=1
// is set. This setting will be removed in a future Go release. Instead, use
// [testing/cryptotest.SetGlobalRandom].
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
sig, err := SignASN1(rand, priv, hash)
if err != nil {

View file

@ -17,12 +17,15 @@ package ed25519
import (
"crypto"
"crypto/internal/fips140/drbg"
"crypto/internal/fips140/ed25519"
"crypto/internal/fips140cache"
"crypto/internal/fips140only"
"crypto/internal/rand"
cryptorand "crypto/rand"
"crypto/subtle"
"errors"
"internal/godebug"
"io"
"strconv"
)
@ -135,18 +138,31 @@ type Options struct {
// HashFunc returns o.Hash.
func (o *Options) HashFunc() crypto.Hash { return o.Hash }
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, [crypto/rand.Reader] will be used.
var cryptocustomrand = godebug.New("cryptocustomrand")
// GenerateKey generates a public/private key pair using entropy from random.
//
// If random is nil, a secure random source is used. (Before Go 1.26, a custom
// [crypto/rand.Reader] was used if set by the application. That behavior can be
// restored with GODEBUG=cryptocustomrand=1. This setting will be removed in a
// future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].)
//
// The output of this function is deterministic, and equivalent to reading
// [SeedSize] bytes from rand, and passing them to [NewKeyFromSeed].
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
if rand == nil {
rand = cryptorand.Reader
// [SeedSize] bytes from random, and passing them to [NewKeyFromSeed].
func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error) {
if random == nil {
if cryptocustomrand.Value() == "1" {
random = cryptorand.Reader
if _, ok := random.(drbg.DefaultReader); !ok {
cryptocustomrand.IncNonDefault()
}
} else {
random = rand.Reader
}
}
seed := make([]byte, SeedSize)
if _, err := io.ReadFull(rand, seed); err != nil {
if _, err := io.ReadFull(random, seed); err != nil {
return nil, nil, err
}

View file

@ -6,7 +6,7 @@ package hpke
import (
"crypto/ecdh"
"crypto/rand"
"crypto/internal/rand"
"errors"
"internal/byteorder"
)

View file

@ -8,8 +8,9 @@ import (
"bytes"
"crypto"
"crypto/ecdh"
"crypto/internal/fips140/drbg"
"crypto/internal/rand"
"crypto/mlkem"
"crypto/rand"
"crypto/sha3"
"errors"
"internal/byteorder"
@ -246,7 +247,7 @@ func NewHybridPrivateKey(pq crypto.Decapsulator, t ecdh.KeyExchanger) (PrivateKe
func (kem *hybridKEM) GenerateKey() (PrivateKey, error) {
seed := make([]byte, 32)
rand.Read(seed)
drbg.Read(seed)
return kem.NewPrivateKey(seed)
}

View file

@ -11,7 +11,6 @@ package drbg
import (
entropy "crypto/internal/entropy/v1.0.0"
"crypto/internal/fips140"
"crypto/internal/randutil"
"crypto/internal/sysrand"
"io"
"sync"
@ -63,6 +62,15 @@ var drbgPool = sync.Pool{
// uses an SP 800-90A Rev. 1 Deterministic Random Bit Generator (DRBG).
// Otherwise, it uses the operating system's random number generator.
func Read(b []byte) {
if testingReader != nil {
fips140.RecordNonApproved()
// Avoid letting b escape in the non-testing case.
bb := make([]byte, len(b))
testingReader.Read(bb)
copy(b, bb)
return
}
if !fips140.Enabled {
sysrand.Read(b)
return
@ -101,36 +109,33 @@ func Read(b []byte) {
}
}
var testingReader io.Reader
// SetTestingReader sets a global, deterministic cryptographic randomness source
// for testing purposes. Its Read method must never return an error, it must
// never return short, and it must be safe for concurrent use.
//
// This is only intended to be used by the testing/cryptotest package.
func SetTestingReader(r io.Reader) {
testingReader = r
}
// 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.
//
// Any Reader that implements this interface is assumed to
// call [Read] as its Read method.
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

View file

@ -272,8 +272,8 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash hash.Hash, hashed []byte, sa
checkApprovedHash(hash)
// Note that while we don't commit to deterministic execution with respect
// to the rand stream, we also don't apply MaybeReadByte, so per Hyrum's Law
// it's probably relied upon by some. It's a tolerable promise because a
// to the rand stream, we also never applied MaybeReadByte, so per Hyrum's
// Law it's probably relied upon by some. It's a tolerable promise because a
// well-specified number of random bytes is included in the signature, in a
// well-specified way.
@ -286,7 +286,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash hash.Hash, hashed []byte, sa
fips140.RecordNonApproved()
}
salt := make([]byte, saltLength)
if err := drbg.ReadWithReaderDeterministic(rand, salt); err != nil {
if err := drbg.ReadWithReader(rand, salt); err != nil {
return nil, err
}
@ -372,7 +372,7 @@ func checkApprovedHash(hash hash.Hash) {
// EncryptOAEP encrypts the given message with RSAES-OAEP.
func EncryptOAEP(hash, mgfHash hash.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
// to the random stream, we also never applied MaybeReadByte, so per Hyrum's
// Law it's probably relied upon by some. It's a tolerable promise because a
// well-specified number of random bytes is included in the ciphertext, in a
// well-specified way.
@ -402,7 +402,7 @@ func EncryptOAEP(hash, mgfHash hash.Hash, random io.Reader, pub *PublicKey, msg
db[len(db)-len(msg)-1] = 1
copy(db[len(db)-len(msg):], msg)
if err := drbg.ReadWithReaderDeterministic(random, seed); err != nil {
if err := drbg.ReadWithReader(random, seed); err != nil {
return nil, err
}

View file

@ -0,0 +1,65 @@
// Copyright 2025 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 rand
import (
"crypto/internal/boring"
"crypto/internal/fips140/drbg"
"crypto/internal/randutil"
"internal/godebug"
"io"
_ "unsafe"
)
type reader struct {
drbg.DefaultReader
}
func (r reader) Read(b []byte) (n int, err error) {
if boring.Enabled {
if _, err := boring.RandReader.Read(b); err != nil {
panic("crypto/rand: boring RandReader failed: " + err.Error())
}
return len(b), nil
}
drbg.Read(b)
return len(b), nil
}
// Reader is an io.Reader that calls [drbg.Read].
//
// It should be used internally instead of [crypto/rand.Reader], because the
// latter can be set by applications outside of tests. These applications then
// risk breaking between Go releases, if the way the Reader is used changes.
var Reader io.Reader = reader{}
// SetTestingReader overrides all calls to [drbg.Read]. The Read method of
// r must never return an error or return short.
//
// SetTestingReader panics when building against Go Cryptographic Module v1.0.0.
//
// SetTestingReader is pulled by [testing/cryptotest.setGlobalRandom] via go:linkname.
//
//go:linkname SetTestingReader crypto/internal/rand.SetTestingReader
func SetTestingReader(r io.Reader) {
fips140SetTestingReader(r)
}
var cryptocustomrand = godebug.New("cryptocustomrand")
// CustomReader returns [Reader] or, only if the GODEBUG setting
// "cryptocustomrand=1" is set, the provided io.Reader.
//
// If returning a non-default Reader, it calls [randutil.MaybeReadByte] on it.
func CustomReader(r io.Reader) io.Reader {
if cryptocustomrand.Value() == "1" {
if _, ok := r.(drbg.DefaultReader); !ok {
randutil.MaybeReadByte(r)
cryptocustomrand.IncNonDefault()
}
return r
}
return Reader
}

View file

@ -0,0 +1,13 @@
// Copyright 2025 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.
//go:build fips140v1.0
package rand
import "io"
func fips140SetTestingReader(r io.Reader) {
panic("cryptotest.SetGlobalRandom is not supported when building against Go Cryptographic Module v1.0.0")
}

View file

@ -0,0 +1,16 @@
// Copyright 2025 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.
//go:build !fips140v1.0
package rand
import (
"crypto/internal/fips140/drbg"
"io"
)
func fips140SetTestingReader(r io.Reader) {
drbg.SetTestingReader(r)
}

View file

@ -31,6 +31,9 @@ var testingOnlyFailRead bool
// system. It always fills b entirely and crashes the program irrecoverably if
// an error is encountered. The operating system APIs are documented to never
// return an error on all but legacy Linux systems.
//
// Note that Read is not affected by [testing/cryptotest.SetGlobalRand], and it
// should not be used directly by algorithm implementations.
func Read(b []byte) {
if firstUse.CompareAndSwap(false, true) {
// First use of randomness. Start timer to warn about

View file

@ -43,7 +43,7 @@ type DecapsulationKey768 struct {
}
// GenerateKey768 generates a new decapsulation key, drawing random bytes from
// the default crypto/rand source. The decapsulation key must be kept secret.
// a secure source. The decapsulation key must be kept secret.
func GenerateKey768() (*DecapsulationKey768, error) {
key, err := mlkem.GenerateKey768()
if err != nil {
@ -118,7 +118,7 @@ func (ek *EncapsulationKey768) Bytes() []byte {
}
// Encapsulate generates a shared key and an associated ciphertext from an
// encapsulation key, drawing random bytes from the default crypto/rand source.
// encapsulation key, drawing random bytes from a secure source.
//
// The shared key must be kept secret.
//
@ -135,7 +135,7 @@ type DecapsulationKey1024 struct {
}
// GenerateKey1024 generates a new decapsulation key, drawing random bytes from
// the default crypto/rand source. The decapsulation key must be kept secret.
// a secure source. The decapsulation key must be kept secret.
func GenerateKey1024() (*DecapsulationKey1024, error) {
key, err := mlkem.GenerateKey1024()
if err != nil {
@ -210,7 +210,7 @@ func (ek *EncapsulationKey1024) Bytes() []byte {
}
// Encapsulate generates a shared key and an associated ciphertext from an
// encapsulation key, drawing random bytes from the default crypto/rand source.
// encapsulation key, drawing random bytes from a secure source.
//
// The shared key must be kept secret.
//

View file

@ -8,11 +8,14 @@ package rand
import (
"crypto/internal/boring"
"crypto/internal/fips140"
"crypto/internal/fips140/drbg"
"crypto/internal/sysrand"
"crypto/internal/rand"
"io"
_ "unsafe"
// Ensure the go:linkname from testing/cryptotest to
// crypto/internal/rand.SetTestingReader works.
_ "crypto/internal/rand"
)
// Reader is a global, shared instance of a cryptographically
@ -35,21 +38,7 @@ func init() {
Reader = boring.RandReader
return
}
Reader = &reader{}
}
type reader struct {
drbg.DefaultReader
}
func (r *reader) Read(b []byte) (n int, err error) {
boring.Unreachable()
if fips140.Enabled {
drbg.Read(b)
} else {
sysrand.Read(b)
}
return len(b), nil
Reader = rand.Reader
}
// fatal is [runtime.fatal], pushed via linkname.
@ -68,8 +57,9 @@ func Read(b []byte) (n int, err error) {
// through a potentially overridden Reader, so we special-case the default
// case which we can keep non-escaping, and in the general case we read into
// a heap buffer and copy from it.
if r, ok := Reader.(*reader); ok {
_, err = r.Read(b)
if _, ok := Reader.(drbg.DefaultReader); ok {
boring.Unreachable()
drbg.Read(b)
} else {
bb := make([]byte, len(b))
_, err = io.ReadFull(Reader, bb)

View file

@ -6,7 +6,7 @@ package rand
import (
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/internal/rand"
"errors"
"io"
"math/big"
@ -14,7 +14,11 @@ import (
// Prime returns a number of the given bit length that is prime with high probability.
// Prime will return error for any error returned by rand.Read or if bits < 2.
func Prime(rand io.Reader, bits int) (*big.Int, error) {
//
// Since Go 1.26, a secure source of random bytes is always used, and the Reader is
// ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed
// in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].
func Prime(r io.Reader, bits int) (*big.Int, error) {
if fips140only.Enforced() {
return nil, errors.New("crypto/rand: use of Prime is not allowed in FIPS 140-only mode")
}
@ -22,7 +26,7 @@ func Prime(rand io.Reader, bits int) (*big.Int, error) {
return nil, errors.New("crypto/rand: prime size must be at least 2-bit")
}
randutil.MaybeReadByte(rand)
r = rand.CustomReader(r)
b := uint(bits % 8)
if b == 0 {
@ -33,7 +37,7 @@ func Prime(rand io.Reader, bits int) (*big.Int, error) {
p := new(big.Int)
for {
if _, err := io.ReadFull(rand, bytes); err != nil {
if _, err := io.ReadFull(r, bytes); err != nil {
return nil, err
}

View file

@ -8,7 +8,7 @@ import (
"crypto/internal/boring"
"crypto/internal/fips140/rsa"
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/internal/rand"
"crypto/subtle"
"errors"
"io"
@ -36,12 +36,11 @@ type PKCS1v15DecryptOptions struct {
// scheme from PKCS #1 v1.5. The message must be no longer than the
// length of the public modulus minus 11 bytes.
//
// The random parameter is used as a source of entropy to ensure that
// encrypting the same message twice doesn't result in the same
// ciphertext. Most applications should use [crypto/rand.Reader]
// as random. Note that the returned ciphertext does not depend
// deterministically on the bytes read from random, and may change
// between calls and/or between versions.
// The random parameter is used as a source of entropy to ensure that encrypting
// the same message twice doesn't result in the same ciphertext. Since Go 1.26,
// a secure source of random bytes is always used, and the Reader is ignored
// unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed in a
// future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].
//
// Deprecated: PKCS #1 v1.5 encryption is dangerous and should not be used.
// See [draft-irtf-cfrg-rsa-guidance-05] for more information. Use
@ -57,8 +56,6 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro
return nil, err
}
randutil.MaybeReadByte(random)
k := pub.Size()
if len(msg) > k-11 {
return nil, ErrMessageTooLong
@ -73,6 +70,8 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro
}
boring.UnreachableExceptTests()
random = rand.CustomReader(random)
// EM = 0x00 || 0x02 || PS || 0x00 || M
em := make([]byte, k)
em[1] = 2

View file

@ -48,8 +48,8 @@ import (
"crypto/internal/fips140/bigmod"
"crypto/internal/fips140/rsa"
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/rand"
"crypto/internal/rand"
cryptorand "crypto/rand"
"crypto/subtle"
"errors"
"fmt"
@ -304,9 +304,9 @@ func checkPublicKeySize(k *PublicKey) error {
// If bits is less than 1024, [GenerateKey] returns an error. See the "[Minimum
// key size]" section for further details.
//
// Most applications should use [crypto/rand.Reader] as rand. Note that the
// returned key does not depend deterministically on the bytes read from rand,
// and may change between calls and/or between versions.
// Since Go 1.26, a secure source of random bytes is always used, and the Reader is
// ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed
// in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].
//
// [Minimum key size]: https://pkg.go.dev/crypto/rsa#hdr-Minimum_key_size
func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
@ -350,6 +350,8 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
return key, nil
}
random = rand.CustomReader(random)
if fips140only.Enforced() && bits < 2048 {
return nil, errors.New("crypto/rsa: use of keys smaller than 2048 bits is not allowed in FIPS 140-only mode")
}
@ -415,6 +417,10 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
// This package does not implement CRT optimizations for multi-prime RSA, so the
// keys with more than two primes will have worse performance.
//
// Since Go 1.26, a secure source of random bytes is always used, and the Reader is
// ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed
// in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].
//
// Deprecated: The use of this function with a number of primes different from
// two is not recommended for the above security, compatibility, and performance
// reasons. Use [GenerateKey] instead.
@ -428,7 +434,7 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey
return nil, errors.New("crypto/rsa: multi-prime RSA is not allowed in FIPS 140-only mode")
}
randutil.MaybeReadByte(random)
random = rand.CustomReader(random)
priv := new(PrivateKey)
priv.E = 65537
@ -473,7 +479,7 @@ NextSetOfPrimes:
}
for i := 0; i < nprimes; i++ {
var err error
primes[i], err = rand.Prime(random, todo/(nprimes-i))
primes[i], err = cryptorand.Prime(random, todo/(nprimes-i))
if err != nil {
return nil, err
}

View file

@ -448,6 +448,10 @@ func runMain(m *testing.M) int {
os.Exit(1)
}
// TODO(filippo): deprecate Config.Rand, and regenerate handshake recordings
// to use cryptotest.SetGlobalRandom instead.
os.Setenv("GODEBUG", "cryptocustomrand=1,"+os.Getenv("GODEBUG"))
testConfig = &Config{
Time: func() time.Time { return time.Unix(0, 0) },
Rand: zeroSource{},

View file

@ -560,6 +560,7 @@ var depsRules = `
< crypto/cipher
< crypto/internal/boring
< crypto/boring
< crypto/internal/rand
< crypto/aes,
crypto/des,
crypto/rc4,
@ -713,6 +714,9 @@ var depsRules = `
log/slog, testing
< testing/slogtest;
testing, crypto/rand
< testing/cryptotest;
FMT, crypto/sha256, encoding/binary, encoding/json,
go/ast, go/parser, go/token,
internal/godebug, math/rand, encoding/hex

View file

@ -29,6 +29,7 @@ var All = []Info{
{Name: "allowmultiplevcs", Package: "cmd/go"},
{Name: "asynctimerchan", Package: "time", Changed: 23, Old: "1"},
{Name: "containermaxprocs", Package: "runtime", Changed: 25, Old: "0"},
{Name: "cryptocustomrand", Package: "crypto", Changed: 26, Old: "1"},
{Name: "dataindependenttiming", Package: "crypto/subtle", Opaque: true},
{Name: "decoratemappings", Package: "runtime", Opaque: true, Changed: 25, Old: "0"},
{Name: "embedfollowsymlinks", Package: "cmd/go"},

View file

@ -271,6 +271,11 @@ Below is the full list of supported metrics, ordered lexicographically.
package due to a non-default GODEBUG=containermaxprocs=...
setting.
/godebug/non-default-behavior/cryptocustomrand:events
The number of non-default behaviors executed by the crypto
package due to a non-default GODEBUG=cryptocustomrand=...
setting.
/godebug/non-default-behavior/embedfollowsymlinks:events
The number of non-default behaviors executed by the cmd/go
package due to a non-default GODEBUG=embedfollowsymlinks=...

View file

@ -0,0 +1,76 @@
// Copyright 2025 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 cryptotest provides deterministic random source testing.
package cryptotest
import (
cryptorand "crypto/rand"
"internal/byteorder"
"io"
mathrand "math/rand/v2"
"sync"
"testing"
// Import unsafe and crypto/rand, which imports crypto/internal/rand,
// for the crypto/internal/rand.SetTestingReader go:linkname.
_ "crypto/rand"
_ "unsafe"
)
//go:linkname randSetTestingReader crypto/internal/rand.SetTestingReader
func randSetTestingReader(r io.Reader)
//go:linkname testingCheckParallel testing.checkParallel
func testingCheckParallel(t *testing.T)
// SetGlobalRandom sets a global, deterministic cryptographic randomness source
// for the duration of test t. It affects crypto/rand, and all implicit sources
// of cryptographic randomness in the crypto/... packages.
//
// SetGlobalRandom may be called multiple times in the same test to reset the
// random stream or change the seed.
//
// Because SetGlobalRandom affects the whole process, it cannot be used in
// parallel tests or tests with parallel ancestors.
//
// Note that the way cryptographic algorithms use randomness is generally not
// specified and may change over time. Thus, if a test expects a specific output
// from a cryptographic function, it may fail in the future even if it uses
// SetGlobalRandom.
//
// SetGlobalRandom is not supported when building against the Go Cryptographic
// Module v1.0.0 (i.e. when [crypto/fips140.Version] returns "v1.0.0").
func SetGlobalRandom(t *testing.T, seed uint64) {
if t == nil {
panic("cryptotest: SetGlobalRandom called with a nil *testing.T")
}
if !testing.Testing() {
panic("cryptotest: SetGlobalRandom used in a non-test binary")
}
testingCheckParallel(t)
var s [32]byte
byteorder.LEPutUint64(s[:8], seed)
r := &lockedReader{r: mathrand.NewChaCha8(s)}
randSetTestingReader(r)
previous := cryptorand.Reader
cryptorand.Reader = r
t.Cleanup(func() {
cryptorand.Reader = previous
randSetTestingReader(nil)
})
}
type lockedReader struct {
sync.Mutex
r *mathrand.ChaCha8
}
func (lr *lockedReader) Read(b []byte) (n int, err error) {
lr.Lock()
defer lr.Unlock()
return lr.r.Read(b)
}

View file

@ -0,0 +1,202 @@
// Copyright 2025 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.
//go:build !fips140v1.0
package cryptotest
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/mlkem"
"crypto/rand"
"encoding/hex"
"testing"
)
func TestSetGlobalRandom(t *testing.T) {
seed1, _ := hex.DecodeString("6ae6783f4fbde91b6eb88b73a48ed247dbe5882e2579683432c1bfc525454add" +
"0cd87274d67084caaf0e0d36c8496db7fef55fe0e125750aa608d5e20ffc2d12")
t.Run("rand.Read", func(t *testing.T) {
buf := make([]byte, 64)
t.Run("seed 1", func(t *testing.T) {
SetGlobalRandom(t, 1)
rand.Read(buf)
if !bytes.Equal(buf, seed1) {
t.Errorf("rand.Read with seed 1 = %x; want %x", buf, seed1)
}
rand.Read(buf)
if bytes.Equal(buf, seed1) {
t.Errorf("rand.Read with seed 1 returned same output twice: %x", buf)
}
SetGlobalRandom(t, 1)
rand.Read(buf)
if !bytes.Equal(buf, seed1) {
t.Errorf("rand.Read with seed 1 after reset = %x; want %x", buf, seed1)
}
SetGlobalRandom(t, 1)
})
rand.Read(buf)
if bytes.Equal(buf, seed1) {
t.Errorf("rand.Read returned seeded output after test end")
}
t.Run("seed 2", func(t *testing.T) {
SetGlobalRandom(t, 2)
rand.Read(buf)
if bytes.Equal(buf, seed1) {
t.Errorf("rand.Read with seed 2 = %x; want different from %x", buf, seed1)
}
})
})
t.Run("rand.Reader", func(t *testing.T) {
buf := make([]byte, 64)
t.Run("seed 1", func(t *testing.T) {
SetGlobalRandom(t, 1)
rand.Reader.Read(buf)
if !bytes.Equal(buf, seed1) {
t.Errorf("rand.Reader.Read with seed 1 = %x; want %x", buf, seed1)
}
SetGlobalRandom(t, 1)
})
rand.Reader.Read(buf)
if bytes.Equal(buf, seed1) {
t.Errorf("rand.Reader.Read returned seeded output after test end")
}
oldReader := rand.Reader
t.Cleanup(func() { rand.Reader = oldReader })
rand.Reader = bytes.NewReader(bytes.Repeat([]byte{5}, 64))
t.Run("seed 1 again", func(t *testing.T) {
SetGlobalRandom(t, 1)
rand.Reader.Read(buf)
if !bytes.Equal(buf, seed1) {
t.Errorf("rand.Reader.Read with seed 1 = %x; want %x", buf, seed1)
}
})
rand.Reader.Read(buf)
if !bytes.Equal(buf, bytes.Repeat([]byte{5}, 64)) {
t.Errorf("rand.Reader not restored")
}
})
// A direct internal use of drbg.Read.
t.Run("mlkem.GenerateKey768", func(t *testing.T) {
exp, err := mlkem.NewDecapsulationKey768(seed1)
if err != nil {
t.Fatalf("mlkem.NewDecapsulationKey768: %v", err)
}
SetGlobalRandom(t, 1)
got, err := mlkem.GenerateKey768()
if err != nil {
t.Fatalf("mlkem.GenerateKey768: %v", err)
}
if gotBytes := got.Bytes(); !bytes.Equal(gotBytes, exp.Bytes()) {
t.Errorf("mlkem.GenerateKey768 with seed 1 = %x; want %x", gotBytes, exp.Bytes())
}
})
// An ignored passed-in Reader.
t.Run("ecdsa.GenerateKey", func(t *testing.T) {
exp, err := ecdsa.ParseRawPrivateKey(elliptic.P384(), seed1[:48])
if err != nil {
t.Fatalf("ecdsa.ParseRawPrivateKey: %v", err)
}
SetGlobalRandom(t, 1)
got, err := ecdsa.GenerateKey(elliptic.P384(), bytes.NewReader([]byte("this reader is ignored")))
if err != nil {
t.Fatalf("ecdsa.GenerateKey: %v", err)
}
if !got.Equal(exp) {
t.Errorf("ecdsa.GenerateKey with seed 1 = %x; want %x", got.D.Bytes(), exp.D.Bytes())
}
})
// The passed-in Reader is used if cryptocustomrand=1 is set,
// and MaybeReadByte is called on it.
t.Run("cryptocustomrand=1", func(t *testing.T) {
t.Setenv("GODEBUG", "cryptocustomrand=1")
buf := make([]byte, 49)
buf[0] = 42
for i := 2; i < 49; i++ {
buf[i] = 1
}
exp1, err := ecdsa.ParseRawPrivateKey(elliptic.P384(), buf[:48])
if err != nil {
t.Fatalf("ecdsa.ParseRawPrivateKey: %v", err)
}
exp2, err := ecdsa.ParseRawPrivateKey(elliptic.P384(), buf[1:49])
if err != nil {
t.Fatalf("ecdsa.ParseRawPrivateKey: %v", err)
}
seen := [2]bool{}
for i := 0; i < 1000; i++ {
r := bytes.NewReader(buf)
got, err := ecdsa.GenerateKey(elliptic.P384(), r)
if err != nil {
t.Fatalf("ecdsa.GenerateKey: %v", err)
}
switch {
case got.Equal(exp1):
seen[0] = true
case got.Equal(exp2):
seen[1] = true
default:
t.Fatalf("ecdsa.GenerateKey with custom reader = %x; want %x or %x", got.D.Bytes(), exp1.D.Bytes(), exp2.D.Bytes())
}
if seen[0] && seen[1] {
break
}
}
if !seen[0] || !seen[1] {
t.Errorf("ecdsa.GenerateKey with custom reader did not produce both expected keys")
}
// Again, with SetGlobalRandom.
SetGlobalRandom(t, 1)
seen = [2]bool{}
for i := 0; i < 1000; i++ {
r := bytes.NewReader(buf)
got, err := ecdsa.GenerateKey(elliptic.P384(), r)
if err != nil {
t.Fatalf("ecdsa.GenerateKey: %v", err)
}
switch {
case got.Equal(exp1):
seen[0] = true
case got.Equal(exp2):
seen[1] = true
default:
t.Fatalf("ecdsa.GenerateKey with custom reader and SetGlobalRandom = %x; want %x or %x", got.D.Bytes(), exp1.D.Bytes(), exp2.D.Bytes())
}
if seen[0] && seen[1] {
break
}
}
if !seen[0] || !seen[1] {
t.Errorf("ecdsa.GenerateKey with custom reader and SetGlobalRandom did not produce both expected keys")
}
})
}

View file

@ -1749,7 +1749,7 @@ func pcToName(pc uintptr) string {
return frame.Function
}
const parallelConflict = `testing: test using t.Setenv or t.Chdir can not use t.Parallel`
const parallelConflict = `testing: test using t.Setenv, t.Chdir, or cryptotest.SetGlobalRandom can not use t.Parallel`
// Parallel signals that this test is to be run in parallel with (and only with)
// other parallel tests. When a test is run multiple times due to use of
@ -1820,6 +1820,13 @@ func (t *T) Parallel() {
t.lastRaceErrors.Store(int64(race.Errors()))
}
// checkParallel is called by [testing/cryptotest.SetGlobalRandom].
//
//go:linkname checkParallel testing.checkParallel
func checkParallel(t *T) {
t.checkParallel()
}
func (t *T) checkParallel() {
// Non-parallel subtests that have parallel ancestors may still
// run in parallel with other tests: they are only non-parallel