From ddc7dead63f3593cddf45599358c5f26cdd49687 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Fri, 8 May 2020 02:01:05 +0300 Subject: [PATCH] crypto/rand: use BCryptGenRandom instead of CryptGenRandom on Windows The existing function that is used is CryptGenRandom. This function and the whole underling API is deprecated. Use the function BCryptGenRandom from the new recommended API called "Cryptography API: Next Generation (CNG)". Fixes #33542 --- src/crypto/rand/rand.go | 2 +- src/crypto/rand/rand_windows.go | 36 +++++---------------------------- src/syscall/syscall_windows.go | 1 + src/syscall/types_windows.go | 5 +++++ src/syscall/zsyscall_windows.go | 14 +++++++++++++ 5 files changed, 26 insertions(+), 32 deletions(-) diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go index a5ccd19de32994..107e154494c742 100644 --- a/src/crypto/rand/rand.go +++ b/src/crypto/rand/rand.go @@ -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 BCryptGenRandom API. // On Wasm, Reader uses the Web Crypto API. var Reader io.Reader diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go index 78a4ed6d67b5c8..fb3a95aaf648bf 100644 --- a/src/crypto/rand/rand_windows.go +++ b/src/crypto/rand/rand_windows.go @@ -9,48 +9,22 @@ package rand import ( "os" - "sync" - "sync/atomic" "syscall" - "time" ) -// Implemented by using Windows CryptoAPI 2.0. +// Implemented by using the Cryptography Next Generation (CNG) API. 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() - if len(b) == 0 { return 0, nil } - err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) + err = syscall.BCryptGenRandom(0, &b[0], uint32(len(b)), syscall.BCRYPT_USE_SYSTEM_PREFERRED_RNG) if err != nil { - return 0, os.NewSyscallError("CryptGenRandom", err) + return 0, os.NewSyscallError("BCryptGenRandom", err) } - return len(b), nil + return int(uint32(len(b))), nil } diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 89c0a930cb6b93..39cb8d6832b966 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -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 BCryptGenRandom(algorithmhandle Handle, buf *byte, buflen uint32, flags uint32) (err error) [failretval!=0] = bcrypt.BCryptGenRandom //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/types_windows.go b/src/syscall/types_windows.go index 0349f3b180dea0..9ee66875fbc599 100644 --- a/src/syscall/types_windows.go +++ b/src/syscall/types_windows.go @@ -214,6 +214,11 @@ const ( FILE_ACTION_RENAMED_NEW_NAME ) +const ( + // bcrypt.h + BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002 +) + const ( // wincrypt.h PROV_RSA_FULL = 1 diff --git a/src/syscall/zsyscall_windows.go b/src/syscall/zsyscall_windows.go index 2348f6534f7af7..31e9c1a3dac09c 100644 --- a/src/syscall/zsyscall_windows.go +++ b/src/syscall/zsyscall_windows.go @@ -37,6 +37,7 @@ func errnoErr(e Errno) error { var ( modkernel32 = NewLazyDLL(sysdll.Add("kernel32.dll")) modadvapi32 = NewLazyDLL(sysdll.Add("advapi32.dll")) + modbcrypt = NewLazyDLL(sysdll.Add("bcrypt.dll")) modshell32 = NewLazyDLL(sysdll.Add("shell32.dll")) modmswsock = NewLazyDLL(sysdll.Add("mswsock.dll")) modcrypt32 = NewLazyDLL(sysdll.Add("crypt32.dll")) @@ -95,6 +96,7 @@ var ( procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW") procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext") procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom") + procBCryptGenRandom = modbcrypt.NewProc("BCryptGenRandom") procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW") procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW") procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW") @@ -821,6 +823,18 @@ func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) { return } +func BCryptGenRandom(algorithmhandle Handle, buf *byte, buflen uint32, flags uint32) (err error) { + r1, _, e1 := Syscall6(procBCryptGenRandom.Addr(), 4, uintptr(algorithmhandle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(flags), 0, 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))