rand.mx raw

   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([]byte)
  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 []byte
  43  		if !testingOnlyFailRead {
  44  			errStr = []byte(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