random: add WC_RNG_SEED_DEVICE to seed from a nominated device

pull/11216/head
David Garske 2026-08-19 21:32:58 -07:00
parent 437bca85a7
commit fcb31dea21
5 changed files with 112 additions and 17 deletions

View File

@ -268,6 +268,12 @@
{"name": "no-verify-oid-fpki", "minutes": 1.2,
"configure": ["CPPFLAGS=-DNO_VERIFY_OID -DWOLFSSL_FPKI"]},
{"name": "no-verify-oid", "minutes": 1.1, "configure": ["CPPFLAGS=-DNO_VERIFY_OID"]},
{"name": "rng-seed-device", "minutes": 1.1,
"comment": "Seed the RNG from a nominated device. /dev/urandom stands in for a hardware RNG so the WC_RNG_SEED_DEVICE read path is actually exercised on a runner.",
"configure": ["--with-rng-seed-device=/dev/urandom"]},
{"name": "rng-seed-device-fallback", "minutes": 1.1,
"comment": "Nominated seed device that cannot be opened, so every test runs through the fallback to the default seed sources.",
"configure": ["--with-rng-seed-device=/nonexistent/hwrng"]},
{"name": "no-server-no-client-auth", "minutes": 1.0,
"configure": ["CPPFLAGS=-DNO_WOLFSSL_SERVER -DWOLFSSL_NO_CLIENT_AUTH"]},
{"name": "no-wolfssl-client", "minutes": 1.0,

View File

@ -3768,6 +3768,14 @@ if(WOLFSSL_ECC AND WOLFSSL_ECC_MIN_KEY_SZ)
list(APPEND WOLFSSL_DEFINITIONS "-DECC_MIN_KEY_SZ=${WOLFSSL_ECC_MIN_KEY_SZ}")
endif()
# RNG seed device (best effort, falls back to the default sources)
set(WOLFSSL_RNG_SEED_DEVICE "" CACHE STRING
"Seed the RNG from this device before the default sources, e.g. /dev/hwrng (default: disabled)")
if(WOLFSSL_RNG_SEED_DEVICE)
list(APPEND WOLFSSL_DEFINITIONS
"-DWC_RNG_SEED_DEVICE=\"${WOLFSSL_RNG_SEED_DEVICE}\"")
endif()
####################################################
# Additional crypto/TLS feature options (parity with configure.ac)

View File

@ -611,6 +611,8 @@ extern "C" {
#cmakedefine MAX_EX_DATA @MAX_EX_DATA@
#undef ECC_MIN_KEY_SZ
#cmakedefine ECC_MIN_KEY_SZ @ECC_MIN_KEY_SZ@
#undef WC_RNG_SEED_DEVICE
#cmakedefine WC_RNG_SEED_DEVICE @WC_RNG_SEED_DEVICE@
/* parity: crypto/TLS feature options (wave B) */
#undef WOLFSSL_MD2

View File

@ -8749,6 +8749,23 @@ then
fi
# RNG seed device (best effort, falls back to the default sources)
AC_ARG_WITH([rng-seed-device],
[AS_HELP_STRING([--with-rng-seed-device=PATH],[Seed the RNG from PATH before the default sources, e.g. /dev/hwrng (default: disabled)])],
[ RNG_SEED_DEVICE=$withval ],
[ RNG_SEED_DEVICE=no ]
)
if test "$RNG_SEED_DEVICE" != "no"
then
if test "$RNG_SEED_DEVICE" = "yes"
then
RNG_SEED_DEVICE="/dev/hwrng"
fi
AM_CFLAGS="$AM_CFLAGS -DWC_RNG_SEED_DEVICE=\\\"$RNG_SEED_DEVICE\\\""
fi
# Filesystem Build
if test "$HAVE_KERNEL_MODE" = "yes"
then

View File

@ -58,6 +58,9 @@ This library contains implementation for the random number generator.
* FORCE_FAILURE_GETRANDOM: Force getrandom failure (testing) default: off
* NO_DEV_RANDOM: Don't use /dev/random for seeding default: off
* NO_DEV_URANDOM: Don't use /dev/urandom for seeding default: off
* WC_RNG_SEED_DEVICE: Device tried before the usual seed default: off
* sources. Must be a quoted string,
* e.g. -DWC_RNG_SEED_DEVICE='"/dev/hwrng"'
* HAVE_INTEL_RDRAND: Use Intel RDRAND instruction default: off
* HAVE_INTEL_RDSEED: Use Intel RDSEED instruction default: off
* HAVE_AMD_RDSEED: Use AMD RDSEED instruction default: off
@ -352,6 +355,11 @@ enum {
#error RNG_MAX_BLOCK_LEN is larger than NIST DBRG max request length
#endif
/* the seed device is read through the filesystem API */
#if defined(WC_RNG_SEED_DEVICE) && defined(NO_FILESYSTEM)
#error WC_RNG_SEED_DEVICE requires filesystem support
#endif
enum {
drbgInitC = 0,
drbgReseed = 1,
@ -5933,6 +5941,16 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
{
int ret = 0;
/* Same condition as the seed-device block below: with
* ENTROPY_MEMUSE_FORCE_FAILURE the code that uses these is compiled out,
* and declaring them anyway is an unused-variable error under -Werror. */
#if defined(WC_RNG_SEED_DEVICE) && (!defined(HAVE_ENTROPY_MEMUSE) || \
!defined(ENTROPY_MEMUSE_FORCE_FAILURE))
byte* devOut;
word32 devSz;
int devFd;
int devLen;
#endif
/* Validate output before any entropy backend dereferences it: some
* (e.g. glibc's vDSO getrandom()) fault on a NULL buffer rather than
@ -5971,6 +5989,43 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#if !defined(HAVE_ENTROPY_MEMUSE) || !defined(ENTROPY_MEMUSE_FORCE_FAILURE)
#ifdef WC_RNG_SEED_DEVICE
/* Nominated entropy source, usually a hardware RNG. Best effort: on
* any failure fall through to the default sources below. Those refill
* the whole request from the start, so bytes a partial read left in
* output are overwritten rather than mixed in. */
devOut = output;
devSz = sz;
devFd = wc_open_cloexec(WC_RNG_SEED_DEVICE, O_RDONLY);
if (devFd != XBADFD) {
while (devSz > 0) {
errno = 0;
devLen = (int)read(devFd, devOut, devSz);
if (devLen < 0) {
if (errno == EINTR)
continue; /* interrupted, read again */
break;
}
if (devLen == 0)
break; /* at EOF, will never fill the request */
devSz -= (word32)devLen;
devOut += devLen;
}
close(devFd);
}
#if defined(DEBUG_WOLFSSL)
if (devSz == 0)
WOLFSSL_MSG("seeded from WC_RNG_SEED_DEVICE.");
else
WOLFSSL_MSG("WC_RNG_SEED_DEVICE unusable, using default.");
#endif /* DEBUG_WOLFSSL */
if (devSz == 0) {
/* success, we're done */
return 0;
}
#endif /* WC_RNG_SEED_DEVICE */
#if defined(HAVE_INTEL_RDSEED) || defined(HAVE_AMD_RDSEED)
if (IS_INTEL_RDSEED(intel_flags)) {
ret = wc_GenerateSeed_IntelRD(NULL, output, sz);
@ -6038,25 +6093,27 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#ifdef WOLFSSL_KEEP_RNG_SEED_FD_OPEN
if (!os->seedFdOpen)
{
os->fd = XBADFD;
#ifndef NO_DEV_URANDOM /* way to disable use of /dev/urandom */
os->fd = wc_open_cloexec("/dev/urandom", O_RDONLY);
if (os->fd == XBADFD) {
os->fd = wc_open_cloexec("/dev/urandom", O_RDONLY);
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG("opened /dev/urandom.");
if (os->fd != XBADFD)
WOLFSSL_MSG("opened /dev/urandom.");
#endif /* DEBUG_WOLFSSL */
if (os->fd == XBADFD)
}
#endif /* NO_DEV_URANDOM */
{
if (os->fd == XBADFD) {
/* may still have /dev/random */
os->fd = wc_open_cloexec("/dev/random", O_RDONLY);
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG("opened /dev/random.");
if (os->fd != XBADFD)
WOLFSSL_MSG("opened /dev/random.");
#endif /* DEBUG_WOLFSSL */
if (os->fd == XBADFD)
return OPEN_RAN_E;
else {
os->keepSeedFdOpen = 0;
os->seedFdOpen = 1;
}
os->keepSeedFdOpen = 0;
os->seedFdOpen = 1;
}
else {
os->keepSeedFdOpen = 1;
@ -6064,18 +6121,22 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
}
}
#else /* WOLFSSL_KEEP_RNG_SEED_FD_OPEN */
#ifndef NO_DEV_URANDOM /* way to disable use of /dev/urandom */
os->fd = wc_open_cloexec("/dev/urandom", O_RDONLY);
os->fd = XBADFD;
#ifndef NO_DEV_URANDOM /* way to disable use of /dev/urandom */
if (os->fd == XBADFD) {
os->fd = wc_open_cloexec("/dev/urandom", O_RDONLY);
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG("opened /dev/urandom.");
if (os->fd != XBADFD)
WOLFSSL_MSG("opened /dev/urandom.");
#endif /* DEBUG_WOLFSSL */
if (os->fd == XBADFD)
#endif /* !NO_DEV_URANDOM */
{
}
#endif /* !NO_DEV_URANDOM */
if (os->fd == XBADFD) {
/* may still have /dev/random */
os->fd = wc_open_cloexec("/dev/random", O_RDONLY);
#if defined(DEBUG_WOLFSSL)
WOLFSSL_MSG("opened /dev/random.");
if (os->fd != XBADFD)
WOLFSSL_MSG("opened /dev/random.");
#endif /* DEBUG_WOLFSSL */
if (os->fd == XBADFD)
return OPEN_RAN_E;
@ -6086,7 +6147,8 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
#endif /* DEBUG_WOLFSSL */
while (sz) {
int len = (int)read(os->fd, output, sz);
if (len == -1) {
/* EOF never fills the request, don't retry forever */
if (len <= 0) {
ret = READ_RAN_E;
break;
}