Source file src/testing/cryptotest/rand.go
1 // Copyright 2025 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package cryptotest provides deterministic random source testing. 6 package cryptotest 7 8 import ( 9 cryptorand "crypto/rand" 10 "internal/byteorder" 11 "io" 12 mathrand "math/rand/v2" 13 "sync" 14 "testing" 15 16 // Import unsafe and crypto/rand, which imports crypto/internal/rand, 17 // for the crypto/internal/rand.SetTestingReader go:linkname. 18 _ "crypto/rand" 19 _ "unsafe" 20 ) 21 22 //go:linkname randSetTestingReader crypto/internal/rand.SetTestingReader 23 func randSetTestingReader(r io.Reader) 24 25 //go:linkname testingCheckParallel testing.checkParallel 26 func testingCheckParallel(t *testing.T) 27 28 // SetGlobalRandom sets a global, deterministic cryptographic randomness source 29 // for the duration of test t. It affects crypto/rand, and all implicit sources 30 // of cryptographic randomness in the crypto/... packages. 31 // 32 // SetGlobalRandom may be called multiple times in the same test to reset the 33 // random stream or change the seed. 34 // 35 // Because SetGlobalRandom affects the whole process, it cannot be used in 36 // parallel tests or tests with parallel ancestors. 37 // 38 // Note that the way cryptographic algorithms use randomness is generally not 39 // specified and may change over time. Thus, if a test expects a specific output 40 // from a cryptographic function, it may fail in the future even if it uses 41 // SetGlobalRandom. 42 // 43 // SetGlobalRandom is not supported when building against the Go Cryptographic 44 // Module v1.0.0 (i.e. when [crypto/fips140.Version] returns "v1.0.0"). 45 func SetGlobalRandom(t *testing.T, seed uint64) { 46 if t == nil { 47 panic("cryptotest: SetGlobalRandom called with a nil *testing.T") 48 } 49 if !testing.Testing() { 50 panic("cryptotest: SetGlobalRandom used in a non-test binary") 51 } 52 testingCheckParallel(t) 53 54 var s [32]byte 55 byteorder.LEPutUint64(s[:8], seed) 56 r := &lockedReader{r: mathrand.NewChaCha8(s)} 57 58 randSetTestingReader(r) 59 previous := cryptorand.Reader 60 cryptorand.Reader = r 61 t.Cleanup(func() { 62 cryptorand.Reader = previous 63 randSetTestingReader(nil) 64 }) 65 } 66 67 type lockedReader struct { 68 sync.Mutex 69 r *mathrand.ChaCha8 70 } 71 72 func (lr *lockedReader) Read(b []byte) (n int, err error) { 73 lr.Lock() 74 defer lr.Unlock() 75 return lr.r.Read(b) 76 } 77