]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/rand: generate random numbers using RtlGenRandom on Windows
authorJason A. Donenfeld <Jason@zx2c4.com>
Thu, 5 Dec 2019 17:48:21 +0000 (18:48 +0100)
committerJason A. Donenfeld <Jason@zx2c4.com>
Tue, 27 Oct 2020 12:00:35 +0000 (12:00 +0000)
CryptGenRandom appears to be unfavorable these days, whereas the classic
RtlGenRandom is still going strong.

This commit also moves the warnBlocked function into rand_unix, rather
than rand, because it's now only used on unix.

Fixes #33542

Change-Id: I5c02a5917572f54079d627972401efb6e1ce4057
Reviewed-on: https://go-review.googlesource.com/c/go/+/210057
Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>

src/crypto/rand/rand.go
src/crypto/rand/rand_unix.go
src/crypto/rand/rand_windows.go
src/syscall/syscall_windows.go
src/syscall/zsyscall_windows.go

index a5ccd19de3299489e30dc432483b5fbf9d3fa3a7..fddd1147e6e3c692b3f2d4da69f427a10e8eb6fb 100644 (file)
@@ -14,7 +14,7 @@ import "io"
 // 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.
+// On Windows systems, Reader uses the RtlGenRandom API.
 // On Wasm, Reader uses the Web Crypto API.
 var Reader io.Reader
 
@@ -23,7 +23,3 @@ 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")
-}
index 0610f691b0d494d5801e2e90c16145b44d787a39..548a5e4cb9a4d3d97c85d8459199324bd14443ce 100644 (file)
@@ -47,6 +47,10 @@ type devReader struct {
 // urandom-style randomness.
 var altGetRandom func([]byte) (ok bool)
 
+func warnBlocked() {
+       println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
+}
+
 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
index 78a4ed6d67b5c843d94f89e5c5bfea5b1a77c9b6..8b2c960906ae65c5d568596af8f0b4631f0fa006 100644 (file)
@@ -9,48 +9,24 @@ package rand
 
 import (
        "os"
-       "sync"
-       "sync/atomic"
        "syscall"
-       "time"
 )
 
-// Implemented by using Windows CryptoAPI 2.0.
-
 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
-}
+type rngReader struct{}
 
 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
-               const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT
-               err := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags)
-               if err != nil {
-                       r.mu.Unlock()
-                       return 0, os.NewSyscallError("CryptAcquireContext", err)
-               }
-       }
-       r.mu.Unlock()
+       // RtlGenRandom only accepts 2**32-1 bytes at a time, so truncate.
+       inputLen := uint32(len(b))
 
-       if len(b) == 0 {
+       if inputLen == 0 {
                return 0, nil
        }
-       err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0])
+
+       err = syscall.RtlGenRandom(&b[0], inputLen)
        if err != nil {
-               return 0, os.NewSyscallError("CryptGenRandom", err)
+               return 0, os.NewSyscallError("RtlGenRandom", err)
        }
-       return len(b), nil
+       return int(inputLen), nil
 }
index 40c43de84c8607b51c815df046327e2ef2deff4f..0eea2b87a981f59ec37ddda31fc64ceb32fb2a12 100644 (file)
@@ -234,6 +234,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys  CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (err error) = advapi32.CryptAcquireContextW
 //sys  CryptReleaseContext(provhandle Handle, flags uint32) (err error) = advapi32.CryptReleaseContext
 //sys  CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) = advapi32.CryptGenRandom
+//sys  RtlGenRandom(buf *uint8, bytes uint32) (err error) = advapi32.SystemFunction036
 //sys  GetEnvironmentStrings() (envs *uint16, err error) [failretval==nil] = kernel32.GetEnvironmentStringsW
 //sys  FreeEnvironmentStrings(envs *uint16) (err error) = kernel32.FreeEnvironmentStringsW
 //sys  GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, err error) = kernel32.GetEnvironmentVariableW
index 2348f6534f7af7c675250bb8091af1963ffa1c87..b4222f0528bc1fad855781dad523f292e835d538 100644 (file)
@@ -95,6 +95,7 @@ var (
        procCryptAcquireContextW               = modadvapi32.NewProc("CryptAcquireContextW")
        procCryptReleaseContext                = modadvapi32.NewProc("CryptReleaseContext")
        procCryptGenRandom                     = modadvapi32.NewProc("CryptGenRandom")
+       procSystemFunction036                  = modadvapi32.NewProc("SystemFunction036")
        procGetEnvironmentStringsW             = modkernel32.NewProc("GetEnvironmentStringsW")
        procFreeEnvironmentStringsW            = modkernel32.NewProc("FreeEnvironmentStringsW")
        procGetEnvironmentVariableW            = modkernel32.NewProc("GetEnvironmentVariableW")
@@ -821,6 +822,18 @@ func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) {
        return
 }
 
+func RtlGenRandom(buf *uint8, bytes uint32) (err error) {
+       r1, _, e1 := Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(bytes), 0)
+       if r1 == 0 {
+               if e1 != 0 {
+                       err = errnoErr(e1)
+               } else {
+                       err = EINVAL
+               }
+       }
+       return
+}
+
 func GetEnvironmentStrings() (envs *uint16, err error) {
        r0, _, e1 := Syscall(procGetEnvironmentStringsW.Addr(), 0, 0, 0, 0)
        envs = (*uint16)(unsafe.Pointer(r0))