From 8c3a5728865ed62d3928aa84fdb9d76c9035a420 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 30 Oct 2026 17:56:16 -0500 Subject: [PATCH 1/7] wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h wolfcrypt/src/rng_bank.c, linuxkm/lkcapi_sha_glue.c, wolfcrypt/test/test.c, doc/dox_comments/header_files/random.h: rename "nonce" to "addIn" in reseed APIs, for consistent naming of "additional" data as such. --- doc/dox_comments/header_files/random.h | 112 ++++++++++++------------- linuxkm/lkcapi_sha_glue.c | 4 +- wolfcrypt/src/random.c | 104 +++++++++++------------ wolfcrypt/src/rng_bank.c | 20 ++--- wolfcrypt/test/test.c | 6 +- wolfssl/wolfcrypt/random.h | 30 +++---- 6 files changed, 138 insertions(+), 138 deletions(-) diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index 2bcd2a64219..2319073b636 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -6,12 +6,12 @@ with wc_FreeRng). This is a blocking operation. One WC_RNG may be shared between threads for generating and reseeding: - wc_RNG_GenerateBlock(), wc_RNG_DRBG_Reseed(), wc_RNG_DRBG_Reseed_Nonce() + wc_RNG_GenerateBlock(), wc_RNG_DRBG_Reseed(), wc_RNG_DRBG_Reseed_AddIn() and wc_RNG_DRBG_Reseed_Now() each hold the instance lock. So do the DRBG - management calls: wc_RNG_DRBG_Stir(), wc_RNG_DRBG_Stir_Nonce() and + management calls: wc_RNG_DRBG_Stir(), wc_RNG_DRBG_Stir_AddIn() and wc_RNG_DRBG_ScheduleReseed(), and in --enable-rng-extras builds wc_RNG_DRBG_NextStirNow(), wc_RNG_DRBG_NextSeedNow() and - wc_RNG_DRBG_NextSeedNow_Nonce(). The generate path calls their internal + wc_RNG_DRBG_NextSeedNow_AddIn(). The generate path calls their internal cores, which already run under the lock. wc_RNG_DRBG_ReseedRBGC() and wc_RNG_DRBG_StirRBGC() are the exception: @@ -1316,7 +1316,7 @@ int wc_RNG_global_fallback_free(void); \endcode \sa wc_InitRngNonce_ex2 - \sa wc_RNG_DRBG_Reseed_Nonce + \sa wc_RNG_DRBG_Reseed_AddIn \sa wc_RNG_TestSeed \sa wc_RNG_DRBG_GetRBGCStratum */ @@ -1417,7 +1417,7 @@ int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); \ingroup Random \brief Immediately reseed rng from the module's built-in, registered, or - associated seed source, with an optional nonce as additional input. The + associated seed source, with an optional addIn as additional input. The credited reseed resets the reseed counter. \details For an SP 800-90C chain member (RBGC stratum > 0, in builds with @@ -1432,7 +1432,7 @@ int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); \return 0 Success -- or no-op: an instance with no DRBG (RDRAND et al.) reports success without action; wc_RNG_DRBG_Present() disambiguates. - \return BAD_FUNC_ARG rng is null, or nonce is null with nonceSz nonzero. + \return BAD_FUNC_ARG rng is null, or addIn is null with addInSz nonzero. \return OBJECT_NOT_LOCKED_E The instance requires its lock and the caller does not hold it. \return BAD_MUTEX_E The rng's lock could not be taken. @@ -1453,21 +1453,21 @@ int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng); remains usable until its reseed interval is reached. \param rng The RNG object to reseed. - \param nonce Optional additional input. - \param nonceSz Length of nonce in bytes. + \param addIn Optional additional input. + \param addInSz Length of addIn in bytes. \sa wc_RNG_DRBG_ScheduleReseed - \sa wc_RNG_DRBG_Reseed_Nonce + \sa wc_RNG_DRBG_Reseed_AddIn \sa wc_RNG_DRBG_Present */ -int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz); +int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* addIn, word32 addInSz); /*! \ingroup Random \brief The forced-primary form of wc_RNG_DRBG_Reseed_Now(): reseed rng directly from the module's primary (initial) randomness source, bypassing - the chain fallback ladder, with an optional nonce as additional input. + the chain fallback ladder, with an optional addIn as additional input. This is the SP 800-90A prediction-resistance primitive -- a reseed from a live entropy source immediately ahead of generation -- and is available to SP 800-90C chain members (RBGC stratum > 0) as well: the initial @@ -1501,7 +1501,7 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz); retryable from a blockable context. \return 0 Success - \return BAD_FUNC_ARG rng is null, or nonce is null with nonceSz nonzero. + \return BAD_FUNC_ARG rng is null, or addIn is null with addInSz nonzero. \return OBJECT_NOT_LOCKED_E The instance requires its lock and the caller does not hold it. \return WRONG_TYPE_OBJECT_E The instance does not assert @@ -1518,8 +1518,8 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz); \param rng The RNG object to reseed; in builds with instance locking it must assert WC_RNG_LOCK_REQUIRED and be held by the caller. - \param nonce Optional additional input. - \param nonceSz Length of nonce in bytes. + \param addIn Optional additional input. + \param addInSz Length of addIn in bytes. _Example_ \code @@ -1537,14 +1537,14 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz); \sa wc_RNG_DRBG_Present \sa wc_RNG_DRBG_NextSeedGenerate_Primary */ -int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, const byte* nonce, - word32 nonceSz); +int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, const byte* addIn, + word32 addInSz); /*! \ingroup Random \brief Reseed rng's DRBG with caller-supplied seed material and an - optional nonce as additional input, folded in a single derivation pass. + optional addIn as additional input, folded in a single derivation pass. The material is credited: the reseed counter resets. \details The single pass is one reseed operation over both inputs, not @@ -1557,7 +1557,7 @@ int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, const byte* nonce, \return RNG_FAILURE_E rng is condemned (status DRBG_FAILED): a condemned instance does not accept a credited reseed; recover with wc_FreeRng() then wc_InitRng*(). Also, the reseed itself failed. - \return BAD_FUNC_ARG rng or seed is null, or nonce is null with nonceSz + \return BAD_FUNC_ARG rng or seed is null, or addIn is null with addInSz nonzero. \return OBJECT_NOT_LOCKED_E The instance requires its lock and the caller does not hold it. @@ -1574,15 +1574,15 @@ int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, const byte* nonce, \param rng The RNG object to reseed. \param seed Seed material. \param seedSz Length of seed in bytes. - \param nonce Optional additional input. - \param nonceSz Length of nonce in bytes. + \param addIn Optional additional input. + \param addInSz Length of addIn in bytes. \sa wc_RNG_DRBG_Present \sa wc_RNG_DRBG_Reseed - \sa wc_RNG_DRBG_Stir_Nonce + \sa wc_RNG_DRBG_Stir_AddIn */ -int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, - const byte *nonce, word32 nonceSz); +int wc_RNG_DRBG_Reseed_AddIn(WC_RNG* rng, const byte* seed, word32 seedSz, + const byte *addIn, word32 addInSz); /*! \ingroup Random @@ -1618,7 +1618,7 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, \param seedSz Length of seed in bytes. \sa wc_RNG_DRBG_Reseed - \sa wc_RNG_DRBG_Stir_Nonce + \sa wc_RNG_DRBG_Stir_AddIn \sa wc_RNG_entropy_needs_recovery \sa wc_RNG_DRBG_Present */ @@ -1627,12 +1627,12 @@ int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz); /*! \ingroup Random - \brief The nonce-bearing form of wc_RNG_DRBG_Stir(): the nonce is + \brief The addIn-bearing form of wc_RNG_DRBG_Stir(): the addIn is mixed in as a second, separate generate with additional input. \return 0 Success -- or no-op: an instance with no DRBG (RDRAND et al.) reports success without action; wc_RNG_DRBG_Present() disambiguates. - \return BAD_FUNC_ARG rng or seed is null, or nonce is null with nonceSz + \return BAD_FUNC_ARG rng or seed is null, or addIn is null with addInSz nonzero. \return OBJECT_NOT_LOCKED_E, NEEDS_RECOVERY_E, NOT_READY_E, RNG_FAILURE_E, BAD_MUTEX_E As for wc_RNG_DRBG_Stir(). @@ -1640,16 +1640,16 @@ int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz); \param rng The RNG object to stir. \param seed Material to mix in. \param seedSz Length of seed in bytes. - \param nonce Optional additional input. - \param nonceSz Length of nonce in bytes. + \param addIn Optional additional input. + \param addInSz Length of addIn in bytes. \sa wc_RNG_DRBG_Stir - \sa wc_RNG_DRBG_Reseed_Nonce + \sa wc_RNG_DRBG_Reseed_AddIn \sa wc_RNG_DRBG_Present */ -int wc_RNG_DRBG_Stir_Nonce(WC_RNG* rng, const byte* seed, - word32 seedSz, const byte *nonce, - word32 nonceSz); +int wc_RNG_DRBG_Stir_AddIn(WC_RNG* rng, const byte* seed, + word32 seedSz, const byte *addIn, + word32 addInSz); /*! \ingroup Random @@ -1748,7 +1748,7 @@ int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, \ingroup Random \brief Reseed rng from root's generate output -- the SP 800-90C chain - reseed -- with an optional nonce as additional input. The reseed is + reseed -- with an optional addIn as additional input. The reseed is credited (the reseed counter resets); rng's stratum is updated to root's stratum + 1, unless built with WC_RNG_RBGC_STRATUM_IMMUTABLE (required for FIPS). The caller must hold exclusive access to both instances. @@ -1779,8 +1779,8 @@ int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, \return 0 Success -- or no-op: an instance with no DRBG (RDRAND et al.) reports success without action; wc_RNG_DRBG_Present() disambiguates. (Refusals below take precedence.) - \return BAD_FUNC_ARG rng or root is null, rng equals root, nonce is - null with nonceSz nonzero, or the no-downgrade rule refuses root as a + \return BAD_FUNC_ARG rng or root is null, rng equals root, addIn is + null with addInSz nonzero, or the no-downgrade rule refuses root as a chain parent (see \details). \return WRONG_TYPE_OBJECT_E rng is primary-class (stratum 0) and the build is WC_RNG_RBGC_STRATUM_IMMUTABLE, or (under FIPS) root is neither @@ -1798,16 +1798,16 @@ int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent, const byte* nonce, \param rng The chain member to reseed. \param root The chain parent to draw seed material from. - \param nonce Optional additional input. - \param nonceSz Length of nonce in bytes. + \param addIn Optional additional input. + \param addInSz Length of addIn in bytes. \sa wc_InitRngRBGC \sa wc_RNG_DRBG_StirRBGC \sa wc_RNG_DRBG_Reseed_Now \sa wc_RNG_DRBG_Present */ -int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, - word32 nonceSz); +int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* addIn, + word32 addInSz); /*! \ingroup Random @@ -1817,8 +1817,8 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, \return 0 Success -- or no-op: an instance with no DRBG (RDRAND et al.) reports success without action; wc_RNG_DRBG_Present() disambiguates. - \return BAD_FUNC_ARG rng or root is null, rng equals root, or nonce is - null with nonceSz nonzero. + \return BAD_FUNC_ARG rng or root is null, rng equals root, or addIn is + null with addInSz nonzero. \return OBJECT_NOT_LOCKED_E rng or root requires its lock and the caller does not hold it. \return NEEDS_RECOVERY_E A recovery obligation stands on rng; see @@ -1828,8 +1828,8 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, \param rng The chain member to stir. \param root The chain parent to draw material from. - \param nonce Optional additional input. - \param nonceSz Length of nonce in bytes. + \param addIn Optional additional input. + \param addInSz Length of addIn in bytes. \sa wc_RNG_DRBG_ReseedRBGC \sa wc_RNG_DRBG_Stir @@ -1844,7 +1844,7 @@ int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, const byte* nonce, */ int wc_RNG_DRBG_StirRBGC(WC_RNG* rng, WC_RNG* root, - const byte* nonce, word32 nonceSz); + const byte* addIn, word32 addInSz); /*! \ingroup Random @@ -2091,14 +2091,14 @@ int wc_RNG_DRBG_NextSeedCurrent(const WC_RNG* rng, WC_ATOMIC_INT_ARG* n); \endcode \sa wc_RNG_DRBG_NextSeedGenerate - \sa wc_RNG_DRBG_NextSeedNow_Nonce + \sa wc_RNG_DRBG_NextSeedNow_AddIn */ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); /*! \ingroup Random - \brief The nonce-bearing form of wc_RNG_DRBG_NextSeedNow(): the nonce is + \brief The addIn-bearing form of wc_RNG_DRBG_NextSeedNow(): the addIn is mixed in as uncredited additional input alongside the banked seed. \return 0 Success @@ -2109,18 +2109,18 @@ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); (re-)asserted, and a recovery reseed is scheduled. \return NOT_READY_E No bank is ready, or the reseed failed retryably with reseed runway remaining. - \return BAD_FUNC_ARG rng is null, or nonce is null with nonceSz nonzero. + \return BAD_FUNC_ARG rng is null, or addIn is null with addInSz nonzero. \return WRONG_TYPE_OBJECT_E, OBJECT_NOT_LOCKED_E, BAD_MUTEX_E, RNG_FAILURE_E, DRBG_CONT_FIPS_E As for wc_RNG_DRBG_NextSeedNow(). \param rng The RNG object to reseed. - \param nonce Additional input. - \param nonceSz Length of nonce in bytes. + \param addIn Additional input. + \param addInSz Length of addIn in bytes. \sa wc_RNG_DRBG_NextSeedNow */ -int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, - word32 nonceSz); +int wc_RNG_DRBG_NextSeedNow_AddIn(WC_RNG* rng, const byte* addIn, + word32 addInSz); /*! \ingroup Random @@ -2135,18 +2135,18 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, \return 0 Success (an instance with no DRBG accumulates too; the stir that consumes it is then a no-op -- wc_RNG_DRBG_Present() disambiguates). - \return BAD_FUNC_ARG rng or nonce is null, or nonceSz is 0. + \return BAD_FUNC_ARG rng or addIn is null, or addInSz is 0. \param rng The RNG object whose accumulator to feed. - \param nonce Material to bank. - \param nonceSz Length of nonce in bytes. + \param addIn Material to bank. + \param addInSz Length of addIn in bytes. \sa wc_RNG_DRBG_NextStirNow \sa wc_RNG_DRBG_Stir \sa wc_RNG_DRBG_Present */ -int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, - word32 nonceSz); +int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *addIn, + word32 addInSz); /*! \ingroup Random diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index c23c5d6cbe9..71056189df8 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3568,7 +3568,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) if (ret == 0) { ret = wc_RNG_lock_get_unconditional(global_fallback_rng); if (ret == 0) { - ret = wc_RNG_DRBG_NextSeedNow_Nonce( + ret = wc_RNG_DRBG_NextSeedNow_AddIn( global_fallback_rng, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); @@ -4508,7 +4508,7 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, ) { unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG_BANK_INST_TO_RNG(drbg), + ret = wc_RNG_DRBG_NextSeedNow_AddIn(WC_RNG_BANK_INST_TO_RNG(drbg), (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 65bb0a3a277..5f30ed23fe3 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1060,7 +1060,7 @@ static WARN_UNUSED_RESULT int Hash256_DRBG_Reseed(DRBG_internal* drbg, * (_PRODUCING or _CONSUMING) is repainted _PURGED instead: the holder * still owns the buffer, so it is the holder that wipes and reopens -- * the producer in NextSeedProducerRelease(), the consumer in - * wc_RNG_DRBG_NextSeedNow_Nonce_local()'s failed-release arm. Reopening + * wc_RNG_DRBG_NextSeedNow_AddIn_local()'s failed-release arm. Reopening * EMPTY from under a live holder would let a producer claim and begin a * fill that the holder's own trailing wipe then tears. * The health-test burn arm wipes under its own PRODUCING claim. @@ -1201,7 +1201,7 @@ static WC_MAYBE_UNUSED void NextStirPurge(WC_RNG* rng) #endif /* WC_RNG_HAVE_NEXT_SEED */ /* in_bracketed_consume: nonzero when the caller feeds this reseed from its own - * _CONSUMING claim on the credited aperture (wc_RNG_DRBG_NextSeedNow_Nonce()). + * _CONSUMING claim on the credited aperture (wc_RNG_DRBG_NextSeedNow_AddIn()). * The invalidated-entry purges below must then exempt that aperture: purging * it would steal the caller's claim and defeat its release CAS, converting * the caller's own recovery into a false epoch violation. An external @@ -1487,8 +1487,8 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, return ret; } -int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, - const byte *nonce, word32 nonceSz) +int wc_RNG_DRBG_Reseed_AddIn(WC_RNG* rng, const byte* seed, word32 seedSz, + const byte *addIn, word32 addInSz) { int ret; WC_ATOMIC_UINT_ARG cur_epoch = @@ -1506,7 +1506,7 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, /* Hash_df() skips a NULL input regardless of its stated length, so * without this the caller's additional input would be dropped and * success reported. Matches _InitRng() and ReseedRBGC(). */ - if ((nonce == NULL) && (nonceSz > 0)) + if ((addIn == NULL) && (addInSz > 0)) return BAD_FUNC_ARG; #if defined(WC_RNG_HAVE_RBGC) && defined(WC_RNG_RBGC_STRATUM_IMMUTABLE) @@ -1545,14 +1545,14 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, /* Never allow an undersized seed to clear an invalidated state. Note that * the user seed is not evaluated by wc_RNG_TestSeed() -- calling * wc_RNG_TestSeed() is the user's responsibility, and is inapplicable when - * wc_InitRngNonce_UserSeed() and wc_RNG_DRBG_Reseed_Nonce() are called from + * wc_InitRngAddIn_UserSeed() and wc_RNG_DRBG_Reseed_AddIn() are called from * KAT tests, or are seeded by another healthy DRBG. * * Historically, wc_RNG_DRBG_Reseed() has never imposed wc_RNG_TestSeed(), * i.e. it has always succeeded deterministically when passed valid - * arguments. wc_RNG_DRBG_Reseed_Nonce() continues that semantic whether + * arguments. wc_RNG_DRBG_Reseed_AddIn() continues that semantic whether * called directly or via wc_RNG_DRBG_Reseed(). Novel enforcement here is - * frivolous in any case because wc_InitRngNonce_UserSeed() imposes no + * frivolous in any case because wc_InitRngAddIn_UserSeed() imposes no * wc_RNG_TestSeed(). */ { @@ -1568,7 +1568,7 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, } } - ret = Hash_DRBG_Reseed(rng, seed, seedSz, nonce, nonceSz, + ret = Hash_DRBG_Reseed(rng, seed, seedSz, addIn, addInSz, 0 /* in_bracketed_consume */, cur_epoch, my_pid); if (ret > 0) /* DRBG-internal code: translate at the API */ ret = RNG_FAILURE_E; @@ -1593,7 +1593,7 @@ int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, } int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz) { - return wc_RNG_DRBG_Reseed_Nonce(rng, seed, seedSz, NULL, 0); + return wc_RNG_DRBG_Reseed_AddIn(rng, seed, seedSz, NULL, 0); } #ifdef WC_RNG_HAVE_RBGC @@ -2843,13 +2843,13 @@ static WARN_UNUSED_RESULT int Hash_DRBG_StirGenerate(WC_RNG* rng, return ret; } -static WARN_UNUSED_RESULT int wc_RNG_DRBG_Stir_Nonce_local(WC_RNG* rng, +static WARN_UNUSED_RESULT int wc_RNG_DRBG_Stir_AddIn_local(WC_RNG* rng, const byte* seed, word32 seedSz, - const byte *nonce, word32 nonceSz) + const byte *addIn, word32 addInSz) { if (rng == NULL || seed == NULL) return BAD_FUNC_ARG; - if ((nonce == NULL) && (nonceSz != 0)) + if ((addIn == NULL) && (addInSz != 0)) return BAD_FUNC_ARG; if (rng->status != WC_DRBG_OK) @@ -2863,18 +2863,18 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_Stir_Nonce_local(WC_RNG* rng, { int ret = Hash_DRBG_StirGenerate(rng, seed, seedSz); - if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { + if ((ret == 0) && (addIn != NULL) && (addInSz > 0)) { /* Second chunk as its own specified generate: additional * input is per-call, and chunking beats concatenation * scratch. */ - ret = Hash_DRBG_StirGenerate(rng, nonce, nonceSz); + ret = Hash_DRBG_StirGenerate(rng, addIn, addInSz); } return ret; } } -int wc_RNG_DRBG_Stir_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, - const byte *nonce, word32 nonceSz) +int wc_RNG_DRBG_Stir_AddIn(WC_RNG* rng, const byte* seed, word32 seedSz, + const byte *addIn, word32 addInSz) { int ret; @@ -2883,14 +2883,14 @@ int wc_RNG_DRBG_Stir_Nonce(WC_RNG* rng, const byte* seed, word32 seedSz, ret = RngAutoLockEnter(rng); if (ret != 0) return ret; - ret = wc_RNG_DRBG_Stir_Nonce_local(rng, seed, seedSz, nonce, nonceSz); + ret = wc_RNG_DRBG_Stir_AddIn_local(rng, seed, seedSz, addIn, addInSz); RngAutoLockExit(rng); return ret; } int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz) { - return wc_RNG_DRBG_Stir_Nonce(rng, seed, seedSz, NULL, 0); + return wc_RNG_DRBG_Stir_AddIn(rng, seed, seedSz, NULL, 0); } /* SP800-90B 4.4 health tests run over conditioned seed material, which makes @@ -5196,7 +5196,7 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { * post-purge apertures, so a READY it then claims necessarily postdates * this event; a consumer that raced ahead of the latch loses its claim * to the purge above and is refused at its release CAS (see - * wc_RNG_DRBG_NextSeedNow_Nonce()); and an event landing mid-reseed + * wc_RNG_DRBG_NextSeedNow_AddIn()); and an event landing mid-reseed * strips _ENTROPY_RECOVERING here, so the recovery's exit CAS refuses * the clear. No interleaving recovers from pre-event material. */ @@ -6188,7 +6188,7 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_ReseedRBGC_local( } } else { - ret = wc_RNG_DRBG_Stir_Nonce_local(rng, seed, SEED_SZ, nonce, + ret = wc_RNG_DRBG_Stir_AddIn_local(rng, seed, SEED_SZ, nonce, nonceSz); } } @@ -6482,13 +6482,13 @@ static WARN_UNUSED_RESULT int PollAndReSeed(WC_RNG* rng, const byte* additional, * * The caller must hold exclusive access to rng, as for all WC_RNG operations. */ -int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) +int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* addIn, word32 addInSz) { int ret; if (rng == NULL) return BAD_FUNC_ARG; - if ((nonce == NULL) && (nonceSz > 0)) + if ((addIn == NULL) && (addInSz > 0)) return BAD_FUNC_ARG; ret = rng_lock_required_check(rng); if (ret != 0) @@ -6511,7 +6511,7 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) goto out; } - ret = PollAndReSeed(rng, nonce, nonceSz); + ret = PollAndReSeed(rng, addIn, addInSz); if (ret != DRBG_SUCCESS) ret = RngGenerateFailure(rng, ret); @@ -6528,7 +6528,7 @@ int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, word32 nonceSz) * Multithreaded configurations without _HAVE_LOCK cannot support PR and the flag * RMW it requires. */ int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, - const byte* nonce, word32 nonceSz) + const byte* addIn, word32 addInSz) { int ret; if (rng == NULL) @@ -6541,8 +6541,8 @@ int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, return WRONG_TYPE_OBJECT_E; #endif #if !defined(WC_RNG_HAVE_LOCK) && !defined(SINGLE_THREADED) - (void)nonce; - (void)nonceSz; + (void)addIn; + (void)addInSz; return NOT_COMPILED_IN; #else /* WC_RNG_HAVE_LOCK || SINGLE_THREADED */ if (rng->flags & WC_RNG_FLAG_NO_PRIMARY_SEED) @@ -6555,7 +6555,7 @@ int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, } else restore_flag_bits = 0U; - ret = wc_RNG_DRBG_Reseed_Now(rng, nonce, nonceSz); + ret = wc_RNG_DRBG_Reseed_Now(rng, addIn, addInSz); if (restore_flag_bits) rng->flags &= ~restore_flag_bits; } @@ -6574,11 +6574,11 @@ int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, * _NextSeedCurrent() reports the raw aperture value (racy snapshot). * _NextSeedNow() claims a ready bank and performs a source-free * credited reseed with it -- safe in atomic context -- or returns - * NOT_READY_E when no bank is ready; _NextSeedNow_Nonce() is the same - * with a nonce as uncredited additional input. An instance with no + * NOT_READY_E when no bank is ready; _NextSeedNow_AddIn() is the same + * with a addIn as uncredited additional input. An instance with no * DRBG (RDRAND et al.) never opens its seed aperture, and producers * and consumers alike refuse it with WRONG_TYPE_OBJECT_E. The caller - * must own the instance for _NextSeedNow[_Nonce](). */ + * must own the instance for _NextSeedNow[_AddIn](). */ /* Banked-next-seed protocol. * @@ -7076,9 +7076,9 @@ int wc_RNG_DRBG_NextSeedCurrent(const WC_RNG* rng, WC_ATOMIC_INT_ARG* n) * construction); wc_RNG_DRBG_Reseed_Now() remains the live-gather shape. */ /* safe (bounded-at-once) recursion for RBGC reseed. */ /* NOLINTNEXTLINE(misc-no-recursion) */ -static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedNow_Nonce_local( - WC_RNG* rng, const byte* nonce, - word32 nonceSz) +static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedNow_AddIn_local( + WC_RNG* rng, const byte* addIn, + word32 addInSz) { WC_ATOMIC_INT_ARG expected = WC_DRBG_NEXT_SEED_READY; int ret; @@ -7098,7 +7098,7 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedNow_Nonce_local( if (rng == NULL) return BAD_FUNC_ARG; - if ((nonce == NULL) && (nonceSz != 0)) + if ((addIn == NULL) && (addInSz != 0)) return BAD_FUNC_ARG; ret = rng_lock_required_check(rng); @@ -7158,7 +7158,7 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedNow_Nonce_local( /* Identical byte accounting to PollAndReSeed(): the SEED_BLOCK_SZ * prefix was consumed by the bank-time health testing. */ ret = Hash_DRBG_Reseed(rng, rng->nextSeed + SEED_BLOCK_SZ, SEED_SZ, - nonce, nonceSz, 1 /* in_bracketed_consume */, + addIn, addInSz, 1 /* in_bracketed_consume */, cur_epoch, my_pid); /* Use-once: consumed by the attempt, success or not. The scrub must be @@ -7319,8 +7319,8 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedNow_Nonce_local( return ret; } -int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, - word32 nonceSz) +int wc_RNG_DRBG_NextSeedNow_AddIn(WC_RNG* rng, const byte* addIn, + word32 addInSz) { int ret; @@ -7329,7 +7329,7 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, ret = RngAutoLockEnter(rng); if (ret != 0) return ret; - ret = wc_RNG_DRBG_NextSeedNow_Nonce_local(rng, nonce, nonceSz); + ret = wc_RNG_DRBG_NextSeedNow_AddIn_local(rng, addIn, addInSz); RngAutoLockExit(rng); return ret; } @@ -7341,12 +7341,12 @@ int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, const byte* nonce, /* safe (bounded-at-once) recursion for RBGC reseed. */ /* NOLINTNEXTLINE(misc-no-recursion) */ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedNow_local(WC_RNG* rng) { - return wc_RNG_DRBG_NextSeedNow_Nonce_local(rng, NULL, 0); + return wc_RNG_DRBG_NextSeedNow_AddIn_local(rng, NULL, 0); } #endif int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng) { - return wc_RNG_DRBG_NextSeedNow_Nonce(rng, NULL, 0); + return wc_RNG_DRBG_NextSeedNow_AddIn(rng, NULL, 0); } /* Deposit raw uncredited stir material into rng's accumulator. Callable from @@ -7356,11 +7356,11 @@ int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng) { * fragments of compatible provenance are harmless. A full accumulator * publishes WC_DRBG_NEXT_SEED_READY (no health test -- no claim is being made) * and shunts further deposits to xorbuf() until consumed. */ -int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, word32 nonceSz) +int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *addIn, word32 addInSz) { WC_ATOMIC_INT_ARG cur; - if ((rng == NULL) || (nonce == NULL) || (nonceSz == 0)) + if ((rng == NULL) || (addIn == NULL) || (addInSz == 0)) return BAD_FUNC_ARG; /* Note, rng need not be locked -- that's the whole point of the @@ -7372,30 +7372,30 @@ int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, const byte *nonce, word32 nonceSz) if ((cur >= 0) && (cur < (WC_ATOMIC_INT_ARG)sizeof(rng->nextStir))) { word32 fill = (word32)cur; word32 room = (word32)sizeof(rng->nextStir) - fill; - word32 take = (nonceSz < room) ? nonceSz : room; + word32 take = (addInSz < room) ? addInSz : room; - XMEMCPY(rng->nextStir + fill, nonce, take); + XMEMCPY(rng->nextStir + fill, addIn, take); fill += take; if (fill == (word32)sizeof(rng->nextStir)) { WOLFSSL_ATOMIC_STORE(rng->nextStirLen, WC_DRBG_NEXT_SEED_READY); #ifdef WC_RNG_DEBUG_STATS ++rng->_stats_nextstirs_banked; /* racy */ #endif - nonce += take; - nonceSz -= take; /* spillover falls through to the xorbuf() */ + addIn += take; + addInSz -= take; /* spillover falls through to the xorbuf() */ } else { WOLFSSL_ATOMIC_STORE(rng->nextStirLen, cur + (WC_ATOMIC_INT_ARG)take); return 0; } - if (nonceSz == 0) + if (addInSz == 0) return 0; } - if (nonceSz > (word32)sizeof(rng->nextStir)) - nonceSz = (word32)sizeof(rng->nextStir); - xorbuf(rng->nextStir, nonce, nonceSz); + if (addInSz > (word32)sizeof(rng->nextStir)) + addInSz = (word32)sizeof(rng->nextStir); + xorbuf(rng->nextStir, addIn, addInSz); return 0; } @@ -11633,7 +11633,7 @@ int wc_hwrng_generate_block(byte *output, word32 sz) * - request/byte counts in the DRBG arm of wc_RNG_GenerateBlock() * (hardware-offload arms -- RDRAND, Silabs, async, cryptocb, custom -- * are deliberately uncounted: these are DRBG-facility statistics); - * - banked-seed redemption provenance in wc_RNG_DRBG_NextSeedNow_Nonce(); + * - banked-seed redemption provenance in wc_RNG_DRBG_NextSeedNow_AddIn(); * - seed health failures at the two sites that observe them per-instance * (PollAndReSeed(), NextSeedGenerate); * - chain-provenance bytes (RBGC_bytes_produced: output generated while diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index fdda3d0f1aa..ece5f94a891 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -58,10 +58,10 @@ #define WC_RNG_INIT_FLAG_NONE 0 #endif /* backward-compat shim and helper declarations */ - static int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, + static int wc_RNG_DRBG_Reseed_AddIn(WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); - static int wc_RNG_DRBG_Stir_Nonce( + static int wc_RNG_DRBG_Stir_AddIn( WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz); @@ -2649,27 +2649,27 @@ WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank, } else { if (flags & WC_RNG_BANK_FLAG_STIR) { - ret = wc_RNG_DRBG_Stir_Nonce( + ret = wc_RNG_DRBG_Stir_AddIn( WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz, nonce, nonceSz); #ifdef WC_VERBOSE_RNG if ((ret != 0) && (! (bank->flags & WC_RNG_BANK_FLAG_QUIET))) { WOLFSSL_DEBUG_PRINTF( "WARNING: wc_rng_bank_seed_range(): " - "wc_RNG_DRBG_Stir_Nonce() for inst#%d " + "wc_RNG_DRBG_Stir_AddIn() for inst#%d " "returned %d\n", n, ret); } #endif } else { - ret = wc_RNG_DRBG_Reseed_Nonce( + ret = wc_RNG_DRBG_Reseed_AddIn( WC_RNG_BANK_INST_TO_RNG(drbg), seed, seedSz, nonce, nonceSz); #ifdef WC_VERBOSE_RNG if ((ret != 0) && (! (bank->flags & WC_RNG_BANK_FLAG_QUIET))) { WOLFSSL_DEBUG_PRINTF( "WARNING: wc_rng_bank_seed_range(): " - "wc_RNG_DRBG_Reseed_Nonce() for inst#%d " + "wc_RNG_DRBG_Reseed_AddIn() for inst#%d " "returned %d\n", n, ret); } #endif @@ -3785,7 +3785,7 @@ WOLFSSL_TEST_VIS int wc_RNG_DRBG_Present(const WC_RNG* rng) return (rng != NULL) && (! WC_RNG_BANK_DRBG_NULL(rng)); } -static int wc_RNG_DRBG_Reseed_Nonce( +static int wc_RNG_DRBG_Reseed_AddIn( WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz) @@ -3820,7 +3820,7 @@ WOLFSSL_TEST_VIS int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng) #if FIPS_VERSION3_EQ(5,2,4) -static int wc_RNG_DRBG_Stir_Nonce( +static int wc_RNG_DRBG_Stir_AddIn( WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz) { (void)nonce; @@ -3836,7 +3836,7 @@ WOLFSSL_TEST_VIS int wc_RNG_DRBG_Stir( #else /* FIPS_VERSION3_NE(5,2,4) */ -static int wc_RNG_DRBG_Stir_Nonce( +static int wc_RNG_DRBG_Stir_AddIn( WC_RNG* rng, const byte* seed, word32 seedSz, const byte *nonce, word32 nonceSz) { wc_drbg_reseed_ctr_t saved_ctr; @@ -3862,7 +3862,7 @@ static int wc_RNG_DRBG_Stir_Nonce( WOLFSSL_TEST_VIS int wc_RNG_DRBG_Stir( WC_RNG* rng, const byte* seed, word32 seedSz) { - return wc_RNG_DRBG_Stir_Nonce(rng, seed, seedSz, NULL, 0); + return wc_RNG_DRBG_Stir_AddIn(rng, seed, seedSz, NULL, 0); } WOLFSSL_TEST_VIS int wc_RNG_DRBG_Reseed_Now( diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 9fabf2da026..c78266fb4b9 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -35945,7 +35945,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) api_ret = wc_RNG_DRBG_NextSeedNow(NULL); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_RNG_DRBG_NextSeedNow_Nonce(NULL, NULL, 0); + api_ret = wc_RNG_DRBG_NextSeedNow_AddIn(NULL, NULL, 0); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); api_ret = wc_RNG_DRBG_NextSeedGenerate_Primary(NULL, 1); @@ -35976,7 +35976,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) api_ret = wc_RNG_DRBG_NextSeedCurrent(root, NULL); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); - api_ret = wc_RNG_DRBG_NextSeedNow_Nonce(root, NULL, 5); + api_ret = wc_RNG_DRBG_NextSeedNow_AddIn(root, NULL, 5); if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); @@ -36095,7 +36095,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void) if (i >= 64) ERROR_OUT(WC_TEST_RET_ENC_NC, out); RNG_STATS_SNAP(root); - api_ret = wc_RNG_DRBG_NextSeedNow_Nonce(root, matter, + api_ret = wc_RNG_DRBG_NextSeedNow_AddIn(root, matter, sizeof(matter)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out); diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 6e91e8706ab..5666f51b335 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -879,9 +879,9 @@ WOLFSSL_API int wc_RNG_GetFlags(const WC_RNG* rng, word32 *flags); #ifdef HAVE_HASHDRBG WOLFSSL_API int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz); - WOLFSSL_API int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed, - word32 seedSz, const byte *nonce, - word32 nonceSz); + WOLFSSL_API int wc_RNG_DRBG_Reseed_AddIn(WC_RNG* rng, const byte* seed, + word32 seedSz, const byte *addIn, + word32 addInSz); /* Capability markers for code compiled against a random.h of uncertain * vintage (FIPS builds substitute the module's pinned random.[ch] for * the tree's): consumers outside the boundary gate on these rather than @@ -890,14 +890,14 @@ WOLFSSL_API int wc_RNG_GetFlags(const WC_RNG* rng, word32 *flags); WOLFSSL_API int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz); - WOLFSSL_API int wc_RNG_DRBG_Stir_Nonce( + WOLFSSL_API int wc_RNG_DRBG_Stir_AddIn( WC_RNG* rng, const byte* seed, word32 seedSz, - const byte *nonce, word32 nonceSz); + const byte *addIn, word32 addInSz); #define WC_RNG_HAVE_RESEED_NOW - WOLFSSL_API int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce, - word32 nonceSz); + WOLFSSL_API int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* addIn, + word32 addInSz); WOLFSSL_API int wc_RNG_DRBG_Reseed_Now_Primary(WC_RNG* rng, - const byte* nonce, word32 nonceSz); + const byte* addIn, word32 addInSz); WOLFSSL_API int wc_RNG_TestSeed(const byte* seed, word32 seedSz); #ifdef WC_RNG_HAVE_RBGC @@ -1034,11 +1034,11 @@ WOLFSSL_API int wc_RNG_GetFlags(const WC_RNG* rng, word32 *flags); word32 flags); #endif /* !WC_NO_CONSTRUCTORS */ WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root, - const byte* nonce, word32 nonceSz); + const byte* addIn, word32 addInSz); WOLFSSL_API int wc_RNG_DRBG_StirRBGC(WC_RNG* rng, WC_RNG* root, - const byte* nonce, - word32 nonceSz); + const byte* addIn, + word32 addInSz); #endif /* WC_RNG_HAVE_RBGC */ #ifdef WC_RNG_HAVE_NEXT_SEED @@ -1060,12 +1060,12 @@ WOLFSSL_API int wc_RNG_GetFlags(const WC_RNG* rng, word32 *flags); #endif WOLFSSL_API int wc_RNG_DRBG_NextSeedCurrent(const WC_RNG* rng, WC_ATOMIC_INT_ARG* n); - WOLFSSL_API int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng, - const byte* nonce, - word32 nonceSz); + WOLFSSL_API int wc_RNG_DRBG_NextSeedNow_AddIn(WC_RNG* rng, + const byte* addIn, + word32 addInSz); WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); WOLFSSL_API int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, - const byte *nonce, word32 nonceSz); + const byte *addIn, word32 addInSz); WOLFSSL_API int wc_RNG_DRBG_NextStirNow(WC_RNG* rng); #endif /* WC_RNG_HAVE_NEXT_SEED */ From 91c7f74edb590a74f7abf4ebb0fc8ecd7341da8e Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 6 Nov 2026 09:26:43 -0600 Subject: [PATCH 2/7] tests/api/test_random.c, tests/unit-mcdc/test_random_fault_whitebox.c, tests/unit-mcdc/test_random_whitebox.c: rename "nonce" to "addIn" in reseed APIs. --- tests/api/test_random.c | 28 ++++++++++---------- tests/unit-mcdc/test_random_fault_whitebox.c | 8 +++--- tests/unit-mcdc/test_random_whitebox.c | 12 ++++----- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/api/test_random.c b/tests/api/test_random.c index e9676986574..c4b2c16afdf 100644 --- a/tests/api/test_random.c +++ b/tests/api/test_random.c @@ -1807,37 +1807,37 @@ int test_wc_DrbgReworkDecisionCoverage(void) XMEMSET(&rng, 0, sizeof(rng)); ExpectIntEQ(wc_InitRng_ex(&rng, HEAP_HINT, INVALID_DEVID), 0); - /* wc_RNG_DRBG_Reseed_Nonce: "nonce == NULL && nonceSz > 0" pair. */ - ExpectIntEQ(wc_RNG_DRBG_Reseed_Nonce(&rng, seed, sizeof(seed), NULL, + /* wc_RNG_DRBG_Reseed_AddIn: "nonce == NULL && nonceSz > 0" pair. */ + ExpectIntEQ(wc_RNG_DRBG_Reseed_AddIn(&rng, seed, sizeof(seed), NULL, sizeof(nonce)), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); #if defined(WC_RNG_HAVE_RBGC) && defined(WC_RNG_RBGC_STRATUM_IMMUTABLE) - ExpectIntEQ(wc_RNG_DRBG_Reseed_Nonce(&rng, seed, sizeof(seed), nonce, + ExpectIntEQ(wc_RNG_DRBG_Reseed_AddIn(&rng, seed, sizeof(seed), nonce, sizeof(nonce)), WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)); - ExpectIntEQ(wc_RNG_DRBG_Reseed_Nonce(&rng, seed, sizeof(seed), nonce, 0), + ExpectIntEQ(wc_RNG_DRBG_Reseed_AddIn(&rng, seed, sizeof(seed), nonce, 0), WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)); #else - ExpectIntEQ(wc_RNG_DRBG_Reseed_Nonce(&rng, seed, sizeof(seed), nonce, + ExpectIntEQ(wc_RNG_DRBG_Reseed_AddIn(&rng, seed, sizeof(seed), nonce, sizeof(nonce)), 0); - ExpectIntEQ(wc_RNG_DRBG_Reseed_Nonce(&rng, seed, sizeof(seed), nonce, 0), + ExpectIntEQ(wc_RNG_DRBG_Reseed_AddIn(&rng, seed, sizeof(seed), nonce, 0), 0); #endif - /* wc_RNG_DRBG_Stir_Nonce: rng/seed NULL pair, nonce pair, and the + /* wc_RNG_DRBG_Stir_AddIn: rng/seed NULL pair, nonce pair, and the * second-chunk decision (nonce non-NULL with zero size). */ - ExpectIntEQ(wc_RNG_DRBG_Stir_Nonce(NULL, seed, sizeof(seed), nonce, + ExpectIntEQ(wc_RNG_DRBG_Stir_AddIn(NULL, seed, sizeof(seed), nonce, sizeof(nonce)), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); - ExpectIntEQ(wc_RNG_DRBG_Stir_Nonce(&rng, NULL, sizeof(seed), nonce, + ExpectIntEQ(wc_RNG_DRBG_Stir_AddIn(&rng, NULL, sizeof(seed), nonce, sizeof(nonce)), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); - ExpectIntEQ(wc_RNG_DRBG_Stir_Nonce(&rng, seed, sizeof(seed), NULL, + ExpectIntEQ(wc_RNG_DRBG_Stir_AddIn(&rng, seed, sizeof(seed), NULL, sizeof(nonce)), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); - ExpectIntEQ(wc_RNG_DRBG_Stir_Nonce(&rng, seed, sizeof(seed), nonce, + ExpectIntEQ(wc_RNG_DRBG_Stir_AddIn(&rng, seed, sizeof(seed), nonce, sizeof(nonce)), 0); - ExpectIntEQ(wc_RNG_DRBG_Stir_Nonce(&rng, seed, sizeof(seed), nonce, 0), + ExpectIntEQ(wc_RNG_DRBG_Stir_AddIn(&rng, seed, sizeof(seed), nonce, 0), 0); /* wc_RNG_DRBG_Stir: valid seed only (no nonce chunk). */ @@ -1953,12 +1953,12 @@ int test_wc_RngInitFlagsDecisionCoverage(void) ExpectIntEQ(wc_GenerateSeed(&os, NULL, sizeof(out)), BAD_FUNC_ARG); #endif - /* wc_RNG_DRBG_Stir_Nonce with a valid rng and a NULL seed: the + /* wc_RNG_DRBG_Stir_AddIn with a valid rng and a NULL seed: the * rng-NULL clause's false side. */ XMEMSET(&rng, 0, sizeof(rng)); ExpectIntEQ(wc_InitRng_ex2(&rng, HEAP_HINT, INVALID_DEVID, WC_RNG_INIT_FLAG_NONE), 0); - ExpectIntEQ(wc_RNG_DRBG_Stir_Nonce(&rng, NULL, 0, out, sizeof(out)), + ExpectIntEQ(wc_RNG_DRBG_Stir_AddIn(&rng, NULL, 0, out, sizeof(out)), BAD_FUNC_ARG); ExpectIntEQ(wc_FreeRng(&rng), 0); #endif diff --git a/tests/unit-mcdc/test_random_fault_whitebox.c b/tests/unit-mcdc/test_random_fault_whitebox.c index 96335ec854c..0ed8c5f5edb 100644 --- a/tests/unit-mcdc/test_random_fault_whitebox.c +++ b/tests/unit-mcdc/test_random_fault_whitebox.c @@ -1301,11 +1301,11 @@ static void wb_drbg_reseedctr_type_rows(void) #endif /* -------------------------------------------------------------------------- - * 11. wc_RNG_DRBG_Stir_Nonce() argument guard: + * 11. wc_RNG_DRBG_Stir_AddIn() argument guard: * :2550 (rng == NULL) || (seed == NULL) * * c0 (rng == NULL) is the open operand. c0-T is a structural residual: the - * public wc_RNG_DRBG_Stir_Nonce wrapper guards rng == NULL before calling the + * public wc_RNG_DRBG_Stir_AddIn wrapper guards rng == NULL before calling the * _local function, so the _local guard never sees a NULL rng. The reachable * row is c0-F + c1-T (valid rng, seed == NULL). * ------------------------------------------------------------------------- */ @@ -1320,13 +1320,13 @@ static void wb_stir_nonce_null_rng(void) XMEMSET(nonce, 0xBB, sizeof(nonce)); /* 2550 c0-T: rng == NULL (seed present, so the row is c0's). */ - (void)wc_RNG_DRBG_Stir_Nonce(NULL, seed, sizeof(seed), nonce, + (void)wc_RNG_DRBG_Stir_AddIn(NULL, seed, sizeof(seed), nonce, sizeof(nonce)); /* 2550 c0-F: valid rng, seed == NULL (decision true via the seed arm). */ XMEMSET(&rng, 0, sizeof(rng)); if (wc_InitRng(&rng) == 0) { - (void)wc_RNG_DRBG_Stir_Nonce(&rng, NULL, 0, nonce, sizeof(nonce)); + (void)wc_RNG_DRBG_Stir_AddIn(&rng, NULL, 0, nonce, sizeof(nonce)); (void)wc_FreeRng(&rng); } diff --git a/tests/unit-mcdc/test_random_whitebox.c b/tests/unit-mcdc/test_random_whitebox.c index 40271f61b6d..22046ca24ed 100644 --- a/tests/unit-mcdc/test_random_whitebox.c +++ b/tests/unit-mcdc/test_random_whitebox.c @@ -765,13 +765,13 @@ static void wb_stir_nonce_local(void) } /* :2550 rows: (rng==NULL) and (rng!=NULL, seed==NULL). */ - if (wc_RNG_DRBG_Stir_Nonce_local(NULL, seed, sizeof(seed), + if (wc_RNG_DRBG_Stir_AddIn_local(NULL, seed, sizeof(seed), nonce, sizeof(nonce)) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { WB_NOTE("stir-nonce rng==NULL not rejected"); wb_fail = 1; } - if (wc_RNG_DRBG_Stir_Nonce_local(&rng, NULL, 0, + if (wc_RNG_DRBG_Stir_AddIn_local(&rng, NULL, 0, nonce, sizeof(nonce)) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { WB_NOTE("stir-nonce seed==NULL not rejected"); @@ -779,7 +779,7 @@ static void wb_stir_nonce_local(void) } /* Baseline: all-valid stir, nonce chunk evaluated with ret==0. */ - ret = wc_RNG_DRBG_Stir_Nonce_local(&rng, seed, sizeof(seed), + ret = wc_RNG_DRBG_Stir_AddIn_local(&rng, seed, sizeof(seed), nonce, sizeof(nonce)); if (ret != 0) { WB_NOTE("stir-nonce valid call failed"); @@ -788,13 +788,13 @@ static void wb_stir_nonce_local(void) /* :2566 nonce-operand rows: ret==0 with nonce==NULL and with * nonce!=NULL/nonceSz==0, pairing the nonce-true baseline above. */ - ret = wc_RNG_DRBG_Stir_Nonce_local(&rng, seed, sizeof(seed), + ret = wc_RNG_DRBG_Stir_AddIn_local(&rng, seed, sizeof(seed), NULL, 0); if (ret != 0) { WB_NOTE("stir-nonce no-nonce call failed"); wb_fail = 1; } - ret = wc_RNG_DRBG_Stir_Nonce_local(&rng, seed, sizeof(seed), + ret = wc_RNG_DRBG_Stir_AddIn_local(&rng, seed, sizeof(seed), nonce, 0); if (ret != 0) { WB_NOTE("stir-nonce zero-length-nonce call failed"); @@ -804,7 +804,7 @@ static void wb_stir_nonce_local(void) /* :2566 ret!=0 row: reseed due, live nonce pending. The reseed counter * now belongs to WC_RNG and is shared by both DRBG widths. */ rng.reseedCtr = WC_RESEED_INTERVAL; - ret = wc_RNG_DRBG_Stir_Nonce_local(&rng, seed, sizeof(seed), + ret = wc_RNG_DRBG_Stir_AddIn_local(&rng, seed, sizeof(seed), nonce, sizeof(nonce)); if (ret == 0) { WB_NOTE("stir-nonce with saturated reseed counter succeeded"); From b0582cc0cb5ea1229809fab499776293dd3a45e1 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 6 Nov 2026 09:32:43 -0600 Subject: [PATCH 3/7] .github/workflows/codespell.yml: add addIn to ignore_words_list. --- .github/workflows/codespell.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 5488e9ca3d4..69e7911374e 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -28,7 +28,7 @@ jobs: check_filenames: true check_hidden: true # Add comma separated list of words that occur multiple times that should be ignored (sorted alphabetically, case sensitive) - ignore_words_list: adin,ameba,aNULL,brunch,carryIn,chainG,ciph,cLen,cliKs,cna,dout,FPR,fpr,haveA,inCreated,inOut,inout,larg,LEAPYEAR,Merget,optionA,parm,parms,repid,rIn,userA,ser,siz,te,Te,HSI,failT,toLen,vor,checkin + ignore_words_list: addIn,adin,ameba,aNULL,brunch,carryIn,chainG,ciph,cLen,cliKs,cna,dout,FPR,fpr,haveA,inCreated,inOut,inout,larg,LEAPYEAR,Merget,optionA,parm,parms,repid,rIn,userA,ser,siz,te,Te,HSI,failT,toLen,vor,checkin # The exclude_file contains lines of code that should be ignored. This is useful for individual lines which have non-words that can safely be ignored. exclude_file: '.codespellexcludelines' # To skip files entirely from being processed, add it to the following list: From 9b49ea9faa96136dde0709dd849822bf1406d45d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 6 Nov 2026 14:07:12 -0600 Subject: [PATCH 4/7] wolfssl/wolfcrypt/wc_port.h, wolfcrypt/src/wc_port.c: add wolfSSL_Atomic_Func_Ptr_CompareExchange() -- casting wrapper around wolfSSL_Atomic_Ptr_CompareExchange(). --- wolfcrypt/src/wc_port.c | 6 ++++++ wolfssl/wolfcrypt/wc_port.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/wolfcrypt/src/wc_port.c b/wolfcrypt/src/wc_port.c index 0e3bf981338..d3731b338a9 100644 --- a/wolfcrypt/src/wc_port.c +++ b/wolfcrypt/src/wc_port.c @@ -2639,6 +2639,12 @@ int wolfSSL_Atomic_Ptr_CompareExchange( #endif /* WOLFSSL_ATOMIC_OPS */ +/* Make sure the pointer size assumptions of + * wolfSSL_Atomic_Func_Ptr_CompareExchange() are met. */ +#ifdef wolfSSL_Atomic_Func_Ptr_CompareExchange + wc_static_assert(sizeof(void (*)(void)) == sizeof(void *)); +#endif + #if !defined(SINGLE_THREADED) void wolfSSL_RefWithMutexInit(wolfSSL_RefWithMutex* ref, int* err) diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index 86f8385e419..ee3051b5d7a 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -831,6 +831,12 @@ } #endif +#define wolfSSL_Atomic_Func_Ptr_CompareExchange(c, expected_ptr, new_ptr) \ + wolfSSL_Atomic_Ptr_CompareExchange( \ + (void * volatile *)(c), \ + (void **)(expected_ptr), \ + (void *)(wc_ptr_t)(new_ptr)) + /*** Macro abstractions for compare-and-exchange retry loops, allowing ***/ /*** platform-specific instrumentation and failure paths. ***/ From 7f53ef212cf9fb3af73bfc97f0586dd1b0cf9c67 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 6 Nov 2026 14:07:55 -0600 Subject: [PATCH 5/7] RNG: add auxiliary invalidation pathway, both sender-side and receiver-side. * add APIs wc_RNG_entropy_epoch_extra_inv_init() and wc_RNG_extra_entropy_epoch_init(), and associated callback types. * add internal routine current_entropy_epoch() and associated internal state. * refactor all ad hoc invalidation checks of global_entropy_epoch to use current_entropy_epoch(). * refactor entropy epochs as 64 bit width, with wc_RNG_entropy_epoch_t (word64). * add struct wc_RNG_entropy_epoch_atomic and associated initializers. * add inline helpers wc_RNG_entropy_epoch_export(), wc_RNG_entropy_epoch_import(), and wc_RNG_entropy_epoch_increment_atomic(). * refactor Hash_DRBG_Reseed() and its caller to pass cur_epoch only when defined(WC_RNG_HAVE_ENTROPY_EPOCH). * update tests and documentation for 64 bit epochs. * add test and documentation coverage of auxiliary epoch facility. --- doc/dox_comments/header_files/random.h | 123 ++++++++++++++- wolfcrypt/src/random.c | 194 ++++++++++++++++------- wolfcrypt/test/test.c | 203 ++++++++++++++++++++++++- wolfcrypt/test/test.h | 1 + wolfssl/wolfcrypt/random.h | 94 +++++++++++- 5 files changed, 549 insertions(+), 66 deletions(-) diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index 2319073b636..bab5c3fb698 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -2787,16 +2787,21 @@ int wc_rng_debug_stats_sum(struct wc_rng_debug_stats_snapshot *s, call. The pending event is delivered into each instance -- purging its banked and pooled pre-event material via wc_RNG_invalidate_entropy() -- at the instance's next detection point: a generate, a recovery query, a - banked-seed redemption, or a pool extraction. The call is lock-free and - safe from any context that may call the atomic-increment primitive. + banked-seed redemption, or a pool extraction. If an auxiliary + invalidation publisher is registered (see + wc_RNG_entropy_epoch_extra_inv_init()), the fresh epoch is published + through it after the advance is globally visible. The call is lock-free + and safe from any context that may call the atomic-increment primitive + (and, when a publisher is registered, the publisher). \return The new (post-increment) global entropy epoch. \sa wc_RNG_entropy_needs_recovery \sa wc_RNG_invalidate_entropy + \sa wc_RNG_entropy_epoch_extra_inv_init \sa wc_RNG_GenerateBlock */ -WC_ATOMIC_UINT_ARG wc_RNG_global_invalidate_entropy(void); +wc_RNG_entropy_epoch_t wc_RNG_global_invalidate_entropy(void); /*! \ingroup Random @@ -2804,16 +2809,120 @@ WC_ATOMIC_UINT_ARG wc_RNG_global_invalidate_entropy(void); \brief Read the process-global entropy epoch: the count of wc_RNG_global_invalidate_entropy() events so far. An instance whose stamp differs from this value has an undelivered event pending (see - wc_RNG_entropy_needs_recovery(), which delivers it). A relaxed - snapshot, for diagnostics and for callers that want to notice an event - without touching any instance; nothing is propagated. + wc_RNG_entropy_needs_recovery(), which delivers it). + + \details Every epoch read is also a detection point for the auxiliary + pathway: with an external epoch source registered (see + wc_RNG_extra_entropy_epoch_init()), the source is consulted first, and + an external event detected there advances the global epoch before the + read returns -- so a read can itself move the epoch. No per-instance + delivery is performed either way: this is for diagnostics and for + callers that want to notice an event without touching any instance. \return The current global epoch. \sa wc_RNG_global_invalidate_entropy + \sa wc_RNG_extra_entropy_epoch_init + \sa wc_RNG_entropy_needs_recovery +*/ +wc_RNG_entropy_epoch_t wc_RNG_get_global_entropy_epoch(void); + +/*! + \ingroup Random + + \brief Register a publisher for global entropy-epoch advances -- the + sender half of the auxiliary invalidation pathway, carrying + wc_RNG_global_invalidate_entropy() events to consumers outside the + process. + + \details The canonical sender is a kernel module exporting its epoch to + user processes through read-only shared memory: the handler writes each + fresh epoch to the exported word, and user-mode libraries detect the + change through their own registered source (see + wc_RNG_extra_entropy_epoch_init() -- a build registering both is a + relay, receiving events upstream and republishing them downstream). + The "extra" here is an extra invalidation step -- transmission of the + one and only epoch -- not an extra epoch. + + The handler is called after each advance is globally visible, with the + fresh epoch and the registered arg. It must be cheap, nonblocking, and + safe in every context wc_RNG_global_invalidate_entropy() can run in, + and must not call back into wolfCrypt (a call back into the invalidator + is direct recursion). Registration is one-shot for the life of the + process, with no deregistration, by design. + + Publication is only guaranteed for advances after this call returns + success: either defer normal service until registration completes + (natural in module-init contexts), or accept that an advance racing the + registration may go unpublished. + + \return 0 Success: the handler is registered and will receive every + subsequent advance. + \return BAD_FUNC_ARG handler is null. + \return ALREADY_E a publisher is already registered. + \return BUSY_E lost a registration race; nothing was registered by this + call. + + \param handler The publisher callback. + \param arg Opaque argument passed to the callback. + + \sa wc_RNG_global_invalidate_entropy + \sa wc_RNG_extra_entropy_epoch_init +*/ +int wc_RNG_entropy_epoch_extra_inv_init( + wc_RNG_entropy_epoch_extra_inv_handler_t handler, void *arg); + +/*! + \ingroup Random + + \brief Register a getter for an external entropy-epoch word -- the + receiver half of the auxiliary invalidation pathway, folding platform + entropy-invalidation events (a VM fork or resume, a snapshot restore) + into the process-global epoch. + + \details The canonical receiver is a user-mode library polling a + kernel-exported epoch through read-only shared memory. The "extra" + here is a whole extra epoch -- the external one, shadow-tracked + internally so that only its changes are forwarded. Every epoch read + (every invalidation check, in every instance) consults the getter; a + changed value advances the process-global epoch exactly once per change + before the shadow is updated, so an external event is detected at the + first check after it lands, with no thread of control required on the + receiving side. Events between checks coalesce into one advance. + + The getter must be cheap, nonblocking, callable concurrently, and must + not call into wolfCrypt (every wolfCrypt invalidation check would + recurse). It returns the external epoch, or + WC_RNG_ENTROPY_EPOCH_INVALID (0) when no signal is available yet, which + makes the read inert. + + Registration probes the getter once: a 0 result refuses the + registration and leaves the slot unclaimed. A successful registration + primes the shadow with the probed value, so registering is itself quiet + -- material seeded before registration is trusted. For a conservative + start that distrusts it instead, call + wc_RNG_global_invalidate_entropy() after this call returns. + Registration is one-shot for the life of the process, with no + deregistration, by design. + + \return 0 Success: every subsequent epoch read consults the getter. + \return BAD_FUNC_ARG handler is null, or its probe returned + WC_RNG_ENTROPY_EPOCH_INVALID. + \return ALREADY_E a source is already registered. + \return BUSY_E lost a registration race; nothing was registered by this + call. + + \param handler The getter callback. + \param arg Opaque argument passed to the callback. + + \sa wc_RNG_get_global_entropy_epoch + \sa wc_RNG_global_invalidate_entropy + \sa wc_RNG_entropy_epoch_extra_inv_init \sa wc_RNG_entropy_needs_recovery */ -WC_ATOMIC_UINT_ARG wc_RNG_get_global_entropy_epoch(void); +int wc_RNG_extra_entropy_epoch_init( + wc_RNG_extra_entropy_epoch_get_handler_t handler, + void *arg); /*! \ingroup Random diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 5f30ed23fe3..238709bfa35 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -179,10 +179,6 @@ This library contains implementation for the random number generator. wc_static_assert(WC_RNG_LOCK_FREE == 0); #endif -#ifdef WC_RNG_HAVE_ENTROPY_EPOCH - static wolfSSL_Atomic_Uint global_entropy_epoch = WOLFSSL_ATOMIC_INITIALIZER(1U); -#endif - #if defined(WOLFSSL_SGX) #include #elif defined(USE_WINDOWS_API) @@ -360,6 +356,114 @@ wc_static_assert(DRBG_SUCCESS == 0); #define DRBG_FAILED WC_DRBG_FAILED #define DRBG_CONT_FAILED WC_DRBG_CONT_FAILED +#ifdef WC_RNG_HAVE_ENTROPY_EPOCH + static struct wc_RNG_entropy_epoch_atomic global_entropy_epoch = + WC_RNG_ENTROPY_EPOCH_ATOMIC_INITIALIZER; + static struct wc_RNG_entropy_epoch_atomic extra_entropy_epoch_local = + WC_RNG_ENTROPY_EPOCH_ATOMIC_INVALID; + static wc_RNG_extra_entropy_epoch_get_handler_t + extra_entropy_epoch_get_handler = NULL; + static byte invalid_arg_sentinel; + static void *extra_entropy_epoch_get_arg = &invalid_arg_sentinel; + + int wc_RNG_extra_entropy_epoch_init( + wc_RNG_extra_entropy_epoch_get_handler_t handler, + void *arg) + { + wc_RNG_extra_entropy_epoch_get_handler_t + cur_extra_entropy_epoch_get_handler = + extra_entropy_epoch_get_handler; + wc_RNG_entropy_epoch_t init_extra_entropy_epoch; + if (handler == NULL) + return BAD_FUNC_ARG; + if (extra_entropy_epoch_get_handler != NULL) + return ALREADY_E; + init_extra_entropy_epoch = handler(arg); + if (init_extra_entropy_epoch == WC_RNG_ENTROPY_EPOCH_INVALID) + return BAD_FUNC_ARG; + if (wolfSSL_Atomic_Func_Ptr_CompareExchange( + &extra_entropy_epoch_get_handler, + &cur_extra_entropy_epoch_get_handler, + handler)) + { + (void)wc_RNG_entropy_epoch_import(WC_RNG_ENTROPY_EPOCH_INVALID, + init_extra_entropy_epoch, + &extra_entropy_epoch_local); + extra_entropy_epoch_get_arg = arg; + return 0; + } + else + return BUSY_E; + } + + static WC_INLINE wc_RNG_entropy_epoch_t current_entropy_epoch(void) { + if ((extra_entropy_epoch_get_handler != NULL) && + (extra_entropy_epoch_get_arg != &invalid_arg_sentinel)) + { + wc_RNG_entropy_epoch_t cur_extra_entropy_epoch = + extra_entropy_epoch_get_handler(extra_entropy_epoch_get_arg); + if (cur_extra_entropy_epoch != WC_RNG_ENTROPY_EPOCH_INVALID) { + wc_RNG_entropy_epoch_t cur_extra_entropy_epoch_local = + wc_RNG_entropy_epoch_export(&extra_entropy_epoch_local); + if (cur_extra_entropy_epoch != cur_extra_entropy_epoch_local) { + wc_RNG_global_invalidate_entropy(); + (void)wc_RNG_entropy_epoch_import( + cur_extra_entropy_epoch_local, + cur_extra_entropy_epoch, + &extra_entropy_epoch_local); + } + } + } + return wc_RNG_entropy_epoch_export(&global_entropy_epoch); + } + + wc_RNG_entropy_epoch_t wc_RNG_get_global_entropy_epoch(void) { + return current_entropy_epoch(); + } + + static wc_RNG_entropy_epoch_extra_inv_handler_t + entropy_epoch_extra_inv_handler = NULL; + static void *entropy_epoch_extra_inv_arg = &invalid_arg_sentinel; + int wc_RNG_entropy_epoch_extra_inv_init( + wc_RNG_entropy_epoch_extra_inv_handler_t handler, void *arg) + { + wc_RNG_entropy_epoch_extra_inv_handler_t + cur_entropy_epoch_extra_inv_handler = + entropy_epoch_extra_inv_handler; + if (handler == NULL) + return BAD_FUNC_ARG; + if (cur_entropy_epoch_extra_inv_handler != NULL) + return ALREADY_E; + if (wolfSSL_Atomic_Func_Ptr_CompareExchange( + &entropy_epoch_extra_inv_handler, + &cur_entropy_epoch_extra_inv_handler, + handler)) + { + entropy_epoch_extra_inv_arg = arg; + return 0; + } + else + return BUSY_E; + } + + wc_RNG_entropy_epoch_t wc_RNG_global_invalidate_entropy(void) { + wc_RNG_entropy_epoch_t new_epoch = + wc_RNG_entropy_epoch_increment_atomic(&global_entropy_epoch); + /* Note that extra invalidation propagation is only guaranteed after + * wc_RNG_entropy_epoch_extra_inv_init() has returned with success -- + * for guaranteed propagation, callers must defer normal service until + * after completion. */ + if ((entropy_epoch_extra_inv_handler != NULL) && + (entropy_epoch_extra_inv_arg != &invalid_arg_sentinel)) + { + entropy_epoch_extra_inv_handler( + new_epoch, + entropy_epoch_extra_inv_arg); + } + return new_epoch; + } +#endif /* WC_RNG_HAVE_ENTROPY_EPOCH */ + /* enforcement helper for WC_RNG_LOCK_REQUIRED: instance-consuming public APIs * call this on entry. (WC_RNG_LOCK_ENTROPY_INVALIDATED is deliberately not * enforced here -- see the body.) */ @@ -1211,7 +1315,9 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, word32 seedSz, const byte* additional, word32 additionalSz, int in_bracketed_consume, - WC_ATOMIC_UINT_ARG cur_epoch, +#ifdef WC_RNG_HAVE_ENTROPY_EPOCH + wc_RNG_entropy_epoch_t cur_epoch, +#endif WC_RNG_PID_ARG my_pid) { int ret; @@ -1219,9 +1325,6 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, WC_RNG_lock_arg_t cur_lock; #endif -#ifndef WC_RNG_HAVE_ENTROPY_EPOCH - (void)cur_epoch; -#endif #if !defined(HAVE_GETPID) || defined(WOLFSSL_NO_GETPID) (void)my_pid; #endif @@ -1426,7 +1529,7 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, */ if ((ret == 0) && (cur_lock & WC_RNG_LOCK_ENTROPY_INVALIDATED) #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - && (cur_epoch == WOLFSSL_ATOMIC_LOAD(global_entropy_epoch)) + && (cur_epoch == current_entropy_epoch()) #endif ) { @@ -1491,13 +1594,9 @@ int wc_RNG_DRBG_Reseed_AddIn(WC_RNG* rng, const byte* seed, word32 seedSz, const byte *addIn, word32 addInSz) { int ret; - WC_ATOMIC_UINT_ARG cur_epoch = #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - WOLFSSL_ATOMIC_LOAD(global_entropy_epoch) -#else - 0 + wc_RNG_entropy_epoch_t cur_epoch = current_entropy_epoch(); #endif - ; WC_RNG_PID_ARG my_pid = WC_RNG_GETPID(); if (rng == NULL || seed == NULL) { return BAD_FUNC_ARG; @@ -1569,7 +1668,11 @@ int wc_RNG_DRBG_Reseed_AddIn(WC_RNG* rng, const byte* seed, word32 seedSz, } ret = Hash_DRBG_Reseed(rng, seed, seedSz, addIn, addInSz, - 0 /* in_bracketed_consume */, cur_epoch, my_pid); + 0 /* in_bracketed_consume */, +#ifdef WC_RNG_HAVE_ENTROPY_EPOCH + cur_epoch, +#endif + my_pid); if (ret > 0) /* DRBG-internal code: translate at the API */ ret = RNG_FAILURE_E; #if defined(WC_RNG_HAVE_RBGC) && !defined(WC_RNG_RBGC_STRATUM_IMMUTABLE) @@ -3595,7 +3698,7 @@ static WARN_UNUSED_RESULT int _InitRng(WC_RNG* rng, #ifdef WC_RNG_HAVE_ENTROPY_EPOCH /* note this assignment must precede the later seed operation. */ - rng->entropy_epoch = WOLFSSL_ATOMIC_LOAD(global_entropy_epoch); + rng->entropy_epoch = current_entropy_epoch(); #endif #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) @@ -4682,7 +4785,7 @@ int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) (wc_RNG_DRBG_NextSeedCurrent(rng, &NextSeedCurrent) == 0) && (NextSeedCurrent == WC_DRBG_NEXT_SEED_READY) #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - && (rng->entropy_epoch == WOLFSSL_ATOMIC_LOAD(global_entropy_epoch)) + && (rng->entropy_epoch == current_entropy_epoch()) #endif #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) && (rng->pid == getpid()) @@ -4922,7 +5025,7 @@ int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits) * the latch carries, delivered lazily: report it here so that a holder * whose last operation straddled the event learns of it at release. The * read is unleased by design (see rng_entropy_event_propagate()). */ - if (rng->entropy_epoch != WOLFSSL_ATOMIC_LOAD(global_entropy_epoch)) + if (rng->entropy_epoch != current_entropy_epoch()) return NEEDS_RECOVERY_E; #endif return 0; @@ -4985,7 +5088,7 @@ int wc_RNG_lock_put_conditional(WC_RNG* rng, return NEEDS_RECOVERY_E; #ifdef WC_RNG_HAVE_ENTROPY_EPOCH /* as in wc_RNG_lock_put() */ - if (rng->entropy_epoch != WOLFSSL_ATOMIC_LOAD(global_entropy_epoch)) + if (rng->entropy_epoch != current_entropy_epoch()) return NEEDS_RECOVERY_E; #endif return 0; @@ -5107,7 +5210,7 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { #ifdef WC_RNG_HAVE_ENTROPY_EPOCH /* Load the event epoch before any purge: an increment mid-delivery leaves * the stamp stale, forcing a conservative propagation. */ - WC_ATOMIC_UINT_ARG ev_epoch = WOLFSSL_ATOMIC_LOAD(global_entropy_epoch); + wc_RNG_entropy_epoch_t ev_epoch = current_entropy_epoch(); #endif #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) pid_t my_pid = getpid(); @@ -5278,7 +5381,7 @@ static WC_MAYBE_UNUSED int rng_entropy_event_propagate(WC_RNG* rng) if (! wc_RNG_DRBG_Present(rng)) return 0; #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - if (rng->entropy_epoch != WOLFSSL_ATOMIC_LOAD(global_entropy_epoch)) + if (rng->entropy_epoch != current_entropy_epoch()) ret = NEEDS_RECOVERY_E; #endif #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) @@ -5316,7 +5419,7 @@ WOLFSSL_API int wc_RNG_entropy_needs_recovery(WC_RNG* rng) { return ret; #else #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - if (rng->entropy_epoch != WOLFSSL_ATOMIC_LOAD(global_entropy_epoch)) + if (rng->entropy_epoch != current_entropy_epoch()) return NEEDS_RECOVERY_E; #endif #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) @@ -5337,16 +5440,6 @@ WOLFSSL_API int wc_RNG_entropy_needs_recovery(WC_RNG* rng) { return 0; } -#ifdef WC_RNG_HAVE_ENTROPY_EPOCH -WOLFSSL_API WC_ATOMIC_UINT_ARG wc_RNG_global_invalidate_entropy(void) { - return wolfSSL_Atomic_Uint_AddFetch(&global_entropy_epoch, 1U); -} - -WC_ATOMIC_UINT_ARG wc_RNG_get_global_entropy_epoch(void) { - return WOLFSSL_ATOMIC_LOAD(global_entropy_epoch); -} -#endif - #ifdef WC_RNG_HAVE_FREE_HOOK /* This routine is used for mitigation of RNG cloning events, particularly by * hypervisors. */ @@ -6063,13 +6156,9 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_ReseedRBGC_local( byte seed[SEED_SZ]; #endif int ret; - WC_ATOMIC_UINT_ARG cur_epoch = #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - WOLFSSL_ATOMIC_LOAD(global_entropy_epoch) -#else - 0 + wc_RNG_entropy_epoch_t cur_epoch = current_entropy_epoch(); #endif - ; WC_RNG_PID_ARG my_pid = WC_RNG_GETPID(); if ((rng == NULL) || (root == NULL) || (rng == root) || @@ -6174,7 +6263,10 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_ReseedRBGC_local( if (ret == 0) { if (credited) { ret = Hash_DRBG_Reseed(rng, seed, SEED_SZ, nonce, nonceSz, - 0 /* in_bracketed_consume */, cur_epoch, + 0 /* in_bracketed_consume */, +#ifdef WC_RNG_HAVE_ENTROPY_EPOCH + cur_epoch, +#endif my_pid); if (ret > 0) /* DRBG-internal code: translate at the API */ ret = RNG_FAILURE_E; @@ -6226,13 +6318,9 @@ static WARN_UNUSED_RESULT int PollAndReSeed(WC_RNG* rng, const byte* additional, word32 additionalSz) { int ret = WC_NO_ERR_TRACE(DRBG_NEED_RESEED); - WC_ATOMIC_UINT_ARG cur_epoch = #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - WOLFSSL_ATOMIC_LOAD(global_entropy_epoch) -#else - 0 + wc_RNG_entropy_epoch_t cur_epoch = current_entropy_epoch(); #endif - ; WC_RNG_PID_ARG my_pid = WC_RNG_GETPID(); int devId = INVALID_DEVID; #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB) @@ -6410,7 +6498,10 @@ static WARN_UNUSED_RESULT int PollAndReSeed(WC_RNG* rng, const byte* additional, if (ret == DRBG_SUCCESS) { ret = Hash_DRBG_Reseed(rng, newSeed + SEED_BLOCK_SZ, SEED_SZ, additional, additionalSz, - 0 /* in_bracketed_consume */, cur_epoch, + 0 /* in_bracketed_consume */, +#ifdef WC_RNG_HAVE_ENTROPY_EPOCH + cur_epoch, +#endif my_pid); #if defined(WC_RNG_HAVE_RBGC) && \ @@ -6450,7 +6541,7 @@ static WARN_UNUSED_RESULT int PollAndReSeed(WC_RNG* rng, const byte* additional, #ifdef WC_RNG_HAVE_ENTROPY_EPOCH if ((ret == 0) && - (cur_epoch != WOLFSSL_ATOMIC_LOAD(global_entropy_epoch))) + (cur_epoch != current_entropy_epoch())) { /* An event landed between the caller's capture and here: the material * reseeded is possibly pre-event. The reseed stands (the state is no @@ -7083,13 +7174,9 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedNow_AddIn_local( WC_ATOMIC_INT_ARG expected = WC_DRBG_NEXT_SEED_READY; int ret; int devId; - WC_ATOMIC_UINT_ARG cur_epoch = #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - WOLFSSL_ATOMIC_LOAD(global_entropy_epoch) -#else - 0 + wc_RNG_entropy_epoch_t cur_epoch = current_entropy_epoch(); #endif - ; WC_RNG_PID_ARG my_pid = WC_RNG_GETPID(); #ifdef WC_RNG_HAVE_LOCK wc_drbg_reseed_ctr_t baseline_reseedCtr; @@ -7159,7 +7246,10 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedNow_AddIn_local( * prefix was consumed by the bank-time health testing. */ ret = Hash_DRBG_Reseed(rng, rng->nextSeed + SEED_BLOCK_SZ, SEED_SZ, addIn, addInSz, 1 /* in_bracketed_consume */, - cur_epoch, my_pid); +#ifdef WC_RNG_HAVE_ENTROPY_EPOCH + cur_epoch, +#endif + my_pid); /* Use-once: consumed by the attempt, success or not. The scrub must be * visible before the aperture reopens. */ @@ -7495,7 +7585,7 @@ static WC_INLINE int RngFailFastVerdict(const WC_RNG* rng) return NEEDS_RECOVERY_E; #endif #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - if (rng->entropy_epoch != WOLFSSL_ATOMIC_LOAD(global_entropy_epoch)) + if (rng->entropy_epoch != current_entropy_epoch()) return NEEDS_RECOVERY_E; #endif #if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c78266fb4b9..588636af4bc 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -1067,6 +1067,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); !defined(CUSTOM_RAND_GENERATE_BLOCK) && !defined(HAVE_INTEL_RDRAND) && \ !defined(HAVE_SELFTEST) && (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void); +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_extra_test(void); #endif #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ !defined(HAVE_INTEL_RDRAND) && !defined(HAVE_SELFTEST) && \ @@ -2971,6 +2972,10 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_FAIL("RNGEPOCH test failed!\n", ret); else TEST_PASS("RNGEPOCH test passed!\n"); + if ((ret = rng_epoch_extra_test()) != 0) + TEST_FAIL("RNGEPEXT test failed!\n", ret); + else + TEST_PASS("RNGEPEXT test passed!\n"); #endif #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ !defined(HAVE_INTEL_RDRAND) && !defined(HAVE_SELFTEST) && \ @@ -36805,7 +36810,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) WC_DECLARE_VAR(rng, WC_RNG, 1, HEAP_HINT); int rng_inited = 0; byte block[32]; - WC_ATOMIC_UINT_ARG epoch_before, epoch_after; + wc_RNG_entropy_epoch_t epoch_before, epoch_after; RNG_STATS_DECLS; @@ -37171,6 +37176,202 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) } #endif /* WC_RNG_SEED_CB && WC_RNG_HAVE_LOCK */ +out_l: + + if (rng_inited) { + int cleanup_ret = wc_FreeRng(rng); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + WC_FREE_VAR(rng, HEAP_HINT); + + return ret; +} + +/* Fixtures for rng_epoch_extra_test(): a stand-in "external" epoch word -- + * the observable surface an mmap'd kernel counter presents, minus the + * kernel -- plus a recording publication handler. Registration is + * process-lifetime one-shot by design, so these persist across reruns of + * the suite, and stay quiet for later tests: the word only moves when this + * test moves it. */ +static wolfSSL_Atomic_Uint rng_epoch_extra_ext_word = + WOLFSSL_ATOMIC_INITIALIZER(1U); +static int rng_epoch_extra_get_zero_mode = 0; +static void *rng_epoch_extra_get_arg_seen = NULL; +static int rng_epoch_extra_get_marker = 0; +static int rng_epoch_extra_inv_marker = 0; +static unsigned int rng_epoch_extra_inv_count = 0; +static wc_RNG_entropy_epoch_t rng_epoch_extra_inv_last_epoch = 0; +static void *rng_epoch_extra_inv_arg_seen = NULL; +static int rng_epoch_extra_registered = 0; + +static wc_RNG_entropy_epoch_t rng_epoch_extra_getCb(void *arg) +{ + rng_epoch_extra_get_arg_seen = arg; + if (rng_epoch_extra_get_zero_mode) + return 0; + return WOLFSSL_ATOMIC_LOAD(rng_epoch_extra_ext_word); +} + +static void rng_epoch_extra_invCb(wc_RNG_entropy_epoch_t epoch, void *arg) +{ + ++rng_epoch_extra_inv_count; + rng_epoch_extra_inv_last_epoch = epoch; + rng_epoch_extra_inv_arg_seen = arg; +} + +/* The auxiliary invalidation pathway: one-shot registration semantics for + * both handlers, publication of local events to the registered handler + * with the registrant's arg, detection of an external edge at the next + * epoch-reading call (with relay publication), exactly-once accounting + * per edge, and the 0-guard (a signalless getter is inert). */ +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_extra_test(void) +{ + wc_test_ret_t ret = 0; + int api_ret; + WC_DECLARE_VAR(rng, WC_RNG, 1, HEAP_HINT); + int rng_inited = 0; + byte block[32]; + wc_RNG_entropy_epoch_t epoch_before, epoch_after; + unsigned int inv_count_before; + + WOLFSSL_ENTER("rng_epoch_extra_test"); + + WC_ALLOC_VAR_EX(rng, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out_l)); + + if (! rng_epoch_extra_registered) { + /* refusals leave the slot unclaimed */ + api_ret = wc_RNG_extra_entropy_epoch_init(NULL, NULL); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + /* a getter with no signal yet is probed and refused -- and the + * probe demonstrably ran, with the caller's arg */ + rng_epoch_extra_get_zero_mode = 1; + rng_epoch_extra_get_arg_seen = NULL; + api_ret = wc_RNG_extra_entropy_epoch_init( + rng_epoch_extra_getCb, &rng_epoch_extra_get_marker); + rng_epoch_extra_get_zero_mode = 0; + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (rng_epoch_extra_get_arg_seen != + (void *)&rng_epoch_extra_get_marker) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, out_l); + } + api_ret = wc_RNG_entropy_epoch_extra_inv_init(NULL, NULL); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + + /* the real registrations */ + api_ret = wc_RNG_extra_entropy_epoch_init( + rng_epoch_extra_getCb, &rng_epoch_extra_get_marker); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_entropy_epoch_extra_inv_init( + rng_epoch_extra_invCb, &rng_epoch_extra_inv_marker); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + rng_epoch_extra_registered = 1; + } + + /* one-shot forever after */ + api_ret = wc_RNG_extra_entropy_epoch_init( + rng_epoch_extra_getCb, &rng_epoch_extra_get_marker); + if (api_ret != WC_NO_ERR_TRACE(ALREADY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_entropy_epoch_extra_inv_init( + rng_epoch_extra_invCb, &rng_epoch_extra_inv_marker); + if (api_ret != WC_NO_ERR_TRACE(ALREADY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + + /* quiet start: the registration probe primed the shadow word, so a + * read is just a read */ + epoch_before = wc_RNG_get_global_entropy_epoch(); + if (wc_RNG_get_global_entropy_epoch() != epoch_before) + ERROR_OUT(WC_TEST_RET_ENC_NC, out_l); + + /* a local event publishes the fresh epoch to the registered handler, + * with the registrant's arg */ + inv_count_before = rng_epoch_extra_inv_count; + epoch_after = wc_RNG_global_invalidate_entropy(); + if (epoch_after != epoch_before + 1U) + ERROR_OUT(WC_TEST_RET_ENC_I((int)epoch_after), out_l); + if (rng_epoch_extra_inv_count != inv_count_before + 1U) + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_epoch_extra_inv_count), out_l); + if (rng_epoch_extra_inv_last_epoch != epoch_after) + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_epoch_extra_inv_last_epoch), + out_l); + if (rng_epoch_extra_inv_arg_seen != (void *)&rng_epoch_extra_inv_marker) + ERROR_OUT(WC_TEST_RET_ENC_NC, out_l); + + /* an instance stamped now is current */ + api_ret = wc_InitRng_ex(rng, HEAP_HINT, RNG_DRBG_DEVID); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + rng_inited = 1; + api_ret = wc_RNG_entropy_needs_recovery(rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + + /* the external edge: the instance learns of it at its next + * invalidation check, without anyone calling the invalidator -- the + * check itself is the detecting read, and detection relays through + * the publication handler */ + inv_count_before = rng_epoch_extra_inv_count; + epoch_before = epoch_after; + (void)wolfSSL_Atomic_Uint_AddFetch(&rng_epoch_extra_ext_word, 1U); + api_ret = wc_RNG_entropy_needs_recovery(rng); + if (api_ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + epoch_after = wc_RNG_get_global_entropy_epoch(); + if (epoch_after != epoch_before + 1U) + ERROR_OUT(WC_TEST_RET_ENC_I((int)epoch_after), out_l); + if (rng_epoch_extra_inv_count != inv_count_before + 1U) + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_epoch_extra_inv_count), out_l); + if (rng_epoch_extra_inv_last_epoch != epoch_after) + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_epoch_extra_inv_last_epoch), + out_l); + /* exactly-once per edge: more reads are just reads */ + if (wc_RNG_get_global_entropy_epoch() != epoch_after) + ERROR_OUT(WC_TEST_RET_ENC_NC, out_l); + if (rng_epoch_extra_inv_count != inv_count_before + 1U) + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_epoch_extra_inv_count), out_l); + + /* recovery is rng_epoch_test()'s story; here one credited generate + * (or a reinit where generate is remote) just clears the verdict so + * the 0-guard check below runs against a current instance */ + if (rng_test_generate_is_local(rng)) { + api_ret = wc_RNG_GenerateBlock(rng, block, (word32)sizeof(block)); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + } + else { + api_ret = wc_FreeRng(rng); + rng_inited = 0; + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_InitRng_ex(rng, HEAP_HINT, RNG_DRBG_DEVID); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + rng_inited = 1; + } + api_ret = wc_RNG_entropy_needs_recovery(rng); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + + /* the 0-guard: a getter with no signal is inert, not an edge */ + inv_count_before = rng_epoch_extra_inv_count; + rng_epoch_extra_get_zero_mode = 1; + api_ret = wc_RNG_entropy_needs_recovery(rng); + rng_epoch_extra_get_zero_mode = 0; + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (wc_RNG_get_global_entropy_epoch() != epoch_after) + ERROR_OUT(WC_TEST_RET_ENC_NC, out_l); + if (rng_epoch_extra_inv_count != inv_count_before) + ERROR_OUT(WC_TEST_RET_ENC_I((int)rng_epoch_extra_inv_count), out_l); + out_l: if (rng_inited) { diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index b63b5810bd6..b2b469091fe 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -308,6 +308,7 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void); +extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_extra_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void); #endif /* WC_NO_RNG */ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 5666f51b335..600607631f9 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -559,10 +559,15 @@ wc_static_assert(WC_RNG_RBGC_USER_SEED_STRATUM >= 256); #endif #endif -#if ((defined(WOLFSSL_ATOMIC_OPS) && !defined(SINGLE_THREADED)) || \ +#if ((defined(WOLFSSL_ATOMIC_OPS) && !defined(SINGLE_THREADED) && \ + defined(WORD64_AVAILABLE)) || \ defined(WC_RNG_WANT_ENTROPY_EPOCH)) && \ !defined(WC_RNG_NO_ENTROPY_EPOCH) && !defined(WC_NO_RNG) + #if !defined(WOLFSSL_ATOMIC_OPS) || !defined(WORD64_AVAILABLE) + #error WC_RNG_HAVE_ENTROPY_EPOCH requires WOLFSSL_ATOMIC_OPS and WORD64_AVAILABLE. + #endif #define WC_RNG_HAVE_ENTROPY_EPOCH + typedef word64 wc_RNG_entropy_epoch_t; #endif /* RNG context */ @@ -572,7 +577,7 @@ struct WC_RNG { byte status; word32 flags; #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - WC_ATOMIC_UINT_ARG entropy_epoch; + wc_RNG_entropy_epoch_t entropy_epoch; #endif #ifdef WC_RNG_DEBUG_STATS wc_rng_debug_counter_t _stats_total_bytes_requested; @@ -1064,8 +1069,8 @@ WOLFSSL_API int wc_RNG_GetFlags(const WC_RNG* rng, word32 *flags); const byte* addIn, word32 addInSz); WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng); - WOLFSSL_API int wc_RNG_DRBG_NextStirStore(WC_RNG* rng, - const byte *addIn, word32 addInSz); + WOLFSSL_API int wc_RNG_DRBG_NextStirStore( + WC_RNG* rng, const byte *addIn, word32 addInSz); WOLFSSL_API int wc_RNG_DRBG_NextStirNow(WC_RNG* rng); #endif /* WC_RNG_HAVE_NEXT_SEED */ @@ -1112,8 +1117,85 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng); WOLFSSL_API int wc_RNG_entropy_needs_recovery(WC_RNG* rng); #ifdef WC_RNG_HAVE_ENTROPY_EPOCH - WOLFSSL_API WC_ATOMIC_UINT_ARG wc_RNG_global_invalidate_entropy(void); - WOLFSSL_API WC_ATOMIC_UINT_ARG wc_RNG_get_global_entropy_epoch(void); + struct wc_RNG_entropy_epoch_atomic { + wolfSSL_Atomic_Uint high; + wolfSSL_Atomic_Uint low; + }; + #define WC_RNG_ENTROPY_EPOCH_ATOMIC_INITIALIZER { \ + WOLFSSL_ATOMIC_INITIALIZER(0U), \ + WOLFSSL_ATOMIC_INITIALIZER(1U) \ + } + #define WC_RNG_ENTROPY_EPOCH_ATOMIC_INVALID { \ + WOLFSSL_ATOMIC_INITIALIZER(0U), \ + WOLFSSL_ATOMIC_INITIALIZER(0U) \ + } + #define WC_RNG_ENTROPY_EPOCH_INVALID 0 + static WC_INLINE wc_RNG_entropy_epoch_t wc_RNG_entropy_epoch_export( + struct wc_RNG_entropy_epoch_atomic *epoch) + { + wc_RNG_entropy_epoch_t epoch_export; + epoch_export = WOLFSSL_ATOMIC_LOAD(epoch->low); + /* A tear here will always preserve the epoch mismatch via the low word, + * and at worst have the wrong high word. */ + epoch_export |= + (wc_RNG_entropy_epoch_t)WOLFSSL_ATOMIC_LOAD(epoch->high) << + (wc_RNG_entropy_epoch_t)32; + return epoch_export; + } + static WC_INLINE int wc_RNG_entropy_epoch_import( + wc_RNG_entropy_epoch_t old_epoch, + wc_RNG_entropy_epoch_t new_epoch, + struct wc_RNG_entropy_epoch_atomic *dest) + { + int ret = 1; + WC_ATOMIC_UINT_ARG expected_low = (WC_ATOMIC_UINT_ARG)old_epoch; + WC_ATOMIC_UINT_ARG expected_high = + (WC_ATOMIC_UINT_ARG)(old_epoch >> + (wc_RNG_entropy_epoch_t)32); + if (! wolfSSL_Atomic_Uint_CompareExchange( + &dest->low, + &expected_low, + (WC_ATOMIC_UINT_ARG)new_epoch)) + ret = 0; + if (((new_epoch >> 32) != (old_epoch >> 32)) && + (! wolfSSL_Atomic_Uint_CompareExchange( + &dest->high, + &expected_high, + (WC_ATOMIC_UINT_ARG)(new_epoch >> (wc_RNG_entropy_epoch_t)32)))) + ret = 0; + return ret; + } + static WC_INLINE wc_RNG_entropy_epoch_t wc_RNG_entropy_epoch_increment_atomic( + struct wc_RNG_entropy_epoch_atomic *epoch) + { + wc_RNG_entropy_epoch_t new_epoch = + wolfSSL_Atomic_Uint_AddFetch(&epoch->low, 1U); + if (new_epoch == 0) + new_epoch += + (wc_RNG_entropy_epoch_t)wolfSSL_Atomic_Uint_AddFetch( + &epoch->high, 1U) << + (wc_RNG_entropy_epoch_t)32; + else { + /* Note this is a benign tear between .low and .high here -- worst + * case, the returned value glitches high by 2^32 - 1, resulting in + * an extra cycle of invalidation and recovery. */ + new_epoch += + (wc_RNG_entropy_epoch_t)WOLFSSL_ATOMIC_LOAD(epoch->high) << + (wc_RNG_entropy_epoch_t)32; + } + return new_epoch; + } + WOLFSSL_API wc_RNG_entropy_epoch_t wc_RNG_global_invalidate_entropy(void); + WOLFSSL_API wc_RNG_entropy_epoch_t wc_RNG_get_global_entropy_epoch(void); + typedef void (*wc_RNG_entropy_epoch_extra_inv_handler_t)( + wc_RNG_entropy_epoch_t epoch, void *arg); + WOLFSSL_API int wc_RNG_entropy_epoch_extra_inv_init( + wc_RNG_entropy_epoch_extra_inv_handler_t handler, void *arg); + typedef wc_RNG_entropy_epoch_t (*wc_RNG_extra_entropy_epoch_get_handler_t)( + void *arg); + WOLFSSL_API int wc_RNG_extra_entropy_epoch_init( + wc_RNG_extra_entropy_epoch_get_handler_t handler, + void *arg); #endif #ifdef WC_RNG_HAVE_FREE_HOOK From 2775dca14246650d7816ce3ef20cf55df8ff1769 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 6 Nov 2026 22:09:46 -0600 Subject: [PATCH 6/7] wolfcrypt/src/random.c and wolfssl/wolfcrypt/random.h: add wc_RNG_extra_entropy_epoch_reset() and wc_RNG_entropy_epoch_extra_inv_reset() (test helpers). wolfcrypt/test/test.c and wolfcrypt/test/test.h: call rng_epoch_extra_test() from rng_epoch_test() rather than reporting its results separately; fix data race in rng_epoch_extra_getCb(); implement ext epoch save/restore in rng_epoch_extra_test(). --- doc/dox_comments/header_files/random.h | 8 +- wolfcrypt/src/random.c | 61 +++++++++- wolfcrypt/test/test.c | 160 ++++++++++++++++++------- wolfcrypt/test/test.h | 1 - wolfssl/wolfcrypt/random.h | 5 + 5 files changed, 187 insertions(+), 48 deletions(-) diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index bab5c3fb698..ab8e05e6a68 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -2889,11 +2889,17 @@ int wc_RNG_entropy_epoch_extra_inv_init( before the shadow is updated, so an external event is detected at the first check after it lands, with no thread of control required on the receiving side. Events between checks coalesce into one advance. + Under concurrency the accounting is best-effort in the safe direction: + racing readers can each advance for the same external change, and a + duplicate advance (with its duplicate publication, when a publisher is + registered) costs at most a spurious recovery cycle. Duplicates are + tolerated by design -- the alternative single-broadcaster protocols + can lose an advance outright, which is never tolerable. The getter must be cheap, nonblocking, callable concurrently, and must not call into wolfCrypt (every wolfCrypt invalidation check would recurse). It returns the external epoch, or - WC_RNG_ENTROPY_EPOCH_INVALID (0) when no signal is available yet, which + WC_RNG_ENTROPY_EPOCH_INVALID (0) when no signal is available, which makes the read inert. Registration probes the getter once: a 0 result refuses the diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 238709bfa35..38acc77f357 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -396,6 +396,42 @@ wc_static_assert(DRBG_SUCCESS == 0); return BUSY_E; } + /* _TEST_VIS routine to allow swapping of handlers. Minimal safety + * provisions -- only safe to use where no threads need synchronizing, due + * to tearing of handler from arg. */ + int wc_RNG_extra_entropy_epoch_reset( + wc_RNG_extra_entropy_epoch_get_handler_t *handler, + void **arg) + { + wc_RNG_entropy_epoch_t init_extra_entropy_epoch; + if ((handler == NULL) || (arg == NULL)) + return BAD_FUNC_ARG; + *handler = extra_entropy_epoch_get_handler; + if (*handler == NULL) + return BAD_STATE_E; + *arg = extra_entropy_epoch_get_arg; + init_extra_entropy_epoch = + wc_RNG_entropy_epoch_export(&extra_entropy_epoch_local); + if (! wolfSSL_Atomic_Func_Ptr_CompareExchange( + &extra_entropy_epoch_get_handler, + handler, + NULL)) + return BUSY_E; + if (! wc_RNG_entropy_epoch_import(init_extra_entropy_epoch, + WC_RNG_ENTROPY_EPOCH_INVALID, + &extra_entropy_epoch_local)) + { + wc_RNG_extra_entropy_epoch_get_handler_t expected_null = NULL; + (void)wolfSSL_Atomic_Func_Ptr_CompareExchange( + &extra_entropy_epoch_get_handler, + &expected_null, + *handler); + return BUSY_E; + } + extra_entropy_epoch_get_arg = &invalid_arg_sentinel; + return 0; + } + static WC_INLINE wc_RNG_entropy_epoch_t current_entropy_epoch(void) { if ((extra_entropy_epoch_get_handler != NULL) && (extra_entropy_epoch_get_arg != &invalid_arg_sentinel)) @@ -446,13 +482,36 @@ wc_static_assert(DRBG_SUCCESS == 0); return BUSY_E; } + /* _TEST_VIS routine to allow swapping of handlers. Minimal safety + * provisions -- only safe to use where no threads need synchronizing, due + * to tearing of handler from arg. */ + int wc_RNG_entropy_epoch_extra_inv_reset( + wc_RNG_entropy_epoch_extra_inv_handler_t *handler, void **arg) + { + if ((handler == NULL) || (arg == NULL)) + return BAD_FUNC_ARG; + *handler = entropy_epoch_extra_inv_handler; + if (*handler == NULL) + return BAD_STATE_E; + *arg = entropy_epoch_extra_inv_arg; + if (! wolfSSL_Atomic_Func_Ptr_CompareExchange( + &entropy_epoch_extra_inv_handler, + handler, + NULL)) + return BUSY_E; + entropy_epoch_extra_inv_arg = &invalid_arg_sentinel; + return 0; + } + wc_RNG_entropy_epoch_t wc_RNG_global_invalidate_entropy(void) { wc_RNG_entropy_epoch_t new_epoch = wc_RNG_entropy_epoch_increment_atomic(&global_entropy_epoch); /* Note that extra invalidation propagation is only guaranteed after * wc_RNG_entropy_epoch_extra_inv_init() has returned with success -- * for guaranteed propagation, callers must defer normal service until - * after completion. */ + * after completion. The handler must not call back into wolfCrypt: + * any epoch read from inside it re-detects the same event and + * recurses. */ if ((entropy_epoch_extra_inv_handler != NULL) && (entropy_epoch_extra_inv_arg != &invalid_arg_sentinel)) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 588636af4bc..659bb3b8ab0 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -1067,7 +1067,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); !defined(CUSTOM_RAND_GENERATE_BLOCK) && !defined(HAVE_INTEL_RDRAND) && \ !defined(HAVE_SELFTEST) && (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void); -WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_extra_test(void); #endif #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ !defined(HAVE_INTEL_RDRAND) && !defined(HAVE_SELFTEST) && \ @@ -2972,10 +2971,6 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_FAIL("RNGEPOCH test failed!\n", ret); else TEST_PASS("RNGEPOCH test passed!\n"); - if ((ret = rng_epoch_extra_test()) != 0) - TEST_FAIL("RNGEPEXT test failed!\n", ret); - else - TEST_PASS("RNGEPEXT test passed!\n"); #endif #if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \ !defined(HAVE_INTEL_RDRAND) && !defined(HAVE_SELFTEST) && \ @@ -36799,6 +36794,8 @@ static int rng_epoch_nomem_seedCb(OS_Seed* os, byte* output, word32 sz) } #endif /* WC_RNG_SEED_CB && WC_RNG_HAVE_LOCK */ +static wc_test_ret_t rng_epoch_extra_test(void); + /* The process-global entropy epoch (wc_RNG_global_invalidate_entropy()): * detection, delivery into the instance's apertures, recovery by one credited * generate, inheritance by spawned chain members, and the capture rule for a @@ -37176,6 +37173,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) } #endif /* WC_RNG_SEED_CB && WC_RNG_HAVE_LOCK */ + api_ret = rng_epoch_extra_test(); + out_l: if (rng_inited) { @@ -37190,10 +37189,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) /* Fixtures for rng_epoch_extra_test(): a stand-in "external" epoch word -- * the observable surface an mmap'd kernel counter presents, minus the - * kernel -- plus a recording publication handler. Registration is - * process-lifetime one-shot by design, so these persist across reruns of - * the suite, and stay quiet for later tests: the word only moves when this - * test moves it. */ + * kernel -- plus a recording publication handler. The fixtures are + * registered afresh each run (any production handlers are displaced via + * the _TEST_VIS resets and restored at exit) and stay quiet for later + * tests: the word only moves when this test moves it. */ static wolfSSL_Atomic_Uint rng_epoch_extra_ext_word = WOLFSSL_ATOMIC_INITIALIZER(1U); static int rng_epoch_extra_get_zero_mode = 0; @@ -37203,13 +37202,17 @@ static int rng_epoch_extra_inv_marker = 0; static unsigned int rng_epoch_extra_inv_count = 0; static wc_RNG_entropy_epoch_t rng_epoch_extra_inv_last_epoch = 0; static void *rng_epoch_extra_inv_arg_seen = NULL; -static int rng_epoch_extra_registered = 0; static wc_RNG_entropy_epoch_t rng_epoch_extra_getCb(void *arg) { - rng_epoch_extra_get_arg_seen = arg; - if (rng_epoch_extra_get_zero_mode) + if (rng_epoch_extra_get_zero_mode) { + /* Probe-verification path, single-threaded by construction. The + * normal path stays read-only: the getter remains registered for + * the life of the process, and concurrent epoch reads (e.g. in + * random_thread_test() on a suite rerun) must race on nothing. */ + rng_epoch_extra_get_arg_seen = arg; return 0; + } return WOLFSSL_ATOMIC_LOAD(rng_epoch_extra_ext_word); } @@ -37225,7 +37228,7 @@ static void rng_epoch_extra_invCb(wc_RNG_entropy_epoch_t epoch, void *arg) * with the registrant's arg, detection of an external edge at the next * epoch-reading call (with relay publication), exactly-once accounting * per edge, and the 0-guard (a signalless getter is inert). */ -WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_extra_test(void) +static wc_test_ret_t rng_epoch_extra_test(void) { wc_test_ret_t ret = 0; int api_ret; @@ -37234,48 +37237,74 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_extra_test(void) byte block[32]; wc_RNG_entropy_epoch_t epoch_before, epoch_after; unsigned int inv_count_before; + wc_RNG_extra_entropy_epoch_get_handler_t saved_get_handler = NULL; + void *saved_get_arg = NULL; + int saved_get = 0; + wc_RNG_entropy_epoch_extra_inv_handler_t saved_inv_handler = NULL; + void *saved_inv_arg = NULL; + int saved_inv = 0; WOLFSSL_ENTER("rng_epoch_extra_test"); WC_ALLOC_VAR_EX(rng, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out_l)); - if (! rng_epoch_extra_registered) { - /* refusals leave the slot unclaimed */ - api_ret = wc_RNG_extra_entropy_epoch_init(NULL, NULL); - if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); - /* a getter with no signal yet is probed and refused -- and the - * probe demonstrably ran, with the caller's arg */ - rng_epoch_extra_get_zero_mode = 1; - rng_epoch_extra_get_arg_seen = NULL; - api_ret = wc_RNG_extra_entropy_epoch_init( - rng_epoch_extra_getCb, &rng_epoch_extra_get_marker); - rng_epoch_extra_get_zero_mode = 0; - if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); - if (rng_epoch_extra_get_arg_seen != - (void *)&rng_epoch_extra_get_marker) - { - ERROR_OUT(WC_TEST_RET_ENC_NC, out_l); - } - api_ret = wc_RNG_entropy_epoch_extra_inv_init(NULL, NULL); - if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + /* Retrieve-and-reset any installed handlers (e.g. the kernel + * module's own production glue, registered at module init) -- + * restored at out_l. Both slots are then virgin, so every run + * exercises the full registration path. */ + api_ret = wc_RNG_extra_entropy_epoch_reset(&saved_get_handler, + &saved_get_arg); + if (api_ret == 0) + saved_get = 1; + else if (api_ret != WC_NO_ERR_TRACE(BAD_STATE_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_entropy_epoch_extra_inv_reset(&saved_inv_handler, + &saved_inv_arg); + if (api_ret == 0) + saved_inv = 1; + else if (api_ret != WC_NO_ERR_TRACE(BAD_STATE_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); - /* the real registrations */ - api_ret = wc_RNG_extra_entropy_epoch_init( - rng_epoch_extra_getCb, &rng_epoch_extra_get_marker); - if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); - api_ret = wc_RNG_entropy_epoch_extra_inv_init( - rng_epoch_extra_invCb, &rng_epoch_extra_inv_marker); - if (api_ret != 0) + /* refusals leave the slot unclaimed */ + api_ret = wc_RNG_extra_entropy_epoch_init(NULL, NULL); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + /* a getter with no signal yet is probed and refused -- and the + * probe demonstrably ran, with the caller's arg */ + rng_epoch_extra_get_zero_mode = 1; + rng_epoch_extra_get_arg_seen = NULL; + api_ret = wc_RNG_extra_entropy_epoch_init( + rng_epoch_extra_getCb, &rng_epoch_extra_get_marker); + rng_epoch_extra_get_zero_mode = 0; + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + if (rng_epoch_extra_get_arg_seen != (void *)&rng_epoch_extra_get_marker) + ERROR_OUT(WC_TEST_RET_ENC_NC, out_l); + api_ret = wc_RNG_entropy_epoch_extra_inv_init(NULL, NULL); + if (api_ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + /* resetting a virgin slot is a state error, not a crash */ + { + wc_RNG_extra_entropy_epoch_get_handler_t dummy_get_handler = NULL; + void *dummy_arg = NULL; + api_ret = wc_RNG_extra_entropy_epoch_reset(&dummy_get_handler, + &dummy_arg); + if (api_ret != WC_NO_ERR_TRACE(BAD_STATE_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); - rng_epoch_extra_registered = 1; } - /* one-shot forever after */ + /* the real registrations */ + api_ret = wc_RNG_extra_entropy_epoch_init( + rng_epoch_extra_getCb, &rng_epoch_extra_get_marker); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + api_ret = wc_RNG_entropy_epoch_extra_inv_init( + rng_epoch_extra_invCb, &rng_epoch_extra_inv_marker); + if (api_ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + + /* one-shot while registered */ api_ret = wc_RNG_extra_entropy_epoch_init( rng_epoch_extra_getCb, &rng_epoch_extra_get_marker); if (api_ret != WC_NO_ERR_TRACE(ALREADY_E)) @@ -37381,6 +37410,47 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_extra_test(void) } WC_FREE_VAR(rng, HEAP_HINT); + /* Tear down whichever fixtures are installed (an error path may have + * left either slot vacant), restore any displaced handlers through + * the public inits (whose probes re-prime the shadow), and close + * with one conservative bump to cover events that landed while the + * pathway was ours. */ + { + wc_RNG_extra_entropy_epoch_get_handler_t dummy_get_handler = NULL; + wc_RNG_entropy_epoch_extra_inv_handler_t dummy_inv_handler = NULL; + void *dummy_arg = NULL; + int cleanup_ret = wc_RNG_extra_entropy_epoch_reset( + &dummy_get_handler, &dummy_arg); + if ((cleanup_ret != 0) && + (cleanup_ret != WC_NO_ERR_TRACE(BAD_STATE_E)) && + (ret == 0)) + { + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + cleanup_ret = wc_RNG_entropy_epoch_extra_inv_reset( + &dummy_inv_handler, &dummy_arg); + if ((cleanup_ret != 0) && + (cleanup_ret != WC_NO_ERR_TRACE(BAD_STATE_E)) && + (ret == 0)) + { + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (saved_get) { + cleanup_ret = wc_RNG_extra_entropy_epoch_init(saved_get_handler, + saved_get_arg); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (saved_inv) { + cleanup_ret = wc_RNG_entropy_epoch_extra_inv_init( + saved_inv_handler, saved_inv_arg); + if ((cleanup_ret != 0) && (ret == 0)) + ret = WC_TEST_RET_ENC_EC(cleanup_ret); + } + if (saved_get || saved_inv) + (void)wc_RNG_global_invalidate_entropy(); + } + return ret; } #endif /* WC_RNG_HAVE_ENTROPY_EPOCH && HAVE_HASHDRBG && ... */ diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index b2b469091fe..b63b5810bd6 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -308,7 +308,6 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_entropy_invalidate_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_nextseedstest(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_pool_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void); -extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_extra_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void); #endif /* WC_NO_RNG */ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void); diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 600607631f9..cb6cd3b07e6 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -1191,11 +1191,16 @@ WOLFSSL_API int wc_RNG_entropy_needs_recovery(WC_RNG* rng); wc_RNG_entropy_epoch_t epoch, void *arg); WOLFSSL_API int wc_RNG_entropy_epoch_extra_inv_init( wc_RNG_entropy_epoch_extra_inv_handler_t handler, void *arg); + WOLFSSL_TEST_VIS int wc_RNG_entropy_epoch_extra_inv_reset( + wc_RNG_entropy_epoch_extra_inv_handler_t *handler, void **arg); typedef wc_RNG_entropy_epoch_t (*wc_RNG_extra_entropy_epoch_get_handler_t)( void *arg); WOLFSSL_API int wc_RNG_extra_entropy_epoch_init( wc_RNG_extra_entropy_epoch_get_handler_t handler, void *arg); + WOLFSSL_TEST_VIS int wc_RNG_extra_entropy_epoch_reset( + wc_RNG_extra_entropy_epoch_get_handler_t *handler, + void **arg); #endif #ifdef WC_RNG_HAVE_FREE_HOOK From 813520c0652d4010208be9ae2a0f76ad275d684e Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 7 Nov 2026 00:07:28 -0600 Subject: [PATCH 7/7] wolfcrypt/test/test.c: fixes for AI review items: * in rng_epoch_test(), capture retval from rng_epoch_extra_test() directly to ret, and move call to end of rng_epoch_test() so that it isn't conditional on rng_test_generate_is_local(rng). * in rng_epoch_extra_test(), direct return on failed initial allocation to avoid spurious and destructive handler resets at cleanup. --- wolfcrypt/test/test.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 659bb3b8ab0..25a62035b33 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -37173,8 +37173,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) } #endif /* WC_RNG_SEED_CB && WC_RNG_HAVE_LOCK */ - api_ret = rng_epoch_extra_test(); - out_l: if (rng_inited) { @@ -37184,6 +37182,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) } WC_FREE_VAR(rng, HEAP_HINT); + if (ret == 0) + ret = rng_epoch_extra_test(); + return ret; } @@ -37247,7 +37248,7 @@ static wc_test_ret_t rng_epoch_extra_test(void) WOLFSSL_ENTER("rng_epoch_extra_test"); WC_ALLOC_VAR_EX(rng, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, - ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out_l)); + return WC_TEST_RET_ENC_EC(MEMORY_E)); /* Retrieve-and-reset any installed handlers (e.g. the kernel * module's own production glue, registered at module init) --