diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index 33f0d3cc493..2bcd2a64219 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -1898,7 +1898,18 @@ int wc_RNG_DRBG_GetNextSeedRBGCStratum(const WC_RNG* rng); instance -- the single-writer fill and the atomic aperture hand-off make it safe alongside a concurrent consumer. - \details For an SP 800-90C chain member (RBGC stratum > 0, in builds + \details The next-seed bank is an intermediate staging buffer in the + seed pipeline -- the ordinary decoupling of entropy gather from seed + consumption, as in any entropy source with an internal FIFO or + collector thread. Banked material is not DRBG state: it becomes + state only in the consuming thread's reseed transition + (wc_RNG_DRBG_NextSeedNow(), or an opportunistic generate-path + consumption), is zeroized on consumption, and is purged unpublished + if its source crossed an invalidation event mid-fill. The aperture + protocol (WC_RNG.nextSeedLen) guarantees single production and + single consumption. + + For an SP 800-90C chain member (RBGC stratum > 0, in builds with instance locking), the material is drawn by the same fallback ladder as wc_RNG_DRBG_Reseed_Now(): the retained parent first (nonblocking acquisition), then the global fallback instance (under @@ -2534,6 +2545,18 @@ int wc_RNG_GetFlags(const WC_RNG* rng, word32 *flags); atomic aperture word, so it is safe against a concurrent wc_FreeRng() or wc_RNG_Pool_Collect2() on the same instance. + \details The pool facility involves exactly one generator: the source + that fills it (the instance itself via wc_RNG_Pool_Collect(), or, via + wc_RNG_Pool_Collect2(), the instance or its direct RBGC parent). The + pool is buffered output of that generator, held inside the module + boundary, and the host WC_RNG performs no cryptographic operation on + pool data: it contributes buffer storage, lock protection, and + scheduling/affinity convenience, and the bytes travel through their + own retrieval path (wc_RNG_Pool_Extract()), which never touches the + host's DRBG state. In-boundary placement is a conservatism: pooled + output inherits the entropy-invalidation machinery (purged on events, + wiped on failure) that an out-of-boundary buffer would forgo. + \return 0 Success \return BAD_FUNC_ARG rng is null, or size is less than 2 or greater than 32767. @@ -2613,7 +2636,10 @@ int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n); eligibility rule is narrower still: rng_src must be rng_dest itself, or (under WC_RNG_HAVE_RBGC) rng_dest's direct RBGC parent -- the two relationships under which rng_src's output is already sanctioned to - reach rng_dest. Any other source is refused with + reach rng_dest. The rule is an attribution guarantee, not a seeding + criterion -- no reseed occurs here: extracted pool bytes are served + under rng_dest's identity and stratum, and eligibility ensures that + label is never misleading. Any other source is refused with WRONG_TYPE_OBJECT_E before any other processing, the n = 0 no-op included. diff --git a/doc/dox_comments/header_files/rng_bank.h b/doc/dox_comments/header_files/rng_bank.h index 4fa4fb0e980..40f8da1b927 100644 --- a/doc/dox_comments/header_files/rng_bank.h +++ b/doc/dox_comments/header_files/rng_bank.h @@ -944,6 +944,66 @@ int wc_rng_bank_root_rng_reinit(struct wc_rng_bank *bank, const byte *perso, word32 persoSz, word32 flags); +/*! + \ingroup RNGBank + + \brief Force an immediate reseed of the bank's root RNG, serialized + against other root touchers. With WC_RNG_HAVE_LOCK, the root's lease + is taken by unconditional try-lock and held across the reseed. + Without it, the serializing inst-op gate is claimed by try-once + (fail-fast, atomic-legal), excluding member reinstantiations chaining + from the root, entropy-invalidation walks, and root reinstantiation + for the reseed's span. + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return BUSY_E The root's lease or the inst-op gate is held; nothing + was touched, retry later. + \return negative wc_RNG_DRBG_Reseed_Now() failure. + + \param bank The bank whose root to reseed. + \param nonce Optional reseed nonce (may be null). + \param nonceSz Length of nonce in bytes. + + \sa wc_rng_bank_root_rng_reinit + \sa wc_RNG_DRBG_Reseed_Now +*/ +int wc_rng_bank_root_rng_reseed_now(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz); + +/*! + \ingroup RNGBank + + \brief Threshold form of wc_rng_bank_root_rng_reseed_now(): reseed the + bank's root RNG only when its reseed counter has reached threshold, + with the counter read and the reseed performed under a single hold of + the configuration's root serialization (the root's lease with + WC_RNG_HAVE_LOCK, else the inst-op gate). Without the lock facility + the saturated counter is the invalidation walk's only root-side + record, so a threshold of WC_RESEED_INTERVAL makes this the root's + deterministic post-invalidation recovery. + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return ALREADY_E The counter is below threshold: nothing due. + \return BUSY_E The root's lease or the inst-op gate is held; nothing + was touched, retry later. + \return negative wc_RNG_DRBG_GetReseedCtr() or + wc_RNG_DRBG_Reseed_Now() failure. + + \param bank The bank whose root to reseed. + \param nonce Optional reseed nonce (may be null). + \param nonceSz Length of nonce in bytes. + \param threshold Reseed-counter value at which a reseed is due; 0 + reseeds unconditionally. + + \sa wc_rng_bank_root_rng_reseed_now + \sa wc_RNG_DRBG_GetReseedCtr +*/ +int wc_rng_bank_root_rng_reseed_if_due(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz, + wc_drbg_reseed_ctr_t threshold); + /*! \ingroup Random diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 46aad8c45b1..ce60d5f4386 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2739,8 +2739,13 @@ static acpi_status wc_linuxkm_vmgenid_acpi_cb(acpi_handle handle, u32 depth, return AE_OK; } -static void wc_linuxkm_vmgenid_poll(struct wc_linuxkm_vmgenid_poll_state *st, - WC_RNG *local_root) +static void wc_linuxkm_vmgenid_poll(struct wc_linuxkm_vmgenid_poll_state *st + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG + , struct wc_rng_bank *bank + #else + , WC_RNG *local_root + #endif + ) { if (st->state == 0) { /* one-time discovery, in daemon task context. ACPICA uppercases @@ -2782,18 +2787,23 @@ static void wc_linuxkm_vmgenid_poll(struct wc_linuxkm_vmgenid_poll_state *st, * unconditionally as in the loop-head arm (the standing latch is * the point); if the lease can't be had this instant, the loop * head recovers the root on this same pass, without the fold. */ + + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG + (void)wc_rng_bank_root_rng_reseed_now(bank, + (const byte *)st->last, 16); + #else if (local_root != NULL) { -#ifdef WC_RNG_HAVE_LOCK - if (wc_RNG_lock_get_unconditional(local_root) == 0) { - (void)wc_RNG_DRBG_Reseed_Now(local_root, - (const byte *)st->last, 16); - wc_RNG_lock_put_unconditional(local_root); - } -#else + /* The memberless root is daemon-private -- its only reference + * is on this thread's stack, and the invalidation walk's root + * arm doesn't exist in this configuration -- so nothing + * concurrent can touch it, and unlocked access is trivially + * safe. The flip side: no invalidation event can ever reach + * it, so this fold and the periodic countdown reseed are its + * entire freshness regime. */ (void)wc_RNG_DRBG_Reseed_Now(local_root, (const byte *)st->last, 16); -#endif } + #endif } } @@ -2905,9 +2915,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) #ifdef WC_RNG_HAVE_GLOBAL_FALLBACK_RNG if (bank->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK) { + /* note wc_RNG_global_fallback_get() just gets the pointer to the static + * WC_RNG, not a hold. */ ret = wc_RNG_global_fallback_get(&global_fallback_rng); if (ret != 0) { - pr_err("wc_linuxkm_entropy_daemon: wc_RNG_global_fallback_get() failed with " + pr_err("wc_linuxkm_entropy_daemon: wc_RNG_global_fallback_get() failed with " "code %d -- continuing without.\n", ret); } } @@ -2943,7 +2955,13 @@ static int wc_linuxkm_entropy_daemon(void *arg) int congested_progress = 0; #ifdef WC_LINUXKM_VMGENID_POLL - wc_linuxkm_vmgenid_poll(&vmgenid_poll_state, root_rng); + wc_linuxkm_vmgenid_poll(&vmgenid_poll_state + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG + , bank + #else + , root_rng + #endif + ); #endif #ifdef WC_RNG_HAVE_LOCK @@ -2953,7 +2971,12 @@ static int wc_linuxkm_entropy_daemon(void *arg) * epoch stamp (the reseed counter is the SP 800-90A budget only, * and is never saturated here) -- and * wc_RNG_entropy_needs_recovery() reads both; its verdict is the - * authoritative signal and this is the authoritative response. */ + * authoritative signal and this is the authoritative response. + * + * Note, any nonzero retval from wc_RNG_entropy_needs_recovery() + * triggers recovery -- a dire code here means the root needs the + * reinit-based recovery below. + */ if ((root_rng != NULL) && ((ret = wc_RNG_entropy_needs_recovery(root_rng)) != 0)) { unsigned long uncredited_nonce = random_get_entropy(); int inv_ret = wc_RNG_lock_get_unconditional(root_rng); @@ -2969,11 +2992,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) goto next_pass; } -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG pr_info("wc_linuxkm_entropy_daemon: starting root recovery reseed (%d).\n", ret); -#endif + #endif -#if !defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) || defined(WC_SVR_USE_NATIVE_REG_BUFS) + #if !defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) || defined(WC_SVR_USE_NATIVE_REG_BUFS) /* all RNGs depend on the default root reseed -- try a pass with * preemption inhibited to maximize our chances. */ if (bank->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK) { @@ -2984,7 +3007,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) preempt_enable(); } if ((! (bank->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK)) || (inv_ret != 0)) -#endif + #endif { inv_ret = wc_RNG_DRBG_Reseed_Now( root_rng, @@ -2994,9 +3017,9 @@ static int wc_linuxkm_entropy_daemon(void *arg) wc_RNG_lock_put_unconditional(root_rng); if (inv_ret == 0) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG pr_info("wc_linuxkm_entropy_daemon: finished root recovery reseed.\n"); -#endif + #endif progress = 1; } else { @@ -3028,7 +3051,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) "failed (%d) -- attempting reinit\n", inv_ret); } -#ifdef WC_RNG_BANK_HAVE_ROOT_RNG + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG /* Gate-bracketed free+reinstantiate: the inst-op gate * excludes concurrent invalidation walkers, which * dereference the root's DRBG state, for the span of @@ -3037,14 +3060,14 @@ static int wc_linuxkm_entropy_daemon(void *arg) bank, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, NULL, 0, 0); -#else + #else /* Standalone (memberless) root: daemon-private, never * reachable by the invalidation walk -- no gating * needed or possible. */ inv_ret = wc_FreeRng(root_rng); if (inv_ret == 0) inv_ret = wc_InitRng(root_rng); -#endif + #endif if (inv_ret == 0) { progress = 1; if (bank->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK) { @@ -3082,7 +3105,149 @@ static int wc_linuxkm_entropy_daemon(void *arg) ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); } -#endif +#else /* !WC_RNG_HAVE_LOCK */ + /* Deterministic root recovery without the lock facility. + * + * Bank root: the invalidation walk's only root-side record in + * this configuration is the saturated reseed counter + * (wc_rng_bank_invalidate_entropy() -> + * wc_RNG_DRBG_ScheduleReseed()), so recovery is + * threshold-driven: the threshold form of the root reseed + * service reads the counter and reseeds under a single hold of + * the inst-op gate. Ordinary wear-out (a counter at + * WC_RESEED_INTERVAL with no event) is absorbed by the same + * remedy. + * + * Memberless root: no event can reach it (daemon-private; see + * wc_linuxkm_vmgenid_poll()), but when WC_RNG_HAVE_ENTROPY_EPOCH + * is available the global epoch is detection without + * notification, so check it and reseed on staleness. Without + * the epoch facility, the vmgenid fold and the periodic + * countdown reseed are the complete freshness regime, by + * design. */ + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG + if (root_rng != NULL) { + if (wc_RNG_GetStatus(root_rng) != WC_DRBG_OK) { + /* Out-of-service root: the reseed paths refuse a failed + * instance, so recovery is by reinstantiation -- + * gate-serialized inside wc_rng_bank_root_rng_reinit() + * (spin-entered; task context). The status peek is + * lockless and possibly stale: worst case it reinits a + * root another party just fixed (a fresh seed, no harm) + * or misses one cycle. Unlike the lock-facility arm, + * a hard failure does not exit the daemon: without the + * lock facility the members cannot self-reseed inline, + * so the daemon stays and retries. */ + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_rng_bank_root_rng_reinit( + bank, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce, + NULL, 0, 0); + ForceZero(&uncredited_nonce, + (word32)sizeof uncredited_nonce); + if (ret == 0) { + pr_notice("wc_entropyd: root_rng reinitialization " + "succeeded.\n"); + progress = 1; + } + else if (WC_LKM_RNG_ERR_IS_HEALTH(ret)) { + pr_warn_ratelimited( + "wc_entropyd: root_rng reinitialization failed " + "the seed health test: %d -- entropy source " + "suspect\n", ret); + } + else if (! WC_RNG_ERR_IS_TRANSIENT(ret)) { + pr_err_ratelimited( + "wc_entropyd: root_rng reinitialization failed: " + "%d\n", ret); + } + /* else transient codes (a busy seed source -- gate + * contention is spun out, not returned) are deliberately + * unlogged: the next pass retries. */ + } + else { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_rng_bank_root_rng_reseed_if_due( + bank, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce, + (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL); + ForceZero(&uncredited_nonce, + (word32)sizeof uncredited_nonce); + if (ret == 0) { + #ifdef WC_VERBOSE_RNG + pr_info("wc_linuxkm_entropy_daemon: root recovery " + "reseed (saturated counter).\n"); + #endif + progress = 1; + } + else if (WC_LKM_RNG_ERR_IS_HEALTH(ret)) { + pr_warn_ratelimited( + "wc_entropyd: root_rng recovery reseed failed the " + "seed health test: %d -- entropy source suspect\n", + ret); + } + else if ((ret != WC_NO_ERR_TRACE(ALREADY_E)) && + (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) && + (! WC_RNG_ERR_IS_TRANSIENT(ret))) + { + /* ALREADY_E: not due -- the steady state. + * WRONG_TYPE_OBJECT_E: no DRBG behind the root + * (RDRAND et al.) -- permanently nothing to do. + * Transient codes (the gate held) are one pass away. + * A root that failed between the status peek and the + * reseed logs once here; the status branch above + * reinstantiates it next pass. */ + pr_err_ratelimited( + "wc_entropyd: root_rng recovery reseed failed: " + "%d\n", ret); + } + /* else don't log -- see above comment. */ + } + } + #else /* !WC_RNG_BANK_HAVE_ROOT_RNG */ + if (root_rng != NULL) { + if (wc_RNG_GetStatus(root_rng) != WC_DRBG_OK) { + /* Daemon-private root: free+reinstantiate, no + * serialization needed or possible. */ + ret = wc_FreeRng(root_rng); + if (ret == 0) + ret = wc_InitRng(root_rng); + if (ret == 0) { + pr_info("wc_entropyd: root_rng reinitialization " + "succeeded.\n"); + progress = 1; + } + else if (! WC_RNG_ERR_IS_TRANSIENT(ret)) { + pr_err_ratelimited( + "wc_entropyd: root_rng reinitialization failed: " + "%d\n", ret); + } + /* else transient -- don't log. */ + } + #ifdef WC_RNG_HAVE_ENTROPY_EPOCH + else if (wc_RNG_entropy_needs_recovery(root_rng) != 0) { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now( + root_rng, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, + (word32)sizeof uncredited_nonce); + if (ret == 0) + progress = 1; + else if (! WC_RNG_ERR_IS_TRANSIENT(ret)) { + pr_err_ratelimited( + "wc_entropyd: root_rng recovery reseed failed: " + "%d\n", ret); + } + /* else transient -- don't log. */ + } + #endif /* WC_RNG_HAVE_ENTROPY_EPOCH */ + } + #endif /* !WC_RNG_BANK_HAVE_ROOT_RNG */ +#endif /* WC_RNG_HAVE_LOCK */ #ifdef WC_RNG_HAVE_GLOBAL_FALLBACK_RNG if ((global_fallback_rng != NULL) && @@ -3102,10 +3267,15 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_GLOBAL_FALLBACK_RNG */ -#if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) - /* recovery pass: push RBGC seeds to all WC_RNG_LOCK_ENTROPY_INVALIDATED - * instances that need them. */ - if (root_rng != NULL) { +#ifdef HAVE_HASHDRBG + /* recovery pass: if WC_RNG_HAVE_NEXT_SEED, push seeds to all + * WC_RNG_LOCK_ENTROPY_INVALIDATED instances that need them; if not, run + * inline reseeds of all instances that need them. Both are implicitly + * RBGC when the instance has a chain parent, else primary-sourced: + * invalidated instances need recovery whether or not the build has + * RBGC, since invalidation never saturates the reseed counter on + * lock-facility builds, so no other pass would collect them. */ + { #ifdef WC_VERBOSE_RNG int n_rbgc_recovered = 0; int n_self_recovered = 0; @@ -3115,80 +3285,123 @@ static int wc_linuxkm_entropy_daemon(void *arg) for (i = 0; i < bank->n_rngs; i++) { WC_RNG *rng = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); -#ifndef WC_RNG_HAVE_ENTROPY_EPOCH - WC_RNG_lock_arg_t rng_lock_state; - WC_ATOMIC_INT_ARG nextSeedLen; + #ifndef WC_RNG_HAVE_ENTROPY_EPOCH + /* Backward-compat invalidation check for + * !WC_RNG_HAVE_ENTROPY_EPOCH builds -- old FIPS, or any + * build with WC_RNG_NO_ENTROPY_EPOCH, which random.h + * allows independently of the lock and next-seed + * facilities. This one arm serves them all: the + * wc_rng_bank_inst lock accessors map to WC_RNG.lock when + * the lock facility is present and to the + * wc_rng_bank_inst.lock stand-in otherwise, and the + * reseed epilogue's latch clear is epoch-independent + * (wolfcrypt/src/random.c), so the latch sensing and the + * reseed protocol below are configuration-agnostic. */ + + WC_RNG_lock_arg_t rng_inst_lock_state; if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG ++n_unhealthy; -#endif - continue; - } - if (wc_RNG_lock_read(rng, &rng_lock_state) != 0) { -#ifdef WC_VERBOSE_RNG - ++n_unhealthy; -#endif - continue; - } - if (! (rng_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { -#ifdef WC_VERBOSE_RNG - ++n_self_recovered; -#endif + #endif continue; } - if (wc_RNG_DRBG_NextSeedCurrent(rng, &nextSeedLen) != 0) { -#ifdef WC_VERBOSE_RNG + + if (wc_rng_bank_inst_lock_read(&bank->rngs[i], &rng_inst_lock_state) != 0) { + #ifdef WC_VERBOSE_RNG ++n_unhealthy; -#endif + #endif continue; } - if ((nextSeedLen == WC_DRBG_NEXT_SEED_READY) || - (nextSeedLen == WC_DRBG_NEXT_SEED_CONSUMING)) - { -#ifdef WC_VERBOSE_RNG + + if (! (rng_inst_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { + #ifdef WC_VERBOSE_RNG ++n_self_recovered; -#endif + #endif continue; } -#else + + #else /* WC_RNG_HAVE_ENTROPY_EPOCH */ + ret = wc_RNG_entropy_needs_recovery(rng); + #ifndef WC_RNG_HAVE_LOCK + if (ret == 0) { + /* The bank-side stand-in latch is invisible to + * wc_RNG_entropy_needs_recovery(): a member + * invalidated while checked out is latched without + * counter saturation, and a bank-scoped invalidation + * (wc_rng_bank_invalidate_entropy()) moves no global + * epoch, so the member can read as healthy here while + * ordinary checkouts refuse it. Read the latch + * directly; RNG-level verdicts above are preserved. */ + WC_RNG_lock_arg_t inst_lock_state; + if ((wc_rng_bank_inst_lock_read( + &bank->rngs[i], &inst_lock_state) == 0) && + (inst_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + ret = NEEDS_RECOVERY_E; + } + } + #endif /* !WC_RNG_HAVE_LOCK */ if (ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG if (ret == 0) ++n_self_recovered; else ++n_unhealthy; -#endif + #endif continue; } -#endif + #endif /* WC_RNG_HAVE_ENTROPY_EPOCH */ + + #ifdef WC_RNG_HAVE_NEXT_SEED + /* wc_RNG_DRBG_NextSeedGenerate() uses RBGC reseed implicitly + * for reseeds of RBGC RNGs. */ + ret = wc_RNG_DRBG_NextSeedGenerate(rng, WC_DRBG_NEXT_SEED_LEN); + #else /* !WC_RNG_HAVE_NEXT_SEED */ + /* Without _NEXT_SEED support, just perform an inline reseed + * (again, implicitly RBGC). */ + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_rng_bank_inst_lock_get_unconditional(&bank->rngs[i]); + if (ret == 0) { + ret = wc_rng_bank_inst_reseed_now( + &bank->rngs[i], + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + wc_rng_bank_inst_lock_put_unconditional(&bank->rngs[i]); + } + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + } + #endif /* !WC_RNG_HAVE_NEXT_SEED */ - ret = wc_rng_bank_next_seed_generate_rbgc(bank, i, WC_DRBG_NEXT_SEED_LEN); if (ret == 0) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG ++n_rbgc_recovered; -#endif + #endif congested_progress = 1; progress = 1; } -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG else if (ret == WC_NO_ERR_TRACE(ALREADY_E)) ++n_self_recovered; else if (ret == WC_NO_ERR_TRACE(BUSY_E)) ++n_busy; /* gate held by a reinit, or a claim in flight */ else ++n_unhealthy; -#endif + #endif } -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG if (n_rbgc_recovered > 0) { - pr_info("wc_linuxkm_entropy_daemon: RBGC recovery of %d/%d insts, %d self-recovered, %d skipped (busy), %d failed.\n", - n_rbgc_recovered, bank->n_rngs, n_self_recovered, n_busy, n_unhealthy); + pr_info("wc_linuxkm_entropy_daemon: recovery of %d/%d " + "insts, %d self-recovered, %d skipped (busy), " + "%d failed.\n", + n_rbgc_recovered, bank->n_rngs, n_self_recovered, + n_busy, n_unhealthy); } -#endif + #endif } -#endif /* WC_RNG_HAVE_NEXT_SEED && WC_RNG_HAVE_RBGC */ +#endif /* HAVE_HASHDRBG */ /* recovery pass: fix out-of-service instances. The status * peek is lockless and possibly stale -- worst case it sends a @@ -3203,8 +3416,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) } ret = wc_rng_bank_recover_inst( bank, i, 0 /* timeout_secs */, - WC_LKM_BANK_RBGC_FLAG | - WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED); + WC_LKM_BANK_RBGC_FLAG + #ifdef WC_RNG_HAVE_NEXT_SEED + | WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED + #endif + ); if (ret == 0) { (void)wc_rng_bank_inst_flags_down( &bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED); @@ -3258,9 +3474,10 @@ static int wc_linuxkm_entropy_daemon(void *arg) { ret = wc_rng_bank_next_seed_generate_rbgc( bank, i, WC_DRBG_NEXT_SEED_LEN); - congested_progress = 1; - if (ret == 0) + if (ret == 0) { + congested_progress = 1; progress = 1; + } } } } @@ -3276,15 +3493,25 @@ static int wc_linuxkm_entropy_daemon(void *arg) #endif ) { -#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - ret = wc_RNG_DRBG_Reseed_Now(root_rng, NULL, 0); -#else + /* The nonce lands on every boundary: natively in the credited + * reseed on current code and the 5.2.4 backport, and as an + * uncredited stir transition following the reseed in the + * legacy-boundary fallback. */ unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_RNG_DRBG_Reseed_Now(root_rng, - (byte *)&uncredited_nonce, - sizeof uncredited_nonce); - ForceZero(&uncredited_nonce, sizeof uncredited_nonce); +#if !defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_BANK_HAVE_ROOT_RNG) + /* bank-root DRBG access serializes on the inst-op gate -- see + * wc_rng_bank_root_rng_reseed_now(). A BUSY_E refusal is + * transient: the countdown stays expired and the next pass + * retries. */ + ret = wc_rng_bank_root_rng_reseed_now( + bank, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); +#else + ret = wc_RNG_DRBG_Reseed_Now( + root_rng, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); #endif + ForceZero(&uncredited_nonce, sizeof uncredited_nonce); if (ret == 0) { root_rng_reseed_countdown = WC_LINUXKM_BONUS_RESEED_INTERVAL; @@ -3318,7 +3545,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) if (global_fallback_rng != NULL) { wc_drbg_reseed_ctr_t this_reseedCtr; ret = wc_RNG_DRBG_GetReseedCtr(global_fallback_rng, &this_reseedCtr); - if ((ret == 0) && (this_reseedCtr > WC_LINUXKM_BONUS_RESEED_INTERVAL)) { + if ((ret == 0) && (this_reseedCtr >= WC_LINUXKM_BONUS_RESEED_INTERVAL)) { unsigned long uncredited_nonce = random_get_entropy(); #ifdef WC_RNG_HAVE_NEXT_SEED /* use the NextSeed aperture to minimize held time. */ @@ -3442,60 +3669,141 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_POOL */ -#if defined(WC_LINUXKM_HAVE_RNG_REGISTRY) && \ - defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) - /* registered-leaf pass: bank RBGC seeds from root_rng into - * long-lived leaves that are invalidated or chain-backed, so their - * next generate recovers/promotes in place - * (WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED). - * Sleepable-mutex context; entropy gathers are legal under it by - * the atomic-born exclusion rule. */ - if (root_rng != NULL) { +#if defined(WC_LINUXKM_HAVE_RNG_REGISTRY) && \ + (defined(WC_RNG_HAVE_LOCK) || defined(WC_RNG_HAVE_NEXT_SEED)) + /* The sweep compiles wherever it has a safe action: the next-seed + * aperture, or a lock-bracketed inline reseed (the !_NEXT_SEED + * arm below is lock-facility API, so the gate guarantees + * WC_RNG_HAVE_LOCK whenever that arm compiles). The entropy + * epoch is not a precondition: wc_RNG_entropy_needs_recovery() + * reads the invalidation latch independently of epochs. Without + * either facility, standalone leaves have no lock word to sense + * or exclude by, and recover owner-side instead. */ + /* reseed pass for long-lived leaves that need it: if _HAVE_NEXT_SEED, + * then reseed is by RBGC from parent, after which their next generate + * recovers/promotes in place (WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED); + * if !_HAVE_NEXT_SEED, reseed inline. Sleepable-mutex context; entropy + * gathers are legal under it by the atomic-born exclusion rule. */ + { WC_ATOMIC_INT_ARG needs_recovery_snapshot = WOLFSSL_ATOMIC_LOAD(wc_linuxkm_rng_registry_needs_recovery); if (needs_recovery_snapshot != 0) { struct linuxkm_rng_object *obj; + int sweep_incomplete = 0; mutex_lock(&wc_linuxkm_rng_registry_mutex); for (obj = wc_linuxkm_rng_registry_head; obj != NULL; obj = obj->next) { - WC_RNG_lock_arg_t leaf_lock_state; if (obj->is_bank) continue; - if (wc_RNG_lock_read(obj->rng, &leaf_lock_state) != 0) - continue; - if (((leaf_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) || - (wc_RNG_DRBG_GetRBGCStratum(obj->rng) > 0)) -#ifdef WC_RNG_HAVE_LOCK - && (wc_RNG_lock_get(root_rng, 0) == 0) -#endif - ) + + #ifdef WC_RNG_HAVE_NEXT_SEED + /* Note, _BONUS_RESEED_INTERVAL reseeds are only possible in + * _HAVE_NEXT_SEED configurations, otherwise the entropy + * daemon's wc_RNG_lock_get_unconditional(obj->rng) will + * contend with application callers, glitching the + * application. + */ + { - if (wc_RNG_DRBG_NextSeedGenerate_RBGC(obj->rng, - root_rng, WC_DRBG_NEXT_SEED_LEN) == 0) - progress = 1; -#ifdef WC_RNG_HAVE_LOCK - ret = wc_RNG_lock_put(root_rng, 0); - if (ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { - /* the invalidation walk landed under our hold: not a - * failure, the recovery pass above owns it next turn. */ + word32 this_flags; + if (wc_RNG_GetFlags(obj->rng, &this_flags) != 0) + continue; + if (this_flags & WC_RNG_FLAG_UPGRADE_FROM_NEXT_SEED) { + wc_drbg_reseed_ctr_t this_reseedCtr; + ret = wc_RNG_DRBG_GetReseedCtr(obj->rng, &this_reseedCtr); + if ((ret != 0) || + ((this_reseedCtr < WC_LINUXKM_BONUS_RESEED_INTERVAL) && + (wc_RNG_entropy_needs_recovery(obj->rng) != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)))) + { + continue; + } } - else if (ret != 0) { - pr_crit_ratelimited( - "wc_entropyd: root_rng unlock failed: %d\n", ret); + else { + if (wc_RNG_entropy_needs_recovery(obj->rng) != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { + continue; + } } -#endif + } + /* If obj->rng has a parent, wc_RNG_DRBG_NextSeedGenerate() + * is implicitly RBGC. */ + ret = wc_RNG_DRBG_NextSeedGenerate(obj->rng, WC_DRBG_NEXT_SEED_LEN); + #else /* !WC_RNG_HAVE_NEXT_SEED */ + if (wc_RNG_entropy_needs_recovery(obj->rng) != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) + continue; + /* If the leaf isn't enforcing locks, then we have no thread + * synchronization mechanism to lean on and we can't reseed + * from here. + * + * This limitation doesn't exist for bank leaves, because + * they are always _LOCK_REQUIRED or the equivalent thereof, + * either directly via WC_RNG.lock or via the + * wc_rng_bank_inst.lock stand-in, and are accessed only + * through the bank checkout API. + * + * Moreover, this limitation doesn't exist in the + * WC_RNG_HAVE_NEXT_SEED configuration because + * wc_RNG_DRBG_NextSeedGenerate() has its own atomics-based + * thread synchronization mechanism pivoting on + * WC_RNG.nextSeedLen. + * + * Notice that _needs_recovery() above has already + * propagated an invalidation event if any; only the inline + * reseed operation is inhibited. + */ + { + WC_RNG_lock_arg_t this_lock; + if (wc_RNG_lock_read(obj->rng, &this_lock) != 0) + continue; + if (! (this_lock & WC_RNG_LOCK_REQUIRED)) + continue; + } + + /* Without _NEXT_SEED support, just perform an inline reseed + * (again, implicitly RBGC). */ + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_lock_get_unconditional(obj->rng); + if (ret == 0) { + ret = wc_RNG_DRBG_Reseed_Now( + obj->rng, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + wc_RNG_lock_put_unconditional(obj->rng); + } + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + } + #endif /* !WC_RNG_HAVE_NEXT_SEED */ + if (ret == 0) { + progress = 1; + congested_progress = 1; + } + else if (WC_RNG_ERR_IS_TRANSIENT(ret)) { + /* Refused this pass -- contention, source not + * ready, or a standing latch. A lock-required + * leaf's own consumers are refused by the same + * latch, so this sweep is its only recovery + * agent: keep the needs-recovery counter hot + * below rather than consuming the only signal + * that brings the sweep back. */ + sweep_incomplete = 1; } } mutex_unlock(&wc_linuxkm_rng_registry_mutex); - /* on failure, an invalidation landed since the snapshot: - * leave the counter hot and re-sweep next pass. */ - (void)wolfSSL_Atomic_Int_CompareExchange( - &wc_linuxkm_rng_registry_needs_recovery, - &needs_recovery_snapshot, 0); + /* Clear the counter only after a complete sweep. A CAS + * failure means an invalidation landed since the + * snapshot; sweep_incomplete means a leaf was refused on + * a transient. Either way the counter stays hot and the + * next pass re-sweeps. */ + if (! sweep_incomplete) { + (void)wolfSSL_Atomic_Int_CompareExchange( + &wc_linuxkm_rng_registry_needs_recovery, + &needs_recovery_snapshot, 0); + } } } -#endif /* WC_LINUXKM_HAVE_RNG_REGISTRY && WC_RNG_HAVE_NEXT_SEED && WC_RNG_HAVE_RBGC */ +#endif /* WC_LINUXKM_HAVE_RNG_REGISTRY && + * (WC_RNG_HAVE_LOCK || WC_RNG_HAVE_NEXT_SEED) */ /* if we're coping with congestion hits, continue here, don't bog down * in primary seed ops. */ @@ -3503,9 +3811,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) goto next_pass; #ifdef WC_RNG_HAVE_NEXT_SEED - /* seed banking pass: one gather granule per instance per turn. */ - for (i = 0; i < bank->n_rngs; i++) { + /* seed banking pass: one gather granule per instance per turn. + * Generators will reseed opportunistically, using the banked seed, at + * the WC_LINUXKM_BONUS_RESEED_INTERVAL. */ + for (i = 0; i < bank->n_rngs; i++) { ret = wc_rng_bank_next_seed_generate( bank, i, WC_LINUXKM_ENTROPY_DAEMON_GRANULE); if (ret == 0) { @@ -3533,7 +3843,57 @@ static int wc_linuxkm_entropy_daemon(void *arg) "returned %d\n", i, ret); } } -#endif /* WC_RNG_HAVE_NEXT_SEED */ +#elif defined(WC_RNG_HAVE_LOCK) + /* !_HAVE_NEXT_SEED: bank leaf bonus reseed pass on current + (WC_RNG.lock) code. */ + + for (i = 0; i < bank->n_rngs; i++) { + WC_RNG *leaf = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); + wc_drbg_reseed_ctr_t cur_counter = 0; + ret = wc_RNG_DRBG_GetReseedCtr(leaf, &cur_counter); + if ((ret == 0) && + (cur_counter >= WC_LINUXKM_BONUS_RESEED_INTERVAL) && + (wc_RNG_lock_get_unconditional(leaf) == 0)) + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now(leaf, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret == 0) + progress = 1; + wc_RNG_lock_put_unconditional(leaf); + } + } +#else /* !WC_RNG_HAVE_NEXT_SEED && !WC_RNG_HAVE_LOCK */ + /* !_HAVE_NEXT_SEED && !_HAVE_LOCK: bank leaf bonus reseed pass on + legacy FIPS. */ + + for (i = 0; i < bank->n_rngs; i++) { + wc_drbg_reseed_ctr_t cur_counter = 0; + + /* Note, we have to get the lock first to exclude competing reinits, + * because on legacy code, wc_RNG_DRBG_GetReseedCtr() requires + * dereferencing the internal DRBG state. */ + + if (wc_rng_bank_inst_lock_get_unconditional(&bank->rngs[i]) != 0) + continue; + ret = wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), + &cur_counter); + if ((ret == 0) && + (cur_counter >= WC_LINUXKM_BONUS_RESEED_INTERVAL)) + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_rng_bank_inst_reseed_now(&bank->rngs[i], + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret == 0) + progress = 1; + } + wc_rng_bank_inst_lock_put_unconditional(&bank->rngs[i]); + } +#endif /* !WC_RNG_HAVE_NEXT_SEED && !WC_RNG_HAVE_LOCK */ next_pass: @@ -3546,11 +3906,9 @@ static int wc_linuxkm_entropy_daemon(void *arg) } } - if (root_rng != NULL) { #ifdef WC_LINUXKM_VMGENID_POLL - wc_linuxkm_vmgenid_poll_teardown(&vmgenid_poll_state); + wc_linuxkm_vmgenid_poll_teardown(&vmgenid_poll_state); #endif - } #ifndef WC_RNG_BANK_HAVE_ROOT_RNG if (root_rng != NULL) { diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 80ea87ec8b7..cb57a44eb94 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -6602,12 +6602,19 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( * because the tree may have been broken by a wc_FreeRng(), so we use the * same criterion as wc_RNG_DRBG_ReseedRBGC_local() to qualify the supplied * root. */ - if ((rng->RBGC_parent != NULL) && - (rng->RBGC_parent != root) && - ((rng->RBGC_parent->RBGCStratum > 0) || - (root->RBGCStratum > 0))) { - return WRONG_TYPE_OBJECT_E; + /* One load: reachable from the unowned-caller entry + * (wc_RNG_DRBG_NextSeedGenerate()), where a concurrent + * teardown can CAS-null rng->RBGC_parent between the null + * check and the dereference. */ + WC_RNG * const parent = rng->RBGC_parent; + if ((parent != NULL) && + (parent != root) && + ((parent->RBGCStratum > 0) || + (root->RBGCStratum > 0))) + { + return WRONG_TYPE_OBJECT_E; + } } #endif /* HAVE_FIPS */ @@ -6897,11 +6904,26 @@ int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) { if (rng->RBGCStratum > 0) { int ret = WC_NO_ERR_TRACE(NEEDS_RECOVERY_E); int put_ret; - if ((wc_RNG_Status(rng->RBGC_parent) == 0) && - (wc_RNG_lock_get(rng->RBGC_parent, 0) == 0)) + /* Captured once: this entry admits unowned callers by design (a + * scheduling daemon synchronizes through the next-seed aperture, + * not the instance lease), so a concurrent teardown of rng can + * CAS-null rng->RBGC_parent mid-call, and the put below must + * release exactly what the get acquired. Contrast the owned-rng + * reseed paths (PollAndReSeed() et al.): ownership of rng makes + * RBGC_parent stable there, and no capture is used. + * + * Storage lifetime is the usual shell contract: wc_FreeRng() may + * retire parent or child at any time (the shells' words stay live + * and refuse coherently), but free() of either shell requires + * quiescing unowned callers first. Every in-tree parent is a + * bank root or the static global fallback, satisfying this by + * construction. */ + WC_RNG * const parent = rng->RBGC_parent; + if ((wc_RNG_Status(parent) == 0) && + (wc_RNG_lock_get(parent, 0) == 0)) { - ret = wc_RNG_DRBG_NextSeedGenerate_local(rng, rng->RBGC_parent, n); - put_ret = wc_RNG_lock_put(rng->RBGC_parent, 0); + ret = wc_RNG_DRBG_NextSeedGenerate_local(rng, parent, n); + put_ret = wc_RNG_lock_put(parent, 0); if (put_ret != 0) { if (put_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { int purge_ret = NextSeedPurge(rng); @@ -6923,10 +6945,10 @@ int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) { /* SP-800-90C requires that an alternative randomness source be "a * sibling of the parent, an ancestor, or the initial randomness * source". */ - (rng->RBGC_parent != NULL) && + (parent != NULL) && ((global_fallback_rng.RBGCStratum == 0) && (global_fallback_rng.RBGCStratum == - rng->RBGC_parent->RBGCStratum)) && + parent->RBGCStratum)) && #endif (wc_RNG_lock_get(&global_fallback_rng, 0) == 0)) { diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 5ef2b6867f6..4ce65e8237a 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -156,7 +156,7 @@ WOLFSSL_API int wc_rng_bank_init_nonce( wolfSSL_Atomic_Int_Init(&ctx->inst_op_gate, WC_RNG_BANK_INST_OP_FREE); #endif ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; -#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED +#ifdef WC_RNG_HAVE_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED) rng_flags |= WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED; #endif @@ -1632,6 +1632,83 @@ WOLFSSL_API int wc_rng_bank_root_rng_reinit(struct wc_rng_bank *bank, ); } +#ifdef HAVE_HASHDRBG +/* Forced reseed of the bank root, serialized however the configuration + * serializes root access. With WC_RNG_HAVE_LOCK, the root's lease is + * taken by unconditional try-lock and held across the reseed. Without + * it, every other root toucher serializes on the inst-op gate -- member + * reinstantiation chaining from the root (wc_rng_bank_inst_reinit()), + * the invalidation walk's counter saturation + * (wc_rng_bank_invalidate_entropy()), and root reinstantiation + * (wc_rng_bank_root_rng_reinit()) -- so the reseed holds the gate, + * claimed per the holder contract try-once, fail-fast (atomic-legal). + * Either way a refusal (BUSY_E) means nothing was touched, and the + * caller's next pass retries. + * + * The threshold form reseeds only when the root's reseed counter has + * reached threshold, with the counter read under the same hold as the + * reseed -- no TOCTOU, and on legacy boundaries (where the read + * dereferences internal DRBG state) no unexcluded deref. A below- + * threshold counter returns ALREADY_E: steady state, not an error. + * threshold == 0 reseeds unconditionally. Without the lock facility + * the saturated counter is the invalidation walk's only root-side + * record, so threshold == WC_RESEED_INTERVAL makes this the root's + * deterministic post-invalidation recovery. */ +WOLFSSL_API int wc_rng_bank_root_rng_reseed_if_due( + struct wc_rng_bank *bank, const byte *nonce, word32 nonceSz, + wc_drbg_reseed_ctr_t threshold) +{ + int ret; + + if (bank == NULL) + return BAD_FUNC_ARG; + +#ifdef WC_RNG_HAVE_LOCK + ret = wc_RNG_lock_get_unconditional(&bank->root_rng); + if (ret != 0) + return ret; +#else /* WC_RNG_BANK_HAVE_INST_OP_GATE */ + { + WC_ATOMIC_INT_ARG expected = WC_RNG_BANK_INST_OP_FREE; + if (! wolfSSL_Atomic_Int_CompareExchange(&bank->inst_op_gate, + &expected, + WC_RNG_BANK_INST_OP_DAEMON)) + { + return BUSY_E; + } + } +#endif + + if (threshold > 0) { + wc_drbg_reseed_ctr_t cur_ctr = 0; + ret = wc_RNG_DRBG_GetReseedCtr(&bank->root_rng, &cur_ctr); + if ((ret == 0) && (cur_ctr < threshold)) + ret = WC_NO_ERR_TRACE(ALREADY_E); /* avoid polling noise. */ + } + else { + ret = 0; + } + + if (ret == 0) + ret = wc_RNG_DRBG_Reseed_Now(&bank->root_rng, nonce, nonceSz); + +#ifdef WC_RNG_HAVE_LOCK + wc_RNG_lock_put_unconditional(&bank->root_rng); +#else /* WC_RNG_BANK_HAVE_INST_OP_GATE */ + WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, WC_RNG_BANK_INST_OP_FREE); +#endif + + return ret; +} + +WOLFSSL_API int wc_rng_bank_root_rng_reseed_now(struct wc_rng_bank *bank, + const byte *nonce, + word32 nonceSz) +{ + return wc_rng_bank_root_rng_reseed_if_due(bank, nonce, nonceSz, 0); +} +#endif /* HAVE_HASHDRBG */ + #endif /* WC_RNG_BANK_HAVE_ROOT_RNG */ #ifdef WC_HAVE_RNG_BANKREF @@ -1988,7 +2065,7 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( if (ret < 0) return ret; -#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED +#ifdef WC_RNG_HAVE_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED) rng_flags |= WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED; #endif @@ -2347,7 +2424,7 @@ static int rng_bank_spawn( { word32 child_init_flags = WC_RNG_INIT_FLAG_NONE; -#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED +#ifdef WC_RNG_HAVE_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED) child_init_flags |= WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED; @@ -3194,6 +3271,26 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get_conditional( return UNEXPECTED_STATE_E; } +WOLFSSL_LOCAL int wc_rng_bank_inst_lock_get_unconditional(struct wc_rng_bank_inst *inst) +{ + WC_RNG_lock_arg_t cur_lock; + + if (inst == NULL) + return BAD_FUNC_ARG; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && + (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_HELD))) + { + return 0; + } + + return BUSY_E; +} + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) { WC_RNG_lock_arg_t cur_lock, new_lock; @@ -3275,6 +3372,13 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_in return UNEXPECTED_STATE_E; } +WOLFSSL_LOCAL void wc_rng_bank_inst_lock_put_unconditional(struct wc_rng_bank_inst *inst) +{ + if (inst == NULL) + return; + (void)wolfSSL_Atomic_Uint_FetchSub(&inst->lock, WC_RNG_LOCK_HELD); +} + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state) { if ((inst == NULL) || (state == NULL)) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 270f8b4e0b4..ab633c5a41f 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -36590,54 +36590,72 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) * parent's own recovery: a lease taken before the event is not * revoked by it, only reported at release. */ api_ret = wc_RNG_lock_get(&parent, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_InitRngNonceRBGC(&child, &parent, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) child_inited = 1; - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(&child); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { (void)wc_RNG_global_invalidate_entropy(); api_ret = wc_RNG_entropy_needs_recovery(&child); if (api_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if (api_ret == 0) { /* the parent is in the same boat, and recovers under the * lease; the child then recovers from the recovered parent. */ api_ret = wc_RNG_GenerateBlock(&parent, block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } { + /* recovered: no latch to report */ int put_ret = wc_RNG_lock_put(&parent, 0); if ((api_ret == 0) && (put_ret != 0)) - api_ret = put_ret; /* recovered: no latch to report */ + api_ret = WC_TEST_RET_ENC_EC(put_ret); } } - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(&child, block, (word32)sizeof(block)); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(&child); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { /* still a chain member: recovery did not relabel it */ if (wc_RNG_DRBG_GetRBGCStratum(&child) != 1) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } if (child_inited) { int free_ret = wc_FreeRng(&child); if ((api_ret == 0) && (free_ret != 0)) - api_ret = free_ret; + api_ret = WC_TEST_RET_ENC_EC(free_ret); } if (parent_inited) { int free_ret = wc_FreeRng(&parent); if ((api_ret == 0) && (free_ret != 0)) - api_ret = free_ret; + api_ret = WC_TEST_RET_ENC_EC(free_ret); } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* WC_RNG_HAVE_RBGC && WC_RNG_HAVE_LOCK */ @@ -36692,20 +36710,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); api_ret = wc_RNG_DRBG_ScheduleReseed(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_DRBG_Reseed_Now(rng, NULL, 0); - if (api_ret == WC_NO_ERR_TRACE(BUSY_E)) + if (api_ret == WC_NO_ERR_TRACE(BUSY_E)) { api_ret = wc_RNG_Status(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } { int cb_ret = wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT); if ((api_ret == 0) && (cb_ret != 0)) - api_ret = cb_ret; + api_ret = WC_TEST_RET_ENC_EC(cb_ret); } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ 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); @@ -36717,20 +36742,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); api_ret = wc_RNG_DRBG_ScheduleReseed(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_DRBG_Reseed_Now(rng, NULL, 0); - if (api_ret == WC_NO_ERR_TRACE(MEMORY_E)) + if (api_ret == WC_NO_ERR_TRACE(MEMORY_E)) { api_ret = wc_RNG_Status(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } { int cb_ret = wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT); if ((api_ret == 0) && (cb_ret != 0)) - api_ret = cb_ret; + api_ret = WC_TEST_RET_ENC_EC(cb_ret); } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ 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); @@ -36795,19 +36827,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_DRBG_SEED_SZ, NULL, 0, NULL, 0, HEAP_HINT, devId, WC_RNG_INIT_FLAG_NO_PRIMARY_SEED); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { u_inited = 1; if (wc_RNG_DRBG_GetRBGCStratum(&u) != WC_RNG_RBGC_USER_SEED_STRATUM) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(&u, block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } #ifdef WC_RNG_HAVE_LOCK - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_invalidate_entropy(&u); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { WC_RNG *fb = NULL; #ifndef HAVE_FIPS @@ -36821,12 +36861,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (fb != NULL) { api_ret = wc_RNG_GenerateBlock(&u, block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if ((api_ret == 0) && (wc_RNG_DRBG_GetRBGCStratum(&u) != RBGC_RESEED_STRATUM(wc_RNG_DRBG_GetRBGCStratum(fb) + 1, WC_RNG_RBGC_USER_SEED_STRATUM))) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } else { @@ -36835,29 +36877,43 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) api_ret = wc_RNG_GenerateBlock(&u, block, (word32)sizeof(block)); if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); else if ((api_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) || (api_ret == WC_NO_ERR_TRACE(NOT_READY_E))) + { api_ret = wc_RNG_Status(&u); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } } #endif if (api_ret == 0) { XMEMSET(user_seed, 0xa5, WC_DRBG_SEED_SZ); api_ret = wc_RNG_DRBG_Reseed(&u, user_seed, WC_DRBG_SEED_SZ); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } WC_FREE_VAR(user_seed, HEAP_HINT); - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(&u, block, (word32)sizeof(block)); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(&u); /* discharged */ + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (u_inited) { int free_ret = wc_FreeRng(&u); if ((api_ret == 0) && (free_ret != 0)) - api_ret = free_ret; + api_ret = WC_TEST_RET_ENC_EC(free_ret); } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* WC_RNG_HAVE_RBGC */ @@ -36872,20 +36928,35 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) api_ret = wc_InitRngNonce_ex2(&m, NULL, 0, NULL, 0, HEAP_HINT, devId, WC_RNG_INIT_FLAG_LOCK_REQUIRED | WC_RNG_INIT_FLAG_USE_FULL_MUTEX); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + if (api_ret == 0) { api_ret = wc_FreeRng(&m); /* never held: free takes and releases */ - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_InitRngNonce_ex2(&m, NULL, 0, NULL, 0, HEAP_HINT, devId, WC_RNG_INIT_FLAG_LOCK_REQUIRED | WC_RNG_INIT_FLAG_USE_FULL_MUTEX); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { api_ret = wc_RNG_lock_get(&m, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(&m, block, (word32)sizeof(block)); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + if (api_ret == 0) { api_ret = wc_FreeRng_PreLocked(&m); /* holder's teardown */ + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } else (void)wc_RNG_lock_put(&m, 0); } @@ -36899,11 +36970,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) devId, WC_RNG_INIT_FLAG_LOCK_REQUIRED | WC_RNG_INIT_FLAG_USE_FULL_MUTEX); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + if (api_ret == 0) { api_ret = wc_FreeRng(&m); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #if defined(HAVE_WC_RNG_BANK) && defined(WC_RNG_BANK_HAVE_ROOT_RNG) @@ -36922,38 +36998,51 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* vacate the root so the constructor can be tried on it */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_FreeRng(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_init( bank, NULL, 0, NULL, 0, WC_RNG_INIT_FLAG_USE_FULL_MUTEX); if (api_ret == WC_NO_ERR_TRACE(BAD_FUNC_ARG)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if ((api_ret == 0) && (wc_rng_bank_root_rng_get(bank) != NULL)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_init(bank, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if ((api_ret == 0) && (wc_rng_bank_root_rng_get(bank) != root)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && WC_RNG_BANK_HAVE_ROOT_RNG */ #endif /* WC_RNG_HAVE_LOCK_FULL_MUTEX */ @@ -36981,32 +37070,42 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* take member 0 out of service, as a failed reinit would leave it */ if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { member = WC_RNG_BANK_INST_TO_RNG(inst); if (wc_RNG_DRBG_GetRBGCStratum(member) != 1) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - else + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else { api_ret = wc_FreeRng_PreLocked(member); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } /* latch the root, as the invalidation walk would */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_invalidate_entropy(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { api_ret = wc_rng_bank_recover_inst(bank, 0, 0, WC_RNG_BANK_FLAG_NONE); @@ -37014,50 +37113,63 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) /* refused, not substituted: still out of service */ member = WC_RNG_BANK_OFFSET_TO_RNG(bank, 0); if (wc_RNG_GetStatus(member) != WC_DRBG_NOT_INIT) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); else api_ret = 0; } else if (api_ret == 0) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + } + else { + api_ret = WC_TEST_RET_ENC_EC(api_ret); } } /* recover the root the bank's way (the daemon's root recovery: * reinstantiate in place, retaining its children), then the member */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_rng_bank_recover_inst(bank, 0, 0, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { member = WC_RNG_BANK_OFFSET_TO_RNG(bank, 0); if ((wc_RNG_GetStatus(member) != WC_DRBG_OK) || (wc_RNG_DRBG_GetRBGCStratum(member) != 1)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && RBGC && LOCK */ @@ -37087,58 +37199,174 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* the walk: root and both members latched */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_invalidate_entropy(bank, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { if (! (WOLFSSL_ATOMIC_LOAD(root->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } if (api_ret == 0) { api_ret = wc_rng_bank_inst_lock_read(&bank->rngs[1], &word); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if ((api_ret == 0) && (! (word & WC_RNG_LOCK_ENTROPY_INVALIDATED))) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } /* root recovered the bank's way: reinstantiated in place, children * retained, latch discharged */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + + if (api_ret == 0) { + byte scratch[4]; + /* Increment the reseed counter. */ + api_ret = wc_RNG_lock_get(root, 0); + if (api_ret == 0) { + int unlock_ret; + api_ret = wc_RNG_GenerateBlock(root, scratch, + (word32)sizeof(scratch)); + if (api_ret == 0) { + /* Make sure _root_rng_reseed_now() is trying the lock. */ + api_ret = wc_rng_bank_root_rng_reseed_now(bank, NULL, 0); + if (api_ret == WC_NO_ERR_TRACE(BUSY_E)) + api_ret = 0; + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + unlock_ret = wc_RNG_lock_put(root, 0); + if ((unlock_ret != 0) && (api_ret == 0)) + api_ret = WC_TEST_RET_ENC_EC(unlock_ret); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { + wc_drbg_reseed_ctr_t start_root_reseed_ctr; + start_root_reseed_ctr = RESEED_CTR(root); + if ((start_root_reseed_ctr <= 1) || + (start_root_reseed_ctr >= WC_RESEED_INTERVAL)) + { + api_ret = WC_TEST_RET_ENC_I(start_root_reseed_ctr); + } + if (api_ret == 0) { + api_ret = wc_rng_bank_root_rng_reseed_if_due( + bank, NULL, 0, start_root_reseed_ctr + 1); + if (api_ret == WC_NO_ERR_TRACE(ALREADY_E)) { + if (RESEED_CTR(root) != start_root_reseed_ctr) + api_ret = WC_TEST_RET_ENC_I(RESEED_CTR(root)); + else + api_ret = 0; + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { + api_ret = wc_rng_bank_root_rng_reseed_if_due( + bank, NULL, 0, start_root_reseed_ctr); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { + if (RESEED_CTR(root) > 1) + api_ret = WC_TEST_RET_ENC_I(RESEED_CTR(root)); + } + } + if (api_ret == 0) { + byte scratch[4]; + /* Increment the reseed counter. */ + api_ret = wc_RNG_lock_get(root, 0); + if (api_ret == 0) { + int unlock_ret; + api_ret = wc_RNG_GenerateBlock(root, scratch, + (word32)sizeof(scratch)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + unlock_ret = wc_RNG_lock_put(root, 0); + if ((unlock_ret != 0) && (api_ret == 0)) + api_ret = WC_TEST_RET_ENC_EC(unlock_ret); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { + wc_drbg_reseed_ctr_t start_root_reseed_ctr; + start_root_reseed_ctr = RESEED_CTR(root); + if ((start_root_reseed_ctr <= 1) || + (start_root_reseed_ctr >= WC_RESEED_INTERVAL)) + { + api_ret = WC_TEST_RET_ENC_I(start_root_reseed_ctr); + } + if (api_ret == 0) { + api_ret = wc_rng_bank_root_rng_reseed_now(bank, NULL, 0); + if (api_ret == 0) { + if (RESEED_CTR(root) > 1) + api_ret = WC_TEST_RET_ENC_I(RESEED_CTR(root)); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + } + /* a member recovers under its holder's first generate */ if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 1, 10, WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY); if (api_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) api_ret = 0; + else if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if ((api_ret == 0) && (inst == NULL)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), block, (word32)sizeof(block)); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery( WC_RNG_BANK_INST_TO_RNG(inst)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (inst != NULL) { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } /* the walk against a root mid-reinit */ if (api_ret == 0) { api_ret = wc_RNG_lock_get(root, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) root_held = 1; } @@ -37148,15 +37376,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) api_ret = wc_FreeRng_PreLocked(root); if (api_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)) api_ret = 0; + else if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if ((api_ret == 0) && (wc_RNG_GetStatus(root) != WC_DRBG_NOT_INIT)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_rng_bank_invalidate_entropy(bank, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { if (! (WOLFSSL_ATOMIC_LOAD(root->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } /* the reinit's entry discharge retires the latch: its material is * acquired after the event */ @@ -37165,31 +37398,42 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) devId, WC_RNG_INIT_FLAG_LOCK_REQUIRED | WC_RNG_INIT_FLAG_PRESERVE_LOCK); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if (root_held) { int put_ret = wc_RNG_lock_put(root, 0); root_held = 0; if ((api_ret == 0) && (put_ret != 0)) - api_ret = put_ret; + api_ret = WC_TEST_RET_ENC_EC(put_ret); } - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { /* and the members, latched by that walk, recover from it */ api_ret = wc_rng_bank_reseed_range(bank, 0, -1, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_RBGC); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } @@ -37200,11 +37444,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && RBGC && LOCK && PRESERVE_LOCK */ @@ -37235,30 +37479,36 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* spawn under the caller-held root lease: the child pins the root * alongside the two members' pins. */ if (api_ret == 0) { api_ret = wc_RNG_lock_get(root, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_InitRngNonceRBGC(&child, root, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) child_inited = 1; { int put_ret = wc_RNG_lock_put(root, 0); if ((api_ret == 0) && (put_ret != 0)) - api_ret = put_ret; + api_ret = WC_TEST_RET_ENC_EC(put_ret); } } } if ((api_ret == 0) && (child.RBGC_parent != root)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); /* refused with the bank intact: root alive, members in service */ if (api_ret == 0) { @@ -37266,24 +37516,30 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (api_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if ((api_ret == 0) && ((wc_rng_bank_root_rng_get(bank) != root) || (wc_RNG_GetStatus(root) != WC_DRBG_OK))) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } @@ -37291,14 +37547,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) /* a held root is BUSY_E, likewise with no side effects */ if (api_ret == 0) { api_ret = wc_RNG_lock_get(root, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { int fini_ret = wc_rng_bank_fini(bank); int put_ret = wc_RNG_lock_put(root, 0); if (fini_ret != WC_NO_ERR_TRACE(BUSY_E)) api_ret = (fini_ret == 0) ? - WC_NO_ERR_TRACE(UNEXPECTED_STATE_E) : fini_ret; - else - api_ret = put_ret; + WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E) : + WC_TEST_RET_ENC_EC(fini_ret); + else if (put_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(put_ret); } } @@ -37309,41 +37568,45 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (api_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if ((api_ret == 0) && (wc_RNG_GetStatus(root) != WC_DRBG_NOT_INIT)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); if (api_ret == 0) { api_ret = wc_rng_bank_fini(bank); if (api_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } /* intact: the members are still in service */ if ((api_ret == 0) && (wc_RNG_GetStatus(WC_RNG_BANK_OFFSET_TO_RNG(bank, 0)) != WC_DRBG_OK)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } /* the child's release unpins the root; teardown then converges. */ if (child_inited) { int free_ret = wc_FreeRng(&child); if ((api_ret == 0) && (free_ret != 0)) - api_ret = free_ret; + api_ret = WC_TEST_RET_ENC_EC(free_ret); child_inited = 0; (void)child_inited; } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && RBGC && LOCK && RBGC_REFCOUNT */ @@ -37365,13 +37628,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; if ((api_ret == 0) && (wc_RNG_DRBG_GetRBGCStratum(WC_RNG_BANK_OFFSET_TO_RNG(bank, 0)) != 0)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } if (api_ret == 0) { time_t ts1 = XTIME(0); @@ -37380,27 +37645,33 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_RBGC); if (api_ret == WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) { if (XTIME(0) - ts1 >= 3) - api_ret = WC_NO_ERR_TRACE(WC_TIMEOUT_E); + api_ret = WC_TEST_RET_ENC_EC(WC_TIMEOUT_E); else api_ret = 0; } else if (api_ret == 0) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + } + else { + api_ret = WC_TEST_RET_ENC_EC(api_ret); } } /* the members are untouched by the refusal: a primary reseed walk * of the same range succeeds */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_reseed_range(bank, 0, -1, NULL, 0, 3, WC_RNG_BANK_FLAG_CAN_WAIT); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - return WC_TEST_RET_ENC_EC(api_ret); + return api_ret; /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && RBGC && STRATUM_IMMUTABLE */ @@ -37422,25 +37693,33 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if ((api_ret == 0) && (wc_RNG_GetStatus(root) != WC_DRBG_OK)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(root, block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - return WC_TEST_RET_ENC_EC(api_ret); + return api_ret; /* encoded at the failure site */ } #ifdef WC_RNG_HAVE_RBGC @@ -37465,29 +37744,38 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* take member 0 out of service, then the root */ if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_FreeRng(WC_RNG_BANK_INST_TO_RNG(inst)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_FreeRng(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if ((api_ret == 0) && (wc_RNG_GetStatus(root) != WC_DRBG_NOT_INIT)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); /* the root's verdict, promptly: well inside the 3 s timeout */ if (api_ret == 0) { @@ -37496,43 +37784,52 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT); if (api_ret == WC_NO_ERR_TRACE(NOT_READY_E)) { if (XTIME(0) - ts1 >= 3) - api_ret = WC_NO_ERR_TRACE(WC_TIMEOUT_E); + api_ret = WC_TEST_RET_ENC_EC(WC_TIMEOUT_E); else api_ret = 0; } else if (api_ret == 0) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + } + else { + api_ret = WC_TEST_RET_ENC_EC(api_ret); } } if ((api_ret == 0) && (wc_RNG_GetStatus(WC_RNG_BANK_OFFSET_TO_RNG(bank, 0)) != WC_DRBG_NOT_INIT)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } /* root recovered (the gate is free), then the member */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_rng_bank_recover_inst(bank, 0, 3, WC_RNG_BANK_FLAG_CAN_WAIT); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { member = WC_RNG_BANK_OFFSET_TO_RNG(bank, 0); if ((wc_RNG_GetStatus(member) != WC_DRBG_OK) || (wc_RNG_DRBG_GetRBGCStratum(member) != 1)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - return WC_TEST_RET_ENC_EC(api_ret); + return api_ret; /* encoded at the failure site */ } #endif /* WC_RNG_HAVE_RBGC */ #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && !LOCK */ @@ -37599,6 +37896,8 @@ static void rng_lock_fork_child_held(WC_RNG* rng, int adopt, /* The invalidation purged the aperture: the parent's banked seed, which * the parent still holds, is not on offer here. */ api_ret = wc_RNG_DRBG_NextSeedCurrent(rng, &cur); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if ((api_ret == 0) && (cur == WC_DRBG_NEXT_SEED_READY)) { r->step = 3; r->code = (int)cur; return; } @@ -37610,12 +37909,16 @@ static void rng_lock_fork_child_held(WC_RNG* rng, int adopt, * latched) word -- which discharges the latch -- and the lease is a * live one of this process. */ api_ret = wc_FreeRng_PreLocked(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 4; r->code = api_ret; return; } api_ret = wc_InitRng_ex2(rng, HEAP_HINT, devId, RNG_LOCK_FORK_FLAGS | WC_RNG_INIT_FLAG_PRESERVE_LOCK); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 5; r->code = api_ret; return; } @@ -37624,6 +37927,8 @@ static void rng_lock_fork_child_held(WC_RNG* rng, int adopt, * the latch is the recovery -- a credited reseed, stamped with this * pid, same as any post-fork first use. */ api_ret = wc_RNG_GenerateBlock(rng, block, sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 6; r->code = api_ret; return; } @@ -37634,15 +37939,21 @@ static void rng_lock_fork_child_held(WC_RNG* rng, int adopt, r->step = 7; r->code = api_ret; return; } api_ret = wc_RNG_lock_put(rng, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 8; r->code = api_ret; return; } /* and now it is a free instance of this process */ api_ret = wc_RNG_lock_get(rng, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 9; r->code = api_ret; return; } api_ret = wc_FreeRng_PreLocked(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 10; r->code = api_ret; return; } @@ -37661,14 +37972,20 @@ static void rng_lock_fork_child_free(WC_RNG* rng, r->code = 0; api_ret = wc_RNG_lock_get(rng, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 1; r->code = api_ret; return; } api_ret = wc_RNG_GenerateBlock(rng, block, sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 2; r->code = api_ret; return; } api_ret = wc_FreeRng_PreLocked(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 3; r->code = api_ret; return; } diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index d376faf1c5e..1d28a2009df 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -402,6 +402,11 @@ WOLFSSL_API int wc_rng_bank_root_rng_reinit(struct wc_rng_bank *bank, const byte *nonce, word32 nonceSz, const byte *perso, word32 persoSz, word32 flags); +#ifdef HAVE_HASHDRBG +WOLFSSL_API int wc_rng_bank_root_rng_reseed_now(struct wc_rng_bank *bank, + const byte *nonce, + word32 nonceSz); +#endif #endif WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, @@ -443,6 +448,19 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); return wc_RNG_lock_put_conditional( WC_RNG_BANK_INST_TO_RNG(inst), expect_extra_bits, 0); } + #ifdef BUILDING_WOLFSSL + static WC_INLINE int wc_rng_bank_inst_lock_get_unconditional( + struct wc_rng_bank_inst *inst) + { + return wc_RNG_lock_get_unconditional( + WC_RNG_BANK_INST_TO_RNG(inst)); + } + static WC_INLINE void wc_rng_bank_inst_lock_put_unconditional( + struct wc_rng_bank_inst *inst) + { + wc_RNG_lock_put_unconditional(WC_RNG_BANK_INST_TO_RNG(inst)); + } + #endif /* BUILDING_WOLFSSL */ static WC_INLINE int wc_rng_bank_inst_lock_read( struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t *state) { @@ -492,8 +510,10 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get_conditional( struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, WC_RNG_lock_arg_t want_extra_bits); + WOLFSSL_LOCAL int wc_rng_bank_inst_lock_get_unconditional(struct wc_rng_bank_inst *inst); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_LOCAL void wc_rng_bank_inst_lock_put_unconditional(struct wc_rng_bank_inst *inst); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); @@ -561,6 +581,17 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); #endif #endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ +#if defined(WC_RNG_BANK_HAVE_ROOT_RNG) && defined(HAVE_HASHDRBG) +/* Threshold form of wc_rng_bank_root_rng_reseed_now(): under the same + * serialization, reseed only when the root's reseed counter has reached + * threshold (0 means unconditional; a below-threshold counter returns + * ALREADY_E). Declared here, after wc_drbg_reseed_ctr_t is defined on + * all boundaries. */ +WOLFSSL_API int wc_rng_bank_root_rng_reseed_if_due( + struct wc_rng_bank *bank, const byte *nonce, word32 nonceSz, + wc_drbg_reseed_ctr_t threshold); +#endif + #ifdef WC_RNG_DEBUG_STATS WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, struct wc_rng_bank *bank);