From c7fb621afaeca216dca3fed1d3cb596f41d21175 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)". Preload and use the BCRYPT_RNG_ALGORITHM provider. It follows the standards: FIPS 186-2, FIPS 140-2, NIST SP 800-90 Fixes #33542 --- src/crypto/rand/rand.go | 2 +- src/crypto/rand/rand_windows.go | 23 ++++++++++++------- .../syscall/windows/syscall_windows.go | 7 ++++++ .../syscall/windows/zsyscall_windows.go | 15 ++++++++++++ 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go index a5ccd19de32994..6127d60a4f7ab3 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 with provider BCRYPT_RNG_ALGORITHM. // 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..8509e5f0317758 100644 --- a/src/crypto/rand/rand_windows.go +++ b/src/crypto/rand/rand_windows.go @@ -8,6 +8,7 @@ package rand import ( + "internal/syscall/windows" "os" "sync" "sync/atomic" @@ -35,12 +36,18 @@ func (r *rngReader) Read(b []byte) (n int, err error) { } 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) + // BCRYPT_RNG_ALGORITHM is defined here: + // https://docs.microsoft.com/en-us/windows/win32/seccng/cng-algorithm-identifiers + // Standard: FIPS 186-2, FIPS 140-2, NIST SP 800-90 + algID, err := syscall.UTF16PtrFromString(windows.BCRYPT_RNG_ALGORITHM) if err != nil { r.mu.Unlock() - return 0, os.NewSyscallError("CryptAcquireContext", err) + return 0, err + } + status := windows.BCryptOpenAlgorithmProvider(&r.prov, algID, nil, 0) + if status != 0 { + r.mu.Unlock() + return 0, os.NewSyscallError("BCryptOpenAlgorithmProvider", syscall.Errno(status)) } } r.mu.Unlock() @@ -48,9 +55,9 @@ func (r *rngReader) Read(b []byte) (n int, err error) { if len(b) == 0 { return 0, nil } - err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) - if err != nil { - return 0, os.NewSyscallError("CryptGenRandom", err) + status := windows.BCryptGenRandom(r.prov, &b[0], uint32(len(b)), 0) + if status != 0 { + return 0, os.NewSyscallError("BCryptGenRandom", syscall.Errno(status)) } - return len(b), nil + return int(uint32(len(b))), nil } diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go index edf0b5a40b8a28..23e0498ff7d57d 100644 --- a/src/internal/syscall/windows/syscall_windows.go +++ b/src/internal/syscall/windows/syscall_windows.go @@ -145,6 +145,13 @@ const ( //sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW //sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW //sys GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW +//sys BCryptOpenAlgorithmProvider(algHandle *syscall.Handle, algID *uint16, impl *uint16, flags uint32) (status int32) = bcrypt.BCryptOpenAlgorithmProvider +//sys BCryptGenRandom(algHandle syscall.Handle, buf *byte, buflen uint32, flags uint32) (status int32) = bcrypt.BCryptGenRandom + +const ( + // bcrypt.h + BCRYPT_RNG_ALGORITHM = "RNG" +) const ( WSA_FLAG_OVERLAPPED = 0x01 diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go index ca5b4e6f16ddb3..1bc9d3bfdfb587 100644 --- a/src/internal/syscall/windows/zsyscall_windows.go +++ b/src/internal/syscall/windows/zsyscall_windows.go @@ -38,6 +38,7 @@ func errnoErr(e syscall.Errno) error { var ( modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll")) modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) + modbcrypt = syscall.NewLazyDLL(sysdll.Add("bcrypt.dll")) modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) @@ -48,6 +49,8 @@ var ( procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") procMoveFileExW = modkernel32.NewProc("MoveFileExW") procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW") + procBCryptOpenAlgorithmProvider = modbcrypt.NewProc("BCryptOpenAlgorithmProvider") + procBCryptGenRandom = modbcrypt.NewProc("BCryptGenRandom") procWSASocketW = modws2_32.NewProc("WSASocketW") procLockFileEx = modkernel32.NewProc("LockFileEx") procUnlockFileEx = modkernel32.NewProc("UnlockFileEx") @@ -118,6 +121,18 @@ func GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, return } +func BCryptOpenAlgorithmProvider(algHandle *syscall.Handle, algID *uint16, impl *uint16, flags uint32) (status int32) { + r0, _, _ := syscall.Syscall6(procBCryptOpenAlgorithmProvider.Addr(), 4, uintptr(unsafe.Pointer(algHandle)), uintptr(unsafe.Pointer(algID)), uintptr(unsafe.Pointer(impl)), uintptr(flags), 0, 0) + status = int32(r0) + return +} + +func BCryptGenRandom(algHandle syscall.Handle, buf *byte, buflen uint32, flags uint32) (status int32) { + r0, _, _ := syscall.Syscall6(procBCryptGenRandom.Addr(), 4, uintptr(algHandle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(flags), 0, 0) + status = int32(r0) + return +} + func WSASocket(af int32, typ int32, protocol int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (handle syscall.Handle, err error) { r0, _, e1 := syscall.Syscall6(procWSASocketW.Addr(), 6, uintptr(af), uintptr(typ), uintptr(protocol), uintptr(unsafe.Pointer(protinfo)), uintptr(group), uintptr(flags)) handle = syscall.Handle(r0)