Source file src/crypto/internal/sysrand/rand.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  // Package rand provides cryptographically secure random bytes from the
     6  // operating system.
     7  package sysrand
     8  
     9  import (
    10  	"os"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  	_ "unsafe"
    15  )
    16  
    17  var firstUse atomic.Bool
    18  
    19  func warnBlocked() {
    20  	println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
    21  }
    22  
    23  // fatal is [runtime.fatal], pushed via linkname.
    24  //
    25  //go:linkname fatal
    26  func fatal(string)
    27  
    28  var testingOnlyFailRead bool
    29  
    30  // Read fills b with cryptographically secure random bytes from the operating
    31  // system. It always fills b entirely and crashes the program irrecoverably if
    32  // an error is encountered. The operating system APIs are documented to never
    33  // return an error on all but legacy Linux systems.
    34  func Read(b []byte) {
    35  	if firstUse.CompareAndSwap(false, true) {
    36  		// First use of randomness. Start timer to warn about
    37  		// being blocked on entropy not being available.
    38  		t := time.AfterFunc(time.Minute, warnBlocked)
    39  		defer t.Stop()
    40  	}
    41  	if err := read(b); err != nil || testingOnlyFailRead {
    42  		var errStr string
    43  		if !testingOnlyFailRead {
    44  			errStr = err.Error()
    45  		} else {
    46  			errStr = "testing simulated failure"
    47  		}
    48  		fatal("crypto/rand: failed to read random data (see https://go.dev/issue/66821): " + errStr)
    49  		panic("unreachable") // To be sure.
    50  	}
    51  }
    52  
    53  // The urandom fallback is only used on Linux kernels before 3.17 and on AIX.
    54  
    55  var urandomOnce sync.Once
    56  var urandomFile *os.File
    57  var urandomErr error
    58  
    59  func urandomRead(b []byte) error {
    60  	urandomOnce.Do(func() {
    61  		urandomFile, urandomErr = os.Open("/dev/urandom")
    62  	})
    63  	if urandomErr != nil {
    64  		return urandomErr
    65  	}
    66  	for len(b) > 0 {
    67  		n, err := urandomFile.Read(b)
    68  		// Note that we don't ignore EAGAIN because it should not be possible to
    69  		// hit for a blocking read from urandom, although there were
    70  		// unreproducible reports of it at https://go.dev/issue/9205.
    71  		if err != nil {
    72  			return err
    73  		}
    74  		b = b[n:]
    75  	}
    76  	return nil
    77  }
    78  

View as plain text