github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/crypto/rand/rand_unix.go (about) 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 10 package rand 11 12 import ( 13 "crypto/internal/boring" 14 "errors" 15 "io" 16 "os" 17 "sync" 18 "sync/atomic" 19 "syscall" 20 "time" 21 ) 22 23 const urandomDevice = "/dev/urandom" 24 25 func 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 34 type 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. 42 var altGetRandom func([]byte) (err error) 43 44 func warnBlocked() { 45 println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") 46 } 47 48 func (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 77 type hideAgainReader struct { 78 r io.Reader 79 } 80 81 func (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 }