1// Copyright 2010 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 unix 6 7// Unix cryptographically secure pseudorandom number 8// generator. 9 10package rand 11 12import ( 13 "crypto/internal/boring" 14 "errors" 15 "io" 16 "os" 17 "sync" 18 "sync/atomic" 19 "syscall" 20 "time" 21) 22 23const urandomDevice = "/dev/urandom" 24 25func init() { 26 if boring.Enabled { 27 Reader = boring.RandReader 28 return 29 } 30 Reader = &reader{} 31} 32 33// A reader satisfies reads by reading from urandomDevice 34type reader struct { 35 f io.Reader 36 mu sync.Mutex 37 used atomic.Uint32 // Atomic: 0 - never used, 1 - used, but f == nil, 2 - used, and f != nil 38} 39 40// altGetRandom if non-nil specifies an OS-specific function to get 41// urandom-style randomness. 42var altGetRandom func([]byte) (err error) 43 44func warnBlocked() { 45 println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") 46} 47 48func (r *reader) Read(b []byte) (n int, err error) { 49 boring.Unreachable() 50 if r.used.CompareAndSwap(0, 1) { 51 // First use of randomness. Start timer to warn about 52 // being blocked on entropy not being available. 53 t := time.AfterFunc(time.Minute, warnBlocked) 54 defer t.Stop() 55 } 56 if altGetRandom != nil && altGetRandom(b) == nil { 57 return len(b), nil 58 } 59 if r.used.Load() != 2 { 60 r.mu.Lock() 61 if r.used.Load() != 2 { 62 f, err := os.Open(urandomDevice) 63 if err != nil { 64 r.mu.Unlock() 65 return 0, err 66 } 67 r.f = hideAgainReader{f} 68 r.used.Store(2) 69 } 70 r.mu.Unlock() 71 } 72 return io.ReadFull(r.f, b) 73} 74 75// hideAgainReader masks EAGAIN reads from /dev/urandom. 76// See golang.org/issue/9205 77type hideAgainReader struct { 78 r io.Reader 79} 80 81func (hr hideAgainReader) Read(p []byte) (n int, err error) { 82 n, err = hr.r.Read(p) 83 if errors.Is(err, syscall.EAGAIN) { 84 err = nil 85 } 86 return 87} 88