diff options
Diffstat (limited to 'libgo/go/crypto/rand')
-rw-r--r-- | libgo/go/crypto/rand/rand.go | 6 | ||||
-rw-r--r-- | libgo/go/crypto/rand/rand_batched.go | 42 | ||||
-rw-r--r-- | libgo/go/crypto/rand/rand_batched_test.go (renamed from libgo/go/crypto/rand/rand_linux_test.go) | 2 | ||||
-rw-r--r-- | libgo/go/crypto/rand/rand_freebsd.go | 9 | ||||
-rw-r--r-- | libgo/go/crypto/rand/rand_linux.go | 34 | ||||
-rw-r--r-- | libgo/go/crypto/rand/rand_unix.go | 18 | ||||
-rw-r--r-- | libgo/go/crypto/rand/rand_windows.go | 9 |
7 files changed, 77 insertions, 43 deletions
diff --git a/libgo/go/crypto/rand/rand.go b/libgo/go/crypto/rand/rand.go index b8df8a37117..a5ccd19de32 100644 --- a/libgo/go/crypto/rand/rand.go +++ b/libgo/go/crypto/rand/rand.go @@ -11,7 +11,7 @@ import "io" // Reader is a global, shared instance of a cryptographically // secure random number generator. // -// On Linux, Reader uses getrandom(2) if available, /dev/urandom otherwise. +// On Linux and FreeBSD, Reader uses getrandom(2) if available, /dev/urandom otherwise. // On OpenBSD, Reader uses getentropy(2). // On other Unix-like systems, Reader reads from /dev/urandom. // On Windows systems, Reader uses the CryptGenRandom API. @@ -23,3 +23,7 @@ var Reader io.Reader func Read(b []byte) (n int, err error) { return io.ReadFull(Reader, b) } + +func warnBlocked() { + println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") +} diff --git a/libgo/go/crypto/rand/rand_batched.go b/libgo/go/crypto/rand/rand_batched.go new file mode 100644 index 00000000000..60267fd4bc2 --- /dev/null +++ b/libgo/go/crypto/rand/rand_batched.go @@ -0,0 +1,42 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux freebsd + +package rand + +import ( + "internal/syscall/unix" +) + +// maxGetRandomRead is platform dependent. +func init() { + altGetRandom = batched(getRandomBatch, maxGetRandomRead) +} + +// batched returns a function that calls f to populate a []byte by chunking it +// into subslices of, at most, readMax bytes. +func batched(f func([]byte) bool, readMax int) func([]byte) bool { + return func(buf []byte) bool { + for len(buf) > readMax { + if !f(buf[:readMax]) { + return false + } + buf = buf[readMax:] + } + return len(buf) == 0 || f(buf) + } +} + +// If the kernel is too old to support the getrandom syscall(), +// unix.GetRandom will immediately return ENOSYS and we will then fall back to +// reading from /dev/urandom in rand_unix.go. unix.GetRandom caches the ENOSYS +// result so we only suffer the syscall overhead once in this case. +// If the kernel supports the getrandom() syscall, unix.GetRandom will block +// until the kernel has sufficient randomness (as we don't use GRND_NONBLOCK). +// In this case, unix.GetRandom will not return an error. +func getRandomBatch(p []byte) (ok bool) { + n, err := unix.GetRandom(p, 0) + return n == len(p) && err == nil +} diff --git a/libgo/go/crypto/rand/rand_linux_test.go b/libgo/go/crypto/rand/rand_batched_test.go index 77b7b6ebd7a..837db83f770 100644 --- a/libgo/go/crypto/rand/rand_linux_test.go +++ b/libgo/go/crypto/rand/rand_batched_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build linux freebsd + package rand import ( diff --git a/libgo/go/crypto/rand/rand_freebsd.go b/libgo/go/crypto/rand/rand_freebsd.go new file mode 100644 index 00000000000..75f683c386d --- /dev/null +++ b/libgo/go/crypto/rand/rand_freebsd.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +// maxGetRandomRead is the maximum number of bytes to ask for in one call to the +// getrandom() syscall. In FreeBSD at most 256 bytes will be returned per call. +const maxGetRandomRead = 1 << 8 diff --git a/libgo/go/crypto/rand/rand_linux.go b/libgo/go/crypto/rand/rand_linux.go index dbd038cc582..26b93c54d28 100644 --- a/libgo/go/crypto/rand/rand_linux.go +++ b/libgo/go/crypto/rand/rand_linux.go @@ -4,14 +4,6 @@ package rand -import ( - "internal/syscall/unix" -) - -func init() { - altGetRandom = batched(getRandomLinux, maxGetRandomRead) -} - // maxGetRandomRead is the maximum number of bytes to ask for in one call to the // getrandom() syscall. In linux at most 2^25-1 bytes will be returned per call. // From the manpage @@ -20,29 +12,3 @@ func init() { // is returned by a single call to getrandom() on systems where int // has a size of 32 bits. const maxGetRandomRead = (1 << 25) - 1 - -// batched returns a function that calls f to populate a []byte by chunking it -// into subslices of, at most, readMax bytes. -func batched(f func([]byte) bool, readMax int) func([]byte) bool { - return func(buf []byte) bool { - for len(buf) > readMax { - if !f(buf[:readMax]) { - return false - } - buf = buf[readMax:] - } - return len(buf) == 0 || f(buf) - } -} - -// If the kernel is too old (before 3.17) to support the getrandom syscall(), -// unix.GetRandom will immediately return ENOSYS and we will then fall back to -// reading from /dev/urandom in rand_unix.go. unix.GetRandom caches the ENOSYS -// result so we only suffer the syscall overhead once in this case. -// If the kernel supports the getrandom() syscall, unix.GetRandom will block -// until the kernel has sufficient randomness (as we don't use GRND_NONBLOCK). -// In this case, unix.GetRandom will not return an error. -func getRandomLinux(p []byte) (ok bool) { - n, err := unix.GetRandom(p, 0) - return n == len(p) && err == nil -} diff --git a/libgo/go/crypto/rand/rand_unix.go b/libgo/go/crypto/rand/rand_unix.go index ec474d36ccf..f3091f51c5f 100644 --- a/libgo/go/crypto/rand/rand_unix.go +++ b/libgo/go/crypto/rand/rand_unix.go @@ -13,10 +13,12 @@ import ( "bufio" "crypto/aes" "crypto/cipher" + "encoding/binary" "io" "os" "runtime" "sync" + "sync/atomic" "time" ) @@ -38,6 +40,7 @@ type devReader struct { name string f io.Reader mu sync.Mutex + used int32 // atomic; whether this devReader has been used } // altGetRandom if non-nil specifies an OS-specific function to get @@ -45,6 +48,12 @@ type devReader struct { var altGetRandom func([]byte) (ok bool) func (r *devReader) Read(b []byte) (n int, err error) { + if atomic.CompareAndSwapInt32(&r.used, 0, 1) { + // First use of randomness. Start timer to warn about + // being blocked on entropy not being available. + t := time.AfterFunc(60*time.Second, warnBlocked) + defer t.Stop() + } if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) { return len(b), nil } @@ -137,14 +146,7 @@ func (r *reader) Read(b []byte) (n int, err error) { // dst = encrypt(t^seed) // seed = encrypt(t^dst) ns := time.Now().UnixNano() - r.time[0] = byte(ns >> 56) - r.time[1] = byte(ns >> 48) - r.time[2] = byte(ns >> 40) - r.time[3] = byte(ns >> 32) - r.time[4] = byte(ns >> 24) - r.time[5] = byte(ns >> 16) - r.time[6] = byte(ns >> 8) - r.time[7] = byte(ns) + binary.BigEndian.PutUint64(r.time[:], uint64(ns)) r.cipher.Encrypt(r.time[0:], r.time[0:]) for i := 0; i < aes.BlockSize; i++ { r.dst[i] = r.time[i] ^ r.seed[i] diff --git a/libgo/go/crypto/rand/rand_windows.go b/libgo/go/crypto/rand/rand_windows.go index 4d7511a8400..78a4ed6d67b 100644 --- a/libgo/go/crypto/rand/rand_windows.go +++ b/libgo/go/crypto/rand/rand_windows.go @@ -10,7 +10,9 @@ package rand import ( "os" "sync" + "sync/atomic" "syscall" + "time" ) // Implemented by using Windows CryptoAPI 2.0. @@ -19,11 +21,18 @@ func init() { Reader = &rngReader{} } // A rngReader satisfies reads by reading from the Windows CryptGenRandom API. type rngReader struct { + used int32 // atomic; whether this rngReader has been used prov syscall.Handle mu sync.Mutex } func (r *rngReader) Read(b []byte) (n int, err error) { + if atomic.CompareAndSwapInt32(&r.used, 0, 1) { + // First use of randomness. Start timer to warn about + // being blocked on entropy not being available. + t := time.AfterFunc(60*time.Second, warnBlocked) + defer t.Stop() + } r.mu.Lock() if r.prov == 0 { const provType = syscall.PROV_RSA_FULL |