| // 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. |
| |
| //go:build unix |
| |
| // Unix cryptographically secure pseudorandom number |
| // generator. |
| |
| package rand |
| |
| import ( |
| "bufio" |
| "errors" |
| "io" |
| "os" |
| "sync" |
| "sync/atomic" |
| "syscall" |
| "time" |
| ) |
| |
| const urandomDevice = "/dev/urandom" |
| |
| func init() { |
| Reader = &reader{} |
| } |
| |
| // A reader satisfies reads by reading from urandomDevice |
| type reader struct { |
| f io.Reader |
| mu sync.Mutex |
| used int32 // atomic; whether this reader has been used |
| } |
| |
| // altGetRandom if non-nil specifies an OS-specific function to get |
| // urandom-style randomness. |
| var altGetRandom func([]byte) (ok bool) |
| |
| func warnBlocked() { |
| println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") |
| } |
| |
| func (r *reader) Read(b []byte) (n int, err error) { |
| if atomic.CompareAndSwapInt32(&r.used, 0, 1) { |
| // 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 altGetRandom != nil && altGetRandom(b) { |
| return len(b), nil |
| } |
| r.mu.Lock() |
| defer r.mu.Unlock() |
| if r.f == nil { |
| f, err := os.Open(urandomDevice) |
| if err != nil { |
| return 0, err |
| } |
| r.f = bufio.NewReader(hideAgainReader{f}) |
| } |
| return r.f.Read(b) |
| } |
| |
| // hideAgainReader masks EAGAIN reads from /dev/urandom. |
| // See golang.org/issue/9205 |
| type hideAgainReader struct { |
| r io.Reader |
| } |
| |
| func (hr hideAgainReader) Read(p []byte) (n int, err error) { |
| n, err = hr.r.Read(p) |
| if errors.Is(err, syscall.EAGAIN) { |
| err = nil |
| } |
| return |
| } |