blob: bf18c1b533214451293b8e9e73e835c024ac370c [file] [log] [blame] [edit]
// 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")
}
})
}