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