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  

View as plain text