github.com/insolar/x-crypto@v0.0.0-20191031140942-75fab8a325f6/rand/rand_linux.go (about)

     1  // Copyright 2014 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
     6  
     7  import (
     8  	"github.com/insolar/x-crypto/rand/internal/unix"
     9  )
    10  
    11  func init() {
    12  	altGetRandom = batched(getRandomLinux, maxGetRandomRead)
    13  }
    14  
    15  // maxGetRandomRead is the maximum number of bytes to ask for in one call to the
    16  // getrandom() syscall. In linux at most 2^25-1 bytes will be returned per call.
    17  // From the manpage
    18  //
    19  //	*  When reading from the urandom source, a maximum of 33554431 bytes
    20  //	   is returned by a single call to getrandom() on systems where int
    21  //	   has a size of 32 bits.
    22  const maxGetRandomRead = (1 << 25) - 1
    23  
    24  // batched returns a function that calls f to populate a []byte by chunking it
    25  // into subslices of, at most, readMax bytes.
    26  func batched(f func([]byte) bool, readMax int) func([]byte) bool {
    27  	return func(buf []byte) bool {
    28  		for len(buf) > readMax {
    29  			if !f(buf[:readMax]) {
    30  				return false
    31  			}
    32  			buf = buf[readMax:]
    33  		}
    34  		return len(buf) == 0 || f(buf)
    35  	}
    36  }
    37  
    38  // If the kernel is too old (before 3.17) to support the getrandom syscall(),
    39  // unix.GetRandom will immediately return ENOSYS and we will then fall back to
    40  // reading from /dev/urandom in rand_unix.go. unix.GetRandom caches the ENOSYS
    41  // result so we only suffer the syscall overhead once in this case.
    42  // If the kernel supports the getrandom() syscall, unix.GetRandom will block
    43  // until the kernel has sufficient randomness (as we don't use GRND_NONBLOCK).
    44  // In this case, unix.GetRandom will not return an error.
    45  func getRandomLinux(p []byte) (ok bool) {
    46  	n, err := unix.GetRandom(p, 0)
    47  	return n == len(p) && err == nil
    48  }