Source file src/testing/cryptotest/rand_test.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  //go:build !fips140v1.0
     6  
     7  package cryptotest
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/ecdsa"
    12  	"crypto/elliptic"
    13  	"crypto/mlkem"
    14  	"crypto/rand"
    15  	"encoding/hex"
    16  	"testing"
    17  )
    18  
    19  func TestSetGlobalRandom(t *testing.T) {
    20  	seed1, _ := hex.DecodeString("6ae6783f4fbde91b6eb88b73a48ed247dbe5882e2579683432c1bfc525454add" +
    21  		"0cd87274d67084caaf0e0d36c8496db7fef55fe0e125750aa608d5e20ffc2d12")
    22  
    23  	t.Run("rand.Read", func(t *testing.T) {
    24  		buf := make([]byte, 64)
    25  
    26  		t.Run("seed 1", func(t *testing.T) {
    27  			SetGlobalRandom(t, 1)
    28  			rand.Read(buf)
    29  			if !bytes.Equal(buf, seed1) {
    30  				t.Errorf("rand.Read with seed 1 = %x; want %x", buf, seed1)
    31  			}
    32  
    33  			rand.Read(buf)
    34  			if bytes.Equal(buf, seed1) {
    35  				t.Errorf("rand.Read with seed 1 returned same output twice: %x", buf)
    36  			}
    37  
    38  			SetGlobalRandom(t, 1)
    39  			rand.Read(buf)
    40  			if !bytes.Equal(buf, seed1) {
    41  				t.Errorf("rand.Read with seed 1 after reset = %x; want %x", buf, seed1)
    42  			}
    43  
    44  			SetGlobalRandom(t, 1)
    45  		})
    46  
    47  		rand.Read(buf)
    48  		if bytes.Equal(buf, seed1) {
    49  			t.Errorf("rand.Read returned seeded output after test end")
    50  		}
    51  
    52  		t.Run("seed 2", func(t *testing.T) {
    53  			SetGlobalRandom(t, 2)
    54  			rand.Read(buf)
    55  			if bytes.Equal(buf, seed1) {
    56  				t.Errorf("rand.Read with seed 2 = %x; want different from %x", buf, seed1)
    57  			}
    58  		})
    59  	})
    60  
    61  	t.Run("rand.Reader", func(t *testing.T) {
    62  		buf := make([]byte, 64)
    63  
    64  		t.Run("seed 1", func(t *testing.T) {
    65  			SetGlobalRandom(t, 1)
    66  			rand.Reader.Read(buf)
    67  			if !bytes.Equal(buf, seed1) {
    68  				t.Errorf("rand.Reader.Read with seed 1 = %x; want %x", buf, seed1)
    69  			}
    70  
    71  			SetGlobalRandom(t, 1)
    72  		})
    73  
    74  		rand.Reader.Read(buf)
    75  		if bytes.Equal(buf, seed1) {
    76  			t.Errorf("rand.Reader.Read returned seeded output after test end")
    77  		}
    78  
    79  		oldReader := rand.Reader
    80  		t.Cleanup(func() { rand.Reader = oldReader })
    81  		rand.Reader = bytes.NewReader(bytes.Repeat([]byte{5}, 64))
    82  
    83  		t.Run("seed 1 again", func(t *testing.T) {
    84  			SetGlobalRandom(t, 1)
    85  			rand.Reader.Read(buf)
    86  			if !bytes.Equal(buf, seed1) {
    87  				t.Errorf("rand.Reader.Read with seed 1 = %x; want %x", buf, seed1)
    88  			}
    89  		})
    90  
    91  		rand.Reader.Read(buf)
    92  		if !bytes.Equal(buf, bytes.Repeat([]byte{5}, 64)) {
    93  			t.Errorf("rand.Reader not restored")
    94  		}
    95  	})
    96  
    97  	// A direct internal use of drbg.Read.
    98  	t.Run("mlkem.GenerateKey768", func(t *testing.T) {
    99  		exp, err := mlkem.NewDecapsulationKey768(seed1)
   100  		if err != nil {
   101  			t.Fatalf("mlkem.NewDecapsulationKey768: %v", err)
   102  		}
   103  
   104  		SetGlobalRandom(t, 1)
   105  		got, err := mlkem.GenerateKey768()
   106  		if err != nil {
   107  			t.Fatalf("mlkem.GenerateKey768: %v", err)
   108  		}
   109  
   110  		if gotBytes := got.Bytes(); !bytes.Equal(gotBytes, exp.Bytes()) {
   111  			t.Errorf("mlkem.GenerateKey768 with seed 1 = %x; want %x", gotBytes, exp.Bytes())
   112  		}
   113  	})
   114  
   115  	// An ignored passed-in Reader.
   116  	t.Run("ecdsa.GenerateKey", func(t *testing.T) {
   117  		exp, err := ecdsa.ParseRawPrivateKey(elliptic.P384(), seed1[:48])
   118  		if err != nil {
   119  			t.Fatalf("ecdsa.ParseRawPrivateKey: %v", err)
   120  		}
   121  
   122  		SetGlobalRandom(t, 1)
   123  		got, err := ecdsa.GenerateKey(elliptic.P384(), bytes.NewReader([]byte("this reader is ignored")))
   124  		if err != nil {
   125  			t.Fatalf("ecdsa.GenerateKey: %v", err)
   126  		}
   127  
   128  		if !got.Equal(exp) {
   129  			t.Errorf("ecdsa.GenerateKey with seed 1 = %x; want %x", got.D.Bytes(), exp.D.Bytes())
   130  		}
   131  	})
   132  
   133  	// The passed-in Reader is used if cryptocustomrand=1 is set,
   134  	// and MaybeReadByte is called on it.
   135  	t.Run("cryptocustomrand=1", func(t *testing.T) {
   136  		t.Setenv("GODEBUG", "cryptocustomrand=1")
   137  
   138  		buf := make([]byte, 49)
   139  		buf[0] = 42
   140  		for i := 2; i < 49; i++ {
   141  			buf[i] = 1
   142  		}
   143  
   144  		exp1, err := ecdsa.ParseRawPrivateKey(elliptic.P384(), buf[:48])
   145  		if err != nil {
   146  			t.Fatalf("ecdsa.ParseRawPrivateKey: %v", err)
   147  		}
   148  		exp2, err := ecdsa.ParseRawPrivateKey(elliptic.P384(), buf[1:49])
   149  		if err != nil {
   150  			t.Fatalf("ecdsa.ParseRawPrivateKey: %v", err)
   151  		}
   152  
   153  		seen := [2]bool{}
   154  		for i := 0; i < 1000; i++ {
   155  			r := bytes.NewReader(buf)
   156  			got, err := ecdsa.GenerateKey(elliptic.P384(), r)
   157  			if err != nil {
   158  				t.Fatalf("ecdsa.GenerateKey: %v", err)
   159  			}
   160  			switch {
   161  			case got.Equal(exp1):
   162  				seen[0] = true
   163  			case got.Equal(exp2):
   164  				seen[1] = true
   165  			default:
   166  				t.Fatalf("ecdsa.GenerateKey with custom reader = %x; want %x or %x", got.D.Bytes(), exp1.D.Bytes(), exp2.D.Bytes())
   167  			}
   168  			if seen[0] && seen[1] {
   169  				break
   170  			}
   171  		}
   172  		if !seen[0] || !seen[1] {
   173  			t.Errorf("ecdsa.GenerateKey with custom reader did not produce both expected keys")
   174  		}
   175  
   176  		// Again, with SetGlobalRandom.
   177  		SetGlobalRandom(t, 1)
   178  
   179  		seen = [2]bool{}
   180  		for i := 0; i < 1000; i++ {
   181  			r := bytes.NewReader(buf)
   182  			got, err := ecdsa.GenerateKey(elliptic.P384(), r)
   183  			if err != nil {
   184  				t.Fatalf("ecdsa.GenerateKey: %v", err)
   185  			}
   186  			switch {
   187  			case got.Equal(exp1):
   188  				seen[0] = true
   189  			case got.Equal(exp2):
   190  				seen[1] = true
   191  			default:
   192  				t.Fatalf("ecdsa.GenerateKey with custom reader and SetGlobalRandom = %x; want %x or %x", got.D.Bytes(), exp1.D.Bytes(), exp2.D.Bytes())
   193  			}
   194  			if seen[0] && seen[1] {
   195  				break
   196  			}
   197  		}
   198  		if !seen[0] || !seen[1] {
   199  			t.Errorf("ecdsa.GenerateKey with custom reader and SetGlobalRandom did not produce both expected keys")
   200  		}
   201  	})
   202  }
   203  

View as plain text