crypto/rand: generate random numbers using RtlGenRandom on Windows

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>
diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go
index a5ccd19..fddd114 100644
--- a/src/crypto/rand/rand.go
+++ b/src/crypto/rand/rand.go
@@ -14,7 +14,7 @@
 // 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 @@
 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/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go
index 0610f69..548a5e4 100644
--- a/src/crypto/rand/rand_unix.go
+++ b/src/crypto/rand/rand_unix.go
@@ -47,6 +47,10 @@
 // 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
diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go
index 78a4ed6..8b2c960 100644
--- a/src/crypto/rand/rand_windows.go
+++ b/src/crypto/rand/rand_windows.go
@@ -9,48 +9,24 @@
 
 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
 }
diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index 40c43de..0eea2b8 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -234,6 +234,7 @@
 //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
diff --git a/src/syscall/zsyscall_windows.go b/src/syscall/zsyscall_windows.go
index 2348f65..b4222f0 100644
--- a/src/syscall/zsyscall_windows.go
+++ b/src/syscall/zsyscall_windows.go
@@ -95,6 +95,7 @@
 	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 @@
 	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))