mirror of
https://github.com/golang/go.git
synced 2026-02-06 10:55:05 +03:00
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:
committed by
Gopher Robot
parent
21ebed0ac0
commit
2b8dbb35b0
1
api/next/70942.txt
Normal file
1
api/next/70942.txt
Normal file
@@ -0,0 +1 @@
|
||||
pkg testing/cryptotest, func SetGlobalRandom(*testing.T, uint64) #70942
|
||||
@@ -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
|
||||
|
||||
4
doc/next/6-stdlib/99-minor/crypto/dsa/70924.md
Normal file
4
doc/next/6-stdlib/99-minor/crypto/dsa/70924.md
Normal 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.
|
||||
4
doc/next/6-stdlib/99-minor/crypto/ecdh/70924.md
Normal file
4
doc/next/6-stdlib/99-minor/crypto/ecdh/70924.md
Normal 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.
|
||||
4
doc/next/6-stdlib/99-minor/crypto/ecdsa/70924.md
Normal file
4
doc/next/6-stdlib/99-minor/crypto/ecdsa/70924.md
Normal 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.
|
||||
4
doc/next/6-stdlib/99-minor/crypto/ed25519/70924.md
Normal file
4
doc/next/6-stdlib/99-minor/crypto/ed25519/70924.md
Normal 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.
|
||||
4
doc/next/6-stdlib/99-minor/crypto/rand/70924.md
Normal file
4
doc/next/6-stdlib/99-minor/crypto/rand/70924.md
Normal 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.
|
||||
4
doc/next/6-stdlib/99-minor/crypto/rsa/70924.md
Normal file
4
doc/next/6-stdlib/99-minor/crypto/rsa/70924.md
Normal 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.
|
||||
4
doc/next/6-stdlib/99-minor/testing/cryptotest/70942.md
Normal file
4
doc/next/6-stdlib/99-minor/testing/cryptotest/70942.md
Normal 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.
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ package hpke
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"crypto/rand"
|
||||
"crypto/internal/rand"
|
||||
"errors"
|
||||
"internal/byteorder"
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
65
src/crypto/internal/rand/rand.go
Normal file
65
src/crypto/internal/rand/rand.go
Normal 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
|
||||
}
|
||||
13
src/crypto/internal/rand/rand_fipsv1.0.go
Normal file
13
src/crypto/internal/rand/rand_fipsv1.0.go
Normal 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")
|
||||
}
|
||||
16
src/crypto/internal/rand/rand_fipsv2.0.go
Normal file
16
src/crypto/internal/rand/rand_fipsv2.0.go
Normal 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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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=...
|
||||
|
||||
76
src/testing/cryptotest/rand.go
Normal file
76
src/testing/cryptotest/rand.go
Normal 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)
|
||||
}
|
||||
202
src/testing/cryptotest/rand_test.go
Normal file
202
src/testing/cryptotest/rand_test.go
Normal 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")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user