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

Merge pull request #11435 from douzzer/20260907-RNG-extras

20260907-RNG-extras
This commit is contained in:
JacobBarthelmeh
2026-09-17 13:38:00 -06:00
committed by GitHub
29 changed files with 14813 additions and 1546 deletions

View File

@@ -27,7 +27,7 @@ jobs:
check_filenames: true
check_hidden: true
# Add comma separated list of words that occur multiple times that should be ignored (sorted alphabetically, case sensitive)
ignore_words_list: adin,ameba,aNULL,brunch,carryIn,chainG,ciph,cLen,cliKs,cna,dout,FPR,fpr,haveA,inCreated,inOut,inout,larg,LEAPYEAR,Merget,optionA,parm,parms,repid,rIn,userA,ser,siz,te,Te,HSI,failT,toLen,vor,
ignore_words_list: adin,ameba,aNULL,brunch,carryIn,chainG,ciph,cLen,cliKs,cna,dout,FPR,fpr,haveA,inCreated,inOut,inout,larg,LEAPYEAR,Merget,optionA,parm,parms,repid,rIn,userA,ser,siz,te,Te,HSI,failT,toLen,vor,checkin
# The exclude_file contains lines of code that should be ignored. This is useful for individual lines which have non-words that can safely be ignored.
exclude_file: '.codespellexcludelines'
# To skip files entirely from being processed, add it to the following list:

View File

@@ -63,6 +63,7 @@ CIOCGSESSINFO
CLOCK_MONOTONIC
CMSIS_OS2_H_
COMPONENT_WOLFSSL
CONFIG_ACPI
CONFIG_ARCH_CHIP_STM32F746ZG
CONFIG_ARCH_CHIP_STM32H743ZI
CONFIG_ARCH_CHIP_STM32L552ZE
@@ -155,6 +156,7 @@ CONFIG_NET_SOCKETS_SOCKOPT_TLS
CONFIG_NEWLIB_LIBC
CONFIG_NEWLIB_NANO_FORMAT
CONFIG_PICOLIBC
CONFIG_PM_SLEEP
CONFIG_POSIX_API
CONFIG_POSIX_THREADS
CONFIG_PREEMPT_COUNT
@@ -789,6 +791,7 @@ WC_HASH_CUSTOM_MAX_BLOCK_SIZE
WC_HASH_CUSTOM_MAX_DIGEST_SIZE
WC_HASH_CUSTOM_MIN_DIGEST_SIZE
WC_INIT_ERROR_WHEN_CONTENDED
WC_LINUXKM_NO_ENTROPY_DAEMON
WC_LINUXKM_NO_USE_HEAP_WRAPPERS
WC_LINUXKM_SVR_NO_BATCHING
WC_MLDSA_NO_ASM
@@ -803,8 +806,22 @@ WC_PKCS12_PBKDF_USING_MP_API
WC_PROTECT_ENCRYPTED_MEM
WC_PUF_HELPER_COMPACT
WC_PUF_SHA3
WC_RNG_BANK_NO_DAEMON_SUPPORT
WC_RNG_BANK_NO_DEFAULT_SUPPORT
WC_RNG_BLOCKING
WC_RNG_NO_FREE_HOOK
WC_RNG_NO_LOCK
WC_RNG_NO_LOCK_FULL_MUTEX
WC_RNG_NO_NEXT_SEED
WC_RNG_NO_POOL
WC_RNG_NO_RBGC
WC_RNG_NO_RBGC_RESEED
WC_RNG_WANT_FREE_HOOK
WC_RNG_WANT_LOCK
WC_RNG_WANT_LOCK_FULL_MUTEX
WC_RNG_WANT_NEXT_SEED
WC_RNG_WANT_POOL
WC_RNG_WANT_RBGC
WC_RSA_NONBLOCK_TIME
WC_RSA_NO_FERMAT_CHECK
WC_RTL8735B_NO_DERIVE_CACHE

View File

@@ -1703,6 +1703,7 @@ then
test "$enable_base16" = "" && enable_base16=yes
test "$enable_ssh" = "" && test "$enable_hmac" != "no" && enable_ssh=yes
test "$enable_rng_bank" = "" && enable_rng_bank=yes
test "$enable_rng_extras" = "" && enable_rng_extras=yes
test "$enable_dh" = "" && enable_dh=yes
test "$enable_defaultdhparams" = "" && enable_defaultdhparams=yes
@@ -1920,6 +1921,8 @@ then
test "$enable_supportedcurves" = "" && enable_supportedcurves=yes
fi
test "$enable_rng" != "no" && test "$enable_rng_bank" = "" && enable_rng_bank=yes
test "$enable_rng" != "no" && test "$enable_rng_extras" = "" && enable_rng_extras=yes
if test "$ENABLED_FIPS" = "no" || test "$HAVE_FIPS_VERSION" -ge 6
then
test "$enable_aes" != "no" && test "$enable_aescfb" = "" && enable_aescfb=yes
@@ -2791,6 +2794,20 @@ then
AM_CFLAGS="$AM_CFLAGS -DWC_NO_RNG"
fi
# RNG extras
AC_ARG_ENABLE([rng-extras],
[AS_HELP_STRING([--enable-rng-extras],[Enable RNG locks, pools, RBGC, and daemon support (default: disabled)])],
[ ENABLED_RNG_EXTRAS=$enableval ],
[ ENABLED_RNG_EXTRAS=$KERNEL_MODE_DEFAULTS ]
)
if test "$ENABLED_RNG_EXTRAS" = "yes"
then
AM_CFLAGS="$AM_CFLAGS -DWC_RNG_EXTRAS"
fi
AC_ARG_ENABLE([rng-bank],
[AS_HELP_STRING([--enable-rng-bank],[Enable compiling and using RNG banks (default: disabled)])],
[ ENABLED_RNG_BANK=$enableval ],
@@ -7170,7 +7187,8 @@ AS_CASE([$FIPS_VERSION],
-DECC_USER_CURVES \
-DHAVE_ECC384 \
-DHAVE_ECC521 \
-DWOLFSSL_VALIDATE_FFC_IMPORT"
-DWOLFSSL_VALIDATE_FFC_IMPORT \
-DWC_RNG_EXTRAS"
# KCAPI API does not support custom k for sign, don't force enable ECC key sizes and don't use seed callback
AS_IF([test "x$ENABLED_KCAPI_ECC" = "xno"],

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,961 @@
/*!
\ingroup Random
\brief Allocate and initialize a bank of n_rngs pre-instantiated WC_RNG
instances, checked out and back in by consumers (wc_rng_bank_checkout()
et al.). The bank is allocated from heap; release with
wc_rng_bank_free(). Bank-level flags (WC_RNG_BANK_FLAG_*) fix the
bank's posture at initialization: e.g. _CAN_WAIT admits sleeping,
_QUIET suppresses seeding-degradation warnings,
_NO_CHECKOUT_REFCOUNTING suppresses per-checkout refcount traffic for
container-guaranteed lifetimes, _PREDICTION_RESISTANCE imposes a
bank-wide fresh-reseed posture on every sleepable lease.
\return 0 Success
\return BAD_FUNC_ARG ctx is null or n_rngs is out of range.
\return MEMORY_E Allocation failed.
\return RNG_FAILURE_E No instance could be seeded within timeout_secs.
\param ctx Receives the allocated bank.
\param n_rngs Number of instances.
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* bank-posture flags.
\param timeout_secs Seeding timeout budget.
\param heap Heap hint for dynamic allocation.
\param devId Device id, or INVALID_DEVID.
_Example_
\code
struct wc_rng_bank *bank = NULL;
if (wc_rng_bank_new(&bank, 4, WC_RNG_BANK_FLAG_CAN_WAIT, 10,
NULL, INVALID_DEVID) == 0) {
// ... checkout/checkin traffic ...
wc_rng_bank_free(&bank);
}
\endcode
\sa wc_rng_bank_init
\sa wc_rng_bank_checkout
\sa wc_rng_bank_free
*/
int wc_rng_bank_new(struct wc_rng_bank **ctx, int n_rngs, word32 flags,
int timeout_secs, void *heap, int devId);
/*!
\ingroup Random
\brief Initialize a caller-provided bank object. Semantics of
wc_rng_bank_new(), without the allocation; release with
wc_rng_bank_fini().
\return 0 Success
\return BAD_FUNC_ARG ctx is null or n_rngs is out of range.
\return BAD_LENGTH_E n_rngs exceeds the static capacity
(WC_RNG_BANK_STATIC builds).
\return RNG_FAILURE_E No instance could be seeded within timeout_secs.
\param ctx The bank object to initialize.
\param n_rngs Number of instances.
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* bank-posture flags.
\param timeout_secs Seeding timeout budget.
\param heap Heap hint for dynamic allocation.
\param devId Device id, or INVALID_DEVID.
\sa wc_rng_bank_new
\sa wc_rng_bank_init_nonce
\sa wc_rng_bank_fini
*/
int wc_rng_bank_init(struct wc_rng_bank *ctx, int n_rngs, word32 flags,
int timeout_secs, void *heap, int devId);
/*!
\ingroup Random
\brief The nonce-bearing form of wc_rng_bank_init(): the nonce is used
as additional instantiation input for each instance.
\return 0 Success
\return BAD_FUNC_ARG ctx is null, n_rngs is out of range, or nonce is
null with nonceSz nonzero.
\return RNG_FAILURE_E No instance could be seeded within timeout_secs.
\return BAD_LENGTH_E nonceSz exceeds the supported maximum.
\return MEMORY_E Allocation failed.
\return WC_TIMEOUT_E Instance seeding exceeded timeout_secs.
\param ctx The bank object to initialize.
\param n_rngs Number of instances.
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* bank-posture flags.
\param timeout_secs Seeding timeout budget.
\param heap Heap hint for dynamic allocation.
\param devId Device id, or INVALID_DEVID.
\param nonce Additional instantiation input.
\param nonceSz Length of nonce in bytes.
\param perso Optional personalization string (may be null).
\param persoSz Length of perso in bytes.
\sa wc_rng_bank_init
*/
int wc_rng_bank_init_nonce(struct wc_rng_bank *ctx, int n_rngs,
word32 flags, int timeout_secs, void *heap,
int devId, const byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz);
/*!
\ingroup Random
\brief Designate the first instance of the failover pool: checkouts
without a targeted preference rotate through instances at and above
first_failover_inst, reserving the lower offsets for targeted
(affinity or daemon) use.
\return 0 Success
\return BAD_FUNC_ARG ctx is null or first_failover_inst is out of range.
\param ctx The bank to configure.
\param first_failover_inst The first failover-eligible instance offset.
\sa wc_rng_bank_checkout
*/
int wc_rng_bank_first_failover_inst_set(struct wc_rng_bank *ctx,
int first_failover_inst);
/*!
\ingroup Random
\brief Install affinity handlers: callbacks that pin the caller to an
execution context (e.g. disable preemption or migration), report its id
for instance affinity, and unpin. With handlers installed,
WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST checkouts prefer the instance
matching the caller's affinity id, and WC_RNG_BANK_FLAG_AFFINITY_LOCK
holds the pin across the lease.
\return 0 Success
\return BAD_FUNC_ARG ctx is null.
\return BUSY_E The bank is in service; handlers must be set before use.
\param ctx The bank to configure.
\param affinity_lock_cb Pin the caller; may be null.
\param affinity_get_id_cb Report the caller's affinity id.
\param affinity_unlock_cb Unpin the caller; may be null.
\param cb_arg Opaque argument passed to the callbacks.
\sa wc_rng_bank_checkout
*/
int wc_rng_bank_set_affinity_handlers(struct wc_rng_bank *ctx,
wc_affinity_lock_fn_t affinity_lock_cb,
wc_affinity_get_id_fn_t affinity_get_id_cb,
wc_affinity_unlock_fn_t affinity_unlock_cb,
void *cb_arg);
/*!
\ingroup Random
\brief Tear down a bank initialized with wc_rng_bank_init(): once the
refcount and per-instance lease gates pass, fires any registered free
hook and frees every instance. Never waits: a referenced or leased
bank is refused with BUSY_E, and the caller quiesces its consumers and
retries.
\return 0 Success
\return BAD_FUNC_ARG ctx is null.
\return BUSY_E The bank is still referenced, or an instance lease is
outstanding.
\return BAD_STATE_E The refcount is below its initialization baseline
(teardown of an uninitialized or corrupted bank).
\param ctx The bank to tear down.
\sa wc_rng_bank_init
\sa wc_rng_bank_free
\sa wc_rng_bank_register_free_hook
*/
int wc_rng_bank_fini(struct wc_rng_bank *ctx);
/*!
\ingroup Random
\brief Tear down and release a bank allocated with wc_rng_bank_new().
\return 0 Success
\return BAD_FUNC_ARG ctx or *ctx is null.
\return BAD_STATE_E The bank is still referenced.
\param ctx The bank to release; nulled on success.
\sa wc_rng_bank_new
\sa wc_rng_bank_fini
*/
int wc_rng_bank_free(struct wc_rng_bank **ctx);
/*!
\ingroup Random
\brief Register bank as the process-default bank, retrievable with
wc_rng_bank_default_checkout().
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\return BAD_STATE_E A default bank is already registered.
\return BUSY_E Registration is contended; retry.
\param bank The bank to register.
\sa wc_rng_bank_default_checkout
\sa wc_rng_bank_default_clear
*/
int wc_rng_bank_default_set(struct wc_rng_bank *bank);
/*!
\ingroup Random
\brief Take a reference on the process-default bank.
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\return BAD_STATE_E No default bank is registered.
\return NO_DEFAULT_FOUND_E No default bank is registered.
\param bank Receives the default bank.
\sa wc_rng_bank_default_set
\sa wc_rng_bank_default_checkin
*/
int wc_rng_bank_default_checkout(struct wc_rng_bank **bank);
/*!
\ingroup Random
\brief Release a reference taken with wc_rng_bank_default_checkout().
\return 0 Success
\return BAD_FUNC_ARG bank or *bank is null.
\param bank The reference to release; nulled on success.
\sa wc_rng_bank_default_checkout
*/
int wc_rng_bank_default_checkin(struct wc_rng_bank **bank);
/*!
\ingroup Random
\brief Unregister the process-default bank.
\return 0 Success
\return BAD_FUNC_ARG bank is null or is not the registered default.
\return BUSY_E Unregistration is contended; retry.
\param bank The bank to unregister.
\sa wc_rng_bank_default_set
*/
int wc_rng_bank_default_clear(struct wc_rng_bank *bank);
/*!
\ingroup Random
\brief Lease an in-service instance from the bank: either the preferred
(or affinity-matched) instance, or -- with
WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST -- the first available failover
instance. On success the caller owns the instance's lock; access the
WC_RNG with WC_RNG_BANK_INST_TO_RNG() and return the lease with
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.
\details Out-of-service instances: a targeted (non-failover) checkout
admits them; failover checkouts divert around them.
WC_RNG_BANK_FLAG_FOR_RECOVERY makes the targeted admission explicit and
interaction-safe (recovery machinery and patrols): out-of-service
status is expected, the _CONSUME_NEXT_SEED arm is suppressed (a consume
would fail on exactly the instances recovery targets), and failover and
affinity selection are rejected in combination.
WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED gives the opposite guarantee:
either a lease on an in-service instance, or an error with no lease --
never a lease on an out-of-service instance; under _CAN_WAIT an
out-of-service instance is retried within the timeout budget (allowing
a patrol to restore it), and the distinguished error for a lap or wait
that found only out-of-service instances is BAD_STATE_E.
Entropy-invalidated (quarantined) instances refuse ordinary leases with
NEEDS_RECOVERY_E, with two admissions. First, when the quarantined
instance holds a READY banked next seed, any claimant is admitted and
the consume-at-checkout reseed runs unconditionally: the invalidation
purge guarantees READY banked material post-dates the invalidation
event, so admitting the claimant is completing the recovery. Second,
WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY admits the caller to a quarantined
instance with no banked material, transferring the recovery obligation:
checkout then returns NEEDS_RECOVERY_E with the checkout otherwise
complete -- *rng_inst set; instance lock, affinity locks, and any
vector-inhibit state held. This is the robust-mutex (EOWNERDEAD)
pattern: an error return with the acquisition complete and persistent,
because "this resource needs consistency recovery" is only safely
reportable to a caller that already holds it. The caller must either
recover the instance (a credited reseed, e.g. wc_RNG_DRBG_Reseed_Now(),
clears the quarantine) or check it back in. Ordinary consumers that
cannot complete a recovery must not pass this flag.
\return 0 Success; *rng_inst holds the lease.
\return BAD_FUNC_ARG bank or rng_inst is null, or the flags are
contradictory.
\return NEEDS_RECOVERY_E Quarantined: refused without a lease
(ordinary checkout), or lease held with recovery owed
(_MAYBE_FOR_RECOVERY; see \details).
\return BAD_STATE_E (_ERROR_ON_RNG_FAILED) only out-of-service
instances were found.
\return WC_TIMEOUT_E The wait budget expired.
\return RNG_FAILURE_E No serviceable instance.
\return BAD_INDEX_E preferred_inst_offset is out of range.
\return BUSY_E The selected instance is contended (without _CAN_WAIT).
\param bank The bank to lease from.
\param rng_inst Receives the leased instance.
\param preferred_inst_offset The preferred instance, or 0.
\param timeout_secs Wait budget (with _CAN_WAIT).
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* per-call flags.
_Example_
\code
struct wc_rng_bank_inst *inst = NULL;
if (wc_rng_bank_checkout(bank, &inst, 0, 10,
WC_RNG_BANK_FLAG_CAN_WAIT |
WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST) == 0) {
ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst),
out, sizeof(out));
wc_rng_bank_inst_checkin(&inst);
}
\endcode
\sa wc_rng_bank_inst_checkin
\sa wc_rng_bank_recover_inst
\sa wc_rng_bank_spawn
*/
int wc_rng_bank_checkout(struct wc_rng_bank *bank,
struct wc_rng_bank_inst **rng_inst,
int preferred_inst_offset, int timeout_secs,
word32 flags);
/*!
\ingroup Random
\brief Return a lease through the bank object, validating that rng_inst
belongs to bank. Prefer wc_rng_bank_inst_checkin() when only the
instance pointer is at hand.
\return 0 Success
\return BAD_FUNC_ARG bank or rng_inst is null, or the instance does not
belong to bank.
\return OBJECT_NOT_LOCKED_E The instance's lease is not held (e.g. a
stale duplicate check-in).
\return NEEDS_RECOVERY_E Checked in successfully; informational
notice that the instance is entropy-invalidated.
\param bank The bank the instance belongs to.
\param rng_inst The lease to return; nulled on success.
\sa wc_rng_bank_inst_checkin
\sa wc_rng_bank_checkout
*/
int wc_rng_bank_checkin(struct wc_rng_bank *bank,
struct wc_rng_bank_inst **rng_inst);
/*!
\ingroup Random
\brief Return a lease by instance pointer alone.
\return 0 Success
\return BAD_FUNC_ARG rng_inst or *rng_inst is null.
\return OBJECT_NOT_LOCKED_E The instance's lease is not held.
\return NEEDS_RECOVERY_E Checked in successfully; informational
notice that the instance is entropy-invalidated.
\param rng_inst The lease to return; nulled on success.
\sa wc_rng_bank_checkout
\sa wc_rng_bank_checkin
*/
int wc_rng_bank_inst_checkin(struct wc_rng_bank_inst **rng_inst);
/*!
\ingroup Random
\brief Report the instance's offset within its bank.
\return n The instance offset, non-negative.
\return BAD_FUNC_ARG rng_inst is null.
\param rng_inst The instance to interrogate.
\sa wc_rng_bank_checkout
*/
int wc_rng_bank_get_inst_id(struct wc_rng_bank_inst *rng_inst);
/*!
\ingroup Random
\brief Bank next-seed material for the instance at inst_offset --
wc_RNG_DRBG_NextSeedGenerate() through the bank, without taking the
instance lock; the daemon-side serialization word arbitrates against
concurrent whole-instance reinitialization.
\return 0 Bytes were banked.
\return ALREADY_E The instance's bank is ready or being consumed.
\return NOT_READY_E The health test could not run; simply retry.
\return BAD_FUNC_ARG bank is null, inst_offset is out of range, or n
is 0.
\param bank The bank.
\param inst_offset The instance to bank for.
\param n Maximum bytes to bank this call.
\sa wc_RNG_DRBG_NextSeedGenerate
\sa wc_rng_bank_next_seed_generate_rbgc
\details The daemon-side serialization word (not the instance lock)
excludes a concurrent wc_rng_bank_inst_reinit() from freeing the DRBG
out from under the gather; lease-holders never consult it, since
instance-lock exclusion already covers every lease-holder interaction.
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.
*/
int wc_rng_bank_next_seed_generate(struct wc_rng_bank *bank, int inst_offset,
word32 n);
/*!
\ingroup Random
\brief The chain-sourced form of wc_rng_bank_next_seed_generate(): the
banked material is drawn from root's generate function and tagged with
its provenance.
\return 0 Bytes were banked.
\return ALREADY_E The instance's bank is ready or being consumed.
\return NOT_READY_E The health test could not run; simply retry.
\return BAD_FUNC_ARG bank or root is null, inst_offset is out of range,
or n is 0.
\param bank The bank.
\param inst_offset The instance to bank for.
\param n Maximum bytes to bank this call.
\sa wc_rng_bank_next_seed_generate
\sa wc_RNG_DRBG_NextSeedGenerate_RBGC
*/
int wc_rng_bank_next_seed_generate_rbgc(struct wc_rng_bank *bank,
int inst_offset, word32 n);
/*!
\ingroup Random
\brief Free and reinstantiate a leased instance in place. The caller
must hold the lease; the daemon-side serialization word excludes
concurrent lockless banking during the cycle.
\return 0 Success
\return BAD_FUNC_ARG bank or rng_inst is null.
\return RNG_FAILURE_E Reinstantiation could not be seeded within
timeout_secs.
\return BUSY_E The whole-instance-operation gate is held (daemon banking in
progress); retry.
\param bank The bank the instance belongs to.
\param rng_inst The leased instance to reinitialize.
\param timeout_secs Seeding timeout budget.
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
\sa wc_rng_bank_recover_inst
*/
int wc_rng_bank_inst_reinit(struct wc_rng_bank *bank,
struct wc_rng_bank_inst *rng_inst,
int timeout_secs, word32 flags);
/*!
\ingroup Random
\brief Patrol helper: check out the instance at inst_offset with
WC_RNG_BANK_FLAG_FOR_RECOVERY, recover it iff it needs recovery, and
check it back in. Two recovery arms: an out-of-service instance
(wc_RNG_GetStatus() != WC_DRBG_OK) is reinitialized in place; an
in-service but entropy-invalidated (quarantined) instance takes one
credited reseed, which clears the quarantine while preserving instance
identity. A healthy instance is a success no-op, so callers can
invoke this unconditionally on state observed locklessly.
\return 0 Success (recovered, or nothing to recover).
\return BAD_FUNC_ARG bank is null, inst_offset is out of range, or the
flags are contradictory.
\return RNG_FAILURE_E Recovery could not be seeded within timeout_secs.
\return BUSY_E The instance lock or whole-instance-operation gate is
contended; retry on a later patrol turn.
\param bank The bank.
\param inst_offset The instance to patrol.
\param timeout_secs Seeding timeout budget.
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
_Example_
\code
// after a VM duplication event has invalidated the bank:
for (i = 0; i < n_rngs; i++)
(void)wc_rng_bank_recover_inst(bank, i, 10,
WC_RNG_BANK_FLAG_CAN_WAIT);
\endcode
\sa wc_rng_bank_invalidate_entropy
\sa wc_rng_bank_checkout
\details A stale lockless status observation costs one harmless round
trip. BUSY_E reports contention on the instance lock or the
whole-instance-operation gate: retry on a later patrol turn. flags may
include WC_RNG_BANK_FLAG_CAN_WAIT and WC_RNG_BANK_FLAG_AFFINITY_LOCK,
which are passed through.
*/
int wc_rng_bank_recover_inst(struct wc_rng_bank *bank, int inst_offset,
int timeout_secs, word32 flags);
/*!
\ingroup Random
\brief Spawn an SP 800-90C chain RNG from a bank instance: check out a
parent instance (honoring the usual selection flags), instantiate
child_rng as its chain child (wc_InitRngNonceRBGC()), and check the
parent back in. The child's lifetime is thereafter decoupled from the
parent and its bank; release it with wc_FreeRng() (or wc_rng_free() for
the heap form, wc_rng_bank_spawn_new()). The child's RBGC stratum is
one plus the parent's stratum at instantiation.
\details A recommended nonce, when available, is a racy read of a
high-resolution timer (e.g. Linux kernel random_get_entropy()).
WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED composes: a ready banked seed is
redeemed on the parent before the spawn draw.
WC_RNG_BANK_FLAG_STIR and WC_RNG_BANK_FLAG_FOR_RECOVERY are
rejected. WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED is implied: the parent
is guaranteed in-service, or an error is returned with no lease and no
child.
\return 0 Success
\return BAD_FUNC_ARG bank or child_rng is null, or the flags are
contradictory (uncredited or recovery seeding contradict a spawn).
\return RNG_FAILURE_E No serviceable parent within the timeout budget.
\param bank The bank to spawn from.
\param child_rng The caller-provided WC_RNG to instantiate.
\param nonce Optional additional instantiation input.
\param nonceSz Length of nonce in bytes.
\param perso Optional personalization string (may be null).
\param persoSz Length of perso in bytes.
\param preferred_inst_offset The preferred parent instance, or 0.
\param timeout_secs Wait budget (with _CAN_WAIT).
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* per-call flags.
\sa wc_rng_bank_spawn_new
\sa wc_InitRngNonceRBGC
*/
int wc_rng_bank_spawn(struct wc_rng_bank *bank, WC_RNG *child_rng,
byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
int preferred_inst_offset,
int timeout_secs, word32 flags);
/*!
\ingroup Random
\brief The allocating form of wc_rng_bank_spawn(): the child is
allocated from the bank's heap and returned through child_rng; release
with wc_rng_free().
\return 0 Success
\return BAD_FUNC_ARG bank or child_rng is null.
\return MEMORY_E Allocation failed.
\return RNG_FAILURE_E No serviceable parent within the timeout budget.
\param bank The bank to spawn from.
\param child_rng Receives the allocated, instantiated WC_RNG.
\param nonce Optional additional instantiation input.
\param nonceSz Length of nonce in bytes.
\param perso Optional personalization string (may be null).
\param persoSz Length of perso in bytes.
\param preferred_inst_offset The preferred parent instance, or 0.
\param timeout_secs Wait budget (with _CAN_WAIT).
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* per-call flags.
\sa wc_rng_bank_spawn
*/
int wc_rng_bank_spawn_new(struct wc_rng_bank *bank, WC_RNG **child_rng,
byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
int preferred_inst_offset, int timeout_secs,
word32 flags);
/*!
\ingroup Random
\brief Reseed every instance with caller-supplied seed material.
WC_RNG_BANK_FLAG_STIR mixes the material in without
crediting it.
\return 0 Success
\return BAD_FUNC_ARG bank or seed is null.
\return RNG_FAILURE_E An instance could not be reseeded within
timeout_secs.
\param bank The bank to seed.
\param seed Seed material.
\param seedSz Length of seed in bytes.
\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 flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
\sa wc_rng_bank_seed_range
\sa wc_rng_bank_reseed
*/
int wc_rng_bank_seed(struct wc_rng_bank *bank, const byte* seed,
word32 seedSz, const byte *nonce, word32 nonceSz,
int timeout_secs, word32 flags);
/*!
\ingroup Random
\brief The range form of wc_rng_bank_seed(): seed instances first_inst
through last_inst inclusive.
\return 0 Success
\return BAD_FUNC_ARG bank or seed is null, or the range is out of
bounds.
\return RNG_FAILURE_E An instance could not be reseeded within
timeout_secs.
\return BAD_INDEX_E The instance range is invalid.
\return BAD_STATE_E The bank is not initialized.
\return NO_DEFAULT_FOUND_E bank is null and no default bank is registered.
\param bank The bank to seed.
\param first_inst The first instance offset.
\param last_inst The last instance offset.
\param seed Seed material.
\param seedSz Length of seed in bytes.
\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 flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
\sa wc_rng_bank_seed
*/
int wc_rng_bank_seed_range(struct wc_rng_bank *bank, int first_inst,
int last_inst, const byte* seed, word32 seedSz,
const byte *nonce, word32 nonceSz,
int timeout_secs, word32 flags);
/*!
\ingroup Random
\brief Reseed every instance from the module's seed source.
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\return RNG_FAILURE_E An instance could not be reseeded within
timeout_secs.
\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 flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
\sa wc_rng_bank_reseed_range
\sa wc_rng_bank_seed
*/
int wc_rng_bank_reseed(struct wc_rng_bank *bank,
const byte *nonce, word32 nonceSz,
int timeout_secs, word32 flags);
/*!
\ingroup Random
\brief The range form of wc_rng_bank_reseed().
\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 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.
\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 flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
\sa wc_rng_bank_reseed
*/
int wc_rng_bank_reseed_range(struct wc_rng_bank *bank, int first_inst,
int last_inst,
const byte *nonce, word32 nonceSz,
int timeout_secs, word32 flags);
/*!
\ingroup Random
\brief Set the entropy-invalidated latch on every instance (see
wc_RNG_invalidate_entropy()): cached entropy products are discarded,
and each instance is forced through a credited reseed before its next
generate serves output. Lock-free and constant-time per instance; safe
from a state-invalidation event context (VM fork/resume).
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\return BAD_STATE_E The bank is not initialized.
\param bank The bank to invalidate.
\param flags Bitwise-or of WC_RNG_BANK_FLAG_* flags.
_Example_
\code
// VM-resume notifier
(void)wc_rng_bank_invalidate_entropy(bank, WC_RNG_BANK_FLAG_NONE);
// instances recover on next checkout, or by patrol:
// wc_rng_bank_recover_inst()
\endcode
\sa wc_RNG_invalidate_entropy
\sa wc_rng_bank_recover_inst
\details Also purges each instance's banked next-seed apertures: the
purge is the provenance guarantee that a READY bank observed after the
event holds post-event material (see wc_rng_bank_checkout()'s recovery
admissions). Walks every instance even on error, returning the first
error. flags must be 0.
*/
int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank, word32 flags);
/*!
\ingroup Random
\brief Reserve the bank's daemon slot with a caller-chosen nonzero
magic word, admitting exactly one scheduling daemon per bank. The
lifecycle is strictly ordered: _reserve, then _register, then
_unregister, then _release.
\return 0 Success
\return BAD_FUNC_ARG bank is null or magic is the free sentinel.
\return BUSY_E The slot is claimed, or claiming is contended.
\param bank The bank to claim.
\param magic The daemon's magic word.
\sa wc_rng_bank_daemon_register
\sa wc_rng_bank_daemon_release
*/
int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank,
WC_ATOMIC_UINT_ARG magic);
/*!
\ingroup Random
\brief Register the daemon object in a slot reserved with the same
magic word.
\return 0 Success
\return BAD_FUNC_ARG bank or daemon is null.
\return ALREADY_E The slot is already in the requested state.
\return WRONG_TYPE_OBJECT_E magic does not match the claim.
\return BUSY_E The slot transition is contended; retry.
\param bank The bank.
\param daemon The daemon object to register.
\param magic The daemon's magic word.
\sa wc_rng_bank_daemon_reserve
\sa wc_rng_bank_daemon_unregister
*/
int wc_rng_bank_daemon_register(struct wc_rng_bank *bank, void *daemon,
WC_ATOMIC_UINT_ARG magic);
/*!
\ingroup Random
\brief Unregister the daemon object, returning it through daemon.
\return 0 Success
\return BAD_FUNC_ARG bank or daemon is null.
\return ALREADY_E The slot is already in the requested state.
\return WRONG_TYPE_OBJECT_E magic does not match the claim.
\return BUSY_E The slot transition is contended; retry.
\param bank The bank.
\param daemon Receives the registered daemon object.
\param magic The daemon's magic word.
\sa wc_rng_bank_daemon_register
\sa wc_rng_bank_daemon_release
*/
int wc_rng_bank_daemon_unregister(struct wc_rng_bank *bank, void **daemon,
WC_ATOMIC_UINT_ARG magic);
/*!
\ingroup Random
\brief Release the daemon slot claimed with magic, returning it to the
free sentinel.
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\return ALREADY_E The slot is already in the requested state.
\return WRONG_TYPE_OBJECT_E magic does not match the claim.
\return BUSY_E The slot transition is contended; retry.
\param bank The bank.
\param magic The daemon's magic word.
\sa wc_rng_bank_daemon_reserve
*/
int wc_rng_bank_daemon_release(struct wc_rng_bank *bank,
WC_ATOMIC_UINT_ARG magic);
/*!
\ingroup RNGBank
\brief Initialize the bank's root RNG -- the primary-sourced parent
that seeds and recovers the bank's instances.
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\param bank The bank whose root to initialize.
\param nonce Optional instantiation nonce (may be null).
\param nonceSz Length of nonce in bytes.
\param perso Optional personalization string (may be null).
\param persoSz Length of perso in bytes.
\param flags Bitwise-or of WC_RNG_INIT_FLAG_* attributes.
\sa wc_rng_bank_root_rng_get
*/
int wc_rng_bank_root_rng_init(struct wc_rng_bank *bank,
const byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
word32 flags);
/*!
\ingroup RNGBank
\brief Report the bank's root RNG.
\return The root WC_RNG, or null when none is initialized or bank is
null.
\param bank The bank to interrogate.
\sa wc_rng_bank_root_rng_init
*/
WC_RNG *wc_rng_bank_root_rng_get(struct wc_rng_bank *bank);
/*!
\ingroup Random
\brief Register a callback fired by wc_rng_bank_fini() once its
refcount and leak gates pass -- i.e. once teardown is committed -- for
external registries that must drop their reference when the bank dies.
One-shot: the hook is cleared before firing. A null free_hook
unregisters.
\return 0 Success
\return BAD_FUNC_ARG bank is null.
\param bank The bank to hook.
\param free_hook The callback.
\param arg Opaque argument passed to the callback.
\sa wc_rng_bank_fini
\sa wc_RNG_register_free_hook
*/
int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank,
wc_rng_bank_free_hook_cb_t free_hook,
void *arg);
/*!
\ingroup Random
\brief Initialize rng as a bank reference: a WC_RNG with no DRBG of its
own, whose wc_RNG_GenerateBlock() transparently checks an instance out
of bank, generates, and checks it back in. Release with wc_FreeRng().
\return 0 Success
\return BAD_FUNC_ARG bank or rng is null.
\param bank The bank to reference.
\param rng The WC_RNG to initialize as a reference.
_Example_
\code
WC_RNG rng;
if (wc_InitRng_BankRef(bank, &rng) == 0) {
// rng now serves through the bank
ret = wc_RNG_GenerateBlock(&rng, out, sizeof(out));
wc_FreeRng(&rng);
}
\endcode
\sa wc_BankRef_Release
\sa wc_rng_new_bankref
*/
int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng);
/*!
\ingroup Random
\brief Release a bank reference. wc_FreeRng() calls this
automatically for bank references; direct use is rarely needed.
\return 0 Success
\return BAD_FUNC_ARG rng is null or is not a bank reference.
\param rng The bank reference to release.
\sa wc_InitRng_BankRef
*/
int wc_BankRef_Release(WC_RNG *rng);
/*!
\ingroup Random
\brief The allocating form of wc_InitRng_BankRef(): the reference is
allocated from the bank's heap; release with wc_rng_free().
\return 0 Success
\return BAD_FUNC_ARG bank or rng is null.
\return MEMORY_E Allocation failed.
\param bank The bank to reference.
\param rng Receives the allocated bank reference.
\sa wc_InitRng_BankRef
*/
int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng);
/*!
\ingroup Random
\brief Snapshot the RNG debug counters (WC_RNG_DEBUG_STATS) with
bank-level context.
\return 0 Success
\return BAD_FUNC_ARG s is null.
\param s Receives the snapshot.
\param bank Optional bank for context, or null.
\sa wc_rng_debug_stats_snap
*/
int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s,
struct wc_rng_bank *bank);

View File

@@ -35,4 +35,5 @@ EXTRA_DIST += m4/ax_linuxkm.m4 \
linuxkm/patches/6.1.73/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v1v73.patch \
linuxkm/patches/6.12/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v12.patch \
linuxkm/patches/6.15/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v15.patch \
linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch
linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch \
linuxkm/patches/7.3/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v3.patch

View File

@@ -795,6 +795,10 @@
}
#endif
#define WC_LKM_REFCOUNT_TO_INT(refcount) wc_lkm_refcount_to_int(&(refcount))
#include <linux/notifier.h>
#ifdef CONFIG_PM_SLEEP
#include <linux/suspend.h>
#endif
#endif /* !WC_CONTAINERIZE_THIS */
#endif /* LINUXKM_LKCAPI_REGISTER */

View File

@@ -228,14 +228,14 @@ static int linuxkm_lkcapi_unregister(void);
static int enabled_kernel_fips_enabled = 0;
#endif
static ssize_t install_algs_handler(struct kobject *kobj, struct kobj_attribute *attr,
static ssize_t install_algs_handler(WC_MODULE_ATTR_CONST struct module_attribute *mattr, struct module_kobject *mk,
const char *buf, size_t count)
{
int arg;
int ret;
(void)kobj;
(void)attr;
(void)mattr;
(void)mk;
if (kstrtoint(buf, 10, &arg) || arg != 1)
return -EINVAL;
@@ -249,14 +249,14 @@ static ssize_t install_algs_handler(struct kobject *kobj, struct kobj_attribute
return count;
}
static ssize_t deinstall_algs_handler(struct kobject *kobj, struct kobj_attribute *attr,
static ssize_t deinstall_algs_handler(WC_MODULE_ATTR_CONST struct module_attribute *mattr, struct module_kobject *mk,
const char *buf, size_t count)
{
int arg;
int ret;
(void)kobj;
(void)attr;
(void)mattr;
(void)mk;
if (kstrtoint(buf, 10, &arg) || arg != 1)
return -EINVAL;
@@ -279,22 +279,50 @@ static ssize_t deinstall_algs_handler(struct kobject *kobj, struct kobj_attribut
/* create control channels at /sys/module/libwolfssl/{install_algs,deinstall_algs} */
static struct kobj_attribute install_algs_attr = __ATTR(install_algs, 0220, NULL, install_algs_handler);
static struct kobj_attribute deinstall_algs_attr = __ATTR(deinstall_algs, 0220, NULL, deinstall_algs_handler);
static struct module_attribute install_algs_attr = __ATTR(install_algs, 0220, NULL, install_algs_handler);
static struct module_attribute deinstall_algs_attr = __ATTR(deinstall_algs, 0220, NULL, deinstall_algs_handler);
static int installed_sysfs_LKCAPI_files = 0;
static int linuxkm_lkcapi_sysfs_install(void) {
int ret;
if (! installed_sysfs_LKCAPI_files) {
ret = linuxkm_lkcapi_sysfs_install_node(&install_algs_attr, NULL);
ret = linuxkm_sysfs_install_attr(&install_algs_attr.attr, NULL);
if (ret)
return ret;
ret = linuxkm_lkcapi_sysfs_install_node(&deinstall_algs_attr, NULL);
ret = linuxkm_sysfs_install_attr(&deinstall_algs_attr.attr, NULL);
if (ret) {
(void)linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, NULL);
(void)linuxkm_sysfs_deinstall_attr(&install_algs_attr.attr, NULL);
return ret;
}
#ifdef WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER
ret = linuxkm_sysfs_install_attr(&wc_linuxkm_rng_state_invalidate_attr.attr,
NULL);
if (ret) {
(void)linuxkm_sysfs_deinstall_attr(&deinstall_algs_attr.attr,
NULL);
(void)linuxkm_sysfs_deinstall_attr(&install_algs_attr.attr,
NULL);
return ret;
}
#endif
#if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \
defined(WC_RNG_DEBUG_STATS)
ret = linuxkm_sysfs_install_attr(&wc_linuxkm_rng_stats_attr.attr,
NULL);
if (ret) {
#ifdef WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER
(void)linuxkm_sysfs_deinstall_attr(&wc_linuxkm_rng_state_invalidate_attr.attr,
NULL);
#endif
(void)linuxkm_sysfs_deinstall_attr(&deinstall_algs_attr.attr,
NULL);
(void)linuxkm_sysfs_deinstall_attr(&install_algs_attr.attr,
NULL);
return ret;
}
#endif
installed_sysfs_LKCAPI_files = 1;
}
return 0;
@@ -302,10 +330,27 @@ static int linuxkm_lkcapi_sysfs_install(void) {
static int linuxkm_lkcapi_sysfs_deinstall(void) {
if (installed_sysfs_LKCAPI_files) {
int ret = linuxkm_lkcapi_sysfs_deinstall_node(&install_algs_attr, NULL);
int ret;
#if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && \
defined(WC_RNG_DEBUG_STATS)
ret = linuxkm_sysfs_deinstall_attr(&wc_linuxkm_rng_stats_attr.attr,
NULL);
if (ret)
return ret;
ret = linuxkm_lkcapi_sysfs_deinstall_node(&deinstall_algs_attr, NULL);
#endif
#ifdef WC_LINUXKM_HAVE_RNG_STATE_INVALIDATE_HANDLER
/* removed first (LIFO), and in any case before RNG teardown can
* begin: the store handler walks the registry and reaches the
* daemon root. */
ret = linuxkm_sysfs_deinstall_attr(&wc_linuxkm_rng_state_invalidate_attr.attr,
NULL);
if (ret)
return ret;
#endif
ret = linuxkm_sysfs_deinstall_attr(&install_algs_attr.attr, NULL);
if (ret)
return ret;
ret = linuxkm_sysfs_deinstall_attr(&deinstall_algs_attr.attr, NULL);
if (ret)
return ret;
installed_sysfs_LKCAPI_files = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,548 @@
--- 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
@@ -84,6 +84,310 @@ static enum {
} crng_init __read_mostly = CRNG_EMPTY;
static DEFINE_STATIC_KEY_FALSE(crng_is_ready);
#define crng_ready() (static_branch_likely(&crng_is_ready) || crng_init >= CRNG_READY)
+
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+
+#include <linux/percpu-refcount.h>
+
+static atomic_long_t random_bytes_cb_owner =
+ ATOMIC_LONG_INIT((long)NULL);
+static struct percpu_ref random_bytes_cb_ref;
+static bool random_bytes_cb_live; /* READ_ONCE/WRITE_ONCE only */
+static DECLARE_COMPLETION(random_bytes_cb_drained);
+static _get_random_bytes_cb_t _get_random_bytes_cb;
+static get_random_bytes_user_cb_t get_random_bytes_user_cb;
+static crng_ready_cb_t crng_ready_cb;
+static mix_pool_bytes_cb_t mix_pool_bytes_cb;
+static credit_init_bits_cb_t credit_init_bits_cb;
+static crng_reseed_cb_t crng_reseed_cb;
+
+static void random_bytes_cb_ref_release(struct percpu_ref *ref)
+{
+ complete(&random_bytes_cb_drained);
+}
+
+/* distinct non-module value marking teardown in progress; guarantees
+ * non-collision with any real module pointer. */
+static char random_bytes_cb_teardown_sentinel;
+#define RANDOM_BYTES_CB_TEARDOWN ((long)&random_bytes_cb_teardown_sentinel)
+
+int wolfssl_linuxkm_register_random_bytes_handlers(
+ struct module *new_random_bytes_cb_owner,
+ const struct wolfssl_linuxkm_random_bytes_handlers *handlers)
+{
+ int ret;
+
+ if ((new_random_bytes_cb_owner == NULL) ||
+ (handlers == NULL) ||
+ (handlers->_get_random_bytes == NULL) ||
+ (handlers->get_random_bytes_user == NULL))
+ {
+ return -EINVAL;
+ }
+
+ /* random_bytes_cb_owner is used to enforce serialization of
+ * wolfssl_linuxkm_register_random_bytes_handlers() and
+ * wolfssl_linuxkm_unregister_random_bytes_handlers(): NULL means
+ * unowned, a module pointer means registered (or registration in
+ * flight, while random_bytes_cb_live is still false), and
+ * RANDOM_BYTES_CB_TEARDOWN means unregistration in flight.
+ */
+ if (atomic_long_cmpxchg(&random_bytes_cb_owner,
+ (long)NULL,
+ (long)new_random_bytes_cb_owner)
+ != (long)NULL)
+ {
+ return -EBUSY;
+ }
+
+ if (!try_module_get(new_random_bytes_cb_owner)) {
+ atomic_long_set(&random_bytes_cb_owner, (long)NULL);
+ return -ENODEV;
+ }
+
+ /* handlers must be published before the ref goes live. */
+ _get_random_bytes_cb = handlers->_get_random_bytes;
+ get_random_bytes_user_cb = handlers->get_random_bytes_user;
+ crng_ready_cb = handlers->crng_ready;
+ mix_pool_bytes_cb = handlers->mix_pool_bytes;
+ credit_init_bits_cb = handlers->credit_init_bits;
+ crng_reseed_cb = handlers->crng_reseed;
+
+ reinit_completion(&random_bytes_cb_drained);
+ ret = percpu_ref_init(&random_bytes_cb_ref,
+ random_bytes_cb_ref_release, 0, GFP_KERNEL);
+ if (ret) {
+ _get_random_bytes_cb = NULL;
+ get_random_bytes_user_cb = NULL;
+ crng_ready_cb = NULL;
+ mix_pool_bytes_cb = NULL;
+ credit_init_bits_cb = NULL;
+ crng_reseed_cb = NULL;
+ module_put(new_random_bytes_cb_owner);
+ /* an unregister call racing this in-flight registration may
+ * hold the owner word at RANDOM_BYTES_CB_TEARDOWN for the
+ * brief interval before it observes !random_bytes_cb_live
+ * and restores our pointer; wait it out rather than
+ * clobbering its claim. */
+ while (atomic_long_cmpxchg(&random_bytes_cb_owner,
+ (long)new_random_bytes_cb_owner,
+ (long)NULL)
+ != (long)new_random_bytes_cb_owner)
+ {
+ cpu_relax();
+ }
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_VDSO_GETRANDOM)) {
+ /* route vDSO getrandom() users to the syscall, hence to the
+ * callbacks. */
+ WRITE_ONCE(vdso_k_rng_data->is_ready, false);
+ }
+
+ WRITE_ONCE(random_bytes_cb_live, true);
+
+ return 0;
+}
+EXPORT_SYMBOL(wolfssl_linuxkm_register_random_bytes_handlers);
+
+int wolfssl_linuxkm_unregister_random_bytes_handlers(void)
+{
+ long prev = atomic_long_read(&random_bytes_cb_owner);
+ struct module *owner;
+
+ /* claim teardown ownership: exactly one caller transitions the owner
+ * word from a module pointer to RANDOM_BYTES_CB_TEARDOWN; any
+ * concurrent or repeated caller is refused.
+ */
+ for (;;) {
+ if ((prev == (long)NULL) || (prev == RANDOM_BYTES_CB_TEARDOWN))
+ return -ENODEV;
+ if (atomic_long_try_cmpxchg(&random_bytes_cb_owner, &prev,
+ RANDOM_BYTES_CB_TEARDOWN))
+ break;
+ /* prev was refreshed by the failed cmpxchg; loop. */
+ }
+ owner = (struct module *)prev;
+
+ if (!READ_ONCE(random_bytes_cb_live)) {
+ /* registration still in flight (or failing): hand the owner
+ * word back and refuse. */
+ atomic_long_set(&random_bytes_cb_owner, prev);
+ return -EBUSY;
+ }
+
+ WRITE_ONCE(random_bytes_cb_live, false);
+ /* after this, no resolver can be between its liveness check and its
+ * tryget. */
+ synchronize_rcu();
+ percpu_ref_kill(&random_bytes_cb_ref);
+ wait_for_completion(&random_bytes_cb_drained);
+ percpu_ref_exit(&random_bytes_cb_ref);
+
+ _get_random_bytes_cb = NULL;
+ get_random_bytes_user_cb = NULL;
+ crng_ready_cb = NULL;
+ mix_pool_bytes_cb = NULL;
+ credit_init_bits_cb = NULL;
+ crng_reseed_cb = NULL;
+
+ if (IS_ENABLED(CONFIG_VDSO_GETRANDOM))
+ WRITE_ONCE(vdso_k_rng_data->is_ready, crng_ready());
+
+ module_put(owner);
+ atomic_long_set(&random_bytes_cb_owner, (long)NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL(wolfssl_linuxkm_unregister_random_bytes_handlers);
+
+/* Output emitters covered while a generate callback is live:
+ * _get_random_bytes (and the batched get_random_uXX fills, which route
+ * through it), get_random_bytes_user, and vDSO getrandom (readiness
+ * withheld, forcing the syscall path). New crng_make_state callers or
+ * vDSO datapage exports need corresponding treatment. */
+static __always_inline int reserve_random_bytes_cb(void)
+{
+ int ret = -ENODEV;
+
+ rcu_read_lock();
+ if (READ_ONCE(random_bytes_cb_live) &&
+ percpu_ref_tryget_live(&random_bytes_cb_ref))
+ {
+ ret = 0;
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+static __always_inline void release_random_bytes_cb(void)
+{
+ percpu_ref_put(&random_bytes_cb_ref);
+}
+
+static inline int call__get_random_bytes_cb(void *buf, size_t len)
+{
+ int ret;
+
+ if (_get_random_bytes_cb == NULL)
+ return -ENODEV;
+
+ ret = reserve_random_bytes_cb();
+ if (ret)
+ return ret;
+
+ ret = _get_random_bytes_cb(buf, len);
+
+ release_random_bytes_cb();
+
+ return ret;
+}
+
+static inline ssize_t call_get_random_bytes_user_cb(struct iov_iter *iter)
+{
+ ssize_t ret;
+
+ if (get_random_bytes_user_cb == NULL)
+ return -ECANCELED;
+
+ ret = (ssize_t)reserve_random_bytes_cb();
+ if (ret)
+ return ret;
+
+ ret = get_random_bytes_user_cb(iter);
+
+ release_random_bytes_cb();
+
+ return ret;
+}
+
+static inline bool call_crng_ready_cb(void)
+{
+ bool ret;
+
+ /* Null crng_ready_cb signifies that the DRBG is always ready, i.e. that if
+ * called, it will always have or obtain sufficient entropy to fulfill the
+ * call.
+ */
+ if (crng_ready_cb == NULL)
+ return 1;
+
+ if (reserve_random_bytes_cb() != 0)
+ return 0;
+
+ ret = crng_ready_cb();
+
+ release_random_bytes_cb();
+
+ return ret;
+}
+
+static inline int call_mix_pool_bytes_cb(const void *buf, size_t len)
+{
+ int ret;
+
+ if (mix_pool_bytes_cb == NULL)
+ return -ENODEV;
+
+ ret = reserve_random_bytes_cb();
+ if (ret)
+ return ret;
+
+ ret = mix_pool_bytes_cb(buf, len);
+
+ release_random_bytes_cb();
+
+ return ret;
+}
+
+static inline int call_credit_init_bits_cb(size_t bits)
+{
+ int ret;
+
+ if (credit_init_bits_cb == NULL)
+ return -ENODEV;
+
+ ret = reserve_random_bytes_cb();
+ if (ret)
+ return ret;
+
+ ret = credit_init_bits_cb(bits);
+
+ release_random_bytes_cb();
+
+ return ret;
+}
+
+static inline int call_crng_reseed_cb(void)
+{
+ int ret;
+
+ if (crng_reseed_cb == NULL)
+ return -ENODEV;
+
+ ret = reserve_random_bytes_cb();
+ if (ret)
+ return ret;
+
+ ret = crng_reseed_cb();
+
+ release_random_bytes_cb();
+
+ return ret;
+}
+
+#endif /* WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS */
+
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ #define crng_ready_by_cb() (READ_ONCE(random_bytes_cb_live) && call_crng_ready_cb())
+ #define crng_ready_maybe_cb() (READ_ONCE(random_bytes_cb_live) ? \
+ (call_crng_ready_cb() || crng_ready()) : crng_ready())
+#else
+ #define crng_ready_maybe_cb() crng_ready()
+#endif
+
/* Various types of waiters for crng_init->CRNG_READY transition. */
static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait);
static struct fasync_struct *fasync;
@@ -107,7 +411,7 @@ MODULE_PARM_DESC(ratelimit_disable, "Dis
*/
bool rng_is_initialized(void)
{
- return crng_ready();
+ return crng_ready_maybe_cb();
}
EXPORT_SYMBOL(rng_is_initialized);
@@ -131,11 +435,11 @@ static void try_to_generate_entropy(void
*/
int wait_for_random_bytes(void)
{
- while (!crng_ready()) {
+ while (!crng_ready_maybe_cb()) {
int ret;
try_to_generate_entropy();
- ret = wait_event_interruptible_timeout(crng_init_wait, crng_ready(), HZ);
+ ret = wait_event_interruptible_timeout(crng_init_wait, crng_ready_maybe_cb(), HZ);
if (ret)
return ret > 0 ? 0 : ret;
}
@@ -155,7 +459,7 @@ int __cold execute_with_initialized_rng(
int ret = 0;
spin_lock_irqsave(&random_ready_notifier.lock, flags);
- if (crng_ready())
+ if (crng_ready_maybe_cb())
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,
if (!len)
return;
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ {
+ 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);
+ }
+#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
if (unlikely(!iov_iter_count(iter)))
return 0;
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ if (READ_ONCE(random_bytes_cb_live)) {
+ ssize_t cb_ret = call_get_random_bytes_user_cb(iter);
+ /* -ECANCELED: no iter callback registered; iter is intact.
+ * While a generate callback is live, no output bytes come
+ * from the native crng: a live handler set must include the
+ * iter callback. */
+ if (cb_ret != -ECANCELED)
+ return cb_ret;
+ WARN_ONCE(1, "live random_bytes handler set lacks get_random_bytes_user callback");
+ return -EIO;
+ }
+#endif
+
/*
* 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
{
unsigned long flags;
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ (void)call_mix_pool_bytes_cb(buf, len);
+ /* continue to mix into native pool too. */
+#endif
+
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
memzero_explicit(&block, sizeof(block));
}
-#define credit_init_bits(bits) if (!crng_ready()) _credit_init_bits(bits)
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ #define credit_init_bits(bits) do { (void)call_credit_init_bits_cb(bits); \
+ if (!crng_ready()) \
+ _credit_init_bits(bits); } while (0)
+#else
+ #define credit_init_bits(bits) do { if (!crng_ready()) _credit_init_bits(bits); } while (0)
+#endif
static void __cold _credit_init_bits(size_t bits)
{
@@ -731,7 +1078,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);
- if (IS_ENABLED(CONFIG_VDSO_GETRANDOM))
+ if (IS_ENABLED(CONFIG_VDSO_GETRANDOM)
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ && !READ_ONCE(random_bytes_cb_live)
+#endif
+ )
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
_mix_pool_bytes(&entropy, sizeof(entropy));
_mix_pool_bytes(buf, len);
spin_unlock_irqrestore(&input_pool.lock, flags);
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ if (READ_ONCE(random_bytes_cb_live))
+ (void)call_mix_pool_bytes_cb(buf, len);
+#endif
}
EXPORT_SYMBOL(add_device_randomness);
@@ -1392,7 +1747,7 @@ SYSCALL_DEFINE3(getrandom, char __user *
if ((flags & (GRND_INSECURE | GRND_RANDOM)) == (GRND_INSECURE | GRND_RANDOM))
return -EINVAL;
- if (!crng_ready() && !(flags & GRND_INSECURE)) {
+ if (!crng_ready_maybe_cb() && !(flags & GRND_INSECURE)) {
if (flags & GRND_NONBLOCK)
return -EAGAIN;
ret = wait_for_random_bytes();
@@ -1408,6 +1763,10 @@ SYSCALL_DEFINE3(getrandom, char __user *
static __poll_t random_poll(struct file *file, poll_table *wait)
{
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ if (crng_ready_by_cb())
+ return EPOLLIN | EPOLLRDNORM;
+#endif
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
* Opportunistically attempt to initialize the RNG on platforms that
* have fast cycle counters, but don't (for now) require it to succeed.
*/
- if (!crng_ready())
+ if (!crng_ready_maybe_cb())
try_to_generate_entropy();
- if (!crng_ready()) {
+ if (!crng_ready_maybe_cb()) {
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
{
int ret;
- if (!crng_ready() &&
+ if (!crng_ready_by_cb() &&
((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,
case RNDRESEEDCRNG:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ /* continue to reseed native crng too. */
+ if (call_crng_reseed_cb() == 0) {
+ if (crng_ready())
+ crng_reseed(NULL);
+ return 0;
+ }
+#endif
if (!crng_ready())
return -ENODATA;
crng_reseed(NULL);
--- 7.3/include/linux/random.h.dist 2026-09-07 15:16:52.000000000 -0500
+++ 7.3/include/linux/random.h 2026-09-08 14:52:45.063663933 -0500
@@ -139,4 +139,37 @@ int random_online_cpu(unsigned int cpu);
extern const struct file_operations random_fops, urandom_fops;
#endif
+#ifndef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+ #define WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS 1
+#endif
+
+typedef int (*_get_random_bytes_cb_t)(void *buf, size_t len);
+struct iov_iter;
+/* kernels >= 5.17.0 use get_random_bytes_user() */
+typedef ssize_t (*get_random_bytes_user_cb_t)(struct iov_iter *iter);
+/* kernels < 5.17.0 use extract_crng_user(), though some LTS kernels,
+ * e.g. 5.10.236, have the 5.17+ architecture backported.
+ */
+typedef ssize_t (*extract_crng_user_cb_t)(void __user *buf, size_t nbytes);
+typedef bool (*crng_ready_cb_t)(void);
+typedef int (*mix_pool_bytes_cb_t)(const void *buf, size_t len);
+typedef int (*credit_init_bits_cb_t)(size_t bits);
+typedef int (*crng_reseed_cb_t)(void);
+
+struct wolfssl_linuxkm_random_bytes_handlers {
+ _get_random_bytes_cb_t _get_random_bytes;
+ get_random_bytes_user_cb_t get_random_bytes_user;
+ extract_crng_user_cb_t extract_crng_user;
+ crng_ready_cb_t crng_ready;
+ mix_pool_bytes_cb_t mix_pool_bytes;
+ credit_init_bits_cb_t credit_init_bits;
+ crng_reseed_cb_t crng_reseed;
+};
+
+int wolfssl_linuxkm_register_random_bytes_handlers(
+ struct module *new_random_bytes_cb_owner,
+ const struct wolfssl_linuxkm_random_bytes_handlers *handlers);
+
+int wolfssl_linuxkm_unregister_random_bytes_handlers(void);
+
#endif /* _LINUX_RANDOM_H */

View File

@@ -204,16 +204,24 @@ 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);
(void)Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, 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");
wb_fail = 1;
}
mcdc_fh_disarm();
(void)Hash_DRBG_Uninstantiate(&drbg);
/* 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);
(void)Hash_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash_DRBG_Instantiate(&drbg, 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");
wb_fail = 1;
}
mcdc_fh_disarm();
(void)Hash_DRBG_Uninstantiate(&drbg);
@@ -270,15 +278,23 @@ static void wb_hash512_drbg_init_df_chain(void)
/* Vector (F, -). */
mcdc_fh_arm(1);
(void)Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, 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");
wb_fail = 1;
}
mcdc_fh_disarm();
(void)Hash512_DRBG_Uninstantiate(&drbg);
/* Vector (T, F). */
mcdc_fh_arm(n1 + 1);
(void)Hash512_DRBG_Instantiate(&drbg, seed, (word32)sizeof(seed),
ret = Hash512_DRBG_Instantiate(&drbg, 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");
wb_fail = 1;
}
mcdc_fh_disarm();
(void)Hash512_DRBG_Uninstantiate(&drbg);

View File

@@ -300,7 +300,7 @@ 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);
ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0, NULL);
if (ret != DRBG_SUCCESS) {
WB_NOTE("Hash_DRBG_Generate (below interval) failed");
wb_fail = 1;
@@ -308,7 +308,7 @@ static void wb_hash_drbg_generate_reseed(void)
/* 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);
ret = Hash_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0, NULL);
if (ret != DRBG_NEED_RESEED) {
WB_NOTE("Hash_DRBG_Generate did not signal DRBG_NEED_RESEED");
wb_fail = 1;
@@ -448,14 +448,14 @@ static void wb_hash512_drbg_generate_reseed(void)
}
drbg.reseedCtr = 1; /* false side */
ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0);
ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0, NULL);
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);
ret = Hash512_DRBG_Generate(&drbg, out, (word32)sizeof(out), NULL, 0, NULL);
if (ret != DRBG_NEED_RESEED) {
WB_NOTE("Hash512_DRBG_Generate did not signal DRBG_NEED_RESEED");
wb_fail = 1;

View File

@@ -1436,6 +1436,17 @@ int wc_curve25519_set_rng(curve25519_key* key, WC_RNG* rng)
key->rng = rng;
return 0;
}
/* Companion to wc_curve25519_set_rng(): detach the key's RNG association.
* Subsequent blinded operations then fail in the RNG service (BAD_FUNC_ARG
* on the NULL WC_RNG) until a new one is set. */
int wc_curve25519_clear_rng(curve25519_key* key)
{
if (key == NULL)
return BAD_FUNC_ARG;
key->rng = NULL;
return 0;
}
#endif
/* get key size */

View File

@@ -15101,6 +15101,28 @@ int wc_ecc_set_rng(ecc_key* key, WC_RNG* rng)
return err;
}
/* Companion to wc_ecc_set_rng(): detach the key's RNG association.
* Subsequent operations that require the key's RNG then fail with
* MISSING_RNG_E until a new one is set. */
int wc_ecc_clear_rng(ecc_key* key)
{
int err = 0;
#ifdef ECC_TIMING_RESISTANT
if (key == NULL) {
err = BAD_FUNC_ARG;
}
else {
key->rng = NULL;
}
#else
(void)key;
/* report success, not an error if ECC_TIMING_RESISTANT is not defined */
#endif
return err;
}
#ifdef HAVE_ECC_ENCRYPT
@@ -15760,6 +15782,9 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
/* Device for the ECIES callback and the KDF/AES/HMAC steps. It comes
* only from the context; unset means software, or the CB_FIND finder. */
int eciesDevId = INVALID_DEVID;
#ifdef ECC_TIMING_RESISTANT
int lentRng = 0; /* ctx->rng lent to privKey for this op */
#endif
if (privKey == NULL || pubKey == NULL || msg == NULL || out == NULL ||
outSz == NULL)
@@ -15848,8 +15873,14 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
return BUFFER_E;
#ifdef ECC_TIMING_RESISTANT
if (ctx->rng != NULL && privKey->rng == NULL)
if (ctx->rng != NULL && privKey->rng == NULL) {
/* Lend the ctx's RNG to the key for the duration of this operation
* only. Restored to NULL before every subsequent return, so no
* borrowed pointer survives on the caller's key object after the
* call. */
privKey->rng = ctx->rng;
lentRng = 1;
}
#endif
#ifndef WOLFSSL_ECIES_OLD
@@ -15859,23 +15890,42 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
#else
ret = wc_ecc_make_pub_ex(privKey, NULL, NULL);
#endif
if (ret != 0)
if (ret != 0) {
#ifdef ECC_TIMING_RESISTANT
if (lentRng)
privKey->rng = NULL;
#endif
return ret;
}
}
ret = wc_ecc_export_x963_ex(privKey, out, &pubKeySz, compressed);
if (ret != 0)
if (ret != 0) {
#ifdef ECC_TIMING_RESISTANT
if (lentRng)
privKey->rng = NULL;
#endif
return ret;
}
out += pubKeySz;
#endif
#ifdef WOLFSSL_SMALL_STACK
sharedSecret = (byte*)XMALLOC(sharedSz, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER);
if (sharedSecret == NULL)
if (sharedSecret == NULL) {
#ifdef ECC_TIMING_RESISTANT
if (lentRng)
privKey->rng = NULL;
#endif
return MEMORY_E;
}
keys = (byte*)XMALLOC(ECC_BUFSIZE, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER);
if (keys == NULL) {
XFREE(sharedSecret, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER);
#ifdef ECC_TIMING_RESISTANT
if (lentRng)
privKey->rng = NULL;
#endif
return MEMORY_E;
}
#endif
@@ -16160,6 +16210,11 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
WC_FREE_VAR_EX(sharedSecret, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER);
WC_FREE_VAR_EX(keys, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER);
#ifdef ECC_TIMING_RESISTANT
if (lentRng)
privKey->rng = NULL;
#endif
return ret;
}
@@ -16218,6 +16273,9 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
/* Device for the ECIES callback and the KDF/AES/HMAC steps. It comes
* only from the context; unset means software, or the CB_FIND finder. */
int eciesDevId = INVALID_DEVID;
#ifdef ECC_TIMING_RESISTANT
int lentRng = 0; /* ctx->rng lent to privKey for this op */
#endif
if (privKey == NULL || msg == NULL || out == NULL || outSz == NULL)
@@ -16347,8 +16405,14 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
}
#ifdef ECC_TIMING_RESISTANT
if (ctx->rng != NULL && privKey->rng == NULL)
if (ctx->rng != NULL && privKey->rng == NULL) {
/* Lend the ctx's RNG to the key for the duration of this operation
* only. Restored to NULL before every subsequent return, so no
* borrowed pointer survives on the caller's key object after the
* call. */
privKey->rng = ctx->rng;
lentRng = 1;
}
#endif
#ifdef WOLFSSL_SMALL_STACK
@@ -16357,6 +16421,10 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
#ifndef WOLFSSL_ECIES_OLD
if (pubKey == peerKey)
wc_ecc_free(peerKey);
#endif
#ifdef ECC_TIMING_RESISTANT
if (lentRng)
privKey->rng = NULL;
#endif
return MEMORY_E;
}
@@ -16367,6 +16435,10 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
#ifndef WOLFSSL_ECIES_OLD
if (pubKey == peerKey)
wc_ecc_free(peerKey);
#endif
#ifdef ECC_TIMING_RESISTANT
if (lentRng)
privKey->rng = NULL;
#endif
return MEMORY_E;
}
@@ -16675,6 +16747,11 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
XFREE(keys, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER);
#endif
#ifdef ECC_TIMING_RESISTANT
if (lentRng)
privKey->rng = NULL;
#endif
return ret;
}

View File

@@ -760,6 +760,12 @@ const char* wc_GetErrorString(int error)
case WRONG_TYPE_OBJECT_E:
return "Object is wrong type for requested operation";
case NEEDS_RECOVERY_E:
return "Object needs recovery before use";
case UNEXPECTED_STATE_E:
return "Object has unexpected state";
case MAX_CODE_E:
case WC_SPAN1_MIN_CODE_E:
case MIN_CODE_E:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6171,6 +6171,20 @@ int wc_RsaSetRNG(RsaKey* key, WC_RNG* rng)
return 0;
}
/* Companion to wc_RsaSetRNG(): detach the key's RNG association.
* Subsequent operations that require the key's RNG (blinding, pairwise
* consistency) then either fail with MISSING_RNG_E or fall back to a
* locally instantiated RNG, per operation, until a new RNG is set. */
int wc_RsaClearRNG(RsaKey* key)
{
if (key == NULL)
return BAD_FUNC_ARG;
key->rng = NULL;
return 0;
}
#endif /* !WC_NO_RNG */
#ifdef WC_RSA_NONBLOCK

File diff suppressed because it is too large Load Diff

View File

@@ -75,7 +75,7 @@ wc_static_assert(-(long)MIN_CODE_E < 0x7ffL);
#define WC_TEST_RET_ENC_NC WC_TEST_RET_ENC(WC_TEST_RET_LN, 0, WC_TEST_RET_TAG_NC)
/* encode positive integer */
#define WC_TEST_RET_ENC_I(i) WC_TEST_RET_ENC(WC_TEST_RET_LN, i, WC_TEST_RET_TAG_I)
#define WC_TEST_RET_ENC_I(i) WC_TEST_RET_ENC(WC_TEST_RET_LN, ((i) > 0x7ff) ? 0x7ff : (i), WC_TEST_RET_TAG_I)
/* encode error code (negative integer) */
#define WC_TEST_RET_ENC_EC(ec) WC_TEST_RET_ENC(WC_TEST_RET_LN, -(ec), WC_TEST_RET_TAG_EC)
@@ -114,6 +114,10 @@ wc_static_assert(-(long)MIN_CODE_E < 0x7ffL);
#endif
#endif
/* Note, all macro gates used below must be available with just
* wolfcrypt/types.h included, i.e. no macros in alg-specific headers can be
* used here.
*/
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 octets_test(void);
@@ -255,6 +259,14 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void);
#ifdef WC_RNG_BANK_SUPPORT
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void);
#endif
#if defined(HAVE_HASHDRBG) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \
(!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0))
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_drbg_svc_test(void);
#endif
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);
#endif /* WC_NO_RNG */
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void);
#if defined(USE_CERT_BUFFERS_2048) && \

View File

@@ -222,6 +222,8 @@ void wc_curve25519_free(curve25519_key* key);
#ifdef WOLFSSL_CURVE25519_BLINDING
WOLFSSL_API
int wc_curve25519_set_rng(curve25519_key* key, WC_RNG* rng);
WOLFSSL_API
int wc_curve25519_clear_rng(curve25519_key* key);
#endif
#ifndef WC_NO_CONSTRUCTORS

View File

@@ -898,6 +898,8 @@ WOLFSSL_API
void wc_ecc_fp_init(void);
WOLFSSL_API
int wc_ecc_set_rng(ecc_key* key, WC_RNG* rng);
WOLFSSL_API
int wc_ecc_clear_rng(ecc_key* key);
WOLFSSL_API
int wc_ecc_set_curve(ecc_key* key, int keysize, int curve_id);

View File

@@ -354,9 +354,11 @@ enum wolfCrypt_ErrorCodes {
OBJECT_NOT_LOCKED_E = -1031, /* Required lock on object is not held */
WRONG_TYPE_OBJECT_E = -1032, /* Object is wrong type for requested */
/* operation */
NEEDS_RECOVERY_E = -1033, /* Object needs recovery before use */
UNEXPECTED_STATE_E = -1034, /* Object has unexpected state */
WC_SPAN2_LAST_E = -1032, /* Update to indicate last used error code */
WC_LAST_E = -1032, /* the last code used either here or in
WC_SPAN2_LAST_E = -1034, /* Update to indicate last used error code */
WC_LAST_E = -1034, /* 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

@@ -43,6 +43,87 @@
WOLFSSL_LOCAL int wolfCrypt_FIPS_DRBG_sanity(void);
#endif
/***** Setup for RNG extra features *****/
#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_LOCK)) && \
!defined(WC_RNG_NO_LOCK)
#define WC_RNG_HAVE_LOCK
#ifdef WOLFSSL_NO_ATOMICS
typedef word32 WC_RNG_lock_t;
typedef word32 WC_RNG_lock_arg_t;
#else
typedef wolfSSL_Atomic_Uint WC_RNG_lock_t;
typedef WC_ATOMIC_UINT_ARG WC_RNG_lock_arg_t;
#endif
#else
#undef WC_RNG_HAVE_LOCK
#endif
#ifdef WC_RNG_WANT_LOCK_FULL_MUTEX
#ifndef WC_RNG_HAVE_LOCK
#error FULL_MUTEX depends on WC_RNG_HAVE_LOCK.
#endif
#define WC_RNG_HAVE_LOCK_FULL_MUTEX
#else
#undef WC_RNG_HAVE_LOCK_FULL_MUTEX
#endif
#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_RBGC)) && \
!defined(WC_RNG_NO_RBGC) && defined(HAVE_HASHDRBG) && \
!defined(CUSTOM_RAND_GENERATE_BLOCK)
#define WC_RNG_HAVE_RBGC
#else
#undef WC_RNG_HAVE_RBGC
#endif
#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_NEXT_SEED)) && \
!defined(WC_RNG_NO_NEXT_SEED) && defined(HAVE_HASHDRBG) && \
!defined(CUSTOM_RAND_GENERATE_BLOCK)
#define WC_RNG_HAVE_NEXT_SEED
#ifdef WOLFSSL_NO_ATOMICS
typedef sword32 WC_DRBG_nextSeedLen_t;
#else
typedef wolfSSL_Atomic_Int WC_DRBG_nextSeedLen_t;
#endif
#else
#undef WC_RNG_HAVE_NEXT_SEED
#endif
#if defined(WC_RNG_HAVE_NEXT_SEED) && !defined(WC_RNG_HAVE_LOCK) && \
defined(HAVE_FIPS)
/* FIPS builds: pregenerated output requires the full invalidation/purge
* protocol, which rides the lock facility. */
#error WC_RNG_WANT_NEXT_SEED requires WC_RNG_WANT_LOCK in FIPS builds.
#endif
#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_POOL)) && \
!defined(WC_RNG_NO_POOL)
#define WC_RNG_HAVE_POOL
#ifdef WOLFSSL_NO_ATOMICS
typedef word32 WC_RNG_pool_pos_t;
#else
typedef wolfSSL_Atomic_Uint WC_RNG_pool_pos_t;
#endif
#else
#undef WC_RNG_HAVE_POOL
#endif
#if defined(WC_RNG_HAVE_POOL) && !defined(WC_RNG_HAVE_LOCK) && \
defined(HAVE_FIPS)
/* FIPS builds: pregenerated output requires the full invalidation/purge
* protocol, which rides the lock facility. */
#error WC_RNG_WANT_POOL requires WC_RNG_WANT_LOCK in FIPS builds.
#endif
#if (defined(WC_RNG_EXTRAS) || defined(WC_RNG_WANT_FREE_HOOK)) && \
!defined(WC_RNG_NO_FREE_HOOK)
#define WC_RNG_HAVE_FREE_HOOK
struct WC_RNG;
typedef int (*wc_RNG_free_hook_cb_t)(const struct WC_RNG *rng, void *arg);
#else
#undef WC_RNG_HAVE_FREE_HOOK
#endif
/***** End setup for RNG extra features *****/
/* Maximum generate block length */
#ifndef RNG_MAX_BLOCK_LEN
#ifdef HAVE_INTEL_QA
@@ -73,7 +154,7 @@
#undef HAVE_HASHDRBG
#define HAVE_HASHDRBG
#ifndef WC_RESEED_INTERVAL
#define WC_RESEED_INTERVAL (1000000)
#define WC_RESEED_INTERVAL 1000000
#endif
#endif
@@ -295,15 +376,37 @@ struct OS_Seed {
#define RNG_HEALTH_TEST_CHECK_SIZE_SHA512 (WC_SHA512_DIGEST_SIZE * 4)
#endif
#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_STIR_LEN 64
#endif
#ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED
#define WC_DRBG_RESEED_CTR_TYPE_DEFINED
#ifdef WORD64_AVAILABLE
typedef word64 wc_drbg_reseed_ctr_t;
#else
typedef word32 wc_drbg_reseed_ctr_t;
#endif
#endif
#ifndef NO_SHA256
struct DRBG_internal {
#ifdef WORD64_AVAILABLE
word64 reseedCtr;
#else
word32 reseedCtr;
#endif
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
byte nextStir[WC_DRBG_NEXT_STIR_LEN];
WC_DRBG_nextSeedLen_t nextStirLen;
#endif
void* heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
int devId;
@@ -318,9 +421,18 @@ struct DRBG_internal {
#ifdef WOLFSSL_DRBG_SHA512
struct DRBG_SHA512_internal {
word64 reseedCtr;
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
byte nextStir[WC_DRBG_NEXT_STIR_LEN];
WC_DRBG_nextSeedLen_t nextStirLen;
#endif
void* heap;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLF_CRYPTO_CB)
int devId;
@@ -347,29 +459,100 @@ enum wc_RngHealthState {
WC_DRBG_NOT_INIT = 0,
WC_DRBG_OK = 1,
WC_DRBG_FAILED = 2,
WC_DRBG_CONT_FAILED = 3,
#ifdef WC_RNG_BANK_SUPPORT
WC_DRBG_BANKREF = 4, /* Marks the WC_RNG as a ref to a wc_rng_bank,
* with no usable DRBG of its own.
*/
#define WC_HAVE_RNG_BANKREF
#endif
WOLF_ENUM_DUMMY_LAST_ELEMENT(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)
#ifndef WC_RNG_RBGC_USER_SEED_STRATUM
#define WC_RNG_RBGC_USER_SEED_STRATUM 65536
#endif
wc_static_assert(WC_RNG_RBGC_USER_SEED_STRATUM >= 256);
#ifdef WC_RNG_WANT_DEBUG_STATS
#define WC_RNG_DEBUG_STATS
#endif
#if defined(WC_RNG_DEBUG_STATS) && !defined(WC_RNG_HAVE_LOCK)
#error WC_RNG_DEBUG_STATS requires the RNG lock facility (WC_RNG_WANT_LOCK).
#endif
#ifdef WC_RNG_DEBUG_STATS
#ifdef WORD64_AVAILABLE
typedef word64 wc_rng_debug_counter_t;
#else
typedef word32 wc_rng_debug_counter_t;
#endif
#endif
/* RNG context */
struct WC_RNG {
struct OS_Seed seed;
void* heap;
byte status;
word32 flags;
#ifdef WC_RNG_DEBUG_STATS
wc_rng_debug_counter_t _stats_total_bytes_requested;
wc_rng_debug_counter_t _stats_total_bytes_produced;
wc_rng_debug_counter_t _stats_total_requests;
wc_rng_debug_counter_t _stats_reseeds;
wc_rng_debug_counter_t _stats_stirs;
wc_rng_debug_counter_t _stats_seed_failures;
#endif
#ifdef WC_RNG_HAVE_RBGC
int RBGCStratum;
#ifdef WC_RNG_DEBUG_STATS
wc_rng_debug_counter_t _stats_RBGC_bytes_produced;
wc_rng_debug_counter_t _stats_RBGC_reseeds;
#endif
#endif
#ifdef WC_RNG_HAVE_LOCK
WC_RNG_lock_t lock;
#ifdef WC_RNG_HAVE_LOCK_FULL_MUTEX
wolfSSL_Mutex mutex;
#endif
#ifdef WC_RNG_DEBUG_STATS
wc_rng_debug_counter_t _stats_locks_taken;
wc_rng_debug_counter_t _stats_locks_released;
wc_rng_debug_counter_t _stats_locks_refused; /* racy */
#endif
#endif
#ifdef WC_RNG_HAVE_FREE_HOOK
wc_RNG_free_hook_cb_t free_hook;
void *free_hook_arg;
#endif
#ifdef WC_RNG_HAVE_POOL
byte* pool;
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. */
#ifdef WC_RNG_DEBUG_STATS
wc_rng_debug_counter_t _stats_pool_bytes_produced;
wc_rng_debug_counter_t _stats_pool_bytes_missed;
#endif
#endif
#if defined(WC_RNG_BANK_SUPPORT) || defined(HAVE_HASHDRBG)
#ifdef WC_RNG_DEBUG_STATS
#ifdef WC_RNG_HAVE_NEXT_SEED
wc_rng_debug_counter_t _stats_nextseedsprimary_redeemed;
wc_rng_debug_counter_t _stats_nextseedsRBGC_redeemed;
wc_rng_debug_counter_t _stats_nextstirs_redeemed;
wc_rng_debug_counter_t _stats_nextseedsbanked;
wc_rng_debug_counter_t _stats_nextstirs_banked;
#endif
#endif
#if defined(HAVE_HASHDRBG) || defined(WC_HAVE_RNG_BANKREF)
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
union {
#endif
#ifdef WC_RNG_BANK_SUPPORT
#ifdef WC_HAVE_RNG_BANKREF
struct wc_rng_bank *bankref;
#endif
@@ -415,7 +598,7 @@ struct WC_RNG {
};
#endif
#endif /* WC_RNG_BANK_SUPPORT || HAVE_HASHDRBG */
#endif /* HAVE_HASHDRBG || WC_HAVE_RNG_BANKREF */
#if defined(HAVE_GETPID) && !defined(WOLFSSL_NO_GETPID)
pid_t pid;
@@ -554,8 +737,25 @@ 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);
WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, byte* nonce, word32 nonceSz);
WOLFSSL_API int wc_InitRngNonce_ex(WC_RNG* rng, byte* nonce, word32 nonceSz,
WOLFSSL_API int wc_InitRngNonce(WC_RNG* rng, const byte* nonce, word32 nonceSz);
#define WC_RNG_INIT_FLAG_NONE 0
#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)
WOLFSSL_API int wc_InitRng_ex2(WC_RNG* rng, void* heap, int devId,
word32 flags);
WOLFSSL_API int wc_InitRngNonce_ex2(WC_RNG* rng, const byte* nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
void* heap, int devId, word32 flags);
WOLFSSL_API int wc_InitRngNonce_ex(WC_RNG* rng, const byte* nonce, word32 nonceSz,
void* heap, int devId);
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);
@@ -566,6 +766,8 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng);
#define wc_InitRng_ex(rng, h, d) NOT_COMPILED_IN
#define wc_InitRngNonce(rng, n, s) NOT_COMPILED_IN
#define wc_InitRngNonce_ex(rng, n, s, h, d) NOT_COMPILED_IN
#define wc_InitRng_ex2(rng, h, d, f) NOT_COMPILED_IN
#define wc_InitRngNonce_ex2(rng, n, s, p, ps, h, d, f) NOT_COMPILED_IN
#if defined(__ghs__) || defined(WC_NO_RNG_SIMPLE)
/* some older compilers do not like macro function in expression */
#define wc_RNG_GenerateBlock(rng, b, s) NOT_COMPILED_IN
@@ -585,10 +787,35 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng);
WOLFSSL_API int wc_SetSeed_Cb(wc_RngSeed_Cb cb);
#endif
WOLFSSL_API int wc_RNG_GetStatus(const WC_RNG* rng);
WOLFSSL_API int wc_RNG_DRBG_Present(const WC_RNG* rng);
#ifdef HAVE_HASHDRBG
WOLFSSL_API int wc_RNG_DRBG_Reseed(WC_RNG* rng, const byte* seed,
word32 seedSz);
WOLFSSL_API int wc_RNG_DRBG_Reseed_Nonce(WC_RNG* rng, const byte* seed,
word32 seedSz, const byte *nonce,
word32 nonceSz);
WOLFSSL_API int wc_RNG_DRBG_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);
WOLFSSL_API int wc_RNG_DRBG_Reseed_Now(WC_RNG* rng, const byte* nonce,
word32 nonceSz);
WOLFSSL_API int wc_RNG_TestSeed(const byte* seed, word32 seedSz);
#ifdef WC_RNG_HAVE_RBGC
WOLFSSL_API int wc_RNG_DRBG_GetRBGCStratum(const WC_RNG* rng);
#ifdef WC_RNG_HAVE_NEXT_SEED
WOLFSSL_API int wc_RNG_DRBG_GetNextSeedRBGCStratum(const WC_RNG* rng);
#endif
#endif /* WC_RNG_HAVE_RBGC */
WOLFSSL_API 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);
#ifndef NO_SHA256
/* SHA-256 Hash_DRBG health test entry points. SHA-512-only builds
* (NO_SHA256 + WOLFSSL_DRBG_SHA512) use wc_RNG_HealthTest_SHA512_ex
@@ -697,6 +924,146 @@ WOLFSSL_API int wc_FreeRng(WC_RNG* rng);
#endif /* HAVE_HASHDRBG */
#ifdef WC_RNG_HAVE_RBGC
/* SP 800-90C RBG-chain spawn: instantiate child as a subordinate DRBG
* seeded from parent's generate output. The _New variants allocate the
* child from parent's heap; release them with ordinary wc_rng_free(). */
WOLFSSL_API int wc_InitRngRBGC(WC_RNG* child, WC_RNG* parent, word32 flags);
WOLFSSL_API int wc_InitRngNonceRBGC(WC_RNG* child, WC_RNG* parent,
const byte* nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
word32 flags);
#ifndef WC_NO_CONSTRUCTORS
/* Flags are per-object (WC_RNG_INIT_FLAG_*), deliberately not
* inherited from the parent: a child's lock policy is its own. */
WOLFSSL_API int wc_InitRngRBGC_New(WC_RNG** child, WC_RNG* parent,
word32 flags);
WOLFSSL_API int wc_InitRngNonceRBGC_New(WC_RNG** child, WC_RNG* parent,
const byte* nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
word32 flags);
#endif /* !WC_NO_CONSTRUCTORS */
WOLFSSL_API int wc_RNG_DRBG_ReseedRBGC(WC_RNG* rng, WC_RNG* root,
const byte* nonce, word32 nonceSz);
WOLFSSL_API int wc_RNG_DRBG_StirRBGC(WC_RNG* rng,
WC_RNG* root,
const byte* nonce,
word32 nonceSz);
#endif /* WC_RNG_HAVE_RBGC */
#ifdef WC_RNG_HAVE_NEXT_SEED
#define WC_DRBG_NEXT_SEED_EMPTY 0
/* All sentinel states are negative; non-negative values are banked byte
* counts. */
#define WC_DRBG_NEXT_SEED_PRODUCING (-1)
#define WC_DRBG_NEXT_SEED_READY (-2)
#define WC_DRBG_NEXT_SEED_CONSUMING (-3)
#define WC_DRBG_NEXT_SEED_PURGED (-4)
WOLFSSL_API int wc_RNG_DRBG_NextSeedGenerate(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,
WC_ATOMIC_INT_ARG* n);
WOLFSSL_API int wc_RNG_DRBG_NextSeedNow_Nonce(WC_RNG* rng,
const byte* nonce,
word32 nonceSz);
WOLFSSL_API int wc_RNG_DRBG_NextSeedNow(WC_RNG* rng);
WOLFSSL_API int wc_RNG_DRBG_NextStirStore(WC_RNG* rng,
const byte *nonce, word32 nonceSz);
WOLFSSL_API int wc_RNG_DRBG_NextStirNow(WC_RNG* rng);
#endif /* WC_RNG_HAVE_NEXT_SEED */
#ifdef WC_RNG_HAVE_LOCK
#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)
/* consumers' annotation bits start here (see e.g. rng_bank.h) */
#define WC_RNG_LOCK_EXTRA_SHIFT 4U
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_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_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 */
#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.
* One-shot: cleared before firing. A NULL free_hook unregisters.
* Reinitialization (wc_InitRng*() on a live object) clears any
* registered hook without firing it: hooks are per-lifetime. */
WOLFSSL_API int wc_RNG_register_free_hook(WC_RNG* rng,
wc_RNG_free_hook_cb_t free_hook,
void *arg);
#endif
#ifdef WC_RNG_HAVE_POOL
WOLFSSL_API int wc_RNG_Pool_Alloc(WC_RNG* rng, word32 size);
WOLFSSL_API int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n);
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);
#endif /* WC_RNG_HAVE_POOL */
#ifdef WC_RNG_DEBUG_STATS
struct wc_rng_debug_stats_snapshot {
wc_rng_debug_counter_t _stats_total_bytes_requested;
wc_rng_debug_counter_t _stats_total_bytes_produced;
wc_rng_debug_counter_t _stats_total_requests;
wc_rng_debug_counter_t _stats_reseeds;
wc_rng_debug_counter_t _stats_stirs;
wc_rng_debug_counter_t _stats_seed_failures;
wc_rng_debug_counter_t _stats_locks_taken;
wc_rng_debug_counter_t _stats_locks_released;
wc_rng_debug_counter_t _stats_locks_refused;
#ifdef WC_RNG_HAVE_RBGC
wc_rng_debug_counter_t _stats_RBGC_bytes_produced;
wc_rng_debug_counter_t _stats_RBGC_reseeds;
#endif
#ifdef WC_RNG_HAVE_POOL
wc_rng_debug_counter_t _stats_pool_bytes_produced;
wc_rng_debug_counter_t _stats_pool_bytes_missed;
#endif
#ifdef WC_RNG_HAVE_NEXT_SEED
wc_rng_debug_counter_t _stats_nextseedsprimary_redeemed;
wc_rng_debug_counter_t _stats_nextseedsRBGC_redeemed;
wc_rng_debug_counter_t _stats_nextstirs_redeemed;
wc_rng_debug_counter_t _stats_nextseedsbanked;
wc_rng_debug_counter_t _stats_nextstirs_banked;
#endif
};
WOLFSSL_API int wc_rng_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s,
const WC_RNG *rng);
WOLFSSL_API int wc_rng_debug_stats_restore(
const struct wc_rng_debug_stats_snapshot *s,
WC_RNG *rng);
WOLFSSL_API int wc_rng_debug_stats_sum(struct wc_rng_debug_stats_snapshot *s,
const WC_RNG *rng);
#endif /* WC_RNG_DEBUG_STATS */
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@@ -40,30 +40,81 @@
#error WC_RNG_BANK_SUPPORT requires RNG support.
#endif
#define WC_RNG_BANK_FLAG_NONE 0
#define WC_RNG_BANK_FLAG_INITED (1<<0)
#define WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST (1<<1)
#define WC_RNG_BANK_FLAG_CAN_WAIT (1<<2)
#define WC_RNG_BANK_FLAG_NO_VECTOR_OPS (1<<3)
#define WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST (1<<4)
#define WC_RNG_BANK_FLAG_AFFINITY_LOCK (1<<5)
#if !defined(WOLFSSL_NO_ATOMICS) && !defined(WC_RNG_BANK_NO_DAEMON_SUPPORT)
#define WC_RNG_BANK_HAVE_DAEMON_SUPPORT
#define WC_RNG_BANK_DAEMON_MAGIC_FREE 0U
#endif
#define WC_RNG_BANK_INST_LOCK_FREE 0
#define WC_RNG_BANK_INST_LOCK_HELD (1<<0)
#define WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED (1<<1)
#define WC_RNG_BANK_INST_LOCK_VEC_OPS_INH (1<<2)
#define WC_RNG_BANK_FLAG_NONE 0
#define WC_RNG_BANK_FLAG_INITED (1U << 0)
#define WC_RNG_BANK_FLAG_CAN_FAIL_OVER_INST (1U << 1)
#define WC_RNG_BANK_FLAG_CAN_WAIT (1U << 2)
#define WC_RNG_BANK_FLAG_NO_VECTOR_OPS (1U << 3)
#define WC_RNG_BANK_FLAG_PREFER_AFFINITY_INST (1U << 4)
#define WC_RNG_BANK_FLAG_AFFINITY_LOCK (1U << 5)
#define WC_RNG_BANK_FLAG_STIR (1U << 6)
#define WC_RNG_BANK_FLAG_CONSUME_NEXT_SEED (1U << 7)
#define WC_RNG_BANK_FLAG_FOR_RECOVERY (1U << 8)
#define WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY (1U << 9)
#define WC_RNG_BANK_FLAG_ERROR_ON_RNG_FAILED (1U << 10)
#define WC_RNG_BANK_FLAG_QUIET (1U << 11)
#define WC_RNG_BANK_FLAG_NO_CHECKOUT_REFCOUNTING (1U << 12)
#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)
#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 */
#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
#ifdef WOLFSSL_NO_ATOMICS
typedef word32 WC_RNG_lock_t;
typedef word32 WC_RNG_lock_arg_t;
#else
typedef wolfSSL_Atomic_Uint WC_RNG_lock_t;
typedef WC_ATOMIC_UINT_ARG WC_RNG_lock_arg_t;
#endif
#endif
/* When WC_RNG_HAVE_LOCK, base lock states are in random.h and the lock word
* itself is in WC_RNG.lock; these annotation bits ride above the base bits via
* wc_RNG_lock_get() / _set_extra() / _clear_extra(). */
#define WC_RNG_BANK_INST_LOCK_AFFINITY_LOCKED (1U<<(WC_RNG_LOCK_EXTRA_SHIFT+0))
#define WC_RNG_BANK_INST_LOCK_VEC_OPS_INH (1U<<(WC_RNG_LOCK_EXTRA_SHIFT+1))
typedef int (*wc_affinity_lock_fn_t)(void *arg);
typedef int (*wc_affinity_get_id_fn_t)(void *arg, int *id);
typedef int (*wc_affinity_unlock_fn_t)(void *arg);
struct wc_rng_bank;
typedef int (*wc_rng_bank_free_hook_cb_t)(const struct wc_rng_bank *bank,
void *arg);
#define WC_RNG_BANK_INST_FLAG_NONE 0
#define WC_RNG_BANK_INST_FLAG_ALREADY_WARNED (1U << 0)
struct wc_rng_bank_inst {
#ifdef WOLFSSL_NO_ATOMICS
int lock;
#else
wolfSSL_Atomic_Int lock;
#endif
#ifdef WC_RNG_HAVE_LOCK
/* the exclusivity latch lives in WC_RNG.lock (wc_RNG_lock_*()) --
* in-FIPS-boundary, module-enforced. */
#else
#ifdef WOLFSSL_NO_ATOMICS
word32 lock;
#else
wolfSSL_Atomic_Uint lock;
#endif
#endif
struct wc_rng_bank *bank;
WC_RNG rng;
volatile word32 flags;
};
#if defined(WOLFSSL_NO_MALLOC) && defined(NO_WOLFSSL_MEMORY) && \
@@ -78,16 +129,31 @@ struct wc_rng_bank_inst {
struct wc_rng_bank {
wolfSSL_Ref refcount;
void *heap;
int devId;
word32 flags;
wc_rng_bank_free_hook_cb_t free_hook;
void *free_hook_arg;
wc_affinity_lock_fn_t affinity_lock_cb;
wc_affinity_get_id_fn_t affinity_get_id_cb;
wc_affinity_unlock_fn_t affinity_unlock_cb;
void *cb_arg; /* if mutable, caller is responsible for thread safety. */
int n_rngs;
int first_failover_inst;
#ifdef WC_RNG_HAVE_NEXT_SEED
wolfSSL_Atomic_Int inst_op_gate;
#endif
#ifdef WC_RNG_BANK_STATIC
struct wc_rng_bank_inst rngs[WC_RNG_BANK_STATIC_SIZE];
#else
struct wc_rng_bank_inst *rngs; /* typically one per CPU ID, plus a few */
struct wc_rng_bank_inst *rngs;
#endif
#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT
wolfSSL_Atomic_Uint daemon_magic;
void *daemon; /* e.g. a task_struct* for a wc_linuxkm_entropy_daemon() */
#endif
#if defined(WC_RNG_HAVE_RBGC) || defined(WC_RNG_HAVE_NEXT_SEED) || \
defined(WC_RNG_HAVE_POOL)
WC_RNG root_rng;
#endif
};
@@ -109,6 +175,20 @@ WOLFSSL_API int wc_rng_bank_init(
void *heap,
int devId);
WOLFSSL_API int wc_rng_bank_init_nonce(
struct wc_rng_bank *ctx,
int n_rngs,
word32 flags,
int timeout_secs,
void *heap,
int devId,
const byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz);
WOLFSSL_API int wc_rng_bank_first_failover_inst_set(
struct wc_rng_bank *ctx,
int first_failover_inst);
WOLFSSL_API int wc_rng_bank_set_affinity_handlers(
struct wc_rng_bank *ctx,
wc_affinity_lock_fn_t affinity_lock_cb,
@@ -141,34 +221,157 @@ WOLFSSL_API int wc_rng_bank_checkout(
int timeout_secs,
word32 flags);
#ifdef WC_RNG_BANK_HAVE_DAEMON_SUPPORT
/* Note, these APIs must be called in order, _reserve -> _register ->
* _unregister -> _release, for lifecycle hygiene. A registered daemon must be
* _unregister()ed, and the bank _release()d, before wc_rng_bank_fini(),
* otherwise _fini() will return BUSY_E. */
WOLFSSL_API int wc_rng_bank_daemon_reserve(struct wc_rng_bank *bank,
WC_ATOMIC_UINT_ARG magic);
WOLFSSL_API int wc_rng_bank_daemon_register(struct wc_rng_bank *bank,
void *daemon,
WC_ATOMIC_UINT_ARG magic);
WOLFSSL_API int wc_rng_bank_daemon_unregister(struct wc_rng_bank *bank,
void **daemon,
WC_ATOMIC_UINT_ARG magic);
WOLFSSL_API int wc_rng_bank_daemon_release(struct wc_rng_bank *bank,
WC_ATOMIC_UINT_ARG magic);
#endif /* WC_RNG_BANK_HAVE_DAEMON_SUPPORT */
#if defined(WC_DRBG_BANKREF) && !defined(WC_HAVE_RNG_BANKREF)
/* forward compat for FIPS v5.2.4 random.h */
#define WC_HAVE_RNG_BANKREF
#endif
#ifdef WC_HAVE_RNG_BANKREF
WOLFSSL_LOCAL int wc_local_rng_bank_checkout_for_bankref(
struct wc_rng_bank *bank,
struct wc_rng_bank_inst **rng_inst);
#endif
WOLFSSL_API int wc_rng_bank_get_inst_id(struct wc_rng_bank_inst *rng_inst);
static WC_INLINE int WC_ARG_NOT_NULL(1) wc_rng_bank_inst_flags_up(
struct wc_rng_bank_inst *rng_inst, word32 flags)
{
if (! (rng_inst->flags & flags)) {
rng_inst->flags = rng_inst->flags | flags;
return 1;
}
else
return 0;
}
static WC_INLINE int WC_ARG_NOT_NULL(1) wc_rng_bank_inst_flags_down(
struct wc_rng_bank_inst *rng_inst, word32 flags)
{
if (rng_inst->flags & flags) {
rng_inst->flags = rng_inst->flags & ~flags;
return 1;
}
else
return 0;
}
WOLFSSL_API int wc_rng_bank_checkin(
struct wc_rng_bank *bank,
struct wc_rng_bank_inst **rng_inst);
WOLFSSL_API int wc_rng_bank_inst_checkin(
struct wc_rng_bank_inst **rng_inst);
#ifdef WC_RNG_HAVE_NEXT_SEED
WOLFSSL_API int wc_rng_bank_next_seed_generate(
struct wc_rng_bank *bank,
int inst_offset,
word32 n);
#ifdef WC_RNG_HAVE_RBGC
WOLFSSL_API int wc_rng_bank_next_seed_generate_rbgc(
struct wc_rng_bank *bank,
int inst_offset,
word32 n);
#endif
#endif
WOLFSSL_API int wc_rng_bank_inst_reinit(
struct wc_rng_bank *bank,
struct wc_rng_bank_inst *rng_inst,
int timeout_secs,
word32 flags);
WOLFSSL_API int wc_rng_bank_recover_inst(
struct wc_rng_bank *bank,
int inst_offset,
int timeout_secs,
word32 flags);
#ifdef WC_RNG_HAVE_RBGC
WOLFSSL_API int wc_rng_bank_spawn(
struct wc_rng_bank *bank,
WC_RNG *child_rng,
byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
int preferred_inst_offset,
int timeout_secs,
word32 flags);
#ifndef WC_NO_CONSTRUCTORS
WOLFSSL_API int wc_rng_bank_spawn_new(
struct wc_rng_bank *bank,
WC_RNG **child_rng,
byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
int preferred_inst_offset,
int timeout_secs,
word32 flags);
#endif /* !WC_NO_CONSTRUCTORS */
#endif /* WC_RNG_HAVE_RBGC */
#ifdef HAVE_HASHDRBG
WOLFSSL_API int wc_rng_bank_seed(struct wc_rng_bank *bank,
const byte* seed, word32 seedSz,
const byte *nonce, word32 nonceSz,
int timeout_secs,
word32 flags);
WOLFSSL_API int wc_rng_bank_seed_range(struct wc_rng_bank *bank,
int first_inst, int last_inst,
const byte* seed, word32 seedSz,
const byte *nonce, word32 nonceSz,
int timeout_secs,
word32 flags);
WOLFSSL_API int wc_rng_bank_reseed(struct wc_rng_bank *bank,
const byte *nonce, word32 nonceSz,
int timeout_secs,
word32 flags);
#if defined(WC_DRBG_BANKREF) && !defined(WC_HAVE_RNG_BANKREF)
/* forward compat for FIPS v5.2.4 random.h */
#define WC_HAVE_RNG_BANKREF
WOLFSSL_API int wc_rng_bank_reseed_range(struct wc_rng_bank *bank,
int first_inst, int last_inst,
const byte *nonce, word32 nonceSz,
int timeout_secs,
word32 flags);
WOLFSSL_API int wc_rng_bank_invalidate_entropy(struct wc_rng_bank *bank,
word32 flags);
#endif /* HAVE_HASHDRBG */
#if defined(WC_RNG_HAVE_RBGC) || defined(WC_RNG_HAVE_NEXT_SEED) || \
defined(WC_RNG_HAVE_POOL)
WOLFSSL_API int wc_rng_bank_root_rng_init(struct wc_rng_bank *bank,
const byte *nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
word32 flags);
WOLFSSL_API WC_RNG *wc_rng_bank_root_rng_get(struct wc_rng_bank *bank);
#endif
WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank,
wc_rng_bank_free_hook_cb_t free_hook, void *arg);
#ifdef WC_HAVE_RNG_BANKREF
WOLFSSL_API int wc_InitRng_BankRef(struct wc_rng_bank *bank, WC_RNG *rng);
@@ -180,7 +383,133 @@ 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)
#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(
struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits)
{
return wc_RNG_lock_get(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits);
}
static WC_INLINE int wc_rng_bank_inst_lock_get_conditional(
struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits,
WC_RNG_lock_arg_t want_extra_bits)
{
return wc_RNG_lock_get_conditional(
WC_RNG_BANK_INST_TO_RNG(inst), expected_extra_bits, want_extra_bits);
}
static WC_INLINE int wc_rng_bank_inst_lock_put(
struct wc_rng_bank_inst *inst)
{
return wc_RNG_lock_put(WC_RNG_BANK_INST_TO_RNG(inst), 0);
}
static WC_INLINE int wc_rng_bank_inst_lock_put_conditional(
struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expect_extra_bits)
{
return wc_RNG_lock_put_conditional(
WC_RNG_BANK_INST_TO_RNG(inst), expect_extra_bits, 0);
}
static WC_INLINE int wc_rng_bank_inst_lock_read(
struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t *state)
{
return wc_RNG_lock_read(WC_RNG_BANK_INST_TO_RNG(inst), state);
}
static WC_INLINE int wc_rng_bank_inst_lock_set_extra(
struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits)
{
return wc_RNG_lock_set_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits);
}
static WC_INLINE int wc_rng_bank_inst_lock_add_extra(
struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits)
{
return wc_RNG_lock_add_extra(WC_RNG_BANK_INST_TO_RNG(inst), extra_bits);
}
static WC_INLINE int wc_rng_bank_inst_lock_clear_extra(
struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits)
{
return wc_RNG_lock_clear_extra(
WC_RNG_BANK_INST_TO_RNG(inst), extra_bits);
}
#ifdef HAVE_HASHDRBG
static WC_INLINE int wc_rng_bank_inst_invalidate_entropy(
struct wc_rng_bank_inst *inst)
{
return wc_RNG_invalidate_entropy(WC_RNG_BANK_INST_TO_RNG(inst));
}
static WC_INLINE int wc_rng_bank_inst_reseed_now(
struct wc_rng_bank_inst *inst, const byte *nonce, word32 nonceSz)
{
return wc_RNG_DRBG_Reseed_Now(
WC_RNG_BANK_INST_TO_RNG(inst), nonce, nonceSz);
}
#ifdef WC_RNG_HAVE_RBGC
static WC_INLINE int wc_rng_bank_inst_reseed_rbgc(
struct wc_rng_bank_inst *inst, WC_RNG *root,
const byte *nonce, word32 nonceSz)
{
return wc_RNG_DRBG_ReseedRBGC(
WC_RNG_BANK_INST_TO_RNG(inst), root, nonce, nonceSz);
}
#endif /* WC_RNG_HAVE_RBGC */
#endif /* HAVE_HASHDRBG */
#else /* !WC_RNG_HAVE_LOCK */
/* Prototypes for backward compat implementations in rng_bank.c */
WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits);
WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get_conditional(
struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits,
WC_RNG_lock_arg_t want_extra_bits);
WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst);
WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits);
WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state);
WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits);
WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits);
WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_clear_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits);
#ifdef HAVE_HASHDRBG
WOLFSSL_TEST_VIS int wc_rng_bank_inst_invalidate_entropy(
struct wc_rng_bank_inst *inst);
WOLFSSL_TEST_VIS int wc_rng_bank_inst_reseed_now(
struct wc_rng_bank_inst *inst, const byte* nonce, word32 nonceSz);
#ifdef WC_RNG_HAVE_RBGC
WOLFSSL_TEST_VIS int wc_rng_bank_inst_reseed_rbgc(
struct wc_rng_bank_inst *inst, WC_RNG* root, const byte* nonce,
word32 nonceSz);
#endif /* WC_RNG_HAVE_RBGC */
#endif /* HAVE_HASHDRBG */
#endif /* !WC_RNG_HAVE_LOCK */
#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)
#ifndef WC_DRBG_RESEED_CTR_TYPE_DEFINED
#define WC_DRBG_RESEED_CTR_TYPE_DEFINED
#if defined(WORD64_AVAILABLE) && FIPS_VERSION3_GE(5,2,4) && \
FIPS_VERSION3_NE(6,0,0)
typedef word64 wc_drbg_reseed_ctr_t;
#else
typedef word32 wc_drbg_reseed_ctr_t;
#endif
#endif
#define wc_InitRngRBGC(leaf, root, flags) \
wc_InitRngNonceRBGC(leaf, root, NULL, 0, flags)
WOLFSSL_TEST_VIS int wc_RNG_GetStatus(const WC_RNG* rng);
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,
const byte* nonce, word32 nonceSz,
const byte *perso, word32 persoSz,
word32 flags);
WOLFSSL_TEST_VIS int wc_InitRngRBGC_New(WC_RNG** leaf, WC_RNG* root, word32 flags);
WOLFSSL_TEST_VIS int wc_RNG_DRBG_ScheduleReseed(WC_RNG* rng);
#endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */
#ifdef WC_RNG_DEBUG_STATS
WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s,
struct wc_rng_bank *bank);
#endif
#endif /* WC_RNG_BANK_SUPPORT */

View File

@@ -447,6 +447,7 @@ WOLFSSL_API int wc_RsaPublicKeyDecodeRaw(const byte* n, word32 nSz,
#ifndef WC_NO_RNG
WOLFSSL_API int wc_RsaSetRNG(RsaKey* key, WC_RNG* rng);
WOLFSSL_API int wc_RsaClearRNG(RsaKey* key);
#endif
#ifdef WC_RSA_NONBLOCK
WOLFSSL_API int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb);

View File

@@ -1515,7 +1515,7 @@
/* user needs to define XTIME to function that provides
* seconds since Unix epoch */
#ifndef XTIME
#error XTIME must be defined in wolfSSL settings.h
#error XTIME must be defined in wolfSSL user_settings.h
/* #define XTIME fnSecondsSinceEpoch */
#endif
@@ -4628,15 +4628,20 @@
#endif
#ifndef WC_RESEED_INTERVAL
/* In kernel mode, use the maximum reseed interval allowed by
/* In kernel mode, use the maximum mandatory reseed threshold allowed by
* NIST SP 800-90A Rev. 1, to avoid unnecessary delays in DRBG
* generation.
*/
#if defined(HAVE_FIPS) && \
FIPS_VERSION_LT(6,0) && FIPS_VERSION3_NE(5,2,4)
#define WC_RESEED_INTERVAL UINT_MAX
#elif defined(WC_16BIT_CPU) || defined(WC_32BIT_CPU) || defined(NO_64BIT)
#define WC_RESEED_INTERVAL UINT_MAX
#elif defined(__x86_64__) || defined(__ia64__) || \
defined(__aarch64__) || defined(__mips64)
#define WC_RESEED_INTERVAL (W64LIT(1) << 48)
#else
#define WC_RESEED_INTERVAL (((word64)1UL)<<48UL)
#define WC_RESEED_INTERVAL UINT_MAX
#endif
#endif
@@ -4644,6 +4649,11 @@
#define WC_VERBOSE_RNG
#endif
#if defined(WC_VERBOSE_RNG) && defined(WOLFSSL_KERNEL_VERBOSE_DEBUG) && \
!defined(WC_RNG_NO_DEBUG_STATS) && !defined(WC_RNG_WANT_DEBUG_STATS)
#define WC_RNG_WANT_DEBUG_STATS
#endif
#if WOLFSSL_GENERAL_ALIGNMENT < SIZEOF_LONG
#undef WOLFSSL_GENERAL_ALIGNMENT
#define WOLFSSL_GENERAL_ALIGNMENT SIZEOF_LONG
@@ -5982,10 +5992,6 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS."
#error "If TLS is enabled please make sure either client or server is enabled."
#endif
#if defined(WC_RNG_BANK_SUPPORT) && defined(NO_ASN_TIME)
#undef WC_RNG_BANK_SUPPORT
#endif
/* The OCSP responder time-stamps every response it generates (producedAt,
* thisUpdate and, for revoked certs, revocationDate), so it needs ASN time
* support. */

View File

@@ -926,6 +926,11 @@ enum {
#endif /* WOLFSSL_STATIC_MEMORY */
#endif
#if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY) && \
!defined(WC_NO_CONSTRUCTORS)
#define WC_NO_CONSTRUCTORS
#endif
#if defined(WOLFSSL_SMALL_STACK) && defined(WC_NO_CONSTRUCTORS)
#error WOLFSSL_SMALL_STACK requires constructors.
#endif

View File

@@ -620,8 +620,11 @@
#endif /* !WOLFSSL_NO_ATOMICS */
#ifdef WOLFSSL_NO_ATOMICS
typedef volatile int wolfSSL_Atomic_Int;
typedef volatile unsigned int wolfSSL_Atomic_Uint;
/* Note, not volatile. _NO_ATOMICS configs promise no concurrent mutation
* (single-threaded, or externally serialized); volatile would imply
* protection these types do not and cannot provide here. */
typedef int wolfSSL_Atomic_Int;
typedef unsigned int wolfSSL_Atomic_Uint;
#define WOLFSSL_ATOMIC_INITIALIZER(x) (x)
#define WOLFSSL_ATOMIC_LOAD(x) (x)
#define WOLFSSL_ATOMIC_STORE(x, val) (x) = (val)
@@ -787,6 +790,88 @@
}
#endif
/*** Macro abstractions for compare-and-exchange retry loops, allowing ***/
/*** platform-specific instrumentation and failure paths. ***/
/* WC_CAS_WITH_RETRY_EXTRA_DECLS allows declaration and initialization of
* variables (e.g. a counter) just above the retry loop in
* WC_CAS_WITH_RETRY_BEGIN().
*/
#ifndef WC_CAS_WITH_RETRY_EXTRA_DECLS
#define WC_CAS_WITH_RETRY_EXTRA_DECLS \
struct wc_cas_with_retry_dummy_struct
#endif
/* Note that freeform code after WC_CAS_WITH_RETRY_BEGIN() and before
* WC_CAS_WITH_RETRY_LOOP_UNTIL() sits inside the loop -- continue
* in that span omits the refresh of cur_var by the CAS (potentially inducing an
* infinite loop), and break in that span without setting result_var leaves it
* at WC_FAILURE. return and goto both behave normally in the freeform span.
*/
#define WC_CAS_WITH_RETRY_BEGIN(targetvar_p, cur_var, result_var) \
do { \
int WC_CAS_WITH_RETRY_keep_looping = 1; \
WC_CAS_WITH_RETRY_EXTRA_DECLS; \
\
(result_var) = WC_NO_ERR_TRACE(WC_FAILURE); \
\
while (WC_CAS_WITH_RETRY_keep_looping)
#define WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(targetvar_p, cur_var, result_var) \
do { \
int WC_CAS_WITH_RETRY_keep_looping = 1; \
WC_CAS_WITH_RETRY_EXTRA_DECLS; \
\
(cur_var) = WOLFSSL_ATOMIC_LOAD(*(targetvar_p)); \
(result_var) = WC_NO_ERR_TRACE(WC_FAILURE); \
\
while (WC_CAS_WITH_RETRY_keep_looping)
#ifndef WC_CAS_WITH_RETRY_ITER_CLAUSE
#define WC_CAS_WITH_RETRY_ITER_CLAUSE(targetvar_p, cur_var, \
want_val, result_var) WC_DO_NOTHING
#endif
/* The "until_clause" should be portable logic of overriding salience, used in
* situ by the direct (portable) user code. The _ITER_CLAUSE is for
* non-portable logic, such as CPU/scheduler relaxation/yield or deadlock
* detection, and is free to set result_var and break as it sees fit.
*
* Freeform code can appear between WC_CAS_WITH_RETRY_LOOP_UNTIL() and
* WC_CAS_WITH_RETRY_END(), and will be evaluated iff the CAS succeeds.
*/
#define WC_CAS_WITH_RETRY_LOOP_UNTIL(cmpxchg_method, targetvar_p, cur_var, \
want_val, result_var, until_clause) \
if (! cmpxchg_method(targetvar_p, &(cur_var), want_val)) { \
(result_var) = (until_clause); \
if ((result_var) != 0) \
break; \
{ \
WC_CAS_WITH_RETRY_ITER_CLAUSE(targetvar_p, cur_var, \
want_val, result_var); \
} \
continue; \
} \
else { \
(result_var) = 0; \
WC_CAS_WITH_RETRY_keep_looping = 0; \
} \
WC_DO_NOTHING
#ifndef WC_CAS_WITH_RETRY_FOREVER_CLAUSE
#define WC_CAS_WITH_RETRY_FOREVER_CLAUSE 0
#endif
#define WC_CAS_WITH_RETRY_LOOP_FOREVER(cmpxchg_method, targetvar_p, \
cur_var, want_val, result_var) \
WC_CAS_WITH_RETRY_LOOP_UNTIL(cmpxchg_method, targetvar_p, cur_var, \
want_val, result_var, \
WC_CAS_WITH_RETRY_FOREVER_CLAUSE)
/* Note, WC_CAS_WITH_RETRY_END() has no side effects -- it's just the success
* arm that ends the loop. */
#define WC_CAS_WITH_RETRY_END } while (0)
/* Reference counting. */
typedef struct wolfSSL_RefWithMutex {
#if !defined(SINGLE_THREADED)
@@ -1582,11 +1667,18 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void);
#endif
#elif defined(TIME_OVERRIDES)
/* Override XTIME() and XGMTIME() functionality.
Requires user to provide these functions:
time_t XTIME(time_t * timer) {}
struct tm* XGMTIME(const time_t* timer, struct tm* tmp) {}
*/
/* User-supplied override XTIME() and XGMTIME() functionality.
*
* Requires user-supplied macro definitions for XTIME() and XGMTIME(),
* mapping to function with signatures time_t time_f(time_t * timer) and
* struct tm* gmtime_f(const time_t* timer, struct tm* tmp) respectively.
*/
#ifndef XTIME
#error TIME_OVERRIDES requires a user-supplied XTIME definition.
#endif
#ifndef XGMTIME
#error TIME_OVERRIDES requires a user-supplied XGMTIME definition.
#endif
#ifndef HAVE_TIME_T_TYPE
#define USE_WOLF_TIME_T
#endif