github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/pkg/rand/random_linux.go (about)

     1  // Copyright 2019 the u-root 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
     6  
     7  import (
     8  	"context"
     9  	"sync"
    10  	"syscall"
    11  
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  var defaultContextReader = &getrandomReader{}
    16  
    17  var backupReader = &urandomReader{}
    18  
    19  type getrandomReader struct {
    20  	once   sync.Once
    21  	backup bool
    22  }
    23  
    24  // ReadContext implements a cancelable read from /dev/urandom.
    25  func (r *getrandomReader) ReadContext(ctx context.Context, b []byte) (int, error) {
    26  	r.once.Do(func() {
    27  		if _, err := unix.Getrandom(b, unix.GRND_NONBLOCK); err == syscall.ENOSYS {
    28  			r.backup = true
    29  		}
    30  	})
    31  	if r.backup {
    32  		return backupReader.ReadContext(ctx, b)
    33  	}
    34  
    35  	for {
    36  		// getrandom(2) with GRND_NONBLOCK uses the urandom number
    37  		// source, but only returns numbers if the crng has been
    38  		// initialized.
    39  		//
    40  		// This is preferrable to /dev/urandom, as /dev/urandom will
    41  		// make up fake random numbers until the crng has been
    42  		// initialized.
    43  		n, err := unix.Getrandom(b, unix.GRND_NONBLOCK)
    44  		if err == nil {
    45  			return n, err
    46  		}
    47  		select {
    48  		case <-ctx.Done():
    49  			return 0, ctx.Err()
    50  
    51  		default:
    52  			if err != nil && err != syscall.EAGAIN && err != syscall.EINTR {
    53  				return n, err
    54  			}
    55  		}
    56  	}
    57  }