// 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) }