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