Source file src/crypto/rand/rand_unix.go

     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  }
    88  

View as plain text