| // 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. | |
| // | |
| // Note that Read is not affected by [testing/cryptotest.SetGlobalRand], and it | |
| // should not be used directly by algorithm implementations. | |
| 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 | |
| } | |