--- /dev/null
+// Copyright 2010 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.
+
+// Package rand provides cryptographically secure random bytes from the
+// operating system.
+package sysrand
+
+import (
+ "os"
+ "sync"
+ "sync/atomic"
+ "time"
+ _ "unsafe"
+)
+
+var firstUse atomic.Bool
+
+func warnBlocked() {
+ println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
+}
+
+// fatal is [runtime.fatal], pushed via linkname.
+//
+//go:linkname fatal
+func fatal(string)
+
+var testingOnlyFailRead bool
+
+// Read fills b with cryptographically secure random bytes from the operating
+// system. It always fills b entirely and crashes the program irrecoverably if
+// an error is encountered. The operating system APIs are documented to never
+// return an error on all but legacy Linux systems.
+func Read(b []byte) {
+ if firstUse.CompareAndSwap(false, true) {
+ // First use of randomness. Start timer to warn about
+ // being blocked on entropy not being available.
+ t := time.AfterFunc(time.Minute, warnBlocked)
+ defer t.Stop()
+ }
+ if err := read(b); err != nil || testingOnlyFailRead {
+ var errStr string
+ if !testingOnlyFailRead {
+ errStr = err.Error()
+ } else {
+ errStr = "testing simulated failure"
+ }
+ fatal("crypto/rand: failed to read random data (see https://go.dev/issue/66821): " + errStr)
+ panic("unreachable") // To be sure.
+ }
+}
+
+// The urandom fallback is only used on Linux kernels before 3.17 and on AIX.
+
+var urandomOnce sync.Once
+var urandomFile *os.File
+var urandomErr error
+
+func urandomRead(b []byte) error {
+ urandomOnce.Do(func() {
+ urandomFile, urandomErr = os.Open("/dev/urandom")
+ })
+ if urandomErr != nil {
+ return urandomErr
+ }
+ for len(b) > 0 {
+ n, err := urandomFile.Read(b)
+ // Note that we don't ignore EAGAIN because it should not be possible to
+ // hit for a blocking read from urandom, although there were
+ // unreproducible reports of it at https://go.dev/issue/9205.
+ if err != nil {
+ return err
+ }
+ b = b[n:]
+ }
+ return nil
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package rand
+package sysrand
func read(b []byte) error {
return urandomRead(b)
//go:build darwin || openbsd
-package rand
+package sysrand
import "internal/syscall/unix"
//go:build dragonfly || freebsd || linux || solaris
-package rand
+package sysrand
import (
"errors"
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package rand
+package sysrand
// The maximum buffer size for crypto.getRandomValues is 65536 bytes.
// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#exceptions
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package rand_test
+package sysrand_test
import (
"bytes"
- "crypto/rand/internal/seccomp"
+ "crypto/internal/sysrand/internal/seccomp"
"internal/syscall/unix"
"internal/testenv"
"os"
t.Skip("skipping test in short mode")
}
testenv.MustHaveExec(t)
- testenv.MustHaveCGO(t)
done := make(chan struct{})
go func() {
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package rand
+package sysrand
import "internal/syscall/unix"
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package rand
+package sysrand
import (
"internal/byteorder"
--- /dev/null
+// Copyright 2010 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.
+
+package sysrand
+
+import (
+ "bytes"
+ "compress/flate"
+ "internal/asan"
+ "internal/msan"
+ "internal/race"
+ "internal/testenv"
+ "os"
+ "runtime"
+ "sync"
+ "testing"
+)
+
+func TestRead(t *testing.T) {
+ // 40MiB, more than the documented maximum of 32Mi-1 on Linux 32-bit.
+ b := make([]byte, 40<<20)
+ Read(b)
+
+ if testing.Short() {
+ b = b[len(b)-100_000:]
+ }
+
+ var z bytes.Buffer
+ f, _ := flate.NewWriter(&z, 5)
+ f.Write(b)
+ f.Close()
+ if z.Len() < len(b)*99/100 {
+ t.Fatalf("Compressed %d -> %d", len(b), z.Len())
+ }
+}
+
+func TestReadByteValues(t *testing.T) {
+ b := make([]byte, 1)
+ v := make(map[byte]bool)
+ for {
+ Read(b)
+ v[b[0]] = true
+ if len(v) == 256 {
+ break
+ }
+ }
+}
+
+func TestReadEmpty(t *testing.T) {
+ Read(make([]byte, 0))
+ Read(nil)
+}
+
+func TestConcurrentRead(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ const N = 100
+ const M = 1000
+ var wg sync.WaitGroup
+ wg.Add(N)
+ for i := 0; i < N; i++ {
+ go func() {
+ defer wg.Done()
+ for i := 0; i < M; i++ {
+ b := make([]byte, 32)
+ Read(b)
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+var sink byte
+
+func TestAllocations(t *testing.T) {
+ if race.Enabled || msan.Enabled || asan.Enabled {
+ t.Skip("urandomRead allocates under -race, -asan, and -msan")
+ }
+ if runtime.GOOS == "plan9" {
+ t.Skip("plan9 allocates")
+ }
+ testenv.SkipIfOptimizationOff(t)
+
+ n := int(testing.AllocsPerRun(10, func() {
+ buf := make([]byte, 32)
+ Read(buf)
+ sink ^= buf[0]
+ }))
+ if n > 0 {
+ t.Errorf("allocs = %d, want 0", n)
+ }
+}
+
+// TestNoUrandomFallback ensures the urandom fallback is not reached in
+// normal operations.
+func TestNoUrandomFallback(t *testing.T) {
+ expectFallback := false
+ if runtime.GOOS == "aix" {
+ // AIX always uses the urandom fallback.
+ expectFallback = true
+ }
+ if os.Getenv("GO_GETRANDOM_DISABLED") == "1" {
+ // We are testing the urandom fallback intentionally.
+ expectFallback = true
+ }
+ Read(make([]byte, 1))
+ if urandomFile != nil && !expectFallback {
+ t.Error("/dev/urandom fallback used unexpectedly")
+ t.Log("note: if this test fails, it may be because the system does not have getrandom(2)")
+ }
+ if urandomFile == nil && expectFallback {
+ t.Error("/dev/urandom fallback not used as expected")
+ }
+}
+
+func TestReadError(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode")
+ }
+ testenv.MustHaveExec(t)
+
+ // We run this test in a subprocess because it's expected to crash.
+ if os.Getenv("GO_TEST_READ_ERROR") == "1" {
+ testingOnlyFailRead = true
+ Read(make([]byte, 32))
+ t.Error("Read did not crash")
+ return
+ }
+
+ cmd := testenv.Command(t, os.Args[0], "-test.run=TestReadError")
+ cmd.Env = append(os.Environ(), "GO_TEST_READ_ERROR=1")
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Error("subprocess succeeded unexpectedly")
+ }
+ exp := "fatal error: crypto/rand: failed to read random data"
+ if !bytes.Contains(out, []byte(exp)) {
+ t.Errorf("subprocess output does not contain %q: %s", exp, out)
+ }
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package rand
+package sysrand
import "syscall"
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package rand
+package sysrand
import "internal/syscall/windows"
import (
"crypto/internal/boring"
+ "crypto/internal/sysrand"
"io"
- "os"
- "sync"
- "sync/atomic"
- "time"
_ "unsafe"
)
Reader = &reader{}
}
-var firstUse atomic.Bool
-
-func warnBlocked() {
- println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
-}
-
type reader struct{}
-// Read always returns len(b) or an error.
func (r *reader) Read(b []byte) (n int, err error) {
boring.Unreachable()
- if firstUse.CompareAndSwap(false, true) {
- // First use of randomness. Start timer to warn about
- // being blocked on entropy not being available.
- t := time.AfterFunc(time.Minute, warnBlocked)
- defer t.Stop()
- }
- if err := read(b); err != nil {
- return 0, err
- }
+ sysrand.Read(b)
return len(b), nil
}
}
return len(b), nil
}
-
-// The urandom fallback is only used on Linux kernels before 3.17 and on AIX.
-
-var urandomOnce sync.Once
-var urandomFile *os.File
-var urandomErr error
-
-func urandomRead(b []byte) error {
- urandomOnce.Do(func() {
- urandomFile, urandomErr = os.Open("/dev/urandom")
- })
- if urandomErr != nil {
- return urandomErr
- }
- for len(b) > 0 {
- n, err := urandomFile.Read(b)
- // Note that we don't ignore EAGAIN because it should not be possible to
- // hit for a blocking read from urandom, although there were
- // unreproducible reports of it at https://go.dev/issue/9205.
- if err != nil {
- return err
- }
- b = b[n:]
- }
- return nil
-}
"testing"
)
+// These tests are mostly duplicates of the tests in crypto/internal/sysrand,
+// and testing both the Reader and Read is pretty redundant when one calls the
+// other, but better safe than sorry.
+
func testReadAndReader(t *testing.T, f func(*testing.T, func([]byte) (int, error))) {
t.Run("Read", func(t *testing.T) {
f(t, Read)
}
}
-// TestNoUrandomFallback ensures the urandom fallback is not reached in
-// normal operations.
-func TestNoUrandomFallback(t *testing.T) {
- expectFallback := false
- if runtime.GOOS == "aix" {
- // AIX always uses the urandom fallback.
- expectFallback = true
- }
- if os.Getenv("GO_GETRANDOM_DISABLED") == "1" {
- // We are testing the urandom fallback intentionally.
- expectFallback = true
- }
- Read(make([]byte, 1))
- if urandomFile != nil && !expectFallback {
- t.Error("/dev/urandom fallback used unexpectedly")
- t.Log("note: if this test fails, it may be because the system does not have getrandom(2)")
- }
- if urandomFile == nil && expectFallback {
- t.Error("/dev/urandom fallback not used as expected")
- }
-}
-
func TestReadError(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode")
Reader = readerFunc(func([]byte) (int, error) {
return 0, errors.New("error")
})
- if _, err := Read(make([]byte, 32)); err == nil {
- t.Error("Read did not return error")
- }
+ Read(make([]byte, 32))
+ t.Error("Read did not crash")
return
}
< net/mail;
STR < crypto/internal/impl;
+ OS < crypto/internal/sysrand;
# FIPS is the FIPS 140 module.
# It must not depend on external crypto packages.
# Internal packages imported by FIPS might need to retain
# backwards compatibility with older versions of the module.
- STR, crypto/internal/impl
+ STR, crypto/internal/impl, crypto/internal/sysrand
< crypto/internal/fips
< crypto/internal/fips/alias
< crypto/internal/fips/subtle
< crypto/internal/cryptotest;
CGO, FMT
- < crypto/rand/internal/seccomp;
+ < crypto/internal/sysrand/internal/seccomp;
# v2 execution trace parser.
FMT
fatal(s)
}
+//go:linkname sysrand_fatal crypto/internal/sysrand.fatal
+func sysrand_fatal(s string) {
+ fatal(s)
+}
+
//go:linkname fips_fatal crypto/internal/fips.fatal
func fips_fatal(s string) {
fatal(s)