Source file
src/crypto/rand/rand_unix.go
1
2
3
4
5
6
7
8
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
34 type reader struct {
35 f io.Reader
36 mu sync.Mutex
37 used atomic.Uint32
38 }
39
40
41
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
52
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
76
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 }
88
View as plain text