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