1
0
mirror of https://github.com/golang/go synced 2024-11-14 06:30:22 -07:00

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>
This commit is contained in:
Jason A. Donenfeld 2019-12-05 18:48:21 +01:00
parent 8a013233ac
commit 333e90448a
5 changed files with 27 additions and 37 deletions

View File

@ -14,7 +14,7 @@ import "io"
// On Linux and FreeBSD, 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 OpenBSD, Reader uses getentropy(2).
// On other Unix-like systems, Reader reads from /dev/urandom. // 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. // On Wasm, Reader uses the Web Crypto API.
var Reader io.Reader var Reader io.Reader
@ -23,7 +23,3 @@ var Reader io.Reader
func Read(b []byte) (n int, err error) { func Read(b []byte) (n int, err error) {
return io.ReadFull(Reader, b) return io.ReadFull(Reader, b)
} }
func warnBlocked() {
println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
}

View File

@ -47,6 +47,10 @@ type devReader struct {
// urandom-style randomness. // urandom-style randomness.
var altGetRandom func([]byte) (ok bool) 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) { func (r *devReader) Read(b []byte) (n int, err error) {
if atomic.CompareAndSwapInt32(&r.used, 0, 1) { if atomic.CompareAndSwapInt32(&r.used, 0, 1) {
// First use of randomness. Start timer to warn about // First use of randomness. Start timer to warn about

View File

@ -9,48 +9,24 @@ package rand
import ( import (
"os" "os"
"sync"
"sync/atomic"
"syscall" "syscall"
"time"
) )
// Implemented by using Windows CryptoAPI 2.0.
func init() { Reader = &rngReader{} } func init() { Reader = &rngReader{} }
// A rngReader satisfies reads by reading from the Windows CryptGenRandom API. type rngReader struct{}
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) { func (r *rngReader) Read(b []byte) (n int, err error) {
if atomic.CompareAndSwapInt32(&r.used, 0, 1) { // RtlGenRandom only accepts 2**32-1 bytes at a time, so truncate.
// First use of randomness. Start timer to warn about inputLen := uint32(len(b))
// 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()
if len(b) == 0 { if inputLen == 0 {
return 0, nil return 0, nil
} }
err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0])
err = syscall.RtlGenRandom(&b[0], inputLen)
if err != nil { if err != nil {
return 0, os.NewSyscallError("CryptGenRandom", err) return 0, os.NewSyscallError("RtlGenRandom", err)
} }
return len(b), nil return int(inputLen), nil
} }

View 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 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 CryptReleaseContext(provhandle Handle, flags uint32) (err error) = advapi32.CryptReleaseContext
//sys CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) = advapi32.CryptGenRandom //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 GetEnvironmentStrings() (envs *uint16, err error) [failretval==nil] = kernel32.GetEnvironmentStringsW
//sys FreeEnvironmentStrings(envs *uint16) (err error) = kernel32.FreeEnvironmentStringsW //sys FreeEnvironmentStrings(envs *uint16) (err error) = kernel32.FreeEnvironmentStringsW
//sys GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, err error) = kernel32.GetEnvironmentVariableW //sys GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, err error) = kernel32.GetEnvironmentVariableW

View File

@ -95,6 +95,7 @@ var (
procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW") procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW")
procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext") procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext")
procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom") procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom")
procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW") procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW")
procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW") procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW")
procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW") procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW")
@ -821,6 +822,18 @@ func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) {
return 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) { func GetEnvironmentStrings() (envs *uint16, err error) {
r0, _, e1 := Syscall(procGetEnvironmentStringsW.Addr(), 0, 0, 0, 0) r0, _, e1 := Syscall(procGetEnvironmentStringsW.Addr(), 0, 0, 0, 0)
envs = (*uint16)(unsafe.Pointer(r0)) envs = (*uint16)(unsafe.Pointer(r0))