1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00

RNG SP-800-90* compliance mechanism completion (the balance of the

WC_RNG_RBGC_STRATUM_IMMUTABLE campaign):

* RNG state-invalidation delivery by entropy epoch

* Fork safety for the WC_RNG lock word

* RBGC lineage persistence with a global fallback source

* A uniform "transient vs. verdict" error taxonomy

* Lifecycle hardening that makes WC_RNG safe to reinstantiate under lease-free
  observers.

Protocol-level changes (random.[ch]):

* Entropy epoch (WC_RNG_HAVE_ENTROPY_EPOCH, on by default with atomics in
  multithreaded builds; WC_RNG_WANT_/WC_RNG_NO_ENTROPY_EPOCH override): a
  global event counter, wc_RNG_global_invalidate_entropy(), replaces the
  per-instance walk as the primary delivery mechanism for state-invalidation
  events (VM fork/resume, hibernation).  Each WC_RNG carries a stamp; every
  seeding operation captures the epoch (and PID) before acquiring material
  and stamps at completion, so an event that lands mid-operation leaves a
  stale stamp.  rng_entropy_event_propagate() converts {stale stamp,
  unlatched} to {latched, swept} lazily, at the instance's next aperture:
  "detection is also delivery," and every aperture consulted after a nonzero
  wc_RNG_entropy_needs_recovery() is post-purge.  wc_RNG_lock_put() reports
  an unpropagated increment as NEEDS_RECOVERY_E, so a holder whose last
  operation straddled the event learns of it at release.  The latch is
  cleared by a recovery only when the recovering caller's capture is still
  current.

* wc_RNG_entropy_needs_recovery() is the single authoritative verdict on a
  standing recovery obligation (latch, stale epoch, PID change, exhausted
  reseed budget), and the generate path, the stir path, the pool reader and
  the banked-seed consumer all key on it.  rng_pid_change_check() is retired
  into it: a fork() is an epoch event.  The reseed counter is thereby
  returned to its SP 800-90A meaning (budget only); invalidation never
  spends runway and is never enforced by counter saturation in lock builds.

* Fork safety for the lock word: WC_RNG_LOCK_ORPHANED (error-crypt.h:
  OBJECT_ORPHANED_E, -1036; HWPUF_* codes shift down by one).  A _HELD word
  stamped with another process's PID was held across a fork() by a holder
  absent in this process; the first post-fork contender marks it _ORPHANED
  (RngLockOrphanCheck(), from every lock getter), applies the invalidation
  once as CAS winner, and reports OBJECT_ORPHANED_E.  The verdict licenses
  nothing to the contender: the owner adopts the lease by
  wc_FreeRng_PreLocked() then wc_InitRng_ex2() with the new
  WC_RNG_INIT_FLAG_PRESERVE_LOCK (which clears _ORPHANED with the latch); a
  live holder's release or completed recovery also clears it.

* RBGC lineage persistence (WC_RNG_HAVE_LOCK && WC_RNG_HAVE_RBGC): a chain
  child retains RBGC_parent and pins it by RBGC_refcount (both live for as
  long as the lock word, preserved across _PRESERVE_LOCK reinits).
  wc_FreeRng() on a pinned parent wipes sensitive state and returns the new
  STILL_REFERENCED_E (error-crypt.h) -- the shell must outlive its
  children; wc_rng_free() leaks rather than freeing referenced memory.
  WC_RNG_FLAG_NO_RBGC_PARENT opts an instance out of being retained
  (wc_rng_new*() and *_New() constructors set it, since the void
  wc_rng_free() cannot report).  HAVE_FIPS forces lock-required parents and
  WC_RNG_RBGC_STRATUM_IMMUTABLE, and random.h refuses HAVE_FIPS with RBGC
  but without the lock facility.

* Global fallback RBGC source (WC_RNG_HAVE_GLOBAL_FALLBACK_RNG):
  wc_RNG_global_fallback_init[_user_seed]() / _get() / _free(), a static
  lock-required WC_RNG, freed by wolfCrypt_Cleanup().  PollAndReSeed() and
  wc_RNG_DRBG_NextSeedGenerate() climb a ladder for a chain-backed
  instance: the retained parent, then the global fallback (under FIPS only
  when it qualifies as "a sibling of the parent, an ancestor, or the initial
  randomness source" per SP 800-90C), then -- unless the instance is
  WC_RNG_FLAG_NO_PRIMARY_SEED -- the primary source.  A ladder rung whose
  release reports NEEDS_RECOVERY_E voids the reseed (DRBG_NEED_RESEED) or
  purges the bank just filled.  SP 800-90C qualification of a supplied root
  against the retained parent is enforced in wc_RNG_DRBG_ReseedRBGC() and
  wc_RNG_DRBG_NextSeedGenerate_RBGC() under FIPS.

* Runtime policy flags: wc_RNG_SetFlags() / wc_RNG_ClearFlags() /
  wc_RNG_GetFlags(), limited to WC_RNG_FLAGS_SETTABLE / _CLEARABLE
  (FAIL_FAST, ONLY_PRIMARY_SEED, NO_PRIMARY_SEED) and to exclusive
  ownership.  WC_RNG_FLAG_FAIL_FAST (WC_RNG_INIT_FLAG_FAIL_FAST at birth):
  a generate that would otherwise reseed in-line reports instead --
  NOT_READY_E for the budget, NEEDS_RECOVERY_E for a latched or stale
  instance (RngFailFastVerdict()) -- leaving the DRBG uncondemned;
  wc_RNG_Pool_Collect2() sets it transiently on its source so the ring claim
  is held for one bounded generate.  WC_RNG_FLAG_NO_PRIMARY_SEED /
  WC_RNG_INIT_FLAG_NO_PRIMARY_SEED forbids the primary source after
  instantiation (chain members that must stay chain-fed; MISSING_RNG_E on an
  instance with no chain parent); WC_RNG_FLAG_ONLY_PRIMARY_SEED is the
  converse, set transiently by the new wc_RNG_DRBG_Reseed_Now_Primary() for
  prediction-resistant constructions on chain-backed instances.
  WC_RNG_{INIT_,}FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED is renamed
  _UPGRADE_FROM_NEXT_SEED (rng_bank.h: WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_
  NEXT_SEED).  WC_RNG_INIT_FLAG_* retain their shipped values, with the
  new flags appended; WC_RNG_FLAG_* are renumbered alongside.

* Transient-vs-verdict taxonomy: WC_RNG_ERR_IS_TRANSIENT() (random.h, with
  a mirror in rng_bank.h for boundaries that predate it) names the codes
  that mean "not now" -- BUSY_E, NOT_READY_E, NEEDS_RECOVERY_E, RAN_BLOCK_E,
  ENTROPY_RT_E, ENTROPY_APT_E.  wc_InitRng*() passes them through from the
  seed source verbatim (ReseedSourceFailure(), shared by instantiate and
  reseed) rather than folding them into RNG_FAILURE_E; rng_bank.c keys its
  retry loops on the same set.  BUSY_E and MEMORY_E -- a resource that
  requires waiting, in a context that can't -- never condemn, even with no
  reseed runway (RngGenerateFailure(), now also the single outcome mapping
  for wc_RNG_DRBG_Reseed_Now()).  wc_RNG_Status() precomputes the code for
  a status (NOT_READY_E / RNG_FAILURE_E / DRBG_CONT_FIPS_E) and replaces
  the ad hoc status checks throughout.  wc_GenerateSeed() with
  HAVE_ENTROPY_MEMUSE returns BUSY_E from a context that cannot run the
  wolfEntropy source rather than silently substituting a backup source.

* WC_RNG_INIT_FLAG_TEST_USER_SEED: caller-supplied seed material is
  health-tested and laid out exactly as module-gathered material (leading
  SEED_BLOCK_SZ bytes are the test block, excluded from DRBG input in FIPS
  and non-FULL_SEED builds); wc_InitRngFixedSeed() uses it, restoring its
  master behavior.  Without the flag the user seed is copied at the
  instantiate offset, so no supplied byte is discarded and no health test
  runs.  The strength floors live at the public entry points
  (UserSeedSzFloor(): WC_DRBG_SEED_SZ, plus a block under the flag where the
  block is excluded; zero refused); _InitRng() keeps only the one-block
  backstop.  wc_InitRngNonce_UserSeed() refuses without WC_RNG_HAVE_RBGC
  (provenance marking lives there) and validates its shape arguments.

Lifecycle hardening (random.[ch]):

* WC_RNG members shared by both DRBG cores -- drbgType, reseedCtr, the
  banked-next-seed aperture (nextSeed, nextSeedLen, nextSeedRBGCStratum) --
  move from DRBG_internal / DRBG_SHA512_internal into WC_RNG, for safe
  lock-free access by lease-free observers; the per-core structs keep the
  SMALL_STACK_CACHE newSeed_buf.  WC_DRBG_SEED_BUF_SIZE names the gathered
  seed length; the generic DRBG functions take the reseed counter by
  pointer.  NextSeedPtrs() and the per-core duplication it served are gone.

* _InitRng() zeroization (RngInitWipe()) skips the atomic words that can
  have lease-free concurrent observers (lock, RBGC_refcount, poolHead/Tail,
  nextStirLen, nextSeedLen), which are instead atomically (re)initialized;
  the apertures are painted _TORNDOWN at entry and reopened as the last act
  before publication, and rng->status becomes DRBG_OK only at the very end.
  WC_RNG_INIT_FLAG_PRESERVE_LOCK leaves the lock word and refcount exactly
  as found (refusing _LOCK_INITIALLY and _USE_FULL_MUTEX, and insisting the
  _REQUIRED bits agree), and discharges an inherited latch by CAS at entry
  rather than erasing it.  WC_RNG_STATIC_INITIALIZER is provided.

* Pool lifecycle: poolHead carries a 2-bit state above the epoch (UNALLOC,
  LIVE, CLAIMED, TORNDOWN).  wc_RNG_Pool_Alloc() allocates unclaimed and
  publishes by CAS; a writer claims by one CAS at entry and releases by CAS
  at every exit; wc_FreeRng() takes LIVE -> TORNDOWN by one CAS before any
  irreversible step and returns BUSY_E (nothing mutated) when a claim is
  held, never spinning; TORNDOWN refuses re-equipment until the next
  _InitRng().  Likewise the credited seed aperture gains
  WC_DRBG_NEXT_SEED_TORNDOWN, every ownership transition is a CAS, and a
  purge repaints a live claim _PURGED for its holder to unwind rather than
  reopening it from under the holder.  wc_FreeRng() frees the DRBG after
  nulling the pointer at every site, and uninstantiates only the core
  matching drbgType.

* wc_FreeRng() in lock builds takes the lease itself (unconditionally, so a
  standing latch or condemnation does not block teardown) and refuses a
  held instance with BUSY_E; wc_FreeRng_PreLocked() is the form for a
  caller already holding the lease, which it retains.  The WC_RNG_LOCK_
  REQUIRED-but-unheld case is OBJECT_NOT_LOCKED_E.  wc_RNG_lock_get_
  unconditional() / _put_unconditional() are the internal trivial forms.

* wc_RNG_DRBG_GetReseedCtr() becomes WOLFSSL_LOCAL (the counter is
  potentially sensitive internal state); wc_RNG_DRBG_NextSeedGenerate_
  Primary() is added; wc_RNG_DRBG_NextSeedCurrent(), wc_RNG_Pool_Current()
  and wc_RNG_lock_read() take const.  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 (was MISSING_RNG_E, which nothing on
  that path returned).  A stir refuses with the needs-recovery verdict
  (NEEDS_RECOVERY_E) when the instance is quarantined, due, or stale, and
  NOT_READY_E only when the interval trips within the call.

* WC_VERBOSE_RNG logging follows the taxonomy: transient codes are never
  logged as errors.

rng_bank.c / rng_bank.h:

* struct wc_rng_bank_inst now places its WC_RNG at offset 0, so
  WC_RNG_BANK_INST_TO_RNG() is a cast; wc_rng_bank_reinit_rng() (LOCAL)
  retires and reinstantiates any bank-owned rng, protected by WC_RNG.lock
  under _PRESERVE_LOCK where available (a held root yields BUSY_E) and by
  the inst-op gate otherwise; wc_rng_bank_inst_reinit() and
  wc_rng_bank_root_rng_reinit() are built on it.  A member is reborn only
  as a chain child of its root -- a root that cannot serve right now is a
  reason to retry, never to substitute a primary reseed.

* wc_rng_bank_reseed_range(): the RBGC arm takes the root's lease for the
  chain draw; the walk carries on past a per-instance failure (marking the
  instance due) and returns the first such failure, with a code that
  aborted the walk (WC_TIMEOUT_E, INTERRUPTED_E, a checkout failure)
  outranking it; wc_rng_bank_next_seed_generate_local() purges the member
  when the root's release reports an event under its hold, as
  wc_RNG_DRBG_NextSeedGenerate()'s chain arm does.

* wc_rng_bank_fini(): a member refusing to free (BUSY_E /
  STILL_REFERENCED_E) restores the bank refcount so the caller can and must
  retry; the root is freed after the members, so member pins are released
  before the root's refcount is judged.  The invalidation walk's root arm is
  unconditional (a root mid-reinit is NOT_INIT with its lock word live).
  Init retry loops key on WC_RNG_ERR_IS_TRANSIENT().

* rng_bank.h: WC_RNG_LOCK_ORPHANED / WC_RNG_LOCK_EXTRA_SHIFT 5 mirrored in
  the no-lock compat arm; for the WCv5.2.4 boundary, which carries the
  backported RNG services, WC_RNG_HAVE_STIR / WC_RNG_HAVE_RESEED_NOW
  capability markers and a wc_InitRngNonceRBGC() perso-argument shim, so
  consumers outside the boundary (src/ssl.c, src/ssl_crypto.c) gate on the
  markers rather than on FIPS version arithmetic.

linuxkm/lkcapi_sha_glue.c:

* wc_linuxkm_rng_state_invalidate(): with the entropy epoch, bumps the
  epoch and materializes only the bank root's latch synchronously
  (rng_invalidation_pre_check() keys on it), leaving members to lazy
  delivery so a consumer is admitted to a still-unlatched member and
  recovers it under its own lease rather than waiting out a quarantine;
  without the epoch, the synchronous walk as before.  The daemon-less bank
  path treats a transient member code from wc_rng_bank_reseed_range() as a
  deferred recovery, not a failure.

* Entropy daemon: a transient failure of the last-ditch root reinit is
  retried next pass rather than ending the daemon (which had made root
  recovery and next-seed feeding end for good on ordinary contention);
  the vmgenid poller's immediate root reseed takes the root's lease (it was
  OBJECT_NOT_LOCKED_E and void-cast); the kernel-log rule throughout --
  transient codes are one pass away and not errors, the SP 800-90B health
  verdicts are warnings (WC_LKM_RNG_ERR_IS_HEALTH()), anything else is an
  error -- at the recovery, banking, bonus-reseed and reinit arms; the
  banking pass's no-DRBG case is WRONG_TYPE_OBJECT_E; busy instances are
  counted in the recovery sweep's tally.

* wc_linuxkm_drbg_generate(): the kernel-facing language is two-valued --
  -EIO for a verdict (non-transient, or ENTROPY_RT_E / ENTROPY_APT_E),
  -EBUSY for every "not now" code; only BUSY_E outside the invalidation
  window (every instance held at once: an undersized bank) is logged as an
  error.  The pooling pass (re)equips any instance it finds poolless each
  turn, so no up-front allocation and no special case after a
  reinstantiation.  Bank sizing rationale documented.

* The _get_random_bytes() callback contract is best-effort, not
  unconditional success: a refusal from atomic context immediately after a
  global invalidation falls through silently to the native generator.
  linuxkm/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch: drop the
  WARN_ONCE on callback failure, and drop the routing of the batched
  get_random_{u8,u16,u32,u64}() through the callback (not FIPS-controlled
  consumers; get_random_bytes_user() and getrandom() remain fully covered).

Tests:

* wolfcrypt/test/test.c: flags_test() (WCFLAGS) replaces rng_flag_abi_test():
  pairwise-disjointness of every bit-flag namespace in the wolfcrypt headers
  at compile time and of the hand-numbered registries at run time, against
  the symbols rather than literal values; rng_epoch_test() (RNGEPOCH: epoch
  propagation on every aperture, mid-gather increments, busy and
  out-of-memory seed sources never condemn); rng_lifecycle_test() (RNGLIFE:
  RBGC parent pinning and STILL_REFERENCED_E, the destruction latch,
  wc_FreeRng_PreLocked() and the _PRESERVE_* reinit, the bank-walk mirror
  arm); rng_lock_fork_test() (RNGLKFRK: the orphaned-lease verdict and
  adoption across a real fork()); rng_init_flags_test() (contradictory and
  not-compiled-in init flags refused) and rng_seed_verdict_test() (transient
  codes from the seed source survive wc_InitRng*() verbatim; FIPS v7+ only,
  the pinned legacy random.c folds them); rng_seed_test() reactivated (dead
  since the seed-size defaults moved into random.h) and steered to SHA-256
  in SHA-512-preferring builds; WC_RNG_INIT_FLAG_TEST_USER_SEED arms in the
  wc_InitRngNonce_UserSeed() strict-contract block; leak fixes and
  goto-teardown in the RBGC-lifetime and pool tests; per-subtest in-kernel
  stack high-water marks (WC_LINUXKM_STACK_DEBUG_PER_TEST); WC_RNG moved to
  the heap in pkcs7authenveloped_run_vectors().

* tests/api/test_random.c, tests/unit-mcdc/test_random_*whitebox.c: follow
  the reseed counter to WC_RNG; forced seed failures use a nonsense code so
  the WC_VERBOSE_RNG line stays quiet; condemnation-on-reseed-failure arms
  saturate the counter explicitly, since invalidation no longer does.

* doc/dox_comments: every new API and flag; the instantiation-input and
  user-seed contracts; reseed/reseed_range return semantics; the next-seed
  return taxonomy.
This commit is contained in:
Daniel Pouzzner
2026-10-14 16:12:13 -05:00
parent cabf3bd681
commit 8a65208daa
19 changed files with 8067 additions and 2072 deletions

View File

@@ -832,6 +832,7 @@ WC_RNG_BANK_NO_DAEMON_SUPPORT
WC_RNG_BANK_NO_DEFAULT_SUPPORT
WC_RNG_BLOCKING
WC_RNG_NO_BANKREF_SUPPORT
WC_RNG_NO_ENTROPY_EPOCH
WC_RNG_NO_FREE_HOOK
WC_RNG_NO_LOCK
WC_RNG_NO_LOCK_FULL_MUTEX
@@ -840,6 +841,7 @@ WC_RNG_NO_POOL
WC_RNG_NO_RBGC
WC_RNG_NO_RBGC_RESEED
WC_RNG_WANT_AUTO_LOCK
WC_RNG_WANT_ENTROPY_EPOCH
WC_RNG_WANT_FREE_HOOK
WC_RNG_WANT_LOCK
WC_RNG_WANT_LOCK_FULL_MUTEX

File diff suppressed because it is too large Load Diff

View File

@@ -261,8 +261,13 @@ int wc_rng_bank_default_clear(struct wc_rng_bank *bank);
wc_rng_bank_inst_checkin(). WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED consumes
a ready banked next seed in an immediate source-free credited reseed
before returning; WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (per-call,
requires _CAN_WAIT) freshly credited-reseeds the lease before the
caller's first draw.
requires _CAN_WAIT) freshly credited-reseeds the lease from the
primary source (wc_RNG_DRBG_Reseed_Now_Primary()) before the caller's
first draw. On a bank whose members are RBG-chain children of a root
(WC_RNG_BANK_FLAG_RBGC) in a WC_RNG_RBGC_STRATUM_IMMUTABLE build, that
reseed is conformant but the lease's output carries no SP 800-90C
prediction-resistance claim -- a chain member is not a DRBG with direct
access to its entropy source (see wc_RNG_DRBG_Reseed_Now_Primary()).
\details Out-of-service instances: a targeted (non-failover) checkout
admits them; failover checkouts divert around them.
@@ -420,10 +425,11 @@ int wc_rng_bank_get_inst_id(struct wc_rng_bank_inst *rng_inst);
The caller must hold a bank reference (e.g. per the daemon association)
for the duration of the call. Return taxonomy for a banking rotation:
BUSY_E, the gate is held by a reinit -- skip this turn; ALREADY_E,
sleep until the bank is consumed; MISSING_RNG_E, the instance has no
DRBG (RDRAND et al.) and can be retired from the rotation permanently;
other errors are transient gather or health-test failures -- skip the
turn, and alarm if persistent.
sleep until the bank is consumed; WRONG_TYPE_OBJECT_E, the instance has
no DRBG -- permanently (RDRAND et al.), retire it from the rotation, or
because it is out of service pending reinstantiation; the other
transient codes (WC_RNG_ERR_IS_TRANSIENT()) are gather or health-test
failures -- skip the turn, and alarm if persistent.
*/
int wc_rng_bank_next_seed_generate(struct wc_rng_bank *bank, int inst_offset,
@@ -658,17 +664,30 @@ int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int first_inst,
/*!
\ingroup Random
\brief Reseed every instance from the module's seed source.
\brief Reseed every instance from the module's seed source. Same
contract as wc_rng_bank_reseed_range() over the whole bank.
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\return RNG_FAILURE_E An instance could not be reseeded within
timeout_secs.
\return 0 Every instance was reseeded, or -- waiting not being allowed
-- marked due for reseed on its next generate.
\return BAD_FUNC_ARG bank is null, or flags selects instance choice
(WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST et al.), which a walk cannot honor.
\return BAD_STATE_E The bank is not initialized, or an instance is out
of service.
\return NOT_READY_E WC_RNG_BANK_FLAG_RBGC with the root not in service.
\return WC_TIMEOUT_E The reseed retries exceeded timeout_secs; the walk
stopped there.
\return INTERRUPTED_E A signal arrived while waiting; the walk stopped
there.
\return other A negative code from wc_rng_bank_checkout(), which
stopped the walk; or, the walk having completed, the first
per-instance reseed failure it carried on past (see
wc_rng_bank_reseed_range()).
\param bank The bank to reseed.
\param nonce Optional per-instance nonce material (may be null).
\param nonceSz Length of nonce in bytes.
\param timeout_secs Wait budget per instance.
\param timeout_secs Wait budget: per instance for the checkout, and
across the whole walk for reseed retries (WC_RNG_BANK_FLAG_CAN_WAIT).
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
\sa wc_rng_bank_reseed_range
@@ -681,22 +700,44 @@ int wc_rng_bank_reseed(struct wc_rng_bank *bank,
/*!
\ingroup Random
\brief The range form of wc_rng_bank_reseed().
\brief The range form of wc_rng_bank_reseed(). A failed reseed does
not stop the walk: the instance is marked due for reseed, the next
entropy-capable generate on it retries in-boundary, and the walk
carries on to the remaining instances -- but the first such failure is
the return value, so the caller knows the range was not reseeded here
and now. A code that stops the walk (WC_TIMEOUT_E, INTERRUPTED_E, a
checkout failure) outranks it: the instances past that point were not
visited and are not marked due. A transient per-instance code
(WC_RNG_ERR_IS_TRANSIENT()) means that instance's reseed is a pass
away from resolving on its own; for the rest, see
wc_RNG_DRBG_Reseed_Now().
\return 0 Success
\return BAD_FUNC_ARG bank is null, or the range is out of bounds.
\return RNG_FAILURE_E An instance could not be reseeded within
timeout_secs.
\return 0 Every instance in range was reseeded, or -- waiting not being
allowed -- marked due for reseed on its next generate.
\return BAD_FUNC_ARG bank is null, or flags selects instance choice
(WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST et al.), which a walk cannot honor.
\return BAD_INDEX_E The instance range is invalid.
\return BAD_STATE_E The bank is not initialized.
\return WC_TIMEOUT_E The walk exceeded timeout_secs.
\return BAD_STATE_E The bank is not initialized, or an instance in
range is out of service.
\return NOT_READY_E WC_RNG_BANK_FLAG_RBGC with the root not in service.
\return WC_TIMEOUT_E The reseed retries exceeded timeout_secs; the walk
stopped there.
\return INTERRUPTED_E A signal arrived while waiting; the walk stopped
there.
\return other A negative code from wc_rng_bank_checkout(), which
stopped the walk; or, the walk having completed, the first
per-instance reseed failure it carried on past, as
wc_RNG_DRBG_Reseed_Now() (or wc_RNG_DRBG_ReseedRBGC() under
WC_RNG_BANK_FLAG_RBGC) returned it.
\param bank The bank to reseed.
\param nonce Optional per-instance nonce material (may be null).
\param nonceSz Length of nonce in bytes.
\param first_inst The first instance offset.
\param last_inst The last instance offset.
\param timeout_secs Wait budget per instance.
\param last_inst The last instance offset, or -1 for the last in the
bank.
\param timeout_secs Wait budget: per instance for the checkout, and
across the whole walk for reseed retries (WC_RNG_BANK_FLAG_CAN_WAIT).
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
\sa wc_rng_bank_reseed
@@ -868,12 +909,16 @@ WC_RNG *wc_rng_bank_root_rng_get(struct wc_rng_bank *bank);
\brief Retire and reinstantiate the bank's root RNG as one serialized
transition, excluding concurrent entropy-invalidation walks and
whole-instance operations for its span. The recovery path of last
resort for a condemned root. Task context only: acquisition of the
serializing gate spin-waits, and reinstantiation performs a full seed
acquisition.
resort for a condemned root. With WC_RNG_HAVE_LOCK the root's lease is
taken by try-lock and held across the cycle, so a held root yields
BUSY_E (retry later); without it, acquisition of the serializing gate
spin-waits, so task context only. Either way reinstantiation performs
a full seed acquisition.
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\return BUSY_E The root's lease is held (WC_RNG_HAVE_LOCK); nothing was
touched, retry later.
\return negative wc_FreeRng() or reinstantiation failure; the root is
left out of service (WC_DRBG_NOT_INIT) and a later reinit may be
attempted.

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
--- 7.3/drivers/char/random.c.dist 2026-09-07 15:16:52.000000000 -0500
+++ 7.3/drivers/char/random.c 2026-09-08 14:52:24.223216472 -0500
+++ 7.3/drivers/char/random.c 2026-10-09 12:21:47.699984161 -0500
@@ -84,6 +84,310 @@ static enum {
} crng_init __read_mostly = CRNG_EMPTY;
static DEFINE_STATIC_KEY_FALSE(crng_is_ready);
@@ -343,7 +343,7 @@
nb->notifier_call(nb, 0, NULL);
else
ret = raw_notifier_chain_register((struct raw_notifier_head *)&random_ready_notifier.head, nb);
@@ -392,6 +696,24 @@ static void _get_random_bytes(void *buf,
@@ -392,6 +696,31 @@ static void _get_random_bytes(void *buf,
if (!len)
return;
@@ -352,23 +352,30 @@
+ int cb_ret = call__get_random_bytes_cb(buf, len);
+ if (cb_ret == 0)
+ return;
+ /* Nonzero cb_ret: no handlers installed (-ENODEV), a lost
+ * race with deinstallation (also -ENODEV), or a genuine
+ * callback failure. Check the sentinel at warn time to
+ * distinguish: only a failure with the sentinel still live is
+ * a contract violation. Continue regardless; native
+ * fallthrough is the only available mechanism to preserve
+ * the void contract. */
+ WARN_ONCE(READ_ONCE(random_bytes_cb_live),
+ "_get_random_bytes callback failed with code %d; "
+ "native fallthrough\n", cb_ret);
+ /* Fall through to native ChaCha20 generator --
+ * _get_random_bytes() returns void, so success is
+ * non-negotiable.
+ *
+ * Nota bene: Due to the absence of a mechanism for error
+ * percolation, _get_random_bytes() is incompatible with
+ * generation of random bytes for a FIPS-controlled purpose.
+ *
+ * Consumption fulfilled by get_random_bytes_user() with the
+ * FIPS callback installed is fully compatible with
+ * FIPS-controlled uses, failing closed, and blocking for
+ * entropy recovery, as needed. This encompasses /dev/random,
+ * /dev/urandom, and the getrandom() syscall. And note that
+ * VDSO-based delivery of random bytes is explicitly disabled
+ * when the callback is installed, to assure consistently
+ * compliant delivery of random bytes.
+ */
+ }
+#endif
+
first_block_len = min_t(size_t, 32, len);
crng_make_state(&chacha_state, buf, first_block_len);
len -= first_block_len;
@@ -437,6 +759,20 @@ static ssize_t get_random_bytes_user(str
@@ -437,6 +766,20 @@ static ssize_t get_random_bytes_user(str
if (unlikely(!iov_iter_count(iter)))
return 0;
@@ -389,16 +396,7 @@
/*
* Immediately overwrite the ChaCha key at index 4 with random
* bytes, in case userspace causes copy_to_iter() below to sleep
@@ -512,7 +848,7 @@ type get_random_ ##type(void) \
struct batch_ ##type *batch; \
unsigned long next_gen; \
\
- if (!crng_ready()) { \
+ if (READ_ONCE(random_bytes_cb_live) || !crng_ready()) { \
_get_random_bytes(&ret, sizeof(ret)); \
return ret; \
} \
@@ -648,6 +984,11 @@ static void mix_pool_bytes(const void *b
@@ -648,6 +991,11 @@ static void mix_pool_bytes(const void *b
{
unsigned long flags;
@@ -410,7 +408,7 @@
spin_lock_irqsave(&input_pool.lock, flags);
_mix_pool_bytes(buf, len);
spin_unlock_irqrestore(&input_pool.lock, flags);
@@ -707,7 +1048,13 @@ static void extract_entropy(void *buf, s
@@ -707,7 +1055,13 @@ static void extract_entropy(void *buf, s
memzero_explicit(&block, sizeof(block));
}
@@ -425,7 +423,7 @@
static void __cold _credit_init_bits(size_t bits)
{
@@ -731,7 +1078,11 @@ static void __cold _credit_init_bits(siz
@@ -731,7 +1085,11 @@ static void __cold _credit_init_bits(siz
if (system_dfl_wq)
queue_work(system_dfl_wq, &set_ready);
atomic_notifier_call_chain(&random_ready_notifier, 0, NULL);
@@ -438,7 +436,7 @@
WRITE_ONCE(vdso_k_rng_data->is_ready, true);
wake_up_interruptible(&crng_init_wait);
kill_fasync(&fasync, SIGIO, POLL_IN);
@@ -934,6 +1285,10 @@ void add_device_randomness(const void *b
@@ -934,6 +1292,10 @@ void add_device_randomness(const void *b
_mix_pool_bytes(&entropy, sizeof(entropy));
_mix_pool_bytes(buf, len);
spin_unlock_irqrestore(&input_pool.lock, flags);
@@ -449,7 +447,7 @@
}
EXPORT_SYMBOL(add_device_randomness);
@@ -1392,7 +1747,7 @@ SYSCALL_DEFINE3(getrandom, char __user *
@@ -1392,7 +1754,7 @@ SYSCALL_DEFINE3(getrandom, char __user *
if ((flags & (GRND_INSECURE | GRND_RANDOM)) == (GRND_INSECURE | GRND_RANDOM))
return -EINVAL;
@@ -458,7 +456,7 @@
if (flags & GRND_NONBLOCK)
return -EAGAIN;
ret = wait_for_random_bytes();
@@ -1408,6 +1763,10 @@ SYSCALL_DEFINE3(getrandom, char __user *
@@ -1408,6 +1770,10 @@ SYSCALL_DEFINE3(getrandom, char __user *
static __poll_t random_poll(struct file *file, poll_table *wait)
{
@@ -469,7 +467,7 @@
poll_wait(file, &crng_init_wait, wait);
return crng_ready() ? EPOLLIN | EPOLLRDNORM : EPOLLOUT | EPOLLWRNORM;
}
@@ -1453,10 +1812,10 @@ static ssize_t urandom_read_iter(struct
@@ -1453,10 +1819,10 @@ static ssize_t urandom_read_iter(struct
* Opportunistically attempt to initialize the RNG on platforms that
* have fast cycle counters, but don't (for now) require it to succeed.
*/
@@ -482,7 +480,7 @@
if (!ratelimit_disable && maxwarn <= 0)
ratelimit_state_inc_miss(&urandom_warning);
else if (ratelimit_disable || __ratelimit(&urandom_warning)) {
@@ -1473,7 +1832,7 @@ static ssize_t random_read_iter(struct k
@@ -1473,7 +1839,7 @@ static ssize_t random_read_iter(struct k
{
int ret;
@@ -491,7 +489,7 @@
((kiocb->ki_flags & (IOCB_NOWAIT | IOCB_NOIO)) ||
(kiocb->ki_filp->f_flags & O_NONBLOCK)))
return -EAGAIN;
@@ -1538,6 +1897,14 @@ static long random_ioctl(struct file *f,
@@ -1538,6 +1904,14 @@ static long random_ioctl(struct file *f,
case RNDRESEEDCRNG:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;

View File

@@ -11270,9 +11270,7 @@ int wolfSSL_FIPS_drbg_reseed(WOLFSSL_DRBG_CTX* ctx, const unsigned char* adin,
{
int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
if (ctx != NULL && ctx->rng != NULL) {
#if ((!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
!defined(HAVE_SELFTEST)) || \
defined(WC_RNG_RBGC_STRATUM_IMMUTABLE)
#ifdef WC_RNG_HAVE_RESEED_NOW
/* OpenSSL FIPS_drbg_reseed semantics: reseed from the module's
* source with adin as additional input. (A credited user-class
* reseed of a conformant instance is refused with

View File

@@ -3963,19 +3963,21 @@ int wolfSSL_RAND_egd(const char* nm)
ret = WOLFSSL_FATAL_ERROR;
}
else {
#if ((!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
!defined(HAVE_SELFTEST)) || \
defined(WC_RNG_RBGC_STRATUM_IMMUTABLE)
#ifdef WC_RNG_HAVE_STIR
/* Caller material carries no entropy credit; mix it as an
* uncredited stir -- credited user-class reseeds of conformant
* instances are refused with WC_RNG_RBGC_STRATUM_IMMUTABLE.
* NOT_READY_E (a credited reseed is due) outranks advisory
* material and is not a failure. */
* A standing reseed or recovery obligation (NEEDS_RECOVERY_E:
* credited reseed due, invalidation latched, or epoch stale;
* NOT_READY_E: the reseed interval tripped within the call)
* outranks advisory material and is not a failure: the next
* generate performs the credited reseed. */
{
int stir_ret = wc_RNG_DRBG_Stir(&globalRNG,
(const byte*) buf, bytes);
if ((stir_ret != 0) &&
(stir_ret != WC_NO_ERR_TRACE(NOT_READY_E)))
(stir_ret != WC_NO_ERR_TRACE(NOT_READY_E)) &&
(stir_ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)))
{
WOLFSSL_MSG("Error with stirring DRBG structure");
ret = WOLFSSL_FATAL_ERROR;
@@ -4246,9 +4248,7 @@ int wolfSSL_RAND_poll(void)
#ifdef HAVE_HASHDRBG
#if ((!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
!defined(HAVE_SELFTEST)) || \
defined(WC_RNG_RBGC_STRATUM_IMMUTABLE)
#ifdef WC_RNG_HAVE_RESEED_NOW
ret = wc_RNG_DRBG_Reseed_Now(&globalRNG, NULL, 0);
if (ret == 0) {
@@ -4259,7 +4259,7 @@ int wolfSSL_RAND_poll(void)
ret = WOLFSSL_FAILURE;
}
#else /* old FIPS and !WC_RNG_RBGC_STRATUM_IMMUTABLE */
#else /* !WC_RNG_HAVE_RESEED_NOW: random.h predates wc_RNG_DRBG_Reseed_Now() */
{
byte entropy[16];
@@ -4284,7 +4284,7 @@ int wolfSSL_RAND_poll(void)
ForceZero(entropy, sizeof(entropy));
}
#endif /* old FIPS or !WC_RNG_RBGC_STRATUM_IMMUTABLE */
#endif /* WC_RNG_HAVE_RESEED_NOW */
#elif defined(HAVE_INTEL_RDRAND)
WOLFSSL_MSG("Not polling with RAND_poll, RDRAND used without "
@@ -4489,15 +4489,15 @@ int wolfSSL_RAND_load_file(const char* fname, long len)
ret = WOLFSSL_FATAL_ERROR;
break;
}
#if ((!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
!defined(HAVE_SELFTEST)) || \
defined(WC_RNG_RBGC_STRATUM_IMMUTABLE)
#ifdef WC_RNG_HAVE_STIR
/* File material carries no entropy credit; mix it as an uncredited
* stir. */
* stir. A standing reseed or recovery obligation (NEEDS_RECOVERY_E,
* NOT_READY_E -- see wolfSSL_RAND_egd()) is not a failure. */
{
int stir_ret = wc_RNG_DRBG_Stir(&globalRNG, buf, (word32)n);
if ((stir_ret != 0) &&
(stir_ret != WC_NO_ERR_TRACE(NOT_READY_E)))
(stir_ret != WC_NO_ERR_TRACE(NOT_READY_E)) &&
(stir_ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)))
{
wc_UnLockMutex(&globalRNGMutex);
WOLFSSL_MSG("RAND_load_file: DRBG stir failed");

View File

@@ -97,6 +97,13 @@ int test_wc_RNG_GenerateBlock_Reseed(void)
return EXPECT_RESULT();
}
#if !defined(HAVE_SELFTEST) && \
(!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0))
#define RESEED_CTR(rng) ((rng)->reseedCtr)
#else
#define RESEED_CTR(rng) (((struct DRBG_internal *)(rng)->drbg)->reseedCtr)
#endif
int test_wc_RNG_ReseedBoundary(void)
{
EXPECT_DECLS;
@@ -111,20 +118,19 @@ int test_wc_RNG_ReseedBoundary(void)
#ifndef NO_SHA256
if (rng.drbgType == WC_DRBG_SHA256) {
struct DRBG_internal* drbg = (struct DRBG_internal*)rng.drbg;
if (drbg != NULL && rng.status == WC_DRBG_OK) {
if (rng.drbg != NULL && rng.status == WC_DRBG_OK) {
#ifdef WORD64_AVAILABLE
word64 startCtr = drbg->reseedCtr;
word64 startCtr = RESEED_CTR(&rng);
#else
word32 startCtr = drbg->reseedCtr;
word32 startCtr = RESEED_CTR(&rng);
#endif
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)), 0);
if (drbg->reseedCtr == startCtr + 1) {
drbg->reseedCtr = WC_RESEED_INTERVAL - 1;
if (RESEED_CTR(&rng) == startCtr + 1) {
RESEED_CTR(&rng) = WC_RESEED_INTERVAL - 1;
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)), 0);
ExpectTrue(drbg->reseedCtr == WC_RESEED_INTERVAL);
ExpectTrue(RESEED_CTR(&rng) == WC_RESEED_INTERVAL);
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)), 0);
ExpectTrue(drbg->reseedCtr == 2);
ExpectTrue(RESEED_CTR(&rng) == 2);
drbgChecked = 1;
}
}
@@ -132,17 +138,15 @@ int test_wc_RNG_ReseedBoundary(void)
#endif
#ifdef WOLFSSL_DRBG_SHA512
if (!drbgChecked && rng.drbgType == WC_DRBG_SHA512) {
struct DRBG_SHA512_internal* drbg =
(struct DRBG_SHA512_internal*)rng.drbg512;
if (drbg != NULL && rng.status == WC_DRBG_OK) {
word64 startCtr = drbg->reseedCtr;
if (rng.drbg512 != NULL && rng.status == WC_DRBG_OK) {
word64 startCtr = RESEED_CTR(&rng);
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)), 0);
if (drbg->reseedCtr == startCtr + 1) {
drbg->reseedCtr = WC_RESEED_INTERVAL - 1;
if (RESEED_CTR(&rng) == startCtr + 1) {
RESEED_CTR(&rng) = WC_RESEED_INTERVAL - 1;
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)), 0);
ExpectTrue(drbg->reseedCtr == WC_RESEED_INTERVAL);
ExpectTrue(RESEED_CTR(&rng) == WC_RESEED_INTERVAL);
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)), 0);
ExpectTrue(drbg->reseedCtr == 2);
ExpectTrue(RESEED_CTR(&rng) == 2);
drbgChecked = 1;
}
}
@@ -1375,7 +1379,9 @@ static int test_random_seedCb_broken(OS_Seed* os, byte* seed, word32 sz)
(void)os;
(void)seed;
(void)sz;
return WC_NO_ERR_TRACE(BAD_FUNC_ARG);
/* ASN_OCSP_CONFIRM_E here is a nonsense error code used to inhibit the
* WC_VERBOSE_RNG message in PollAndReSeed(). */
return WC_NO_ERR_TRACE(ASN_OCSP_CONFIRM_E);
}
#endif
@@ -1407,6 +1413,12 @@ int test_wc_RNG_ReseedVerdict(void)
rng.pid = 0;
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)),
WC_NO_ERR_TRACE(ENTROPY_RT_E));
#ifdef WC_RNG_HAVE_LOCK
/* Saturate the reseed counter to force condemnation on reseed failure. */
RESEED_CTR(&rng) = WC_RESEED_INTERVAL;
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)),
WC_NO_ERR_TRACE(ENTROPY_RT_E));
#endif
/* The failed instance stays failed. */
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)),
WC_NO_ERR_TRACE(RNG_FAILURE_E));
@@ -1428,6 +1440,8 @@ int test_wc_RNG_ReseedVerdict(void)
ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectIntEQ(wc_SetSeed_Cb(test_random_seedCb_broken), 0);
rng.pid = 0;
/* Saturate the reseed counter to force condemnation on reseed failure. */
RESEED_CTR(&rng) = WC_RESEED_INTERVAL;
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, out, sizeof(out)),
WC_NO_ERR_TRACE(RNG_FAILURE_E));
@@ -1655,37 +1669,34 @@ int test_wc_DrbgFeatureCoverage(void)
* that bypass the Hash_DRBG path (e.g. --enable-intelrand) skip it. */
#ifndef NO_SHA256
if (rng.drbgType == WC_DRBG_SHA256) {
struct DRBG_internal* drbg = (struct DRBG_internal*)rng.drbg;
if (drbg != NULL && rng.status == WC_DRBG_OK) {
if (rng.drbg != NULL && rng.status == WC_DRBG_OK) {
#ifdef WORD64_AVAILABLE
word64 startCtr = drbg->reseedCtr;
word64 startCtr = RESEED_CTR(&rng);
#else
word32 startCtr = drbg->reseedCtr;
word32 startCtr = RESEED_CTR(&rng);
#endif
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, big, 32), 0);
if (drbg->reseedCtr == startCtr + 1) {
drbg->reseedCtr = WC_RESEED_INTERVAL - 1;
if (RESEED_CTR(&rng) == startCtr + 1) {
RESEED_CTR(&rng) = WC_RESEED_INTERVAL - 1;
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, big, 32), 0);
ExpectTrue(drbg->reseedCtr == WC_RESEED_INTERVAL);
ExpectTrue(RESEED_CTR(&rng) == WC_RESEED_INTERVAL);
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, big, 32), 0);
ExpectTrue(drbg->reseedCtr == 2);
ExpectTrue(RESEED_CTR(&rng) == 2);
}
}
}
#endif
#ifdef WOLFSSL_DRBG_SHA512
if (rng.drbgType == WC_DRBG_SHA512) {
struct DRBG_SHA512_internal* drbg512 =
(struct DRBG_SHA512_internal*)rng.drbg512;
if (drbg512 != NULL && rng.status == WC_DRBG_OK) {
word64 startCtr = drbg512->reseedCtr;
if (rng.drbg512 != NULL && rng.status == WC_DRBG_OK) {
word64 startCtr = RESEED_CTR(&rng);
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, big, 32), 0);
if (drbg512->reseedCtr == startCtr + 1) {
drbg512->reseedCtr = WC_RESEED_INTERVAL - 1;
if (RESEED_CTR(&rng) == startCtr + 1) {
RESEED_CTR(&rng) = WC_RESEED_INTERVAL - 1;
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, big, 32), 0);
ExpectTrue(drbg512->reseedCtr == WC_RESEED_INTERVAL);
ExpectTrue(RESEED_CTR(&rng) == WC_RESEED_INTERVAL);
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, big, 32), 0);
ExpectTrue(drbg512->reseedCtr == 2);
ExpectTrue(RESEED_CTR(&rng) == 2);
}
}
}

View File

@@ -163,6 +163,7 @@ static int wb_fail = 0;
static void wb_hash_drbg_init_df_chain(void)
{
DRBG_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[48];
byte nonce[16];
word32 i;
@@ -177,7 +178,7 @@ static void wb_hash_drbg_init_df_chain(void)
/* Vector (T, T): unarmed instantiate, in the SAME binary as the two
* rejection vectors below. */
mcdc_fh_disarm();
ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("unarmed Hash_DRBG_Instantiate failed; df-chain skipped");
@@ -204,7 +205,7 @@ static void wb_hash_drbg_init_df_chain(void)
/* Vector (F, -): the first Hash_df fails on its very first primitive
* call, so the && short-circuits and never calls the second one. */
mcdc_fh_arm(1);
ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret == DRBG_SUCCESS) {
WB_NOTE("armed Hash_DRBG_Instantiate unexpectedly succeeded");
@@ -216,7 +217,7 @@ static void wb_hash_drbg_init_df_chain(void)
/* Vector (T, F): the first Hash_df's n1 primitive calls all succeed;
* every call from n1+1 on -- the whole second Hash_df -- fails. */
mcdc_fh_arm(n1 + 1);
ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret == DRBG_SUCCESS) {
WB_NOTE("armed Hash_DRBG_Instantiate unexpectedly succeeded");
@@ -242,6 +243,7 @@ static void wb_hash_drbg_init_df_chain(void)
static void wb_hash512_drbg_init_df_chain(void)
{
DRBG_SHA512_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[48];
byte nonce[16];
word32 i;
@@ -255,7 +257,7 @@ static void wb_hash512_drbg_init_df_chain(void)
/* Vector (T, T). */
mcdc_fh_disarm();
ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("unarmed Hash512_DRBG_Instantiate failed; df-chain skipped");
@@ -278,7 +280,7 @@ static void wb_hash512_drbg_init_df_chain(void)
/* Vector (F, -). */
mcdc_fh_arm(1);
ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret == DRBG_SUCCESS) {
WB_NOTE("armed Hash512_DRBG_Instantiate unexpectedly succeeded");
@@ -289,7 +291,7 @@ static void wb_hash512_drbg_init_df_chain(void)
/* Vector (T, F). */
mcdc_fh_arm(n1 + 1);
ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret == DRBG_SUCCESS) {
WB_NOTE("armed Hash512_DRBG_Instantiate unexpectedly succeeded");
@@ -320,6 +322,7 @@ static void wb_hash512_drbg_init_df_chain(void)
static void wb_hash_drbg_reseed_df_chain(void)
{
DRBG_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[48];
byte nonce[16];
byte seed2[48];
@@ -340,7 +343,7 @@ static void wb_hash_drbg_reseed_df_chain(void)
seed2[i] = (byte)((i * 23u) + 7u);
mcdc_fh_disarm();
ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("unarmed Hash_DRBG_Instantiate failed; reseed chain skipped");
@@ -350,7 +353,7 @@ static void wb_hash_drbg_reseed_df_chain(void)
XMEMCPY(v0, drbg.V, sizeof(v0));
XMEMCPY(c0, drbg.C, sizeof(c0));
rctr0 = (unsigned long long)drbg.reseedCtr;
rctr0 = (unsigned long long)drbg_ctr;
/* Size the FIRST Hash_df of Hash256_DRBG_Reseed: same out size, same
* type byte (drbgReseed), same present/absent operands as the reseed
@@ -369,7 +372,7 @@ static void wb_hash_drbg_reseed_df_chain(void)
/* Vector (F, -): the newV derivation fails on its first primitive. */
mcdc_fh_arm(1);
ret = Hash256_DRBG_Reseed(&drbg, seed2, (word32)sizeof(seed2), NULL, 0);
ret = Hash256_DRBG_Reseed(&drbg, &drbg_ctr, seed2, (word32)sizeof(seed2), NULL, 0);
mcdc_fh_disarm();
if (ret != WC_NO_ERR_TRACE(DRBG_FAILURE)) {
WB_NOTE("armed reseed (F,-) did not return DRBG_FAILURE");
@@ -377,7 +380,7 @@ static void wb_hash_drbg_reseed_df_chain(void)
}
if ((XMEMCMP(drbg.V, v0, sizeof(v0)) != 0) ||
(XMEMCMP(drbg.C, c0, sizeof(c0)) != 0) ||
((unsigned long long)drbg.reseedCtr != rctr0)) {
((unsigned long long)drbg_ctr != rctr0)) {
WB_NOTE("reseed (F,-) mutated DRBG state");
wb_fail = 1;
}
@@ -385,7 +388,7 @@ static void wb_hash_drbg_reseed_df_chain(void)
/* Vector (T, F): the newV derivation's n1 calls all succeed and the newC
* derivation fails -- the seam case. */
mcdc_fh_arm(n1 + 1);
ret = Hash256_DRBG_Reseed(&drbg, seed2, (word32)sizeof(seed2), NULL, 0);
ret = Hash256_DRBG_Reseed(&drbg, &drbg_ctr, seed2, (word32)sizeof(seed2), NULL, 0);
mcdc_fh_disarm();
if (ret != WC_NO_ERR_TRACE(DRBG_FAILURE)) {
WB_NOTE("armed reseed (T,F) did not return DRBG_FAILURE");
@@ -393,20 +396,20 @@ static void wb_hash_drbg_reseed_df_chain(void)
}
if ((XMEMCMP(drbg.V, v0, sizeof(v0)) != 0) ||
(XMEMCMP(drbg.C, c0, sizeof(c0)) != 0) ||
((unsigned long long)drbg.reseedCtr != rctr0)) {
((unsigned long long)drbg_ctr != rctr0)) {
WB_NOTE("reseed (T,F) mutated DRBG state");
wb_fail = 1;
}
/* Retryability: the same reseed, disarmed, succeeds in place, and the
* instance is operational. */
ret = Hash256_DRBG_Reseed(&drbg, seed2, (word32)sizeof(seed2), NULL, 0);
if ((ret != DRBG_SUCCESS) || (drbg.reseedCtr != 1)) {
ret = Hash256_DRBG_Reseed(&drbg, &drbg_ctr, seed2, (word32)sizeof(seed2), NULL, 0);
if ((ret != DRBG_SUCCESS) || (drbg_ctr != 1)) {
WB_NOTE("disarmed reseed retry did not succeed cleanly");
wb_fail = 1;
}
else {
ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0);
ret = Hash_DRBG_Generate(&drbg, &drbg_ctr, out, (word32)sizeof(out), NULL, 0);
if (ret != DRBG_SUCCESS) {
WB_NOTE("generate after retried reseed failed");
wb_fail = 1;
@@ -430,6 +433,7 @@ static void wb_hash_drbg_reseed_df_chain(void)
static void wb_hash512_drbg_reseed_df_chain(void)
{
DRBG_SHA512_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[48];
byte nonce[16];
byte seed2[48];
@@ -450,7 +454,7 @@ static void wb_hash512_drbg_reseed_df_chain(void)
seed2[i] = (byte)((i * 47u) + 13u);
mcdc_fh_disarm();
ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("unarmed Hash512_DRBG_Instantiate failed; "
@@ -461,7 +465,7 @@ static void wb_hash512_drbg_reseed_df_chain(void)
XMEMCPY(v0, drbg.V, sizeof(v0));
XMEMCPY(c0, drbg.C, sizeof(c0));
rctr0 = (unsigned long long)drbg.reseedCtr;
rctr0 = (unsigned long long)drbg_ctr;
/* Size the first Hash512_df of Hash512_DRBG_Reseed. */
mcdc_fh_disarm();
@@ -479,7 +483,7 @@ static void wb_hash512_drbg_reseed_df_chain(void)
/* Vector (F, -). */
mcdc_fh_arm(1);
ret = Hash512_DRBG_Reseed(&drbg, seed2, (word32)sizeof(seed2), NULL, 0);
ret = Hash512_DRBG_Reseed(&drbg, &drbg_ctr, seed2, (word32)sizeof(seed2), NULL, 0);
mcdc_fh_disarm();
if (ret != WC_NO_ERR_TRACE(DRBG_FAILURE)) {
WB_NOTE("armed 512 reseed (F,-) did not return DRBG_FAILURE");
@@ -487,14 +491,14 @@ static void wb_hash512_drbg_reseed_df_chain(void)
}
if ((XMEMCMP(drbg.V, v0, sizeof(v0)) != 0) ||
(XMEMCMP(drbg.C, c0, sizeof(c0)) != 0) ||
((unsigned long long)drbg.reseedCtr != rctr0)) {
((unsigned long long)drbg_ctr != rctr0)) {
WB_NOTE("512 reseed (F,-) mutated DRBG state");
wb_fail = 1;
}
/* Vector (T, F): the seam case. */
mcdc_fh_arm(n1 + 1);
ret = Hash512_DRBG_Reseed(&drbg, seed2, (word32)sizeof(seed2), NULL, 0);
ret = Hash512_DRBG_Reseed(&drbg, &drbg_ctr, seed2, (word32)sizeof(seed2), NULL, 0);
mcdc_fh_disarm();
if (ret != WC_NO_ERR_TRACE(DRBG_FAILURE)) {
WB_NOTE("armed 512 reseed (T,F) did not return DRBG_FAILURE");
@@ -502,19 +506,19 @@ static void wb_hash512_drbg_reseed_df_chain(void)
}
if ((XMEMCMP(drbg.V, v0, sizeof(v0)) != 0) ||
(XMEMCMP(drbg.C, c0, sizeof(c0)) != 0) ||
((unsigned long long)drbg.reseedCtr != rctr0)) {
((unsigned long long)drbg_ctr != rctr0)) {
WB_NOTE("512 reseed (T,F) mutated DRBG state");
wb_fail = 1;
}
/* Retryability. */
ret = Hash512_DRBG_Reseed(&drbg, seed2, (word32)sizeof(seed2), NULL, 0);
if ((ret != DRBG_SUCCESS) || (drbg.reseedCtr != 1)) {
ret = Hash512_DRBG_Reseed(&drbg, &drbg_ctr, seed2, (word32)sizeof(seed2), NULL, 0);
if ((ret != DRBG_SUCCESS) || (drbg_ctr != 1)) {
WB_NOTE("disarmed 512 reseed retry did not succeed cleanly");
wb_fail = 1;
}
else {
ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0);
ret = Hash512_DRBG_Generate(&drbg, &drbg_ctr, out, (word32)sizeof(out), NULL, 0);
if (ret != DRBG_SUCCESS) {
WB_NOTE("generate after retried 512 reseed failed");
wb_fail = 1;
@@ -568,7 +572,9 @@ static void wb_hash512_drbg_reseed_df_chain(void)
static void wb_hash_drbg_generate_atomicity(void)
{
DRBG_internal ctrl;
wc_drbg_reseed_ctr_t ctrl_ctr = 0;
DRBG_internal probe;
wc_drbg_reseed_ctr_t probe_ctr = 0;
wc_Sha256 addsha[1];
byte seed[48];
byte nonce[16];
@@ -596,7 +602,7 @@ static void wb_hash_drbg_generate_atomicity(void)
/* Control: one disarmed generate; its output is the expected block for
* every retry below, and its end state the expected end state. */
mcdc_fh_disarm();
ret = Hash_DRBG_Instantiate(&ctrl, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&ctrl, &ctrl_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("control Hash_DRBG_Instantiate failed; atomicity skipped");
@@ -604,7 +610,7 @@ static void wb_hash_drbg_generate_atomicity(void)
return;
}
mcdc_fh_disarm();
ret = Hash_DRBG_Generate(&ctrl, exp1, (word32)sizeof(exp1),
ret = Hash_DRBG_Generate(&ctrl, &ctrl_ctr, exp1, (word32)sizeof(exp1),
add, (word32)sizeof(add));
t_add = mcdc_fh_seen();
if (ret != DRBG_SUCCESS || t_add <= 0) {
@@ -615,7 +621,7 @@ static void wb_hash_drbg_generate_atomicity(void)
}
/* Probe: identical instantiation, so identical state. */
ret = Hash_DRBG_Instantiate(&probe, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&probe, &probe_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("probe Hash_DRBG_Instantiate failed; atomicity skipped");
@@ -625,7 +631,7 @@ static void wb_hash_drbg_generate_atomicity(void)
}
XMEMCPY(v0, probe.V, sizeof(v0));
XMEMCPY(c0, probe.C, sizeof(c0));
rctr0 = (unsigned long long)probe.reseedCtr;
rctr0 = (unsigned long long)probe_ctr;
/* n_a: the additional-input hash is Update(type) + Update(V) +
* Update(additional) + Final; sizes do not change the call count. */
@@ -665,7 +671,7 @@ static void wb_hash_drbg_generate_atomicity(void)
/* Pre-Hash_gen fault: first primitive of the additional-input hash. */
mcdc_fh_arm(1);
ret = Hash_DRBG_Generate(&probe, out, (word32)sizeof(out),
ret = Hash_DRBG_Generate(&probe, &probe_ctr, out, (word32)sizeof(out),
add, (word32)sizeof(add));
mcdc_fh_disarm();
if (ret != WC_NO_ERR_TRACE(DRBG_FAILURE)) {
@@ -674,14 +680,14 @@ static void wb_hash_drbg_generate_atomicity(void)
}
if ((XMEMCMP(probe.V, v0, sizeof(v0)) != 0) ||
(XMEMCMP(probe.C, c0, sizeof(c0)) != 0) ||
((unsigned long long)probe.reseedCtr != rctr0)) {
((unsigned long long)probe_ctr != rctr0)) {
WB_NOTE("pre-Hash_gen fault mutated DRBG state");
wb_fail = 1;
}
/* Post-Hash_gen fault: first primitive after Hash_gen's last. */
mcdc_fh_arm(n_a + n_gen + 1);
ret = Hash_DRBG_Generate(&probe, out, (word32)sizeof(out),
ret = Hash_DRBG_Generate(&probe, &probe_ctr, out, (word32)sizeof(out),
add, (word32)sizeof(add));
mcdc_fh_disarm();
if (ret != WC_NO_ERR_TRACE(DRBG_FAILURE)) {
@@ -690,13 +696,13 @@ static void wb_hash_drbg_generate_atomicity(void)
}
if ((XMEMCMP(probe.V, v0, sizeof(v0)) != 0) ||
(XMEMCMP(probe.C, c0, sizeof(c0)) != 0) ||
((unsigned long long)probe.reseedCtr != rctr0)) {
((unsigned long long)probe_ctr != rctr0)) {
WB_NOTE("post-Hash_gen fault mutated DRBG state");
wb_fail = 1;
}
/* Retry: byte-identical output, end state equal to the control's. */
ret = Hash_DRBG_Generate(&probe, out2, (word32)sizeof(out2),
ret = Hash_DRBG_Generate(&probe, &probe_ctr, out2, (word32)sizeof(out2),
add, (word32)sizeof(add));
if ((ret != DRBG_SUCCESS) ||
(XMEMCMP(out2, exp1, sizeof(exp1)) != 0)) {
@@ -705,7 +711,7 @@ static void wb_hash_drbg_generate_atomicity(void)
}
if ((XMEMCMP(probe.V, ctrl.V, sizeof(v0)) != 0) ||
(XMEMCMP(probe.C, ctrl.C, sizeof(c0)) != 0) ||
(probe.reseedCtr != ctrl.reseedCtr)) {
(probe_ctr != ctrl_ctr)) {
WB_NOTE("retried generate end-state differs from control");
wb_fail = 1;
}
@@ -731,7 +737,9 @@ static void wb_hash_drbg_generate_atomicity(void)
static void wb_hash512_drbg_generate_atomicity(void)
{
DRBG_SHA512_internal ctrl;
wc_drbg_reseed_ctr_t ctrl_ctr = 0;
DRBG_SHA512_internal probe;
wc_drbg_reseed_ctr_t probe_ctr = 0;
wc_Sha512 addsha[1];
byte seed[48];
byte nonce[16];
@@ -758,7 +766,7 @@ static void wb_hash512_drbg_generate_atomicity(void)
/* Control. */
mcdc_fh_disarm();
ret = Hash512_DRBG_Instantiate(&ctrl, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&ctrl, &ctrl_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("control Hash512_DRBG_Instantiate failed; "
@@ -767,7 +775,7 @@ static void wb_hash512_drbg_generate_atomicity(void)
return;
}
mcdc_fh_disarm();
ret = Hash512_DRBG_Generate(&ctrl, exp1, (word32)sizeof(exp1),
ret = Hash512_DRBG_Generate(&ctrl, &ctrl_ctr, exp1, (word32)sizeof(exp1),
add, (word32)sizeof(add));
t_add = mcdc_fh_seen();
if (ret != DRBG_SUCCESS || t_add <= 0) {
@@ -778,7 +786,7 @@ static void wb_hash512_drbg_generate_atomicity(void)
}
/* Probe. */
ret = Hash512_DRBG_Instantiate(&probe, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&probe, &probe_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("probe Hash512_DRBG_Instantiate failed; atomicity skipped");
@@ -788,7 +796,7 @@ static void wb_hash512_drbg_generate_atomicity(void)
}
XMEMCPY(v0, probe.V, sizeof(v0));
XMEMCPY(c0, probe.C, sizeof(c0));
rctr0 = (unsigned long long)probe.reseedCtr;
rctr0 = (unsigned long long)probe_ctr;
/* n_a. */
ret = wc_InitSha512(addsha);
@@ -828,7 +836,7 @@ static void wb_hash512_drbg_generate_atomicity(void)
/* Pre-Hash512_gen fault. */
mcdc_fh_arm(1);
ret = Hash512_DRBG_Generate(&probe, out, (word32)sizeof(out),
ret = Hash512_DRBG_Generate(&probe, &probe_ctr, out, (word32)sizeof(out),
add, (word32)sizeof(add));
mcdc_fh_disarm();
if (ret != WC_NO_ERR_TRACE(DRBG_FAILURE)) {
@@ -837,14 +845,14 @@ static void wb_hash512_drbg_generate_atomicity(void)
}
if ((XMEMCMP(probe.V, v0, sizeof(v0)) != 0) ||
(XMEMCMP(probe.C, c0, sizeof(c0)) != 0) ||
((unsigned long long)probe.reseedCtr != rctr0)) {
((unsigned long long)probe_ctr != rctr0)) {
WB_NOTE("pre-Hash512_gen fault mutated DRBG state");
wb_fail = 1;
}
/* Post-Hash512_gen fault. */
mcdc_fh_arm(n_a + n_gen + 1);
ret = Hash512_DRBG_Generate(&probe, out, (word32)sizeof(out),
ret = Hash512_DRBG_Generate(&probe, &probe_ctr, out, (word32)sizeof(out),
add, (word32)sizeof(add));
mcdc_fh_disarm();
if (ret != WC_NO_ERR_TRACE(DRBG_FAILURE)) {
@@ -853,13 +861,13 @@ static void wb_hash512_drbg_generate_atomicity(void)
}
if ((XMEMCMP(probe.V, v0, sizeof(v0)) != 0) ||
(XMEMCMP(probe.C, c0, sizeof(c0)) != 0) ||
((unsigned long long)probe.reseedCtr != rctr0)) {
((unsigned long long)probe_ctr != rctr0)) {
WB_NOTE("post-Hash512_gen fault mutated DRBG state");
wb_fail = 1;
}
/* Retry. */
ret = Hash512_DRBG_Generate(&probe, out2, (word32)sizeof(out2),
ret = Hash512_DRBG_Generate(&probe, &probe_ctr, out2, (word32)sizeof(out2),
add, (word32)sizeof(add));
if ((ret != DRBG_SUCCESS) ||
(XMEMCMP(out2, exp1, sizeof(exp1)) != 0)) {
@@ -868,7 +876,7 @@ static void wb_hash512_drbg_generate_atomicity(void)
}
if ((XMEMCMP(probe.V, ctrl.V, sizeof(v0)) != 0) ||
(XMEMCMP(probe.C, ctrl.C, sizeof(c0)) != 0) ||
(probe.reseedCtr != ctrl.reseedCtr)) {
(probe_ctr != ctrl_ctr)) {
WB_NOTE("retried 512 generate end-state differs from control");
wb_fail = 1;
}

View File

@@ -98,6 +98,7 @@ static int wb_fail = 0;
static void wb_hash_gen_outsz(void)
{
DRBG_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[48];
byte nonce[16];
byte out[32];
@@ -112,7 +113,7 @@ static void wb_hash_gen_outsz(void)
nonce[i] = (byte)(i + 2);
}
ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Hash_DRBG_Instantiate setup failed; skip Hash_gen check");
@@ -180,6 +181,7 @@ static void wb_array_add(void)
static void wb_hash512_gen_outsz(void)
{
DRBG_SHA512_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[32];
byte nonce[16];
byte out[32];
@@ -194,7 +196,7 @@ static void wb_hash512_gen_outsz(void)
nonce[i] = (byte)(i + 4);
}
ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Hash512_DRBG_Instantiate setup failed; skip Hash512_gen "
@@ -273,12 +275,13 @@ static void wb_hash_df_multiblock(void)
}
}
/* Hash_DRBG_Generate()'s "drbg->reseedCtr >= WC_RESEED_INTERVAL" decision:
/* Hash_DRBG_Generate()'s "*reseedCtr_ptr >= WC_RESEED_INTERVAL" decision:
* drive the false side (a real generate) and the true side (early
* DRBG_NEED_RESEED return) in the same binary on one instantiated DRBG. */
static void wb_hash_drbg_generate_reseed(void)
{
DRBG_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[48];
byte nonce[16];
byte out[32];
@@ -291,7 +294,7 @@ static void wb_hash_drbg_generate_reseed(void)
for (i = 0; i < (word32)sizeof(nonce); i++)
nonce[i] = (byte)(i + 2);
ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Instantiate failed; skip Hash_DRBG_Generate reseed check");
@@ -299,16 +302,16 @@ static void wb_hash_drbg_generate_reseed(void)
}
/* False side: reseedCtr below the interval -> generate proceeds. */
drbg.reseedCtr = 1;
ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0);
drbg_ctr = 1;
ret = Hash_DRBG_Generate(&drbg, &drbg_ctr, out, (word32)sizeof(out), NULL, 0);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Hash_DRBG_Generate (below interval) failed");
wb_fail = 1;
}
/* True side: reseedCtr at the interval -> early DRBG_NEED_RESEED. */
drbg.reseedCtr = WC_RESEED_INTERVAL;
ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0);
drbg_ctr = WC_RESEED_INTERVAL;
ret = Hash_DRBG_Generate(&drbg, &drbg_ctr, out, (word32)sizeof(out), NULL, 0);
if (ret != DRBG_NEED_RESEED) {
WB_NOTE("Hash_DRBG_Generate did not signal DRBG_NEED_RESEED");
wb_fail = 1;
@@ -428,6 +431,7 @@ static void wb_hash512_df_multiblock(void)
static void wb_hash512_drbg_generate_reseed(void)
{
DRBG_SHA512_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[32];
byte nonce[16];
byte out[32];
@@ -440,22 +444,22 @@ static void wb_hash512_drbg_generate_reseed(void)
for (i = 0; i < (word32)sizeof(nonce); i++)
nonce[i] = (byte)(i + 4);
ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Instantiate512 failed; skip Hash512_DRBG_Generate reseed");
return;
}
drbg.reseedCtr = 1; /* false side */
ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0);
drbg_ctr = 1; /* false side */
ret = Hash512_DRBG_Generate(&drbg, &drbg_ctr, out, (word32)sizeof(out), NULL, 0);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Hash512_DRBG_Generate (below interval) failed");
wb_fail = 1;
}
drbg.reseedCtr = WC_RESEED_INTERVAL; /* true side */
ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0);
drbg_ctr = WC_RESEED_INTERVAL; /* true side */
ret = Hash512_DRBG_Generate(&drbg, &drbg_ctr, out, (word32)sizeof(out), NULL, 0);
if (ret != DRBG_NEED_RESEED) {
WB_NOTE("Hash512_DRBG_Generate did not signal DRBG_NEED_RESEED");
wb_fail = 1;
@@ -539,6 +543,7 @@ static void wb_rng_healthtest512_internal(void)
static void wb_hash_gen_alloc_guard(void)
{
DRBG_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[48];
byte nonce[16];
byte out[32];
@@ -554,7 +559,7 @@ static void wb_hash_gen_alloc_guard(void)
mcdc_fa_install();
/* Setup runs DISARMED so every allocation it needs succeeds. */
ret = Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Instantiate failed; skip Hash_gen alloc guard");
@@ -592,6 +597,7 @@ static void wb_hash_gen_alloc_guard(void)
static void wb_hash512_gen_alloc_guard(void)
{
DRBG_SHA512_internal drbg;
wc_drbg_reseed_ctr_t drbg_ctr = 0;
byte seed[32];
byte nonce[16];
byte out[32];
@@ -606,7 +612,7 @@ static void wb_hash512_gen_alloc_guard(void)
mcdc_fa_install();
ret = Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, &drbg_ctr, seed, (word32)sizeof(seed),
nonce, (word32)sizeof(nonce), NULL, 0, NULL, INVALID_DEVID);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Instantiate512 failed; skip Hash512_gen alloc guard");

View File

@@ -769,6 +769,9 @@ const char* wc_GetErrorString(int error)
case STILL_REFERENCED_E:
return "Object is still referenced (unsafe to deallocate)";
case OBJECT_ORPHANED_E:
return "Object orphaned (can be adopted)";
case HWPUF_INIT_E:
return "HWPUF initialization failed";

File diff suppressed because it is too large Load Diff

View File

@@ -54,6 +54,9 @@
#define WC_DRBG_OK 1
#undef WC_DRBG_FAILED
#define WC_DRBG_FAILED 2
#define wc_RNG_Status(rng) (((rng) == NULL) ? BAD_FUNC_ARG : \
((rng)->status != WC_DRBG_OK) ? \
RNG_FAILURE_E : WC_SUCCESS)
#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */
/* DRBG status and reseed-counter access, and reseed forcing, are via the
@@ -78,8 +81,9 @@
* retry loop or a root reinstantiation, i.e. seed-acquisition timescales.
* Never called with the gate already held (no recursion), and gate holders
* take no locks a spinner can hold, so the wait always resolves. */
static void wc_rng_bank_inst_op_gate_spinenter(struct wc_rng_bank *bank,
WC_ATOMIC_INT_ARG op)
static WC_MAYBE_UNUSED void wc_rng_bank_inst_op_gate_spinenter(
struct wc_rng_bank *bank,
WC_ATOMIC_INT_ARG op)
{
WC_ATOMIC_INT_ARG cur_gate = WC_RNG_BANK_INST_OP_FREE;
int cas_ret;
@@ -117,8 +121,9 @@ WOLFSSL_API int wc_rng_bank_init_nonce(
int ret;
int need_reenable_vec = 0;
wc_static_assert(WC_DRBG_NOT_INIT == 0); /* make sure assumptions are met */
#ifdef WC_RNG_INIT_FLAG_LOCK_REQUIRED
#ifdef WC_RNG_HAVE_LOCK
word32 rng_flags = WC_RNG_INIT_FLAG_LOCK_REQUIRED;
int root_rng_locked = 0;
#else
WC_MAYBE_UNUSED word32 rng_flags = WC_RNG_INIT_FLAG_NONE;
#endif
@@ -151,9 +156,9 @@ 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_RECOVER_AND_PROMOTE_FROM_NEXT_SEED
if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE)
rng_flags |= WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED;
#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED
if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED)
rng_flags |= WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED;
#endif
ctx->heap = heap;
ctx->devId = devId;
@@ -175,7 +180,17 @@ WOLFSSL_API int wc_rng_bank_init_nonce(
/* Note we initialize the root_rng even if ! (flags &
* WC_RNG_BANK_FLAG_RBGC) -- it can be used for other purposes, such as
* pool replenishment, as in the linuxkm entropy daemon. */
ret = wc_rng_bank_root_rng_init(ctx, nonce, nonceSz, perso, persoSz, 0);
ret = wc_rng_bank_root_rng_init(ctx, nonce, nonceSz, perso, persoSz,
#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_INIT_FLAG_LOCK_INITIALLY)
WC_RNG_INIT_FLAG_LOCK_INITIALLY
#else
WC_RNG_INIT_FLAG_NONE
#endif
);
#ifdef WC_RNG_HAVE_LOCK
if (ret == 0)
root_rng_locked = 1;
#endif
}
#endif
@@ -203,16 +218,14 @@ WOLFSSL_API int wc_rng_bank_init_nonce(
WC_RNG_BANK_INST_TO_RNG(rng_inst),
&ctx->root_rng,
(byte *)&rng_inst, sizeof(byte *),
#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)
NULL, 0,
#endif
rng_flags
);
}
else
#endif
{
#ifdef WC_RNG_INIT_FLAG_LOCK_REQUIRED
#ifdef WC_RNG_HAVE_LOCK
ret = wc_InitRngNonce_ex2(
WC_RNG_BANK_INST_TO_RNG(rng_inst),
(byte *)&rng_inst, sizeof(byte *),
@@ -234,22 +247,10 @@ WOLFSSL_API int wc_rng_bank_init_nonce(
if (ret == 0)
break;
/* Several plausible error codes are non-retryable -- fail early
* for these.
*/
switch (ret) {
case WC_NO_ERR_TRACE(BAD_MUTEX_E):
case WC_NO_ERR_TRACE(BAD_FUNC_ARG):
case WC_NO_ERR_TRACE(MEMORY_E):
case WC_NO_ERR_TRACE(NOT_COMPILED_IN):
case WC_NO_ERR_TRACE(MISSING_RNG_E):
case WC_NO_ERR_TRACE(BUFFER_E):
case WC_NO_ERR_TRACE(OPEN_RAN_E):
case WC_NO_ERR_TRACE(FIPS_NOT_ALLOWED_E):
case WC_NO_ERR_TRACE(DRBG_KAT_FIPS_E):
case WC_NO_ERR_TRACE(DRBG_CONT_FIPS_E):
/* The transient codes are retryable -- fail early for the
* rest. */
if (! WC_RNG_ERR_IS_TRANSIENT(ret))
goto out;
}
if (timeout_secs == 0) {
break; /* Retry disabled -- return the real error, not
@@ -285,6 +286,11 @@ WOLFSSL_API int wc_rng_bank_init_nonce(
out:
#ifdef WC_RNG_HAVE_LOCK
if (root_rng_locked)
wc_RNG_lock_put(&ctx->root_rng, 0);
#endif
if (ret != 0)
(void)wc_rng_bank_fini(ctx);
@@ -403,6 +409,10 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) {
(void)wc_rng_bank_inst_lock_read(&ctx->rngs[i], &pre_lock_state);
if (pre_lock_state & WC_RNG_LOCK_HELD)
return BUSY_E;
#ifdef WC_RNG_RBGC_REFCOUNT
if (WC_RNG_RBGC_REFCOUNT(&ctx->rngs[i].rng) > 0)
return STILL_REFERENCED_E;
#endif
}
}
@@ -419,20 +429,6 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) {
return ret;
}
#ifdef WC_RNG_BANK_HAVE_ROOT_RNG
if (wc_RNG_GetStatus(&ctx->root_rng) != WC_DRBG_NOT_INIT) {
int free_ret = wc_FreeRng(&ctx->root_rng);
if (free_ret != 0) {
#ifdef WC_VERBOSE_RNG
WOLFSSL_DEBUG_PRINTF(
"wc_rng_bank_fini(): wc_FreeRng() on root_rng returned "
"error %d.\n", free_ret);
#endif
++rng_free_failed;
}
}
#endif /* WC_RNG_BANK_HAVE_ROOT_RNG */
#ifndef WC_RNG_BANK_STATIC
if (ctx->rngs)
#endif
@@ -471,23 +467,6 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) {
}
for (i = 0; i < ctx->n_rngs; ++i) {
/* Lease-taking teardown, for internal consistency checking --
* structurally uncontended at refcount zero with the held-check
* above passed. The latch dies held in dying memory, per the
* lock-uncleared-on-free contract of wc_FreeRng(). */
if ((wc_rng_bank_inst_lock_get(&ctx->rngs[i], 0) != 0) &&
(wc_rng_bank_inst_lock_get_conditional(&ctx->rngs[i],
WC_RNG_LOCK_ENTROPY_INVALIDATED, 0) != 0))
{
/* can't happen absent corruption; leak, don't crash. */
#ifdef WC_VERBOSE_RNG
WOLFSSL_DEBUG_PRINTF(
"BUG: wc_rng_bank_fini() couldn't take the teardown "
"lease on RNG #%d.\n", i);
#endif
ret = BAD_STATE_E;
continue;
}
{
int free_ret = wc_FreeRng(&ctx->rngs[i].rng);
if (free_ret != 0) {
@@ -496,12 +475,51 @@ WOLFSSL_API int wc_rng_bank_fini(struct wc_rng_bank *ctx) {
"wc_rng_bank_fini(): wc_FreeRng() on RNG #%d returned "
"error %d.\n", i, free_ret);
#endif
/* If any of the RNGs is actively referenced when we try to
* free it, we must summarily abandon the attempt to free
* the bank -- it can be retried again, and proceeding to
* the final deallocation would only induce UB.
*
* Note that the refcount is restored -- the caller can and
* must retry. The free hook has already fired (one-shot):
* until the retry succeeds the bank is alive and
* unregistered, and any invalidation event in that window
* is the caller's to propagate (and if not
* WC_RNG_HAVE_ENTROPY_EPOCH, to deliver in the first
* place). Retry promptly.
*/
if ((free_ret == WC_NO_ERR_TRACE(BUSY_E)) ||
(free_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)))
{
wolfSSL_RefInc2(&ctx->refcount, &new_refcount, &ret);
(void)new_refcount;
(void)ret;
return free_ret;
}
++rng_free_failed;
}
}
}
if (ret == WC_NO_ERR_TRACE(BAD_STATE_E))
return ret;
#ifdef WC_RNG_BANK_HAVE_ROOT_RNG
/* The root is freed after the member instances: members recycled through
* the RBGC rebirth path retain the root as their reseed parent, and each
* member's teardown releases its pin -- freeing the root first would
* report STILL_REFERENCED_E against pins the member frees are about to
* release anyway. A STILL_REFERENCED_E here is therefore an external
* (caller-spawned) child of the root, and the report is honest. */
if (wc_RNG_GetStatus(&ctx->root_rng) != WC_DRBG_NOT_INIT) {
int free_ret = wc_FreeRng(&ctx->root_rng);
if (free_ret != 0) {
#ifdef WC_VERBOSE_RNG
WOLFSSL_DEBUG_PRINTF(
"wc_rng_bank_fini(): wc_FreeRng() on root_rng returned "
"error %d.\n", free_ret);
#endif
++rng_free_failed;
}
}
#endif /* WC_RNG_BANK_HAVE_ROOT_RNG */
#ifndef WC_RNG_BANK_STATIC
XFREE(ctx->rngs, ctx->heap, DYNAMIC_TYPE_RNG);
@@ -1082,7 +1100,7 @@ WOLFSSL_API int wc_rng_bank_checkout(
WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE) &&
(! (flags & WC_RNG_BANK_FLAG_FOR_RECOVERY)))
{
ret = wc_RNG_DRBG_Reseed_Now(
ret = wc_RNG_DRBG_Reseed_Now_Primary(
WC_RNG_BANK_INST_TO_RNG(*rng_inst), NULL, 0);
if (ret != 0) {
(void)wc_rng_bank_inst_lock_put(*rng_inst);
@@ -1379,6 +1397,101 @@ WOLFSSL_API int wc_rng_bank_daemon_release(struct wc_rng_bank *bank,
#endif /* WC_RNG_BANK_HAVE_DAEMON_SUPPORT */
/* Retire and reinstantiate an rng, protected either by WC_RNG.lock (new
* semantics: the lease is taken by try-lock and held across the cycle under
* _PRESERVE_LOCK, so a held root yields BUSY_E rather than a wait) or the
* inst-op gate (legacy semantics, to exclude the entropy invalidation walk's
* dereference of the rng's live DRBG state; the gate exists in every build
* without WC_RNG_HAVE_LOCK, so the gate ops in that arm are unconditional).
* The reinstantiation performs a full seed acquisition. */
int wc_rng_bank_reinit_rng(struct wc_rng_bank *bank,
WC_RNG *rng,
WC_RNG *seedRng,
const byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
word32 flags)
{
int ret;
#ifdef WC_RNG_DEBUG_STATS
struct wc_rng_debug_stats_snapshot s;
int stats_snap_ret;
#endif
if (rng == NULL)
return BAD_FUNC_ARG;
#ifndef WC_RNG_HAVE_RBGC
if (seedRng != NULL)
return NOT_COMPILED_IN;
#endif
#ifdef WC_RNG_HAVE_LOCK
ret = wc_RNG_lock_get_unconditional(rng);
if (ret != 0)
return ret;
#else
wc_rng_bank_inst_op_gate_spinenter(bank, WC_RNG_BANK_INST_OP_ROOT);
#endif
#ifdef WC_RNG_DEBUG_STATS
stats_snap_ret = wc_rng_debug_stats_snap(&s, rng);
#endif
if (wc_RNG_GetStatus(rng) != WC_DRBG_NOT_INIT) {
#ifdef WC_RNG_HAVE_LOCK
ret = wc_FreeRng_PreLocked(rng);
#else
ret = wc_FreeRng(rng);
#endif
if ((ret != 0) && (ret != WC_NO_ERR_TRACE(STILL_REFERENCED_E)))
goto out;
}
#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)
#ifdef WC_RNG_HAVE_RBGC
if (seedRng != NULL) {
ret = wc_InitRngNonceRBGC(rng, seedRng, nonce, nonceSz, perso, persoSz,
flags
| WC_RNG_INIT_FLAG_PRESERVE_LOCK);
}
else
#endif /* WC_RNG_HAVE_RBGC */
{
ret = wc_InitRngNonce_ex2(rng, nonce, nonceSz, perso, persoSz,
bank->heap, bank->devId,
flags
| WC_RNG_INIT_FLAG_PRESERVE_LOCK);
}
#else
(void)perso;
(void)persoSz;
(void)seedRng;
if (flags != 0)
ret = NOT_COMPILED_IN;
else {
ret = wc_InitRngNonce_ex(rng, (byte *)nonce, nonceSz, bank->heap,
bank->devId);
}
#endif
#ifdef WC_RNG_DEBUG_STATS
if (ret == 0) {
if (stats_snap_ret == 0)
wc_rng_debug_stats_restore(&s, rng);
}
#endif
out:
#ifdef WC_RNG_HAVE_LOCK
wc_RNG_lock_put_unconditional(rng);
#else
WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, WC_RNG_BANK_INST_OP_FREE);
#endif
return ret;
}
#ifdef WC_RNG_BANK_HAVE_ROOT_RNG
WOLFSSL_API int wc_rng_bank_root_rng_init(struct wc_rng_bank *bank,
@@ -1396,7 +1509,11 @@ WOLFSSL_API int wc_rng_bank_root_rng_init(struct wc_rng_bank *bank,
return wc_InitRngNonce_ex2(&bank->root_rng,
nonce, nonceSz,
perso, persoSz,
bank->heap, bank->devId, flags);
bank->heap, bank->devId, flags
#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_INIT_FLAG_LOCK_REQUIRED)
| WC_RNG_INIT_FLAG_LOCK_REQUIRED
#endif
);
#else
(void)perso;
(void)persoSz;
@@ -1416,41 +1533,21 @@ WOLFSSL_API WC_RNG *wc_rng_bank_root_rng_get(struct wc_rng_bank *bank)
return &bank->root_rng;
}
/* Retire and reinstantiate the bank's root_rng under the inst-op gate,
* excluding the entropy invalidation walk (which dereferences the root's live
* DRBG state) and instance ops for the span of the transition. The recovery
* path of last resort for a condemned root -- see the linuxkm entropy
* daemon's post-invalidation arm. Task context only: the gate is
* spin-acquired, and the reinstantiation performs a full seed acquisition.
* WC_RNG_BANK_HAVE_ROOT_RNG implies WC_RNG_BANK_HAVE_INST_OP_GATE, so the
* gate ops here are unconditional -- if the derivations ever diverge, this
* breaks loudly rather than compiling unprotected. */
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)
{
int ret;
if (bank == NULL)
return BAD_FUNC_ARG;
wc_rng_bank_inst_op_gate_spinenter(bank, WC_RNG_BANK_INST_OP_ROOT);
if (wc_RNG_GetStatus(&bank->root_rng) != WC_DRBG_NOT_INIT) {
ret = wc_FreeRng(&bank->root_rng);
if (ret != 0)
goto out;
}
ret = wc_rng_bank_root_rng_init(bank, nonce, nonceSz, perso, persoSz,
flags);
out:
WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, WC_RNG_BANK_INST_OP_FREE);
return ret;
/* same flags posture as wc_rng_bank_root_rng_init(): the root is
* _LOCK_REQUIRED. */
return wc_rng_bank_reinit_rng(bank, &bank->root_rng, NULL, nonce, nonceSz,
perso, persoSz, flags
#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_INIT_FLAG_LOCK_REQUIRED)
| WC_RNG_INIT_FLAG_LOCK_REQUIRED
#endif
);
}
#endif /* WC_RNG_BANK_HAVE_ROOT_RNG */
@@ -1666,7 +1763,9 @@ static int wc_rng_bank_next_seed_generate_local(
WC_RNG *root)
{
int ret;
#ifdef WC_RNG_BANK_HAVE_INST_OP_GATE
WC_ATOMIC_INT_ARG expected = WC_RNG_BANK_INST_OP_FREE;
#endif
if (bank == NULL)
return BAD_FUNC_ARG;
@@ -1677,18 +1776,28 @@ static int wc_rng_bank_next_seed_generate_local(
if (inst_offset >= bank->n_rngs)
return BAD_FUNC_ARG;
if (root != NULL) {
#ifndef WC_RNG_HAVE_RBGC
if (root != NULL)
return NOT_COMPILED_IN;
#endif
#ifdef WC_RNG_HAVE_LOCK
ret = wc_RNG_lock_get(root, 0);
if (ret != 0)
return ret;
#endif
}
#ifdef WC_RNG_BANK_HAVE_INST_OP_GATE
if (! wolfSSL_Atomic_Int_CompareExchange(&bank->inst_op_gate, &expected,
WC_RNG_BANK_INST_OP_DAEMON))
{
/* A whole-instance operation (reinit) is in progress somewhere in
* the bank -- skip this turn. */
return BUSY_E;
ret = BUSY_E;
goto out;
}
#endif
#ifdef WC_RNG_HAVE_RBGC
if (root != NULL) {
@@ -1702,8 +1811,38 @@ static int wc_rng_bank_next_seed_generate_local(
WC_RNG_BANK_INST_TO_RNG(&bank->rngs[inst_offset]), n);
}
#ifdef WC_RNG_BANK_HAVE_INST_OP_GATE
WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, WC_RNG_BANK_INST_OP_FREE);
out:
#endif
#ifdef WC_RNG_HAVE_LOCK
if (root != NULL) {
int put_ret = wc_RNG_lock_put(root, 0);
if (put_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) {
/* An invalidation landed on root under our hold, so whatever was
* banked above was drawn from pre-event material. The walk's
* own purge of the member does not necessarily cover it: if the
* walk visited the member before our claim, the fill that
* followed published a READY bank into an already-latched,
* already-stamped instance, which the cheap inline recovery in
* wc_RNG_lock_get() would then redeem as post-event. Purge
* explicitly (idempotent against the walk's delivery) and report
* the event over the fill's result, as the chain arm of
* wc_RNG_DRBG_NextSeedGenerate() does. */
#ifdef HAVE_HASHDRBG
(void)wc_rng_bank_inst_invalidate_entropy(
&bank->rngs[inst_offset]);
#endif
return put_ret;
}
else if (put_ret != 0) {
return put_ret;
}
}
#endif
return ret;
}
@@ -1750,9 +1889,9 @@ WOLFSSL_API int wc_rng_bank_inst_reinit(
struct wc_rng_debug_stats_snapshot s;
int stats_snap_ret;
#endif
#ifdef WC_RNG_INIT_FLAG_LOCK_REQUIRED
#ifdef WC_RNG_HAVE_LOCK
word32 rng_flags = WC_RNG_INIT_FLAG_LOCK_REQUIRED |
WC_RNG_INIT_FLAG_LOCK_INITIALLY;
WC_RNG_INIT_FLAG_PRESERVE_LOCK;
#else
WC_MAYBE_UNUSED word32 rng_flags = WC_RNG_INIT_FLAG_NONE;
#endif
@@ -1767,9 +1906,9 @@ WOLFSSL_API int wc_rng_bank_inst_reinit(
if (ret < 0)
return ret;
#ifdef WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED
if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE)
rng_flags |= WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED;
#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED
if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED)
rng_flags |= WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED;
#endif
/* No DRBG-NULL rejection here. wc_FreeRng() below nulls the DRBG, so an
@@ -1808,10 +1947,10 @@ WOLFSSL_API int wc_rng_bank_inst_reinit(
/* Pre-read the held latch's annotation bits -- an owner-context read
* of a live object (the constructor itself never reads its target).
* The reinit is declared _LOCK_INITIALLY, so the instance is
* invariantly locked across the free/reinstantiate cycle, and the
* annotations (including a sticky WC_RNG_LOCK_REQUIRED, when the
* instance carries one) are re-asserted below on success. */
* The reinit is declared _PRESERVE_LOCK, so the instance stays locked
* across the free/reinstantiate cycle, and the annotations (including
* a sticky WC_RNG_LOCK_REQUIRED, when the instance carries one) are
* re-asserted below on success. */
ret = wc_rng_bank_inst_lock_read(rng_inst, &cur_lock);
if (ret < 0) {
#ifdef WC_RNG_BANK_HAVE_INST_OP_GATE
@@ -1825,18 +1964,82 @@ WOLFSSL_API int wc_rng_bank_inst_reinit(
wc_rng_debug_stats_snap(&s, WC_RNG_BANK_INST_TO_RNG(rng_inst));
#endif
wc_FreeRng(&rng_inst->rng);
/* The free is the first mutating step, and it can refuse: BUSY_E means
* a pool writer holds the ring's claim (the entropy daemon mid-top-off),
* and nothing was touched. Surface it exactly as a held inst-op gate is
* surfaced -- skip this attempt, instance intact and still out of
* service, a later checkout retries. STILL_REFERENCED_E (RBGC children
* pin the shell) is a completed teardown and reinstantiation proceeds. */
#ifdef WC_RNG_HAVE_LOCK
ret = wc_FreeRng_PreLocked(&rng_inst->rng);
#else
ret = wc_FreeRng(&rng_inst->rng);
#endif
if ((ret != 0) && (ret != WC_NO_ERR_TRACE(STILL_REFERENCED_E))) {
#ifdef WC_RNG_BANK_HAVE_INST_OP_GATE
WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, WC_RNG_BANK_INST_OP_FREE);
#endif
return ret;
}
for (;;) {
#ifdef WC_RNG_INIT_FLAG_LOCK_REQUIRED
ret = wc_InitRngNonce_ex2(WC_RNG_BANK_INST_TO_RNG(rng_inst),
(byte *)&rng_inst, sizeof(byte *),
NULL, 0, bank->heap, devId, rng_flags);
#else
ret = wc_InitRngNonce_ex(WC_RNG_BANK_INST_TO_RNG(rng_inst),
(byte *)&rng_inst, sizeof(byte *),
bank->heap, devId);
#ifdef WC_RNG_HAVE_RBGC
if (bank->flags & WC_RNG_BANK_FLAG_RBGC) {
/* An RBGC bank's members are chain children of its root, at
* instantiation and at every reinstantiation alike --
* wc_rng_bank_init() has no primary arm for them, and neither
* does this. A member reborn from the primary source would
* silently change the bank's posture: a stratum-0,
* primary-seeded instance among chain children, permanently so
* under WC_RNG_RBGC_STRATUM_IMMUTABLE. So a root that cannot
* serve right now is a reason to retry, never to substitute:
* out of service (NOT_READY_E / RNG_FAILURE_E, the daemon's
* root recovery is one pass away), latched (NEEDS_RECOVERY_E
* from the lease, likewise), or held (BUSY_E). None of those
* is in the non-retryable list below, so a _CAN_WAIT caller
* relaxes and retries until root is back or the timeout
* expires, and an atomic-context caller returns at once with
* the member still out of service -- checkout diverts, and
* whatever detected the need for this reinit detects the need
* for the next one. */
ret = wc_RNG_Status(&bank->root_rng);
if (ret == 0) {
#ifdef WC_RNG_HAVE_LOCK
ret = wc_RNG_lock_get(&bank->root_rng, 0);
if (ret == 0) {
#endif
ret = wc_InitRngNonceRBGC(
WC_RNG_BANK_INST_TO_RNG(rng_inst), &bank->root_rng,
(byte *)&rng_inst, sizeof(byte *),
NULL, 0, rng_flags);
#ifdef WC_RNG_HAVE_LOCK
/* A NEEDS_RECOVERY_E from the release means the
* invalidation walk latched root under our hold, so the
* child's seed predates the event. Covered without
* action here: in epoch builds the child's stamp is
* stale and its first generate recovers; otherwise the
* walk is root-first, so having latched root after our
* draw it latches the child next -- after the child's
* entry discharge, so the latch survives instantiation
* and the child's first generate recovers. */
(void)wc_RNG_lock_put(&bank->root_rng, 0);
}
#endif
}
}
else
#endif
{
#ifdef WC_RNG_HAVE_LOCK
ret = wc_InitRngNonce_ex2(WC_RNG_BANK_INST_TO_RNG(rng_inst),
(byte *)&rng_inst, sizeof(byte *),
NULL, 0, bank->heap, devId, rng_flags);
#else
ret = wc_InitRngNonce_ex(WC_RNG_BANK_INST_TO_RNG(rng_inst),
(byte *)&rng_inst, sizeof(byte *),
bank->heap, devId);
#endif
}
if (ret == 0) {
if (cur_lock != 0) {
@@ -1857,21 +2060,8 @@ WOLFSSL_API int wc_rng_bank_inst_reinit(
*/
WC_RELAX_LONG_LOOP();
/* Several plausible error codes are non-retryable -- fail early for
* these rather than reattempting until the timeout. Same list as
* wc_rng_bank_init().
*/
switch (ret) {
case WC_NO_ERR_TRACE(BAD_MUTEX_E):
case WC_NO_ERR_TRACE(BAD_FUNC_ARG):
case WC_NO_ERR_TRACE(MEMORY_E):
case WC_NO_ERR_TRACE(NOT_COMPILED_IN):
case WC_NO_ERR_TRACE(MISSING_RNG_E):
case WC_NO_ERR_TRACE(BUFFER_E):
case WC_NO_ERR_TRACE(OPEN_RAN_E):
case WC_NO_ERR_TRACE(FIPS_NOT_ALLOWED_E):
case WC_NO_ERR_TRACE(DRBG_KAT_FIPS_E):
case WC_NO_ERR_TRACE(DRBG_CONT_FIPS_E):
/* The transient codes are retryable -- fail early for the rest. */
if (! WC_RNG_ERR_IS_TRANSIENT(ret)) {
#ifdef WC_VERBOSE_RNG
if (! (bank->flags & WC_RNG_BANK_FLAG_QUIET))
WOLFSSL_DEBUG_PRINTF(
@@ -1929,8 +2119,13 @@ out:
* away from such an instance when the caller allows failover, and the
* seed/reseed walks refuse it.
*/
if (ret != 0)
if (ret != 0) {
#ifdef WC_RNG_HAVE_LOCK
(void)wc_FreeRng_PreLocked(WC_RNG_BANK_INST_TO_RNG(rng_inst));
#else
(void)wc_FreeRng(WC_RNG_BANK_INST_TO_RNG(rng_inst));
#endif
}
#ifdef WC_RNG_BANK_HAVE_INST_OP_GATE
WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, WC_RNG_BANK_INST_OP_FREE);
@@ -1953,7 +2148,7 @@ WOLFSSL_API int wc_rng_bank_recover_inst(
(flags & ~(word32)(WC_RNG_BANK_FLAG_CAN_WAIT |
WC_RNG_BANK_FLAG_AFFINITY_LOCK |
WC_RNG_BANK_FLAG_RBGC |
WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE)))
WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED)))
{
return BAD_FUNC_ARG;
}
@@ -2059,18 +2254,16 @@ static int rng_bank_spawn(
{
word32 child_init_flags = WC_RNG_INIT_FLAG_NONE;
#ifdef WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED
if (flags & WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE)
#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED
if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED)
child_init_flags |=
WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED;
WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED;
#endif
if (leaf_stack != NULL) {
ret = wc_InitRngNonceRBGC(leaf_stack,
WC_RNG_BANK_INST_TO_RNG(rng_inst),
nonce, nonceSz,
#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)
perso, persoSz,
#endif
child_init_flags
);
}
@@ -2473,44 +2666,50 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank,
}
}
#else /* WC_RNG_HAVE_LOCK */
#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)
(void)nonce;
(void)nonceSz;
#ifdef WC_RNG_HAVE_RBGC
if (flags & WC_RNG_BANK_FLAG_RBGC) {
ret = wc_RNG_DRBG_ReseedRBGC(
WC_RNG_BANK_INST_TO_RNG(drbg), &bank->root_rng);
/* The root is _LOCK_REQUIRED: a chain draw needs its
* lease, as in wc_rng_bank_inst_reinit(). A root that
* cannot serve right now -- held (BUSY_E), latched
* (NEEDS_RECOVERY_E) -- is a reason to retry below,
* never to substitute a primary reseed. */
ret = wc_RNG_lock_get(&bank->root_rng, 0);
if (ret == 0) {
#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)
ret = wc_RNG_DRBG_ReseedRBGC(
WC_RNG_BANK_INST_TO_RNG(drbg), &bank->root_rng);
#else
ret = wc_RNG_DRBG_ReseedRBGC(
WC_RNG_BANK_INST_TO_RNG(drbg), &bank->root_rng,
NULL, 0);
#endif
/* A NEEDS_RECOVERY_E from the release means the walk
* latched root under our hold; the member's own
* latch, set by the same walk after root's, stands,
* so this reseed's verdict is the one to keep. */
(void)wc_RNG_lock_put(&bank->root_rng, 0);
}
}
else
#endif
{
#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)
(void)nonce;
(void)nonceSz;
ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg),
NULL, 0);
}
#else
#ifdef WC_RNG_HAVE_RBGC
if (flags & WC_RNG_BANK_FLAG_RBGC) {
ret = wc_RNG_DRBG_ReseedRBGC(
WC_RNG_BANK_INST_TO_RNG(drbg), &bank->root_rng,
NULL, 0);
}
else
#endif
{
ret = wc_RNG_DRBG_Reseed_Now(WC_RNG_BANK_INST_TO_RNG(drbg),
nonce, nonceSz);
}
#endif
}
#endif /* WC_RNG_HAVE_LOCK */
if (ret == 0) {
break;
}
if ((timeout_secs == 0) ||
(! (flags & WC_RNG_BANK_FLAG_CAN_WAIT)))
{
if (timeout_secs == 0)
break;
}
if (timeout_secs > 0) {
ts2 = XTIME(0);
if (ts2 - ts1 > timeout_secs) {
@@ -2535,10 +2734,15 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank,
(void)wc_RNG_DRBG_ScheduleReseed(WC_RNG_BANK_INST_TO_RNG(drbg));
}
#ifdef WC_VERBOSE_RNG
if ((ret != 0) && (ret != WC_NO_ERR_TRACE(WC_TIMEOUT_E)))
/* transient codes are one pass away from resolving on their own
* -- the instance is marked due below -- so they're not errors. */
if ((ret != 0) && (ret != WC_NO_ERR_TRACE(WC_TIMEOUT_E)) &&
!WC_RNG_ERR_IS_TRANSIENT(ret))
{
WOLFSSL_DEBUG_PRINTF(
"ERROR: wc_rng_bank_reseed() wc_RNG_DRBG_Reseed_Now() "
"for DRBG #%d returned %d.", n, ret);
}
#endif
(void)wc_rng_bank_checkin(bank, &drbg);
if ((ret == WC_NO_ERR_TRACE(WC_TIMEOUT_E)) ||
@@ -2546,8 +2750,10 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank,
{
goto out;
}
/* reseed the rest of the bank, keeping the first error seen for
* the return value */
/* Reseed the rest of the bank, keeping the first per-instance
* failure for the return value: the walk completes and every
* failed instance is marked due, but the caller learns that not
* all of them were reseeded here and now. */
if ((ret != 0) && (first_ret == 0))
first_ret = ret;
@@ -2571,7 +2777,11 @@ WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank,
out:
if (first_ret != 0)
/* A code that aborted the walk (WC_TIMEOUT_E, INTERRUPTED_E, a checkout
* failure) outranks a per-instance failure the walk carried on past:
* the former left instances unvisited and not even marked due, which
* the caller must hear about first. */
if ((ret == 0) && (first_ret != 0))
ret = first_ret;
#ifdef WC_RNG_BANK_DEFAULT_SUPPORT
@@ -2631,15 +2841,18 @@ WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank,
#endif
#ifdef WC_RNG_BANK_HAVE_ROOT_RNG
{
if (wc_RNG_GetStatus(&bank->root_rng) != WC_DRBG_NOT_INIT) {
#if !defined(WC_RNG_HAVE_LOCK)
ret = wc_RNG_DRBG_ScheduleReseed(&bank->root_rng);
#else
ret = wc_RNG_invalidate_entropy(&bank->root_rng);
#endif
}
}
#ifdef WC_RNG_HAVE_LOCK
/* Unconditional, like the member arm: a root mid-reinit
* (wc_rng_bank_root_rng_reinit()) is NOT_INIT with its lock word live
* under _PRESERVE_LOCK, and wc_RNG_invalidate_entropy() marks that word
* so the event latches the reborn root rather than being lost. */
ret = wc_RNG_invalidate_entropy(&bank->root_rng);
#else
/* The gate held above excludes a reinit, so NOT_INIT here means an
* uninstantiated root: nothing to schedule. */
if (wc_RNG_GetStatus(&bank->root_rng) != WC_DRBG_NOT_INIT)
ret = wc_RNG_DRBG_ScheduleReseed(&bank->root_rng);
#endif
#endif
/* Best-effort-complete: an error on one instance must not leave the
@@ -3140,10 +3353,12 @@ static int wc_rng_bank_inst_recovery_exit(
wolfSSL_Atomic_Uint_CompareExchange, &inst->lock, cur_lock,
(ret == 0)
? (cur_lock & ~(WC_RNG_LOCK_ENTROPY_INVALIDATED |
WC_RNG_LOCK_ENTROPY_RECOVERING))
WC_RNG_LOCK_ENTROPY_RECOVERING |
WC_RNG_LOCK_ORPHANED))
: (cur_lock & ~WC_RNG_LOCK_ENTROPY_RECOVERING),
cas_ret);
} WC_CAS_WITH_RETRY_END;
(void)cas_ret; /* the forever loop exits only on success. */
return ret;
}
@@ -3272,10 +3487,6 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_reseed_rbgc(
#ifdef WC_RNG_HAVE_RBGC
/* Note, no perso/persoSz in backported wc_InitRngNonceRBGC() */
#define wc_InitRngRBGC(leaf, root, flags) \
wc_InitRngNonceRBGC(leaf, root, NULL, 0, flags)
WOLFSSL_TEST_VIS int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root, word32 flags) {
int ret;
if ((leaf == NULL) || (root == NULL))
@@ -3283,7 +3494,7 @@ WOLFSSL_TEST_VIS int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root, word32 flag
*leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG);
if (*leaf == NULL)
return MEMORY_E;
ret = wc_InitRngNonceRBGC(*leaf, root, NULL, 0, flags);
ret = wc_InitRngNonceRBGC(*leaf, root, NULL, 0, NULL, 0, flags);
if (ret != 0) {
XFREE(*leaf, root->heap, DYNAMIC_TYPE_RNG);
*leaf = NULL;
@@ -3304,7 +3515,7 @@ WOLFSSL_TEST_VIS int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root,
*leaf = (WC_RNG*)XMALLOC(sizeof(WC_RNG), root->heap, DYNAMIC_TYPE_RNG);
if (*leaf == NULL)
return MEMORY_E;
ret = wc_InitRngNonceRBGC(*leaf, root, nonce, nonceSz, flags);
ret = wc_InitRngNonceRBGC(*leaf, root, nonce, nonceSz, NULL, 0, flags);
if (ret != 0) {
XFREE(*leaf, root->heap, DYNAMIC_TYPE_RNG);
*leaf = NULL;

View File

@@ -1249,6 +1249,10 @@ int wolfCrypt_Cleanup(void)
#endif
#endif /* HAVE_ECC */
#ifdef WC_RNG_HAVE_GLOBAL_FALLBACK_RNG
(void)wc_RNG_global_fallback_free();
#endif
#if defined(OPENSSL_EXTRA) || defined(DEBUG_WOLFSSL_VERBOSE)
{
int ret2 = wc_LoggingCleanup();

File diff suppressed because it is too large Load Diff

View File

@@ -65,6 +65,15 @@
#if defined(WC_TEST_RNG_HOLD) && defined(WC_RNG_LOCK_ATFORK)
#define WC_TEST_RNG_AUTOFORK
#endif
/* fork() across a held WC_RNG.lock word: needs the word, a pid to compare,
* and a process model. No threads: the forking thread's own lease is the
* orphan. */
#if defined(WC_RNG_HAVE_LOCK) && defined(HAVE_HASHDRBG) && \
defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID) && \
!defined(__STRICT_ANSI__) && \
(defined(__unix__) || defined(__linux__) || defined(__APPLE__))
#define WC_TEST_RNG_LOCKFORK
#endif
#ifdef HAVE_STACK_SIZE
THREAD_RETURN WOLFSSL_THREAD wolfcrypt_test(void* args);
@@ -147,6 +156,7 @@ wc_static_assert(-(long)MIN_CODE_E < 0x7ffL);
*/
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t macro_test(void);
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t error_test(void);
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t flags_test(void);
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t octets_test(void);
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t base64_test(void);
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t base16_test(void);
@@ -283,7 +293,6 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t srp_test(void);
#endif
#ifndef WC_NO_RNG
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void);
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_flag_abi_test(void);
#ifdef WC_TEST_RNG_AUTOLOCK
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_thread_test(void);
#endif
@@ -298,6 +307,8 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_rbgc_test(void);
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_lifecycle_test(void);
#endif /* WC_NO_RNG */
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void);
#if defined(USE_CERT_BUFFERS_2048) && \

View File

@@ -358,15 +358,16 @@ enum wolfCrypt_ErrorCodes {
UNEXPECTED_STATE_E = -1034, /* Object has unexpected state */
STILL_REFERENCED_E = -1035, /* Object is still referenced (unsafe to
* deallocate) */
HWPUF_INIT_E = -1036, /* HWPUF initialization failed */
HWPUF_ENROLL_E = -1037, /* HWPUF enrollment failed */
HWPUF_START_E = -1038, /* HWPUF start failed */
HWPUF_GENERATE_KEY_E = -1039, /* HWPUF generate key failed */
HWPUF_GET_KEY_E = -1040, /* HWPUF get key failed */
HWPUF_ZEROIZE_E = -1041, /* HWPUF zeroize failed */
OBJECT_ORPHANED_E = -1036, /* Object orphaned (can be adopted) */
HWPUF_INIT_E = -1037, /* HWPUF initialization failed */
HWPUF_ENROLL_E = -1038, /* HWPUF enrollment failed */
HWPUF_START_E = -1039, /* HWPUF start failed */
HWPUF_GENERATE_KEY_E = -1040, /* HWPUF generate key failed */
HWPUF_GET_KEY_E = -1041, /* HWPUF get key failed */
HWPUF_ZEROIZE_E = -1042, /* HWPUF zeroize failed */
WC_SPAN2_LAST_E = -1041, /* Update to indicate last used error code */
WC_LAST_E = -1041, /* the last code used either here or in
WC_SPAN2_LAST_E = -1042, /* Update to indicate last used error code */
WC_LAST_E = -1042, /* the last code used either here or in
* error-ssl.h */
WC_SPAN2_MIN_CODE_E = -1999, /* Last usable code in span 2 */

View File

@@ -183,6 +183,16 @@
#undef WC_HAVE_RNG_BANKREF
#endif
#if defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_HAVE_RBGC)
#define WC_RNG_HAVE_GLOBAL_FALLBACK_RNG
#endif
#if defined(HAVE_FIPS) && defined(WC_RNG_HAVE_RBGC) && \
!defined(WC_RNG_HAVE_LOCK)
/* FIPS SP 800-90C RBGC lineage persistence depends on the lock facility. */
#error HAVE_FIPS with WC_RNG_WANT_RBGC requires WC_RNG_WANT_LOCK.
#endif
/***** End setup for RNG extra features *****/
/* Maximum generate block length */
@@ -380,6 +390,8 @@ struct OS_Seed {
/* Setting the default to 1. */
#define ENTROPY_SCALE_FACTOR (1)
#endif
/* tells test.c the seed-callback known answers apply */
#define WC_DRBG_ENTROPY_SCALE_FACTOR_DEFAULTED
#endif /* !ENTROPY_SCALE_FACTOR */
/* SEED_BLOCK_SZ is unprefixed for backward compat. */
@@ -397,6 +409,7 @@ struct OS_Seed {
/* Setting the default to 4. */
#define SEED_BLOCK_SZ 4
#endif
#define WC_DRBG_SEED_BLOCK_SZ_DEFAULTED
#endif
#define WC_DRBG_SEED_BLOCK_SZ SEED_BLOCK_SZ
@@ -443,11 +456,9 @@ struct OS_Seed {
#define RNG_HEALTH_TEST_CHECK_SIZE_SHA512 (WC_SHA512_DIGEST_SIZE * 4)
#endif
#define WC_DRBG_SEED_BUF_SIZE (WC_DRBG_SEED_SZ + WC_DRBG_SEED_BLOCK_SZ)
#ifdef WC_RNG_HAVE_NEXT_SEED
/* Length of the banked next seed: identical byte accounting to other
* source-fed (re)seeds in the module (gather SEED_SZ + SEED_BLOCK_SZ, apply
* the block-offset remainder). */
#define WC_DRBG_NEXT_SEED_LEN (WC_DRBG_SEED_SZ + WC_DRBG_SEED_BLOCK_SZ)
#define WC_DRBG_NEXT_SEED_LEN WC_DRBG_SEED_BUF_SIZE
#endif
#ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED
@@ -461,16 +472,8 @@ struct OS_Seed {
#ifndef NO_SHA256
struct DRBG_internal {
wc_drbg_reseed_ctr_t reseedCtr;
byte V[DRBG_SEED_LEN];
byte C[DRBG_SEED_LEN];
#ifdef WC_RNG_HAVE_NEXT_SEED
byte nextSeed[WC_DRBG_NEXT_SEED_LEN];
WC_DRBG_nextSeedLen_t nextSeedLen;
#ifdef WC_RNG_HAVE_RBGC
int nextSeedRBGCStratum;
#endif
#endif
void* heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
int devId;
@@ -479,22 +482,15 @@ struct DRBG_internal {
wc_Sha256 sha256;
byte seed_scratch[DRBG_SEED_LEN * 2];
byte digest_scratch[WC_SHA256_DIGEST_SIZE];
byte newSeed_buf[WC_DRBG_SEED_BUF_SIZE];
#endif
};
#endif /* !NO_SHA256 */
#ifdef WOLFSSL_DRBG_SHA512
struct DRBG_SHA512_internal {
wc_drbg_reseed_ctr_t reseedCtr;
byte V[DRBG_SHA512_SEED_LEN];
byte C[DRBG_SHA512_SEED_LEN];
#ifdef WC_RNG_HAVE_NEXT_SEED
byte nextSeed[WC_DRBG_NEXT_SEED_LEN];
WC_DRBG_nextSeedLen_t nextSeedLen;
#ifdef WC_RNG_HAVE_RBGC
int nextSeedRBGCStratum;
#endif
#endif
void* heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
int devId;
@@ -503,6 +499,7 @@ struct DRBG_SHA512_internal {
wc_Sha512 sha512;
byte seed_scratch[DRBG_SHA512_SEED_LEN * 2];
byte digest_scratch[WC_SHA512_DIGEST_SIZE];
byte newSeed_buf[WC_DRBG_SEED_BUF_SIZE];
#endif
};
#endif /* WOLFSSL_DRBG_SHA512 */
@@ -524,11 +521,23 @@ enum wc_RngHealthState {
WC_DRBG_CONT_FAILED = 3
};
#define WC_RNG_FLAG_NONE 0
#define WC_RNG_FLAG_RBGC_NEXT_SEED (1U << 0)
#define WC_RNG_FLAG_FULL_MUTEX (1U << 1)
#define WC_RNG_FLAG_BANKREF (1U << 2)
#define WC_RNG_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3)
#define WC_RNG_FLAG_NONE 0
#define WC_RNG_FLAG_RBGC_NEXT_SEED (1U << 0)
#define WC_RNG_FLAG_BANKREF (1U << 1)
#define WC_RNG_FLAG_FULL_MUTEX (1U << 2)
#define WC_RNG_FLAG_UPGRADE_FROM_NEXT_SEED (1U << 3)
#define WC_RNG_FLAG_FAIL_FAST (1U << 4)
#define WC_RNG_FLAG_NO_RBGC_PARENT (1U << 5)
#define WC_RNG_FLAG_ONLY_PRIMARY_SEED (1U << 6)
#define WC_RNG_FLAG_NO_PRIMARY_SEED (1U << 7)
/* Bits a caller may change after init, via wc_RNG_{Set,Clear}Flags(). The
* remainder are structural (set by init or by the module) and refused. */
#define WC_RNG_FLAGS_SETTABLE (WC_RNG_FLAG_FAIL_FAST | \
WC_RNG_FLAG_ONLY_PRIMARY_SEED | \
WC_RNG_FLAG_NO_PRIMARY_SEED)
#define WC_RNG_FLAGS_CLEARABLE (WC_RNG_FLAG_FAIL_FAST | \
WC_RNG_FLAG_ONLY_PRIMARY_SEED)
#ifndef WC_RNG_RBGC_USER_SEED_STRATUM
#define WC_RNG_RBGC_USER_SEED_STRATUM 65536
@@ -555,12 +564,21 @@ wc_static_assert(WC_RNG_RBGC_USER_SEED_STRATUM >= 256);
#endif
#endif
#if ((defined(WOLFSSL_ATOMIC_OPS) && !defined(SINGLE_THREADED)) || \
defined(WC_RNG_WANT_ENTROPY_EPOCH)) && \
!defined(WC_RNG_NO_ENTROPY_EPOCH)
#define WC_RNG_HAVE_ENTROPY_EPOCH
#endif
/* RNG context */
struct WC_RNG {
struct OS_Seed seed;
void* heap;
byte status;
word32 flags;
#ifdef WC_RNG_HAVE_ENTROPY_EPOCH
WC_ATOMIC_UINT_ARG entropy_epoch;
#endif
#ifdef WC_RNG_DEBUG_STATS
wc_rng_debug_counter_t _stats_total_bytes_requested;
wc_rng_debug_counter_t _stats_total_bytes_produced;
@@ -575,9 +593,13 @@ struct WC_RNG {
wc_rng_debug_counter_t _stats_RBGC_bytes_produced;
wc_rng_debug_counter_t _stats_RBGC_reseeds;
#endif
#endif
#endif /* WC_RNG_HAVE_RBGC */
#ifdef WC_RNG_HAVE_LOCK
WC_RNG_lock_t lock;
#ifdef WC_RNG_HAVE_RBGC
wolfSSL_Atomic_Uint RBGC_refcount;
struct WC_RNG *RBGC_parent;
#endif /* WC_RNG_HAVE_LOCK */
#endif
/* Both lock facilities use this one mutex, so it is declared once here.
* The fork handler build holds a wc_ForkLock instead and needs no mutex. */
@@ -597,10 +619,14 @@ struct WC_RNG {
void *free_hook_arg;
#endif
#ifdef WC_RNG_HAVE_POOL
byte* pool;
byte* pool; /* heap ring; lifecycle arbitrated by poolHead */
word16 poolSize;
WC_RNG_pool_pos_t poolHead; /* written by writer/purger only, CAS */
WC_RNG_pool_pos_t poolTail; /* written by reader only, plain. */
WC_RNG_pool_pos_t poolHead; /* {state:2, epoch:14, pos:16} -- written by
* writer/purger/allocator/teardown, CAS.
* Survives _InitRng()'s wipe (atomically
* set to the unallocated sentinel). */
WC_RNG_pool_pos_t poolTail; /* {0, epoch, pos} written by reader only,
* plain. */
#ifdef WC_RNG_DEBUG_STATS
wc_rng_debug_counter_t _stats_pool_bytes_produced;
wc_rng_debug_counter_t _stats_pool_bytes_missed;
@@ -621,21 +647,36 @@ struct WC_RNG {
#endif
#endif
#ifdef HAVE_HASHDRBG
/* Common members for DRBG cores, here for safe lock-free access. */
byte drbgType; /* WC_DRBG_SHA256 or WC_DRBG_SHA512 */
wc_drbg_reseed_ctr_t reseedCtr; /* SP 800-90A reseed budget */
#ifdef WC_RNG_HAVE_NEXT_SEED
byte *nextSeed;
#if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY)
byte nextSeed_data[WC_DRBG_SEED_BUF_SIZE];
#endif
WC_DRBG_nextSeedLen_t nextSeedLen;
#ifdef WC_RNG_HAVE_RBGC
int nextSeedRBGCStratum;
#endif
#endif
#endif /* HAVE_HASHDRBG */
#if defined(HAVE_HASHDRBG) || defined(WC_HAVE_RNG_BANKREF)
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
union {
#endif
#endif
#ifdef WC_HAVE_RNG_BANKREF
struct wc_rng_bank *bankref;
#endif
#ifdef HAVE_HASHDRBG
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
#if defined(HAVE_HASHDRBG) && !defined(NO_SHA256)
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
struct {
#endif
#ifndef NO_SHA256
#endif
/* SHA-256 Hash-based Deterministic Random Bit Generator */
struct DRBG* drbg;
#if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY)
@@ -646,12 +687,15 @@ struct WC_RNG {
struct DRBG_internal *drbg_scratch;
byte *health_check_scratch;
#endif
#endif /* !NO_SHA256 */
#ifdef WOLFSSL_SMALL_STACK_CACHE
/* Seed buffer for PollAndReSeed -- shared by both DRBG types */
byte *newSeed_buf;
#endif
#ifdef WOLFSSL_DRBG_SHA512
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
};
#endif
#endif /* HAVE_HASHDRBG && !NO_SHA256 */
#if defined(HAVE_HASHDRBG) && defined(WOLFSSL_DRBG_SHA512)
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
struct {
#endif
/* SHA-512 Hash-based Deterministic Random Bit Generator */
struct DRBG_SHA512* drbg512;
#if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY)
@@ -662,16 +706,13 @@ struct WC_RNG {
struct DRBG_SHA512_internal *drbg512_scratch;
byte *health_check_scratch_512;
#endif
#endif /* WOLFSSL_DRBG_SHA512 */
byte drbgType; /* WC_DRBG_SHA256 or WC_DRBG_SHA512 */
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
};
#endif
#endif /* HAVE_HASHDRBG */
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
#endif
#endif /* HAVE_HASHDRBG && WOLFSSL_DRBG_SHA512 */
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
};
#endif
#endif
#endif /* HAVE_HASHDRBG || WC_HAVE_RNG_BANKREF */
@@ -695,6 +736,8 @@ struct WC_RNG {
#endif
};
#define WC_RNG_STATIC_INITIALIZER WC_STATIC_STRUCT_INITIALIZER
#endif /* NO FIPS or have FIPS v2*/
/* NO_OLD_RNGNAME removes RNG struct name to prevent possible type conflicts,
@@ -810,18 +853,18 @@ WOLFSSL_API int wc_NoiseSrc_SelfTest(wc_NoiseSrc* src);
WOLFSSL_API int wc_FreeNetRandom(void);
#endif /* HAVE_WNR */
#ifndef WC_NO_RNG
/* Note, wc_rng_new*, wc_InitRng*, wc_InitRng_BankRef, wc_FreeRng and
* wc_rng_free do return unlocked instances, unless a flag-recognizing variant
* is used and WC_RNG_INIT_FLAG_LOCK_INITIALLY is passed. */
WOLFSSL_ABI WOLFSSL_API WC_RNG* wc_rng_new(byte* nonce, word32 nonceSz,
void* heap);
WOLFSSL_API int wc_rng_new_ex(WC_RNG **rng, byte* nonce, word32 nonceSz,
void* heap, int devId);
/* wc_rng_new*, wc_InitRng*, wc_InitRng_BankRef, wc_FreeRng and wc_rng_free
* do not take the instance lock: no other thread may use the instance across
* them. */
WOLFSSL_ABI WOLFSSL_API void wc_rng_free(WC_RNG* rng);
#ifndef WC_NO_RNG
WOLFSSL_ABI WOLFSSL_API int wc_InitRng(WC_RNG* rng);
WOLFSSL_API int wc_InitRng_ex(WC_RNG* rng, void* heap, int devId);
#if FIPS_VERSION3_GE(7,0,0) && defined(HAVE_HASHDRBG) && \
@@ -838,16 +881,26 @@ WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz)
#define WC_RNG_INIT_FLAG_LOCK_REQUIRED (1U << 0)
#define WC_RNG_INIT_FLAG_LOCK_INITIALLY (1U << 1)
#define WC_RNG_INIT_FLAG_USE_FULL_MUTEX (1U << 2)
/* At each generate, if a banked next seed is READY, consume it when the
* instance is flagged _ENTROPY_INVALIDATED (recovery; any provenance), or
* when the instance is chain-backed and the banked seed is primary
* (promotion). For externally-refreshed long-lived RNGs, e.g. the kernel
* module's registered RBGC leaves. */
#define WC_RNG_INIT_FLAG_RECOVER_AND_PROMOTE_FROM_NEXT_SEED (1U << 3)
/* Force the per-call lock on or off for this one instance, whatever the
* build default is. Asking for one the build has not got is an error. */
#define WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED (1U << 3)
#define WC_RNG_INIT_FLAG_USE_AUTO_LOCK (1U << 4)
#define WC_RNG_INIT_FLAG_NO_AUTO_LOCK (1U << 5)
#define WC_RNG_INIT_FLAG_PRESERVE_LOCK (1U << 6)
#define WC_RNG_INIT_FLAG_NO_PRIMARY_SEED (1U << 7)
#define WC_RNG_INIT_FLAG_TEST_USER_SEED (1U << 8)
#define WC_RNG_INIT_FLAG_FAIL_FAST (1U << 9)
/* Codes from the seed source or the instance that mean "not now", not
* "not ever": a caller that can wait may retry the instantiation, reseed or
* recovery that returned one. wc_InitRng*() passes these through from the
* source rather than folding them into RNG_FAILURE_E, and rng_bank.c keys
* its retry loops on the same set. */
#define WC_RNG_ERR_IS_TRANSIENT(e) \
(((e) == WC_NO_ERR_TRACE(BUSY_E)) || \
((e) == WC_NO_ERR_TRACE(NOT_READY_E)) || \
((e) == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) || \
((e) == WC_NO_ERR_TRACE(RAN_BLOCK_E)) || \
((e) == WC_NO_ERR_TRACE(ENTROPY_RT_E)) || \
((e) == WC_NO_ERR_TRACE(ENTROPY_APT_E)))
WOLFSSL_API int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId,
word32 flags);
@@ -861,9 +914,32 @@ WOLFSSL_API int wc_InitRngNonce_UserSeed(WC_RNG* rng,
const byte* nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
void* heap, int devId, word32 flags);
#ifdef WC_RNG_HAVE_GLOBAL_FALLBACK_RNG
WOLFSSL_API int wc_RNG_global_fallback_init(const byte* nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
void* heap, int devId, word32 flags);
WOLFSSL_API int wc_RNG_global_fallback_init_user_seed(const byte* seed, word32 seedSz,
const byte* nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
void* heap, int devId, word32 flags);
WOLFSSL_API int wc_RNG_global_fallback_get(WC_RNG **rng);
WOLFSSL_API int wc_RNG_global_fallback_free(void);
#endif
WOLFSSL_API int wc_RNG_Status(const WC_RNG *rng);
WOLFSSL_ABI WOLFSSL_API int wc_RNG_GenerateBlock(WC_RNG* rng, byte* output, word32 sz);
WOLFSSL_API int wc_RNG_GenerateByte(WC_RNG* rng, byte* b);
/* Note, when WC_RNG_HAVE_LOCK, wc_FreeRng() takes the instance lock itself, so
* it mustn't be called with the lock already held (use wc_FreeRng_PreLocked()
* for that). A STILL_REFERENCED_E return has persistent side effects:
* sensitive state is wiped, but the object stays pinned by its RBGC children
* and must outlive them. */
WOLFSSL_API int wc_FreeRng(WC_RNG* rng);
#ifdef WC_RNG_HAVE_LOCK
/* Note, caller must hold the instance lock, which is retained on return. */
WOLFSSL_API int wc_FreeRng_PreLocked(WC_RNG* rng);
#endif
#else
#include <wolfssl/wolfcrypt/error-crypt.h>
#define wc_InitRng(rng) NOT_COMPILED_IN
@@ -894,20 +970,35 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng);
WOLFSSL_API int wc_RNG_GetStatus(const WC_RNG* rng);
WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng);
/* Post-init flag policy: only bits in WC_RNG_FLAGS_SETTABLE /
* WC_RNG_FLAGS_CLEARABLE, only with exclusive ownership (SINGLE_THREADED, or
* WC_RNG_HAVE_LOCK with the instance _LOCK_REQUIRED and the lease held). */
WOLFSSL_API int wc_RNG_SetFlags(WC_RNG* rng, word32 flags);
WOLFSSL_API int wc_RNG_ClearFlags(WC_RNG* rng, word32 flags);
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);
/* 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
* on FIPS version arithmetic or on WC_RNG_RBGC_STRATUM_IMMUTABLE. */
#define WC_RNG_HAVE_STIR
WOLFSSL_API int wc_RNG_DRBG_Stir(WC_RNG* rng,
const byte* seed,
word32 seedSz);
WOLFSSL_API int wc_RNG_DRBG_Stir_Nonce(
WC_RNG* rng, const byte* seed, word32 seedSz,
const byte *nonce, word32 nonceSz);
#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_Primary(WC_RNG* rng,
const byte* nonce, word32 nonceSz);
WOLFSSL_API int wc_RNG_TestSeed(const byte* seed, word32 seedSz);
#ifdef WC_RNG_HAVE_RBGC
@@ -915,8 +1006,15 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng);
#ifdef WC_RNG_HAVE_NEXT_SEED
WOLFSSL_API int wc_RNG_DRBG_GetNextSeedRBGCStratum(const WC_RNG* rng);
#endif
#ifdef WC_RNG_HAVE_LOCK
/* get a racy snapshot of the refcount, e.g. to pre-screen deallocation
* eligibility. */
#define WC_RNG_RBGC_REFCOUNT(rng) WOLFSSL_ATOMIC_LOAD((rng)->RBGC_refcount)
#endif
#endif /* WC_RNG_HAVE_RBGC */
WOLFSSL_API int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng,
/* The reseed counter is treated as potentially sensitive internal state --
* only internal users are allowed. */
WOLFSSL_LOCAL int wc_RNG_DRBG_GetReseedCtr(const WC_RNG* rng,
wc_drbg_reseed_ctr_t* reseedCtr);
WOLFSSL_API int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng);
@@ -1063,14 +1161,16 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng);
#define WC_DRBG_NEXT_SEED_READY (-2)
#define WC_DRBG_NEXT_SEED_CONSUMING (-3)
#define WC_DRBG_NEXT_SEED_PURGED (-4)
#define WC_DRBG_NEXT_SEED_TORNDOWN (-5)
WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n);
WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate_Primary(WC_RNG* rng, word32 n);
#ifdef WC_RNG_HAVE_RBGC
WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate_RBGC(WC_RNG* rng,
WC_RNG *root,
word32 n);
#endif
WOLFSSL_API int wc_RNG_DRBG_NextSeedCurrent(WC_RNG* rng,
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,
@@ -1088,29 +1188,46 @@ WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng);
#define WC_RNG_LOCK_REQUIRED (1U<<1)
#define WC_RNG_LOCK_ENTROPY_INVALIDATED (1U<<2)
#define WC_RNG_LOCK_ENTROPY_RECOVERING (1U<<3)
/* The lease was held across a fork() by a holder absent in this process:
* set by the first post-fork contender (wc_RNG_lock_get() and kin report
* OBJECT_ORPHANED_E), sticky until the instance's owner adopts the lease
* (wc_FreeRng_PreLocked(), then wc_InitRng_ex2() with
* WC_RNG_INIT_FLAG_PRESERVE_LOCK) or a surviving holder releases or
* recovers it, either of which proves the lease live after all. */
#define WC_RNG_LOCK_ORPHANED (1U<<4)
/* consumers' annotation bits start here (see e.g. rng_bank.h) */
#define WC_RNG_LOCK_EXTRA_SHIFT 4U
#define WC_RNG_LOCK_EXTRA_SHIFT 5U
WOLFSSL_API int wc_RNG_lock_get(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits);
WOLFSSL_API int wc_RNG_lock_get_conditional(WC_RNG* rng,
WC_RNG_lock_arg_t expected_extra_bits,
WC_RNG_lock_arg_t want_extra_bits);
WOLFSSL_API int wc_RNG_lock_get_conditional(
WC_RNG* rng,
WC_RNG_lock_arg_t expected_extra_bits,
WC_RNG_lock_arg_t want_extra_bits);
WOLFSSL_LOCAL int wc_RNG_lock_get_unconditional(WC_RNG* rng);
WOLFSSL_LOCAL void wc_RNG_lock_put_unconditional(WC_RNG* rng);
WOLFSSL_API int wc_RNG_lock_put(WC_RNG* rng, WC_RNG_lock_arg_t extra_bits);
WOLFSSL_API int wc_RNG_lock_put_conditional(WC_RNG* rng,
WC_RNG_lock_arg_t expected_extra_bits,
WC_RNG_lock_arg_t want_extra_bits);
WOLFSSL_API int wc_RNG_lock_read(WC_RNG* rng, WC_RNG_lock_arg_t* state);
WOLFSSL_API int wc_RNG_lock_put_conditional(
WC_RNG* rng,
WC_RNG_lock_arg_t expected_extra_bits,
WC_RNG_lock_arg_t want_extra_bits);
WOLFSSL_API int wc_RNG_lock_read(const WC_RNG* rng,
WC_RNG_lock_arg_t* state);
WOLFSSL_API int wc_RNG_lock_set_extra(WC_RNG* rng,
WC_RNG_lock_arg_t extra_bits);
WOLFSSL_API int wc_RNG_lock_add_extra(WC_RNG* rng,
WC_RNG_lock_arg_t extra_bits);
WOLFSSL_API int wc_RNG_lock_clear_extra(WC_RNG* rng,
WC_RNG_lock_arg_t extra_bits);
#ifdef HAVE_HASHDRBG
WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng);
#endif
#endif /* WC_RNG_HAVE_LOCK */
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);
#endif
#ifdef WC_RNG_HAVE_FREE_HOOK
/* Register a callback fired by wc_FreeRng() immediately before state
* destruction, e.g. to unlink the object from an external registry.
@@ -1128,7 +1245,7 @@ WOLFSSL_API int wc_RNG_register_free_hook(WC_RNG* rng,
WOLFSSL_API int wc_RNG_Pool_Collect2(WC_RNG* rng_dest, WC_RNG* rng_src,
word32 n);
WOLFSSL_API int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n);
WOLFSSL_API int wc_RNG_Pool_Current(WC_RNG* rng, word32* n);
WOLFSSL_API int wc_RNG_Pool_Current(const WC_RNG* rng, word32* n);
#endif /* WC_RNG_HAVE_POOL */
#ifdef WC_RNG_DEBUG_STATS

View File

@@ -51,12 +51,9 @@
#endif
/* The inst-op gate serializes whole-object transitions -- instance and root
* free/reinstantiate cycles -- against lockless traversals of the same
* objects (the entropy daemon's banking, entropy invalidation walks).
* Present whenever either side of such a race can be compiled in: the
* next-seed banker, or any root_rng (whose transitions the gate brackets).
* free/reinstantiate cycles -- when WC_RNG_HAVE_LOCK is unavailable to support it.
*/
#if defined(WC_RNG_BANK_HAVE_ROOT_RNG) || defined(WC_RNG_HAVE_NEXT_SEED)
#ifndef WC_RNG_HAVE_LOCK
#define WC_RNG_BANK_HAVE_INST_OP_GATE
#endif
@@ -77,18 +74,31 @@
#define WC_RNG_BANK_FLAG_RBGC (1U << 13)
#define WC_RNG_BANK_FLAG_DEFAULT_BANK (1U << 14)
#define WC_RNG_BANK_FLAG_PREDICTION_RESISTANCE (1U << 15)
#define WC_RNG_BANK_FLAG_AUTO_RECOVER_AND_PROMOTE (1U << 16)
#define WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED (1U << 16)
#ifdef WC_RNG_HAVE_LOCK
wc_static_assert(WC_RNG_LOCK_EXTRA_SHIFT == 4U);
#else /* !WC_RNG_HAVE_LOCK */
/* Definitions for backward-compat / WC_RNG_NO_LOCK */
#ifndef WC_RNG_ERR_IS_TRANSIENT
/* mirrors random.h, for legacy boundaries that predate it */
#define WC_RNG_ERR_IS_TRANSIENT(e) \
(((e) == WC_NO_ERR_TRACE(BUSY_E)) || \
((e) == WC_NO_ERR_TRACE(NOT_READY_E)) || \
((e) == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) || \
((e) == WC_NO_ERR_TRACE(RAN_BLOCK_E)) || \
((e) == WC_NO_ERR_TRACE(ENTROPY_RT_E)) || \
((e) == WC_NO_ERR_TRACE(ENTROPY_APT_E)))
#endif
#ifndef WC_RNG_HAVE_LOCK
/* Definitions for backward-compat / WC_RNG_NO_LOCK, mirroring random.h's
* base layout. A fragmentary backport of the in-boundary WC_RNG.lock
* fails to compile against these one way or the other, which is the
* intended tripwire. */
#define WC_RNG_LOCK_FREE 0
#define WC_RNG_LOCK_HELD (1U<<0)
#define WC_RNG_LOCK_REQUIRED (1U<<1)
#define WC_RNG_LOCK_ENTROPY_INVALIDATED (1U<<2)
#define WC_RNG_LOCK_ENTROPY_RECOVERING (1U<<3)
#define WC_RNG_LOCK_EXTRA_SHIFT 4U
#define WC_RNG_LOCK_ORPHANED (1U<<4)
#define WC_RNG_LOCK_EXTRA_SHIFT 5U
#ifdef WOLFSSL_NO_ATOMICS
typedef word32 WC_RNG_lock_t;
typedef word32 WC_RNG_lock_arg_t;
@@ -117,9 +127,9 @@ typedef int (*wc_rng_bank_free_hook_cb_t)(const struct wc_rng_bank *bank,
#define WC_RNG_BANK_INST_FLAG_ALREADY_WARNED (1U << 0)
struct wc_rng_bank_inst {
WC_RNG rng;
#ifdef WC_RNG_HAVE_LOCK
/* the exclusivity latch lives in WC_RNG.lock (wc_RNG_lock_*()) --
* in-FIPS-boundary, module-enforced. */
/* the exclusivity latch lives in rng.lock */
#else
#ifdef WOLFSSL_NO_ATOMICS
word32 lock;
@@ -128,10 +138,15 @@ struct wc_rng_bank_inst {
#endif
#endif
struct wc_rng_bank *bank;
WC_RNG rng;
volatile word32 flags;
};
wc_static_assert(WC_OFFSETOF(struct wc_rng_bank_inst, rng) == 0);
#define WC_RNG_BANK_INST_TO_RNG(rng_inst) ((WC_RNG *)(rng_inst))
#define WC_RNG_BANK_OFFSET_TO_RNG(bank, n) \
((((int)(n) >= 0) && ((int)(n) < (int)(bank)->n_rngs)) ? \
WC_RNG_BANK_INST_TO_RNG(&(bank)->rngs[n]) : NULL)
#if defined(WOLFSSL_NO_MALLOC) && defined(NO_WOLFSSL_MEMORY) && \
!defined(WC_RNG_BANK_STATIC)
#define WC_RNG_BANK_STATIC
@@ -302,6 +317,14 @@ WOLFSSL_API int wc_rng_bank_next_seed_generate_rbgc(
#endif
#endif
WOLFSSL_LOCAL int wc_rng_bank_reinit_rng(
struct wc_rng_bank *bank,
WC_RNG *rng,
WC_RNG *seedRng,
const byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
word32 flags);
WOLFSSL_API int wc_rng_bank_inst_reinit(
struct wc_rng_bank *bank,
struct wc_rng_bank_inst *rng_inst,
@@ -395,12 +418,6 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng);
#endif
#endif /* WC_HAVE_RNG_BANKREF */
#define WC_RNG_BANK_INST_TO_RNG(rng_inst) \
((rng_inst) ? (&(rng_inst)->rng) : NULL)
#define WC_RNG_BANK_OFFSET_TO_RNG(bank, n) \
((((int)(n) >= 0) && ((int)(n) < (int)(bank)->n_rngs)) ? \
WC_RNG_BANK_INST_TO_RNG(&(bank)->rngs[n]) : NULL)
#ifdef WC_RNG_HAVE_LOCK
/* Trivial shims to native lock facility in WC_RNG */
static WC_INLINE int wc_rng_bank_inst_lock_get(
@@ -507,9 +524,27 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng);
sizeof(((struct DRBG_internal *)NULL)->reseedCtr));
#endif
/* no perso/persoSz in backported wc_InitRngNonceRBGC() */
#undef wc_InitRngNonceRBGC
#define wc_InitRngNonceRBGC(leaf,root,nonce,nonceSz,perso,persoSz,flags) \
((void)(perso), (void)(persoSz),\
wc_InitRngNonceRBGC_fips(leaf, root, nonce, nonceSz, flags))
#define wc_InitRngRBGC(leaf, root, flags) \
wc_InitRngNonceRBGC(leaf, root, NULL, 0, flags)
wc_InitRngNonceRBGC(leaf, root, NULL, 0, NULL, 0, flags)
#define wc_RNG_DRBG_Reseed_Now_Primary(rng, nonce, nonceSz) \
wc_RNG_DRBG_Reseed_Now(rng, nonce, nonceSz)
WOLFSSL_TEST_VIS int wc_RNG_GetStatus(const WC_RNG* rng);
#if FIPS_VERSION3_EQ(5,2,4)
/* The 5.2.4 boundary carries the backported RNG services, and the shims
* below complete the API surface that consumers outside the boundary
* (src/ssl.c, src/ssl_crypto.c) gate on, so the capability markers are
* defined here for it just as random.h defines them on the current
* boundary. The other legacy boundaries keep their legacy arms: the
* backports are pinned to 5.2.4, and a 5.2.5+ that carried them forward
* would widen this gate then. */
#define WC_RNG_HAVE_STIR
#define WC_RNG_HAVE_RESEED_NOW
#endif
WOLFSSL_TEST_VIS int wc_RNG_DRBG_Stir(WC_RNG* rng, const byte* seed, word32 seedSz);
WOLFSSL_TEST_VIS int wc_RNG_DRBG_Present(const WC_RNG* rng);
WOLFSSL_TEST_VIS int wc_InitRngNonceRBGC_New(WC_RNG** leaf, WC_RNG* root,