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

810 Commits

Author SHA1 Message Date
philljj
354598aa05 Merge pull request #11842 from stenslae/fix/pqc-cryptocb-verify-res
Report PQC verify as invalid whenever it returns an error
2026-10-23 18:11:17 -05:00
Tobias Frauenschläger
4a4253682a Keep an HMAC key named by id on its device
wc_HmacInit_Id and wc_HmacInit_Label name a key held by a crypto
callback device, and the caller keys the Hmac with wc_HmacSetKey(NULL,
0). Two things lost that key.

Keying the Hmac again, for a new MAC or another hash, frees it first,
and wc_HmacFree zeroes the whole struct. Only devId was put back, so the
id and label were gone and the device got a key with no name and no
bytes. Restore the id and label as well, as devId is.

When the device declined an update or final, the software path ran
with the empty key from wc_HmacSetKey(NULL, 0) and returned a MAC under
it. Return MISSING_KEY instead when the Hmac is named by id or label and
has no key bytes, as AES does for a key held on a device. An Hmac named
by id that was given key bytes still uses them. A build with PK
callbacks but no crypto callbacks also has wc_HmacInit_Id, but no device
to hold the key and no keyRaw to tell, so the check is left out there.
The check uses keyRaw, which is NULL only after wc_HmacSetKey(NULL, 0).
keyLen keeps only the low 16 bits of the length, so it reads 0 for a
key of 64 KiB.

PKCS#11 declined every such key on a token that reports a minimum HMAC
key size, because it checked hmac->keyLen, which is 0 when the key is
on the token. Check the length only for a key passed in from software;
C_SignInit checks the token key.

test_wc_HmacId_CryptoCb keys an Hmac from wc_HmacInit_Id twice and
checks that the device still sees the id, then that a declined update
and final return MISSING_KEY. It also checks that a label survives a
second wc_HmacSetKey. test_wc_HmacId_NoDevice checks that an Hmac named
by id MACs with key bytes it is given, in builds with or without crypto
callbacks, also with a 64 KiB key, and that one without key bytes gets
MISSING_KEY when no device takes it.
2026-10-23 15:23:36 -07:00
Tobias Frauenschläger
ca9ac5d2cb Document device-owned keys for wc_AesSetKey and wc_AesCcmSetKey
wc_AesSetKey now says that a mode the callback does not handle returns
MISSING_KEY once a device claims the key, and that a device error can
be returned. wc_AesCcmSetKey says a cached CCM nonce is kept and lists
its real return values instead of "none".

The SETKEY callback contract also says that a WOLF_CRYPTO_CB_SETKEY
callback must release its key and clear aes->devCtx before returning an
error, because wolfCrypt clears devCtx itself only after an
AES_SETKEY error.
2026-10-23 08:56:15 +02:00
Tobias Frauenschläger
6c712d628e Close three gaps around a key a SETKEY device claims
wc_AesGetKeySize() reports aes->keylen for a key a crypto callback
device owns, so the MAX3266X ECB and CBC entry points, which take a
zero return from it as proof of a key, ran the TPU on the wiped
all-zero aes->key after a claim and returned success. They now check
WC_AES_KEY_IS_SET() first and return MISSING_KEY, as the generic modes
do. The check is only in effect with MAX3266X_CB, which is also the
only MAX3266X build where a device can claim a key.

The wipe before a claim left aes->tmp and aes->left, so the keystream
of the old key buffered by CTR, CFB or OFB stayed in the context. The
generic, ARM32 and PPC set-key now clear both as well.

The docs told a callback to build a software schedule by calling
wc_AesSetKey() with aes->devId set to INVALID_DEVID. Under
WOLF_CRYPTO_CB_FIND the find callback can map INVALID_DEVID back to
the same device, so that call recurses into the callback without end.
The docs now ask for a devId no device is registered under, as the
test devices in tests/api.c already use.
2026-10-23 08:56:15 +02:00
Tobias Frauenschläger
3d3521b833 Document how wc_AesGcmSetKey and a claiming callback treat the key
The wc_AesGcmSetKey() doxygen did not say what this series changed for
its callers: a new key drops the streaming state, so the update calls
return MISSING_IV until the next wc_AesGcmInit(); a nonce cached with
wc_AesGcmSetIV() or wc_AesGcmSetExtIV() is kept; and a key a crypto
callback device claims without a software schedule leaves streaming GCM
at MISSING_KEY.

The callback contract told a callback that sets aes->rounds to write a
key schedule into aes->key. That is not enough in every build: the
bitsliced and C-fallback builds read other fields, which the pre-claim
wipe has just zeroed, and the AES-NI and ARMv8 flags and the buffered
keystream would still describe the previous key. README.md, the
doxygen page and the cryptocb.h docs now say to build the schedule by
calling wc_AesSetKey() on the same object with aes->devId set to
INVALID_DEVID, as the existing test callback does, which sets all of
that. A WOLF_CRYPTO_CB_AES_SETKEY request carries no key direction, so
only a WOLF_CRYPTO_CB_SETKEY callback can do this.

Two comments in aes.c are reworded.
2026-10-23 08:56:15 +02:00
Tobias Frauenschläger
6c8b85e0b5 Keep AES modes off a key a device claimed without a schedule
When a WOLF_CRYPTO_CB_SETKEY or WOLF_CRYPTO_CB_AES_SETKEY callback
claims an AES key, wc_AesSetKey() builds no software key schedule. The
wrapper still marked the context keyInstalled because the set-key
returned 0, so a mode the device then declined passed the
WC_AES_KEY_IS_SET guard and ran in software:

- On a fresh context rounds is 0. With the C implementation the mode
  fails with KEYUSAGE_E; with ARM assembly on aarch64 it crashes
  (SIGSEGV in AES_CBC_encrypt).
- On a context that held a software key before, rounds and the
  schedule were still the old key's. The declined mode then succeeded,
  encrypting with the previous key instead of the one just set.

The set-key now drops the old schedule before offering the key to the
device: rounds goes to 0, and the schedule, including the C-fallback and
bitsliced copies, and devKey are wiped so no material from the previous
key outlives the claim. After a claim the context is marked installed
only if the callback built a schedule itself, as the in-tree test
callbacks do by re-running the software set-key. Otherwise a declined
mode returns MISSING_KEY, matching wc_AesInit_Id().
WOLF_CRYPTO_CB_ONLY_AES builds keep the key marked installed, since
there every block goes to the device's ECB callback.

The same wipe also clears the streaming GCM key and the recorded key
length. Otherwise a context that held a software GCM key and was then
re-keyed through wc_AesSetKey() or wc_AesCcmSetKey() kept gcmKeySet, and
the ARM assembly build streamed GCM on the wiped schedule; and a set-key
the device failed left the old key length behind, which
wc_AesGetKeySize() reported as a valid key.

The ARM32 and PPC assembly builds have their own wc_AesSetKey(), which
set keyInstalled on every claim and kept the old schedule. They get the
same reset. On ARM32 a declined CBC on a freshly claimed key returned 0,
with output from the empty schedule. The PPC copy only offers the key
through WOLF_CRYPTO_CB_AES_SETKEY, so it resets on that path alone.

Streaming GCM had the same problem through gcmKeySet, which
wc_AesGcmSetKey() never cleared. After a software GCM key, a key the
device then claimed left the flag set, and with ARM assembly the
streaming API encrypted under the previous key and returned 0.
wc_AesGcmSetKey() now clears gcmKeySet before setting the key and sets
it again only on success. A set-key the device fails returns before
deriving H or copying the key to devKey, so a caller that ignores the
error cannot stream on the wiped schedule. Dropping the key also clears
nonceSet, so a context already set up for a message under the old key
needs a new wc_AesGcmInit() instead of streaming on that key's counter
state.

The flag is set before H is derived, so a failure there, a refused
vector register save in an AES-NI build or a failing wc_AesEncrypt(),
returned an error with gcmKeySet still set and H from the previous key.
That block now clears the flag on failure, as the aarch64 path already
did. Neither failure can be provoked from the API tests.

wc_AesGcmSetKey() also stops skipping H and the devKey copy when devCtx
is set. A key a device owns without a schedule now leaves through the
early return, so devCtx is only still set at that point when the context
has a software schedule: the device built one, or a
WOLF_CRYPTO_CB_AES_SETKEY device declined a re-key and kept its handle
from an earlier claim. In the second case the skip left H all zero, and
the C build produced streaming GCM tags that did not depend on the
ciphertext. The ARM assembly H derivation never checked devCtx.

The wc_CryptoCb_AesSetKey() docs promised a devKey copy and GCM tables
for software fallback in TLS builds, which no build provides, while the
README and the AES CryptoCB key import page promised the opposite with
no conditions. All three now describe what a claimed key leaves behind,
and that wolfCrypt takes a non-zero aes->rounds to mean the callback
built a software schedule. wc_AesGetKeySize() reports aes->keylen for a
key with a length but no rounds, so a callback has no reason to set
aes->rounds just to make key-size queries work. The keyInstalled field
comment in aes.h now says a claimed key counts only with a schedule.

The docs also now say that a callback declining a new key must first
release any key it holds for the object and set aes->devCtx to NULL.
wolfCrypt leaves devCtx alone on a decline, so a callback that kept its
handle went on running the operations it accepts, such as one-shot GCM,
under the old key while the software modes used the new one.

A WOLF_CRYPTO_CB_SETKEY claim now also stores the IV it was given in
aes->reg, as the AES_SETKEY claim and the software set-key already do,
so a device that reads the IV from aes->reg does not get the previous
key's.

test_wc_CryptoCb_Aes_SetKey_Declined claims a key, declines CBC and
expects MISSING_KEY, both on a fresh context and after a software key,
and checks the old key is gone from devKey. It needs
WOLFSSL_AES_REQUIRE_KEY_SET, since without the key-set check the
declined mode fails with KEYUSAGE_E instead.
test_wc_CryptoCb_AesGcm_SetKey covers the same software-then-claimed
sequence for streaming GCM.

test_wc_CryptoCb_AesSetKey checks the same for a key claimed through
WOLF_CRYPTO_CB_AES_SETKEY: CBC, which its device declines, returns
MISSING_KEY.

Both tests also check a set-key the device fails with WC_HW_E: CBC and
streaming GCM then return MISSING_KEY.

test_wc_CryptoCb_AesSetKey has its device release its handle and
decline a re-key to a different key, checks that one-shot GCM then fails
in the callback instead of using the old key, and compares the streaming
GCM tag with one computed in software under the new key.

test_wc_CryptoCb_Aes_SetKey_Declined also checks that a claimed key
takes the IV passed with it and reports its size, and that a failed
set-key reports none. test_wc_CryptoCb_AesGcm_SetKey re-keys a software
GCM key through wc_AesSetKey() and checks that streaming GCM refuses the
claimed key. test_wc_AesGcmStream checks that wc_AesGcmSetKey() on a
context initialized for a message leaves updates waiting for a new
wc_AesGcmInit().
2026-10-23 08:56:15 +02:00
Tobias Frauenschläger
5044ea40c5 Skip GCM H derivation for an AES key a SETKEY device claimed
With WOLF_CRYPTO_CB_SETKEY, a callback that returns 0 for WC_SETKEY_AES
owns the key: wc_AesSetKey() builds no key schedule and leaves rounds at
0. wc_AesGcmSetKey() still derived the hash subkey H from that empty
schedule. With the C implementation wc_AesEncrypt() rejects rounds 0 and
the set-key fails with KEYUSAGE_E. On aarch64 with ARM assembly,
AES_ECB_encrypt() runs with rounds 0 and crashes (SIGSEGV). The
function also copied the plaintext key into devKey, which should not stay
behind once a device owns the key.

wc_AesGcmSetKey() now returns as soon as the device has claimed the key,
before deriving H, marking the streaming API keyed or copying to devKey.
On that return it wipes the GHASH subkey a previous key may have left,
and the key unwrapped from a CAAM blob. One-shot GCM reaches the device
through its callback as before.

Streaming GCM has no callback, so it must refuse such a key. The update
calls already return MISSING_KEY while gcmKeySet is clear, but
wc_AesGcmInit() set up the IV without checking it: given a key and an
IV, it ran the counter block through the empty schedule, with the same
KEYUSAGE_E or SIGSEGV as above. It now sets up the IV only once a GCM
key is set. Without one it keeps the IV cached and returns 0, so the
first update returns MISSING_KEY and a later call that sets the key
picks the IV up. That is also what an IV passed before the key relies
on, which on master crashed on aarch64 and failed with KEYUSAGE_E in C.
An IV longer than a block cannot be cached, since it goes through GHASH,
so that case returns MISSING_KEY and leaves the context unchanged.

The same applies to a key claimed through the older
WOLF_CRYPTO_CB_AES_SETKEY hook. The C code already skipped H when the
callback set devCtx, but the ARM assembly derivation did not, so
test_wc_CryptoCb_AesSetKey crashed in AES_ECB_encrypt on aarch64 with
--enable-armasm. Both hooks signal ownership by leaving rounds at 0,
which the XTS entry points already rely on.

test_wc_CryptoCb_AesGcm_SetKey registers a device that claims the key and
performs GCM itself, checks the result against software, and checks that
streaming GCM refuses the key in both wc_AesGcmInit() and the update.

test_wc_CryptoCb_AesSetKey also checks that streaming GCM refuses an
AES_SETKEY claim, and test_wc_AesGcmStream that an IV set before the key
is used once the key arrives, through wc_AesGcmInit() with the key or
the first wc_AesGcmInit() after wc_AesGcmSetKey(), and that a long one
is refused.
2026-10-23 08:56:15 +02:00
Chris Conlon
278e98f952 Merge pull request #11798 from aidangarske/remove/whitewood-netrandom
Remove Whitewood netRandom support
2026-10-22 16:01:19 -06:00
philljj
a4bb52ba08 Merge pull request #11840 from Arpan0995/slhdsa-signhash-none-bad-func-arg
SLH-DSA: return BAD_FUNC_ARG for WC_HASH_TYPE_NONE from SignHashWithRandom and SignHashDeterministic
2026-10-22 16:11:13 -05:00
philljj
058fc6101b Merge pull request #11428 from julek-wolfssl/fix/get0-pubkey-borrowed
X509: cache decoded public key, give get0_pubkey borrowed semantics
2026-10-22 11:22:30 -05:00
kaleb-himes
6f594eeed1 Drop the ML-KEM private key when a different public key is decoded 2026-10-21 22:21:07 -07:00
kaleb-himes
81f276fd62 Zero the SLH-DSA key before the parameter lookup in wc_SlhDsaKey_Init 2026-10-21 22:21:07 -07:00
Emma Stensland
e65feb618d Report PQC verify as invalid whenever it returns an error
A device can write *res and then fail or decline, leaving a stale 1 on
an error return. Clear it in the PQC verify dispatchers, and clear
Falcon's *res on entry to cover early errors and silent devices.
2026-10-21 11:21:09 -06:00
Aidan Garske
274193c166 Remove Whitewood netRandom support 2026-10-21 09:52:05 -07:00
Daniel Pouzzner
be7257c6d9 wolfcrypt/src/random.c, wolfssl/wolfcrypt/random.h, wolfcrypt/src/wc_port.c, doc/dox_comments/header_files/random.h: refactor wc_Sha{256,512}Drbg_{Disable,Enable,IsDisabled}() to avoid drbgStateMutex.
* Adds WC_RNG_INIT_FLAG_DRBG_SHA{256,512}.

* Removes wc_DrbgState_MutexInit() and wc_DrbgState_MutexFree(), and the
  associated dependency on wolfCrypt_Init() before first call.

* Existing semantics are preserved exactly, now by compare-and-swap instead of a
  lock: the last enabled flavor cannot be disabled (BAD_STATE_E), concurrent
  enable/disable calls serialize against one another with no lost updates, and
  the setting is read once at each RNG instantiation.  (Note that racing threads
  on WOLFSSL_NO_ATOMICS targets can lose updates, but can never leave
  DrbgType_mask with an invalid value).

* Flags-bearing APIs -- wc_InitRng_ex2() etc -- now honor
  WC_RNG_INIT_FLAG_DRBG_SHA{256,512}, overriding the process-wide runtime
  setting.

* The refactor eliminates an unneeded process-wide mutex previously taken in
  every call to _InitRng(), which was particularly harmful in kernel mode.
  State is now tracked by bits in a single atomic unsigned int.
2026-10-21 02:40:18 -05:00
philljj
3846e798c1 Merge pull request #11804 from deadcaf3/worktree-update-keys-docs
Fix WANT_WRITE return docs for TLS 1.3 post-handshake APIs
2026-10-20 14:50:22 -05:00
Arpan Sharma
2fac436ee9 SLH-DSA: SignHashWithRandom returns BAD_FUNC_ARG for WC_HASH_TYPE_NONE
wc_SlhDsaKey_SignHashWithRandom() returned NOT_COMPILED_IN for
WC_HASH_TYPE_NONE, and so did wc_SlhDsaKey_SignHashDeterministic(),
which calls it. wc_SlhDsaKey_SignHash() and wc_SlhDsaKey_VerifyHash()
return BAD_FUNC_ARG for the same input from an identical check, and
the English doxygen for all four says BAD_FUNC_ARG. Return
BAD_FUNC_ARG here too, and check both functions for it in
test_wc_slhdsa_sign_hash.

Add the hashType BAD_FUNC_ARG line to the Japanese doxygen of the four
functions, as in the English. Fix three "NOT_COMPILED in" typos in the
function comments, and a SignHash() comment that said a bad hashSz
still draws from the DRBG.
2026-10-20 13:04:14 -05:00
philljj
c177f861d0 Merge pull request #11576 from douzzer/20260925-WC_RNG_RBGC_STRATUM_IMMUTABLE
20260925-WC_RNG_RBGC_STRATUM_IMMUTABLE
2026-10-20 10:15:26 -05:00
Daniel Pouzzner
23285d3ff7 wolfcrypt/src/random.c, wolfcrypt/test/test.c, doc/dox_comments/header_files/random.h: fix FIPS eligibility criteria for rng_src in wc_RNG_Pool_Collect2(). 2026-10-19 23:20:36 -05:00
JacobBarthelmeh
5f9ab72a47 Merge pull request #11733 from holtrop-wolfssl/f-7072
Use stored hash type for generic hash APIs
2026-10-19 16:24:31 -06:00
Josh Holtrop
eee10875a7 hash.c: check type argument in both debug and non-debug builds
F-7072.
2026-10-19 14:19:21 -04:00
deadcafe
b234b99d89 Fix WANT_WRITE return docs for TLS 1.3 post-handshake APIs
The Doxygen blocks for wolfSSL_update_keys() and
wolfSSL_request_certificate() told callers to compare the return value
against WANT_WRITE (-327). Both implementations convert that internal
code to the public WOLFSSL_ERROR_WANT_WRITE (3) before returning, so the
documented check could never match and a non-blocking caller would fall
into the generic failure branch.

Document WOLFSSL_ERROR_WANT_WRITE, the continuation contract (the
message stays queued and the next wolfSSL_write() sends it; do not call
the API again for the same update), and the QUIC rejection. For
wolfSSL_update_keys() over DTLS 1.3, note that success means the
KeyUpdate was sent, not acknowledged, and that a repeat call while the
ACK is outstanding returns WOLFSSL_SUCCESS without sending.

Fixes #11763
2026-10-19 08:38:42 +05:30
Emma Stensland
476ce9a5cf Add wc_MlDsaKey_MakePublicKey 2026-10-16 13:05:58 -07:00
philljj
0e4c88b656 Merge pull request #11772 from Arpan0995/lms-sigsleft-no-params
LMS: SigsLeft returns 0 for a key without parameters or verify-only
2026-10-16 13:52:06 -05:00
Juliusz Sosinowicz
a340f20c2e Address review: take const name in X509_NAME text and index getters
Match OpenSSL 3 for X509_NAME_get_text_by_NID, X509_NAME_get_index_by_NID
and X509_NAME_get_index_by_OBJ.
2026-10-16 11:04:33 -07:00
Tobias Frauenschläger
9bfeaff061 Merge pull request #11765 from Arpan0995/mldsa-initid-setparams
ML-DSA: set the device key level in InitId/InitLabel only on success
2026-10-16 10:06:08 +02:00
philljj
eb113cac67 Merge pull request #11547 from padelsbach/cbonly-lmsxmss
Add CB_ONLY mode for LMS+XMSS, add swdev
2026-10-15 17:01:48 -05:00
Arpan Sharma
6997631516 LMS: SigsLeft returns 0 for a key without parameters or verify-only
wc_LmsKey_SigsLeft() checked only for a NULL key and then passed
key->params to wc_hss_sigsleft(), which dereferences it. A key that
was only initialized has no parameters, so the call crashed. A
verify-only key (after wc_LmsKey_ImportPubRaw()) reported 1 although
wc_LmsKey_Sign() refuses it. Return 0 for both, as wc_LmsKey_Sign()
rejects such keys before it calls the device. The check is on the
verify-only state and not on WC_LMS_STATE_OK, because
wc_LmsKey_MakeKey() and wc_LmsKey_Reload() call wc_LmsKey_SigsLeft()
while the key is still in WC_LMS_STATE_PARMSET.

The doxygen for wc_LmsKey_SigsLeft() and wc_XmssKey_SigsLeft() said
they return the number of signatures left or a negative error code;
both return 1 or 0. Fix that in English and Japanese, and in the LMS
and XMSS group descriptions.
2026-10-15 13:16:02 -05:00
Arpan Sharma
f75fbe2cdd ML-DSA: set the device key level in InitId/InitLabel only on success
wc_MlDsaKey_InitId() and wc_MlDsaKey_InitLabel() called
wc_MlDsaKey_SetParams(key, WC_ML_DSA_87) even when their argument
check had failed and the key was never initialized, and ignored its
return value. With ML-DSA-87 compiled in (the default), an argument
error wrote into the caller's uninitialized object, and with the
WC_MLDSA_CACHE_* options or WOLFSSL_MLDSA_DYNAMIC_KEYS it zeroized and
freed whatever pointers that memory held. In builds without
ML-DSA-87 the call failed, so InitId and InitLabel returned 0 with no
parameter set.

Call SetParams only when the key was initialized, return its result,
and use the highest level compiled in, as wc_falcon_init_id() and
wc_falcon_init_label() do with FALCON_MAX_LEVEL.
2026-10-15 11:28:31 -05:00
Josh Holtrop
9c0f6b4624 hash.c: un-deprecate type parameter for generic hash functions in debug builds
Requested during code review feedback (PR 11733).
2026-10-15 11:45:21 -04:00
David Garske
fa565101ab mldsa: add WOLFSSL_MLDSA_ALLOW_WEAK_PREHASH for SHA-256-only TPM firmware 2026-10-15 08:26:38 -07:00
philljj
72b7471544 Merge pull request #11661 from julek-wolfssl/compat-max-send-fragment
Add SSL_[CTX_]set_max_send_fragment()
2026-10-14 23:01:37 -05:00
Daniel Pouzzner
2a133a160c wolfcrypt/src/rng_bank.c: a bank's root cannot be a full-mutex instance.
wc_rng_bank_root_rng_init() now refuses WC_RNG_INIT_FLAG_USE_FULL_MUTEX
with BAD_FUNC_ARG.  The bank operates its root through lock-word
operations alone -- the daemon's claims, reinit-in-place
(wc_rng_bank_reinit_rng()), the invalidation walk -- none of which take
the outer mutex, so a full-mutex root was bypassed by every one of them,
and its reinit's teardown (wc_FreeRng_PreLocked() under a lock-word-only
lease) released and destroyed a mutex the bank never took, under any
getter holding it.  wc_rng_bank_reinit_rng() gets a matching tripwire
(WRONG_TYPE_OBJECT_E on a WC_RNG_FLAG_FULL_MUTEX instance, before any
mutation), unreachable through the bank's own constructors.

doc/dox_comments/header_files/rng_bank.h: wc_rng_bank_root_rng_init()
documents the refusal (and the existing ALREADY_E).

wolfcrypt/test/test.c: rng_lifecycle_test() tries the flag on a vacated
root (refused, root still vacant), then instantiates and reinstantiates
the root without it.  WC_RNG_HAVE_LOCK_FULL_MUTEX builds only; no CI
scenario sets WC_RNG_WANT_LOCK_FULL_MUTEX, so verified locally with
--enable-rng-extras --enable-rng-bank CFLAGS=-DWC_RNG_WANT_LOCK_FULL_MUTEX.
2026-10-14 19:32:59 -05:00
Daniel Pouzzner
267d56d920 RNG review round 2: bank teardown against retained root references, lock-less bank root reinit, user seed on RDRAND hosts, and two linuxkm legacy-kernel fixes.
wolfcrypt/src/rng_bank.c:

* wc_rng_bank_fini(): a root pinned by an external (caller-spawned)
  child, or held, is now refused before commitment, with the bank fully
  intact -- STILL_REFERENCED_E / BUSY_E, as for a member.  Previously a
  root free failure was only tallied, the bank's refcount and flags were
  destroyed, and the retry of wc_rng_bank_free() deallocated the bank
  under the child's RBGC_parent.  The pre-commit check counts member
  pins (released by the member frees) and refuses on any pin beyond
  them, regardless of the root's status: a root torn down by a failed
  reinit is NOT_INIT with its lock word live and its pins outstanding,
  and was previously skipped.  The post-commit root free now restores
  the refcount and returns on BUSY_E / STILL_REFERENCED_E, as the member
  loop does, so a child that arrives after the pre-commit check still
  leaves a retryable bank.

* wc_rng_bank_reinit_rng(): WC_RNG_INIT_FLAG_PRESERVE_LOCK is added
  only when WC_RNG_HAVE_LOCK, as wc_rng_bank_inst_reinit() already does.
  _InitRng() refuses the flag with NOT_COMPILED_IN otherwise, so in a
  bank built without the lock facility wc_rng_bank_root_rng_reinit()
  tore the root down and could not reinstantiate it; the inst-op gate is
  the exclusion in that arm.

wolfcrypt/src/random.c:

* _InitRng(): on an RDRAND-capable CPU with HAVE_INTEL_RDRAND, the DRBG
  bypass now refuses caller-supplied seed material with
  WRONG_TYPE_OBJECT_E, the instance left uninstantiated.  Previously
  wc_InitRngNonce_UserSeed() and wc_RNG_global_fallback_init_user_seed()
  returned success with the seed silently dropped and the instance
  relabeled stratum 0, generating from RDRAND -- a primary-class
  instance under a user-seeded name.  Instantiating the DRBG anyway is
  not an option: wc_RNG_GenerateBlock() reads RDRAND whenever the CPU
  has it, DRBG or not.  wc_InitRngFixedSeed()'s post-hoc backstop
  (which stays, for CUSTOM_RAND_GENERATE_BLOCK) returns the same code.

doc/dox_comments/header_files/random.h: wc_InitRngNonce_UserSeed()
documents the refusal; the previous text described a bypass
"HAVE_INTEL_RDRAND without HAVE_HASHDRBG", which cannot reach the
function (WC_RNG_HAVE_RBGC requires HAVE_HASHDRBG), while the reachable
case went undocumented.

wolfcrypt/test/test.c: rng_lifecycle_test() gains the bank-teardown
scenario (external child of the root; refused fini leaves the bank
generating; held root; torn-down pinned root; convergence once the child
is released) and a lock-less bank root reinit.  The latter fails against
the previous rng_bank.c.

linuxkm/module_hooks.c: in the pre-3.17 NMI-context time accessors, read
jiffies after the calibration latch rather than at entry: a calibration
published by another CPU between the two can carry a later tick, and
the unsigned difference wrapped.

linuxkm/linuxkm_wc_port.h: in_task() shim for kernels before 4.10, where
it first appears (include/linux/preempt.h); wc_linuxkm_can_long_loop()
used it unconditionally.
2026-10-14 17:48:07 -05:00
Arpan Sharma
360f93b322 SLH-DSA: restore one-byte sigSz probe, sync Japanese doxygen
Go back to the one-byte sigSz probe for SignWithRandom() with a
(NULL, 0) message in test_wc_SlhdsaDecisionCoverage(), so the test
stays argument-check only. The signing path for (NULL, 0) is covered
in test.c.

Update the Japanese doxygen for SignDeterministic, SignWithRandom,
Sign and Verify to match the English header.
2026-10-14 15:27:59 -07:00
Arpan Sharma
d3c1d7a1fd SLH-DSA: update msg docs and empty-message tests per review
- Source comments: msg may be NULL if msgSz is 0 for the sign and
  verify functions, and Sign(), Verify() and slhdsakey_sign_external()
  reject a NULL msg only when msgSz is greater than 0.
- Doxygen: note on the msg parameter, and SignDeterministic() returns
  BAD_LENGTH_E for a too-small signature buffer.
- test.c: SignDeterministic() with (NULL, 0) must match the signature
  for a non-NULL message of length 0.
- test_slhdsa.c: SignWithRandom() with (NULL, 0) and a full-size
  signature buffer signs successfully.
2026-10-14 15:27:59 -07:00
Arpan Sharma
e0806fd809 SLH-DSA: accept (NULL, 0) message in SignWithRandom/SignDeterministic
wc_SlhDsaKey_SignWithRandom() rejected msg == NULL for any length, so it
and wc_SlhDsaKey_SignDeterministic() returned BAD_FUNC_ARG for an empty
message passed as (NULL, 0), unlike wc_SlhDsaKey_Sign() and Verify().
Use the same check as Sign() and, like Sign(), replace a NULL message
with a one-byte static stand-in before the crypto callback dispatch.
Update the doxygen and source comments, and add tests for the (NULL, 0)
case.
2026-10-14 15:27:59 -07:00
Daniel Pouzzner
8a65208daa RNG SP-800-90* compliance mechanism completion (the balance of the
WC_RNG_RBGC_STRATUM_IMMUTABLE campaign):

* RNG state-invalidation delivery by entropy epoch

* Fork safety for the WC_RNG lock word

* RBGC lineage persistence with a global fallback source

* A uniform "transient vs. verdict" error taxonomy

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

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

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

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

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

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

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

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

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

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

Lifecycle hardening (random.[ch]):

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

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

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

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

* wc_RNG_DRBG_GetReseedCtr() becomes WOLFSSL_LOCAL (the counter is
  potentially sensitive internal state); wc_RNG_DRBG_NextSeedGenerate_
  Primary() is added; wc_RNG_DRBG_NextSeedCurrent(), wc_RNG_Pool_Current()
  and wc_RNG_lock_read() take const.  An instance with no DRBG (RDRAND et
  al.) never opens its seed aperture, and producers and consumers alike
  refuse it with WRONG_TYPE_OBJECT_E (was MISSING_RNG_E, which nothing on
  that path returned).  A stir refuses with the needs-recovery verdict
  (NEEDS_RECOVERY_E) when the instance is quarantined, due, or stale, and
  NOT_READY_E only when the interval trips within the call.

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

rng_bank.c / rng_bank.h:

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

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

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

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

linuxkm/lkcapi_sha_glue.c:

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

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

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

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

Tests:

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

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

* doc/dox_comments: every new API and flag; the instantiation-input and
  user-seed contracts; reseed/reseed_range return semantics; the next-seed
  return taxonomy.
2026-10-14 16:13:43 -05:00
Daniel Pouzzner
0c6f105fde add WC_RNG_RBGC_STRATUM_IMMUTABLE: freeze WC_RNG.RBGCStratum at its
instantiated value (the genealogical birth stratum); implied by HAVE_FIPS.

wolfssl/wolfcrypt/random.h: define WC_RNG_RBGC_STRATUM_IMMUTABLE whenever
HAVE_FIPS; declare wc_InitRngNonce_UserSeed().

wolfcrypt/src/random.c: in WC_RNG_RBGC_STRATUM_IMMUTABLE builds, inhibit all
post-init stratum relabeling, and refuse credited user-class reseeds
(wc_RNG_DRBG_Reseed(), wc_RNG_DRBG_Reseed_Nonce()) of instances below
WC_RNG_RBGC_USER_SEED_STRATUM with WRONG_TYPE_OBJECT_E, ahead of lock
acquisition and length handling, lest an instance carrying the vetted ESV
provenance marker have its reseed counter cleared by unvetted material --
the conformant aperture for user material is the uncredited
wc_RNG_DRBG_Stir().  add wc_InitRngNonce_UserSeed(), instantiating
born-user-seeded instances at WC_RNG_RBGC_USER_SEED_STRATUM, reseedable by
the user in all builds; a null seed returns BAD_FUNC_ARG, and in Hash_DRBG
builds an undersized seed (seedSz below WC_DRBG_SEED_SZ, zero included)
returns BAD_LENGTH_E, so that an argument mistake cannot mint a
primary-class instance the caller believes is user-seeded.

src/ssl.c, src/ssl_crypto.c: enable the modern RNG arms of
wolfSSL_FIPS_drbg_reseed(), wolfSSL_RAND_seed(), wolfSSL_RAND_poll(), and
wolfSSL_RAND_load_file() whenever WC_RNG_RBGC_STRATUM_IMMUTABLE, alongside
the incumbent ((!HAVE_FIPS || FIPS_VERSION3_GE(7,0,0)) && !HAVE_SELFTEST)
gate; rework the wolfSSL_RAND_poll() modern arm to
wc_RNG_DRBG_Reseed_Now(&globalRNG, NULL, 0), with the wolfCrypt return
mapped to WOLFSSL_SUCCESS/WOLFSSL_FAILURE; route wolfSSL_RAND_seed() and
wolfSSL_RAND_load_file() user material through the uncredited
wc_RNG_DRBG_Stir(), treating NOT_READY_E as success.

wolfcrypt/test/test.c: in rng_drbg_rbgc_test(), add born-user-seeded
instantiation coverage (sentinel stratum at init, user reseed accepted and
stratum retained, generation); in random_test(), add the
wc_InitRngNonce_UserSeed() error-contract battery (null/undersized
arguments, and NOT_COMPILED_IN without WC_RNG_HAVE_RBGC) and sub-sentinel
reseed-refusal probes on a plain instance (WRONG_TYPE_OBJECT_E, including
at zero length, with generation unaffected); pivot stratum expectations on
the new RBGC_RESEED_STRATUM() frozen/updated selector; in the threaded
reseed test, churn with credited wc_RNG_DRBG_Reseed_Now() where user-class
reseeds are refused; drive bank user-seeding through
WC_RNG_BANK_FLAG_STIR, treating NOT_READY_E as success.

tests/api/test_random.c: WC_RNG_RBGC_STRATUM_IMMUTABLE-arm expectations
(WRONG_TYPE_OBJECT_E) at the wc_RNG_DRBG_Reseed() probes in
test_wc_RNG_DRBG_Reseed() and test_wc_DrbgDecisionCoverage().

doc/dox_comments/header_files/random.h: add the wc_InitRngNonce_UserSeed()
entry; update wc_RNG_DRBG_Reseed()/wc_RNG_DRBG_Reseed_Nonce() for the
stratum-immutable refusal, the reseed-obligation BAD_LENGTH_E floor,
condemned-instance RNG_FAILURE_E, and the nonce null-with-nonzero-length
argument check; update wc_RNG_DRBG_GetRBGCStratum(), ReseedRBGC(), the
spawn family, and NextSeedGenerate_RBGC() for frozen-stratum semantics;
rebalance briefs to declarative one-liners with rationale consolidated
into \details.
2026-10-14 16:13:42 -05:00
philljj
e9a8f62d6e Merge pull request #11181 from kareem-wolfssl/fbatch2
Fix multiple Fenrir issues
2026-10-14 12:31:25 -05:00
philljj
fde1138bda Merge pull request #11367 from julek-wolfssl/chain-verify-cb
Add WOLFSSL_CHAIN_VERIFY_CB to replace peer chain verification
2026-10-14 10:16:45 -05:00
Tobias Frauenschläger
a4800697f8 Jenkins fixes 2026-10-14 09:15:37 +02:00
Tobias Frauenschläger
f60510d63b Falcon: keep the verify buffer off the stack
falcon_native_verify_msg placed its 2*n word16 working buffer on the stack
unless WOLFSSL_SMALL_STACK was set. That buffer is sized for the highest
enabled level, so a Falcon-512 verify used 4 KB of stack for it whenever
Falcon-1024 was built in, and verify reached about 5.7 KB of stack on a
Cortex-M33/M7 while keygen and sign stay near 1.2-1.4 KB.

Verify now allocates the buffer per call, sized for the key's own level
(2 KB for Falcon-512, 4 KB for Falcon-1024), so it can return MEMORY_E;
the wc_falcon_verify_msg documentation says so. WOLFSSL_NO_MALLOC builds
keep the buffer on the stack. To keep it off the stack without an
allocator, WOLFSSL_FALCON_VERIFY_NO_MALLOC
(--enable-falcon=verify-no-malloc) holds the buffer in falcon_key
instead, sized for the highest enabled level, the same approach as
WOLFSSL_MLDSA_VERIFY_NO_MALLOC. With it, a key verifies one signature at
a time and falcon_key grows by 4*FALCON_MAX_N bytes.

On an STM32H743 a Falcon-512 verify goes from 5648 B of stack and no heap
to 1568 B of stack and 2048 B of heap, at the same speed. The peak stack
of a TLS 1.3 handshake that verifies a Falcon-512 chain drops from 6.3 KB
to 3.7 KB.

The falcon-interop backend matrix gains verify-no-malloc and
level1+dynamic-keys+verify-no-malloc legs running the full unit suite.
2026-10-14 09:15:37 +02:00
night1rider
28bc7540dd AES: leave WC_AES_DIRECT_CRYPTO_CB off for WOLFSSL_TI_CRYPT, and tie the Direct docs to it
Docs say a device that owns the key must not decline; test comments shortened to one line.
2026-10-13 16:13:51 -07:00
night1rider
1853572e89 AES: send wc_AesEncryptDirect/DecryptDirect to the crypto callback first
Direct skipped the callback, so a device-owned key ran in software with an empty key schedule.
2026-10-13 16:13:51 -07:00
Paul Adelsbach
122d7ecc5d PR feedback: block GetKid when privKey is not set, add test case and entry for CI 2026-10-13 10:35:14 -07:00
Juliusz Sosinowicz
71227c3743 Address review: list TLS 1.3 messages not split to send cap
Only Certificate and CertificateVerify are fragmented in TLS 1.3, the
same set that honours a negotiated max_fragment_length.
2026-10-13 16:14:14 +00:00
Juliusz Sosinowicz
31355ee141 Add SSL_[CTX_]set_max_send_fragment()
Local cap on the plaintext of each record sent, as in OpenSSL. Also
honour a negotiated max_fragment_length on DTLS writes and on
wolfSSL_write_dup() duplicates.
2026-10-13 16:14:14 +00:00
Tobias Frauenschläger
0dbc94b555 Merge pull request #11609 from julek-wolfssl/ctx-get-ciphers-compat
Add SSL_CTX_get_ciphers() compat API
2026-10-13 15:56:24 +02:00
philljj
615662cfbf Merge pull request #11460 from yosuke-wolfssl/fix/f_14055
session: initialize the external cache copy flag to 1
2026-10-13 08:36:50 -05:00