ring/crypto/rand_extra/windows.c
Alex Gough e79649ba4d 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>
2023-06-22 19:51:36 +00:00

94 lines
3.1 KiB
C

/* Copyright (c) 2014, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <openssl/rand.h>
#if defined(OPENSSL_WINDOWS) && !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
#include <limits.h>
#include <stdlib.h>
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <windows.h>
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#include <bcrypt.h>
OPENSSL_MSVC_PRAGMA(comment(lib, "bcrypt.lib"))
#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;
}
if (!BCRYPT_SUCCESS(BCryptGenRandom(
/*hAlgorithm=*/NULL, out, output_bytes_this_pass,
BCRYPT_USE_SYSTEM_PREFERRED_RNG))) {
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) {
CRYPTO_sysrand(out, requested);
}
#endif // OPENSSL_WINDOWS && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE