]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/crypto/rand/rand_unix.go
[dev.boringcrypto] all: merge master into dev.boringcrypto
[gostls13.git] / src / crypto / rand / rand_unix.go
index c9eb7d992de0aea9da3213612bb441da1cfcc47f..0b137e131f0142b50b6082cc4b39168080bab3db 100644 (file)
@@ -15,7 +15,6 @@ import (
        "io"
        "os"
        "sync"
-       "sync/atomic"
        "syscall"
        "time"
 )
@@ -36,20 +35,70 @@ func init() {
 type reader struct {
        f    io.Reader
        mu   sync.Mutex
-       used int32 // atomic; whether this reader has been used
+       used bool // whether this reader has been used
 }
 
 // altGetRandom if non-nil specifies an OS-specific function to get
 // urandom-style randomness.
 var altGetRandom func([]byte) (ok bool)
 
+// batched returns a function that calls f to populate a []byte by chunking it
+// into subslices of, at most, readMax bytes, buffering min(readMax, 4096)
+// bytes at a time.
+func batched(f func([]byte) error, readMax int) func([]byte) bool {
+       bufferSize := 4096
+       if bufferSize > readMax {
+               bufferSize = readMax
+       }
+       fullBuffer := make([]byte, bufferSize)
+       var buf []byte
+       return func(out []byte) bool {
+               // First we copy any amount remaining in the buffer.
+               n := copy(out, buf)
+               out, buf = out[n:], buf[n:]
+
+               // Then, if we're requesting more than the buffer size,
+               // generate directly into the output, chunked by readMax.
+               for len(out) >= len(fullBuffer) {
+                       read := len(out) - (len(out) % len(fullBuffer))
+                       if read > readMax {
+                               read = readMax
+                       }
+                       if f(out[:read]) != nil {
+                               return false
+                       }
+                       out = out[read:]
+               }
+
+               // If there's a partial block left over, fill the buffer,
+               // and copy in the remainder.
+               if len(out) > 0 {
+                       if f(fullBuffer[:]) != nil {
+                               return false
+                       }
+                       buf = fullBuffer[:]
+                       n = copy(out, buf)
+                       out, buf = out[n:], buf[n:]
+               }
+
+               if len(out) > 0 {
+                       panic("crypto/rand batching failed to fill buffer")
+               }
+
+               return true
+       }
+}
+
 func warnBlocked() {
        println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
 }
 
 func (r *reader) Read(b []byte) (n int, err error) {
        boring.Unreachable()
-       if atomic.CompareAndSwapInt32(&r.used, 0, 1) {
+       r.mu.Lock()
+       defer r.mu.Unlock()
+       if !r.used {
+               r.used = true
                // First use of randomness. Start timer to warn about
                // being blocked on entropy not being available.
                t := time.AfterFunc(time.Minute, warnBlocked)
@@ -58,8 +107,6 @@ func (r *reader) Read(b []byte) (n int, err error) {
        if altGetRandom != nil && altGetRandom(b) {
                return len(b), nil
        }
-       r.mu.Lock()
-       defer r.mu.Unlock()
        if r.f == nil {
                f, err := os.Open(urandomDevice)
                if err != nil {