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>
diff --git a/crypto/fipsmodule/rand/internal.h b/crypto/fipsmodule/rand/internal.h
index 3c996f1..56fae23 100644
--- a/crypto/fipsmodule/rand/internal.h
+++ b/crypto/fipsmodule/rand/internal.h
@@ -70,11 +70,15 @@
 // 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 @@
 // 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|.
diff --git a/crypto/rand_extra/windows.c b/crypto/rand_extra/windows.c
index 8ade689..0dbc0e3 100644
--- a/crypto/rand_extra/windows.c
+++ b/crypto/rand_extra/windows.c
@@ -27,43 +27,63 @@
     !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) {