Use ProcessPrng instead of RtlGenRandom on Windows

The Windows system RNG[1] lives in bcryptprimitives.dll which exports
the function ProcessPrng[2] to supply random bytes from its internal
generators. These are seeded and reseeded from the operating
system using a device connection to \\Device\CNG which is opened
when bcryptprimitives.dll is first loaded.

After this CL boringssl calls ProcessPrng() directly.

Before this CL boringssl got its system randomness (on non-UWP
desktop Windows) from calls to RtlGenRandom[3].
This function is undocumented and unsupported, but has always been
available by linking to SystemFunction036 in advadpi32.dll. In
Windows 10 and later, this export simply forwards to
cryptbase.dll!SystemFunction036 which calls ProcessPrng()
directly.

cryptbase!SystemFunction036 decompiled:

```
BOOLEAN SystemFunction036(PVOID RandomBuffer,ULONG RandomBufferLength)
{
  BOOL retval;
  retval = ProcessPrng(RandomBuffer,RandomBufferLength);
  return retval != 0;
}
```

Loading cryptbase.dll has the side effect of opening a device handle
to \\Device\KsecDD which is not used by boringssl's random number
wrappers. Calling ProcessPrng() directly allows sandboxed programs
such as Chromium to avoid having this handle if they do not need it.
ProcessPrng() also takes a size_t length rather than a u32 length,
allowing some simplification of the calling code.

After this CL we require bcryptprimitives to be loaded before the
first call to CRYPTO_srand(). Applications using the library should
either load the module themselves or call CRYPTO_pre_sandbox_init().
Before this CL boringssl required that advapi32, cryptbase and
bcryptprimitives were all loaded so this should not represent a
breaking change.

[1] https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
[2] https://download.microsoft.com/download/1/c/9/1c9813b8-089c-4fef-b2ad-ad80e79403ba/Whitepaper%20-%20The%20Windows%2010%20random%20number%20generation%20infrastructure.pdf
[3] https://docs.google.com/document/d/13n1t5ak0yofzcadQCF7Ew5TewSUkNfQ3n-IYodjeRYc/edit

Bug: chromium:74242
Change-Id: Ifb1d6ef1a4539ff6e9a2c36cc119b7700ca2be8f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60825
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
This commit is contained in:
Alex Gough 2023-06-15 09:44:35 -07:00 committed by Boringssl LUCI CQ
parent bc97b7a8e1
commit e79649ba4d
2 changed files with 41 additions and 19 deletions

View File

@ -70,11 +70,15 @@ void CRYPTO_sysrand(uint8_t *buf, size_t len);
// depending on the vendor's configuration.
void CRYPTO_sysrand_for_seed(uint8_t *buf, size_t len);
#if defined(OPENSSL_URANDOM)
#if defined(OPENSSL_URANDOM) || defined(OPENSSL_WINDOWS)
// CRYPTO_init_sysrand initializes long-lived resources needed to draw entropy
// from the operating system.
void CRYPTO_init_sysrand(void);
#else
OPENSSL_INLINE void CRYPTO_init_sysrand(void) {}
#endif // defined(OPENSSL_URANDOM) || defined(OPENSSL_WINDOWS)
#if defined(OPENSSL_URANDOM)
// CRYPTO_sysrand_if_available fills |len| bytes at |buf| with entropy from the
// operating system, or early /dev/urandom data, and returns 1, _if_ the entropy
// pool is initialized or if getrandom() is not available and not in FIPS mode.
@ -82,13 +86,11 @@ void CRYPTO_init_sysrand(void);
// return 0.
int CRYPTO_sysrand_if_available(uint8_t *buf, size_t len);
#else
OPENSSL_INLINE void CRYPTO_init_sysrand(void) {}
OPENSSL_INLINE int CRYPTO_sysrand_if_available(uint8_t *buf, size_t len) {
CRYPTO_sysrand(buf, len);
return 1;
}
#endif
#endif // defined(OPENSSL_URANDOM)
// rand_fork_unsafe_buffering_enabled returns whether fork-unsafe buffering has
// been enabled via |RAND_enable_fork_unsafe_buffering|.

View File

@ -27,43 +27,63 @@ OPENSSL_MSVC_PRAGMA(warning(push, 3))
!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#include <bcrypt.h>
OPENSSL_MSVC_PRAGMA(comment(lib, "bcrypt.lib"))
#else
// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the
// "Community Additions" comment on MSDN here:
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
#define SystemFunction036 NTAPI SystemFunction036
#include <ntsecapi.h>
#undef SystemFunction036
#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
OPENSSL_MSVC_PRAGMA(warning(pop))
#include "../fipsmodule/rand/internal.h"
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
void CRYPTO_init_sysrand(void) {}
#else
// See: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
typedef BOOL (WINAPI *ProcessPrngFunction)(PBYTE pbData, SIZE_T cbData);
static ProcessPrngFunction g_processprng_fn = NULL;
static void init_processprng(void) {
HMODULE hmod = LoadLibraryW(L"bcryptprimitives");
if (hmod == NULL) {
abort();
}
g_processprng_fn = (ProcessPrngFunction)GetProcAddress(hmod, "ProcessPrng");
if (g_processprng_fn == NULL) {
abort();
}
}
void CRYPTO_init_sysrand(void) {
static CRYPTO_once_t once = CRYPTO_ONCE_INIT;
CRYPTO_once(&once, init_processprng);
}
#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
while (requested > 0) {
ULONG output_bytes_this_pass = ULONG_MAX;
if (requested < output_bytes_this_pass) {
output_bytes_this_pass = (ULONG)requested;
}
// On non-UWP configurations, use RtlGenRandom instead of BCryptGenRandom
// to avoid accessing resources that may be unavailable inside the
// Chromium sandbox. See https://crbug.com/boringssl/307
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
if (!BCRYPT_SUCCESS(BCryptGenRandom(
/*hAlgorithm=*/NULL, out, output_bytes_this_pass,
BCRYPT_USE_SYSTEM_PREFERRED_RNG))) {
#else
if (RtlGenRandom(out, output_bytes_this_pass) == FALSE) {
#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
abort();
}
requested -= output_bytes_this_pass;
out += output_bytes_this_pass;
}
return;
#else
CRYPTO_init_sysrand();
// On non-UWP configurations, use ProcessPrng instead of BCryptGenRandom
// to avoid accessing resources that may be unavailable inside the
// Chromium sandbox. See https://crbug.com/74242
if (!g_processprng_fn(out, requested)) {
abort();
}
#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
}
void CRYPTO_sysrand_for_seed(uint8_t *out, size_t requested) {