fix(crypto): seed CSPRNG via BCryptGenRandom on Windows

mbedTLS auto-installs an entropy source on Windows that uses the
legacy CryptoAPI (CryptAcquireContext + CryptGenRandom). It silently
depends on the RSA crypto provider being installed and reachable,
which is not the case in wine, in several common container images,
or on some hardened Windows deployments. On those systems
mbedtls_ctr_drbg_seed fails, and after 0.2.15's CSPRNG hardening
librist refuses to run any encryption code path at all.

Replace the source with BCryptGenRandom (CNG): same cryptographic
strength, present on every supported Windows release (Vista+), and
reliably implemented by wine. Add bcrypt to the Windows link line.
No behaviour change on Linux or macOS.

Fixes #210.
This commit is contained in:
Sergio Ammirata
2026-05-17 18:15:50 -04:00
parent e2b2581729
commit b05ff88388
2 changed files with 45 additions and 1 deletions
+2 -1
View File
@@ -84,7 +84,8 @@ if host_machine.system() == 'windows'
platform_files += 'src/tun_win.c'
deps += [
cc.find_library('ws2_32'),
cc.find_library('iphlpapi')
cc.find_library('iphlpapi'),
cc.find_library('bcrypt')
]
windows_version_test = '''
+43
View File
@@ -3,12 +3,43 @@
#include "log-private.h"
#if HAVE_MBEDTLS
#include <string.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/entropy_poll.h>
#include "pthread-shim.h"
#include "vcs_version.h"
#ifdef _WIN32
/* mbedTLS's auto-added Windows entropy source uses the legacy CryptoAPI
* (CryptAcquireContext + CryptGenRandom). That path requires the RSA
* crypto provider to be present and initialised, and fails outright
* under wine in stripped-down docker environments (#210). BCryptGenRandom
* is the modern CNG entry point, available on every supported Windows
* version (Vista+) and reliably implemented by wine since ~2014, so we
* substitute it for the default during entropy_init below. */
#include <windows.h>
#include <bcrypt.h>
#ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
#define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
#endif
static int _librist_bcrypt_entropy_poll(void *data, unsigned char *output,
size_t len, size_t *olen)
{
(void) data;
*olen = 0;
NTSTATUS s = BCryptGenRandom(NULL, output, (ULONG) len,
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (s != 0)
return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
*olen = len;
return 0;
}
#endif /* _WIN32 */
static mbedtls_entropy_context entropy_ctx;
static mbedtls_ctr_drbg_context ctr_drbg_ctx;
/* 0 once mbedtls_ctr_drbg_seed succeeds; non-zero (the mbedTLS error)
@@ -37,6 +68,18 @@ static BOOL WINAPI librist_crypto_srp_init_random_func(PINIT_ONCE InitOnce, PVOI
{
#if HAVE_MBEDTLS
mbedtls_entropy_init(&entropy_ctx);
#ifdef _WIN32
/* Drop the default CryptGenRandom source mbedtls auto-added and
* substitute our BCryptGenRandom one. See #210 and the comment
* on _librist_bcrypt_entropy_poll above. The init was called once
* via pthread_once / InitOnce, so this struct mutation runs at
* most once and is not seen by any concurrent caller. */
entropy_ctx.source_count = 0;
memset(entropy_ctx.source, 0, sizeof(entropy_ctx.source));
mbedtls_entropy_add_source(&entropy_ctx, _librist_bcrypt_entropy_poll,
NULL, MBEDTLS_ENTROPY_MIN_PLATFORM,
MBEDTLS_ENTROPY_SOURCE_STRONG);
#endif
mbedtls_ctr_drbg_init(&ctr_drbg_ctx);
//ctr_drbg_ctx is threadsafe, so can be used by multiple threads freely, seeding isn't though.
const char user_custom[] = "libRIST librist_crypto_random_init_func "LIBRIST_VERSION;