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