--- /dev/null
+pkg testing/cryptotest, func SetGlobalRandom(*testing.T, uint64) #70942
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
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
"math/big"
"crypto/internal/fips140only"
- "crypto/internal/randutil"
+ "crypto/internal/rand"
)
// Parameters represents the domain parameters for a key. These parameters can
// 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
k := new(big.Int)
buf := make([]byte, n)
for {
- _, err = io.ReadFull(rand, buf)
+ _, err = io.ReadFull(random, buf)
if err != nil {
return
}
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.
"crypto/internal/boring"
"crypto/internal/fips140/ecdh"
"crypto/internal/fips140only"
+ "crypto/internal/rand"
"errors"
"io"
)
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
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
}
"bytes"
"crypto/internal/fips140/edwards25519/field"
"crypto/internal/fips140only"
- "crypto/internal/randutil"
+ "crypto/internal/rand"
"errors"
"io"
)
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)
"crypto/internal/fips140cache"
"crypto/internal/fips140hash"
"crypto/internal/fips140only"
- "crypto/internal/randutil"
+ "crypto/internal/rand"
"crypto/sha512"
"crypto/subtle"
"errors"
// 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
}
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)
}
}
// 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
}
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)
}
}
// 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 {
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"
)
// 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
}
import (
"crypto/ecdh"
- "crypto/rand"
+ "crypto/internal/rand"
"errors"
"internal/byteorder"
)
"bytes"
"crypto"
"crypto/ecdh"
+ "crypto/internal/fips140/drbg"
+ "crypto/internal/rand"
"crypto/mlkem"
- "crypto/rand"
"crypto/sha3"
"errors"
"internal/byteorder"
func (kem *hybridKEM) GenerateKey() (PrivateKey, error) {
seed := make([]byte, 32)
- rand.Read(seed)
+ drbg.Read(seed)
return kem.NewPrivateKey(seed)
}
import (
entropy "crypto/internal/entropy/v1.0.0"
"crypto/internal/fips140"
- "crypto/internal/randutil"
"crypto/internal/sysrand"
"io"
"sync"
// 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
}
}
+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
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.
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
}
// 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.
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
}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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")
+}
--- /dev/null
+// 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)
+}
// 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
}
// 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 {
}
// 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.
//
}
// 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 {
}
// 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.
//
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
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.
// 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)
import (
"crypto/internal/fips140only"
- "crypto/internal/randutil"
+ "crypto/internal/rand"
"errors"
"io"
"math/big"
// 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")
}
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 {
p := new(big.Int)
for {
- if _, err := io.ReadFull(rand, bytes); err != nil {
+ if _, err := io.ReadFull(r, bytes); err != nil {
return nil, err
}
"crypto/internal/boring"
"crypto/internal/fips140/rsa"
"crypto/internal/fips140only"
- "crypto/internal/randutil"
+ "crypto/internal/rand"
"crypto/subtle"
"errors"
"io"
// 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
return nil, err
}
- randutil.MaybeReadByte(random)
-
k := pub.Size()
if len(msg) > k-11 {
return nil, ErrMessageTooLong
}
boring.UnreachableExceptTests()
+ random = rand.CustomReader(random)
+
// EM = 0x00 || 0x02 || PS || 0x00 || M
em := make([]byte, k)
em[1] = 2
"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"
// 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) {
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")
}
// 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.
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
}
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
}
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{},
< crypto/cipher
< crypto/internal/boring
< crypto/boring
+ < crypto/internal/rand
< crypto/aes,
crypto/des,
crypto/rc4,
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
{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"},
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=...
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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")
+ }
+ })
+}
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
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