The PSS hook can hand back the recovered block through out/outSz/outLen.
A device that reports only a verdict leaves outLen at 0; wolfSSL then
zeroes the buffer and returns saltLen + hLen, and rejects a buffer
smaller than that with RSA_BUFFER_E. A reported length is clamped to the
buffer size, and any positive handler return maps to SIG_VERIFY_E.
Move the Ed448 sign WOLFSSL_CHECK_MEM_ZERO registration below the crypto
callback hook so the device path no longer returns past it, and guard the
RSA-PSS test callback against WOLF_CRYPTO_CB_ONLY_RSA.
Adds tests for the recovered-data, over-claimed-length and undersized
buffer paths, and an os-check config that builds the hooks under
WOLFSSL_CHECK_MEM_ZERO.
Add WOLF_CRYPTO_CB dispatch hooks so a device can service:
* Ed448 sign and verify, mirroring the existing Ed25519 hooks.
* CMAC context free on wc_CmacFree (WOLF_CRYPTO_CB_FREE), letting a device
release offload state.
* RSA-PSS verify with the digest (WOLF_CRYPTO_CB_RSA_PAD) so the device does
the full signature and padding check. On that path *out is set to NULL with a
positive return, documented in rsa.h.
Includes testwolfcrypt and API unit test coverage for each hook.
Added documentation of the defines for algorithms and assembly code and math.
user_settings_embedded.h has configuration defines at the top that are set to
0 for off and 1 for on. Setting these turns on the appropriate defines.
Clean up includes single return point from every function, consistent formatting function block comment and tests added.
ssl_api_rw.c: Cleanup; 6 helpers extracted from write_dup/shutdown; fixed a SendBuffered corner case that returned 0 instead of an error.
ssl_api_ext.c: Cleanup; extracted wolfssl_ticket_key_cb_process, wolfssl_rehandshake_prepare.
ssl_api_hs.c: Cleanup + de-indent; 9 helpers extracted, incl. shared wolfssl_handshake_flush/_done between connect and accept (~100 duplicated lines removed). wolfSSL_connect/accept left multi-exit.
ssl_api_cert.c: Cleanup of newer functions at end of file; extracted PushPeerCertToChain; fixed a double free in CreatePeerCertChain; fixed 5 wrong @param names.
ssl_api_crl_ocsp.c: Full cleanup of all 49 functions; wolfSSL_OCSP_parse_url rewritten; fixed a URL with no host returning success with the rest of the URL as the host; added IPv6 literal support; fixed 4 wrong WOLFSSL_ENTER names.
ssl.c: Moved the x509GetIssuerFromCM forward declaration here for clarity.
wc_ecc_import_raw, wc_ecc_import_raw_ex and wc_ecc_import_unsigned all
reach wc_ecc_import_raw_private, which only checks that the imported
public point lies on the curve when WOLFSSL_VALIDATE_ECC_IMPORT is
defined. That flag is off in default builds and set in FIPS builds. The
x963 and ASN.1 import paths validate unconditionally, so the raw
importers are the one public entry point where a caller has to know to
validate. Nothing in the name or the existing documentation said so.
Also corrects the brief for wc_ecc_import_raw, which described the
function as filling a key with the components of a signature.
wc_LmsKey_Reload and wc_XmssKey_Reload return success without doing any
work whenever key->devId != INVALID_DEVID, on the assumption that a
device-bound key has its private state inside that device. That assumption
does not hold for a caller that sets devId only to route primitives to a
hardware accelerator while keeping the key state in its own storage.
A wolfHSM server is exactly that caller. It configures a server-wide devId
so AES, ECC and RSA reach the platform accelerator, then for LMS/XMSS it
installs read/write callbacks and calls Reload to rebuild the expanded
private key before signing. With a hardware devId configured, Reload
returned 0 immediately and the key was left unusable for the first sign
that follows. The failure needs no invalid input, only a server built with
an accelerator.
For LMS that first sign is a crash: key->priv_data stays NULL, wc_hss_sign
finds priv.inited clear and calls wc_hss_init_auth_path, which derives its
first read from a NULL priv pointer. On a target without a mapping at low
addresses that is a bus fault. For XMSS the skipped reload never allocates
key->sk, leaving both the pointer NULL and sk_len 0, so the outcome depends
on the caller's read callback: one that honours the length it is given
returns nothing and the sign fails with IO_FAILED_E, while one that writes
a fixed-size record faults on the NULL destination.
Key generation was unaffected and hid the problem: wc_LmsKey_MakeKey
already treats devId as "offer the operation to the callback, fall back to
software on CRYPTOCB_UNAVAILABLE", so it populates the key correctly when
the accelerator declines. Reload had no equivalent fall-through, so the
same key and the same devId were interpreted two different ways by the
same API.
Qualify the short-circuit with key->read_private_key == NULL. A caller that
has installed a read callback is asking for the software reload to fetch
the state through it, whereas a genuinely device-backed key installs no
such callback. Key generation keeps offering the operation to the crypto
callback, so a port with real stateful-hash-signature hardware is not
prevented from using it.
The reference POSIX wolfHSM server runs with INVALID_DEVID, which is why
this was not caught by existing tests. test_wc_LmsKey_reload_devid and
test_wc_XmssKey_reload_devid cover both arms of the new condition: a key
whose read callback is set must come back from Reload with its private key
expanded (priv_data for LMS, sk for XMSS) and able to sign, while a key on
the same devId with no read callback must still short-circuit. Both tests
register a crypto callback that declines every operation with
CRYPTOCB_UNAVAILABLE, which is the accelerator this fix is about. Asserting
on the expanded key means the old behaviour fails the assertion rather than
the NULL dereference it leads to. The rest of the LMS and XMSS suite is
unaffected, as every other key there uses INVALID_DEVID.
The XMSS test needs the H10 SHA-256 parameter set, which is only in the
algorithm table when both the hash and the height are compiled in, so it
carries a guard for that. The crypto callback's own guard is the exact
union of the two test guards, or a build with only one of the two
algorithms would emit it with no caller and fail -Werror.
Both the Reload implementation comments and the published Doxygen now state
that the read callback, not the devId, decides whether the software reload
runs.
wc_DhSetKey_ex loads DH parameters as untrusted and validates that the
modulus is prime, but it passed no RNG, so the check fell back to a
Miller-Rabin test using the fixed small-prime bases 2 through 19. That
test is defeatable: a composite crafted as a strong pseudoprime to those
known bases passes as prime, letting an attacker supply a composite
modulus with a smooth factorization for small-subgroup recovery of the
private exponent and shared secret.
When no RNG is supplied on the untrusted path, create a temporary RNG so
mp_prime_is_prime_ex runs with random witnesses, which such crafted
composites cannot reliably pass. Named FFDHE primes still short-circuit
the check, and builds without an RNG keep the deterministic test.
Fixes F-6776.
wc_ecc_shared_secret_gen_sync ran the scalar multiplication and then
copied the x-coordinate to the output without checking whether the
result was the point at infinity. Both math backends report success for
the identity: ecc_map_ex sets x, y to zero and z to one and returns
success, and the single precision generators serialize the identity as
an all-zero x-coordinate. Either way a shared secret that computed to
infinity was handed back as an all-zero secret with a success code,
where SP 800-56Ar3 5.7.1.2 requires an error and stop.
Check the mapped point on the software path, and detect the all-zero
output after the single precision generators, returning ECC_INF_E in
both cases. The scan accumulates over the whole buffer so it does not
branch on the secret.
A key whose private value is resident in an SE050 carries no software
scalar, so the software multiply legitimately yields the identity for
it. Skip the check for those keys specifically, rather than for a zero
scalar: on a prime-order curve a zero scalar is the one way the identity
can arise, so exempting it would disable the check for the case it
exists to catch.
Fixes F-6770.
wc_Chacha_Process validated only its pointer arguments and then produced
keystream directly from the context state. A zero-initialized ChaCha
context, common for static or global storage, that received a nonce via
wc_Chacha_SetIV but never had wc_Chacha_SetKey called would encrypt with
an all-zero, attacker-predictable key and still return success. This is
the same fail-open class already guarded against in wc_Arc4Process.
Add a keySet flag to the ChaCha struct, set it in wc_Chacha_SetKey, and
return MISSING_KEY from wc_Chacha_Process when the key was never set.
Fixes F-6893.
With WOLFSSL_RW_THREADED the read path performs no scheduled work, because
transmitting from the reader would race the write thread over the output
buffer and the sending key schedule, neither of which is covered by a lock.
Post-handshake the only remaining consumers are on the write path, and
wolfSSL_dtls_retransmit() only helps while the handshake is unfinished. An
application that reads without writing therefore never acknowledges a
NewSessionTicket, KeyUpdate or connection ID message, and the peer keeps
retransmitting what it is waiting to have acknowledged. RFC 9147 relies on
those ACKs, so this is a protocol level break rather than a missed
optimisation.
Add wolfSSL_dtls13_do_scheduled_work() so such an application can send that
work from its write thread, and wolfSSL_dtls13_pending_work() so it can tell
when there is any.
Both entry points ask the same helpers rather than each testing conditions of
their own, so they cannot drift into the predicate promising work the pump
then declines or silently discards, which would leave a drain loop spinning or
mislead the caller about what happened. That covers key updates in particular:
none is sent while one of ours is unacknowledged, since DTLS must not have two
in flight and Tls13UpdateKeys() drops a locally scheduled one in that state,
and a peer request is kept rather than dropped until it can be answered. The
predicate also errs towards reporting work when it cannot tell, so a loop
surfaces the error rather than stopping silently.
Refusing an object is treated as a usage error and leaves ssl->error alone.
That field is sticky, since SendData() only clears it for WANT_WRITE, pending
async work and the DTLS MAC and decrypt cases, and wolfSSL_write() skips its
write-dup drain while it is set, so recording one would disable the very drain
a write-dup application depends on.
Write-dup pairs are out of scope on both sides. They park the read side's work
in the shared WriteDup struct, which only wolfSSL_write() reconciles, so they
already have a drain. Completing a key update we started ourselves is out of
scope too: that needs the peer's acknowledgement processed, which rotates the
sending keys and creates an epoch, and the epoch table has no locking while
the read thread mutates it as well.
The declaration is gated to match where the definitions live, so a lean build
is not promised a symbol it does not get.
The lazy creation paths in wolfSSL_CertManagerEnableCRL,
wolfSSL_CertManagerEnableOCSP and wolfSSL_CertManagerEnableOCSPStapling
stored the freshly allocated object in the shared certificate manager
before zeroing and initializing it. A certificate manager is shared by
every WOLFSSL created from a CTX, so another thread could observe the
non-NULL pointer and operate on uninitialized memory, for example by
taking crl->crlLock before InitCRL had created it.
Build each object in a local, initialize it, and store it in the
certificate manager only on success. The CRL lookup callback is set on
the local as well, so a thread that picks the object up cannot find it
without one and fall back to CRL_MISSING.
Serialize the creation with caLock and re-check the pointer after
locking, so that two concurrent Enable calls cannot both allocate and
leak one of the objects. This does not make every writer of the pointer
safe: wolfSSL_X509_STORE_add_crl() still publishes cm->crl with no lock,
and readers observe it without one.
Dispose of a half-built object after releasing caLock rather than under
it. Neither free can actually block here: InitCRL() sets tid to
INVALID_THREAD_VAL before any of its failure returns so FreeCRL() skips
the monitor join, and FreeOCSP() takes no lock at all. The point is to
keep the critical section down to the decision of what to publish, and
to keep caLock out of the CRL free path as a rule: FreeCRL() on a
published object joins the CRL monitor thread, which takes crlLock,
while the verification path already takes crlLock (CheckCertCRLList())
before caLock (GetCA()).
Fixes F-7235.
Add AES-GCM (128/256) as an ECIES DEM next to the AES-CBC/CTR+HMAC modes. Only the encryption key comes from the KDF; the mac salt is bound as GCM AAD and the 16-byte tag replaces the HMAC. The GCM DEM honors all three IV build modes, and default fixed-nonce GCM is gated behind the new WOLFSSL_ECIES_STATIC_GCM_NONCE opt-in. Adds ECIES CryptoCb encrypt/decrypt, the WOLF_CRYPTO_CB ctx getters, devId/heap threading into the DEM primitives, and test/benchmark/CI coverage.
Add WOLFSSL_TLS_READ_AHEAD (--enable-readahead), toggled at runtime via
wolfSSL_set_read_ahead(). When enabled, the record-header read pulls a
full record in one recv() so the body arrives without a second syscall.
The receive window is configurable with
wolfSSL_CTX/SSL_set_default_read_buffer_len() (OpenSSL-compatible):
0 keeps the one-record default, a larger value coalesces several records
per recv(), a smaller value caps the per-connection buffer footprint.
Records exceeding the window are still received correctly, the buffer
grows on demand and is reallocated back down to the window afterwards so
the retained footprint stays bounded.
Includes docs, API tests, and a benchmark toggle.
wolfSSL removed liboqs: Falcon is now provided natively by wolfCrypt, and
--with-liboqs is a deprecated no-op (configure.ac). A build therefore no
longer links liboqs, so recording it as an SBOM dependency is dead code and
the SBOM integration CI (which asserted a liboqs dep package) failed.
Remove the liboqs dependency throughout:
- scripts/gen-sbom: drop DEP_META['liboqs'] and the --dep-liboqs flag.
- Makefile.am / configure.ac: drop --dep-liboqs "$(ENABLED_LIBOQS)" and the
now-unused AC_SUBST([ENABLED_LIBOQS]).
- .github/workflows/sbom.yml: drop the liboqs install / --with-liboqs steps
and the liboqs dep assertion; keep the native-Falcon build so the
HAVE_FALCON build-property capture is still exercised.
- scripts/test_gen_sbom.py: drop the liboqs-specific tests, guard against
the key reappearing, and use openssl as the example dep elsewhere.
- doc/SBOM.md: drop the --dep-liboqs / liboqs dependency references.
- gen-advisory: honour explicit per-version status when defaultStatus is
"affected", so unaffected/fixed releases are no longer marked vulnerable
- gen-advisory: fail loudly when a CVE record has no non-empty English
description (CSAF/CycloneDX note text is required, minLength 1)
- gen-advisory: note that --cve-id fetches from the CVE Services API
- bomsh_verify: scope the object-store shape check to sha1, matching the
sha1 gitoid hashing (drop the unreachable sha256-length branch)
- Makefile.am: fail `make bomsh` early when python3/pyspdxtools are absent;
quote $(ENABLED_LIBZ)/$(ENABLED_LIBOQS); consolidate clean-local so the
omnibor/ and advisories/out/ build dirs are removed on clean
- tests: cover the defaultStatus fix, the _bucket_for unknown-state
hard-fail, and a csaf_validate.mjs runner self-test wired into CI
Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
Add tooling to produce Software Bills of Materials and build provenance
for wolfSSL, supporting EU Cyber Resilience Act (CRA) obligations.
SBOM generation:
- New `make sbom` target producing SPDX 2.3 output with NTIA minimum
elements, urn:uuid document namespaces, and SPDX LicenseRef compliance.
- Reproducible library discovery across autotools and CMake builds, with
liboqs recorded as a linked artefact.
- Standalone `scripts/gen-sbom` for embedded / RTOS / custom-builder
flows that do not use the main build system, plus --srcs-file,
--no-artifact-hash, and hash-source options.
Build provenance (OmniBOR / bomsh):
- End-to-end bomsh tracing of the built binaries with ArtifactID
insertion, snapshotting the traced library before libtool relink and
hashing the bomsh-traced binary.
- `scripts/bomsh_verify.py` to validate provenance against the traced
gitoid.
Security advisories:
- `scripts/gen-advisory` generating CSAF 2.0 and CycloneDX VEX, with a
`make` target, VEX overlay schema/example, and CWE name data.
Docs, tests, and CI:
- doc/SBOM.md and doc/CRA.md, plus README/INSTALL updates.
- Unit and regression tests for gen-sbom and gen-advisory.
- New sbom.yml and advisory.yml workflows: SPDX validation via
pyspdxtools, CSAF validation, bomsh provenance verification, SBOM
artifact archiving, macOS coverage, and actions pinned to SHAs.
Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
Remaining fixes from the second review round:
- keygen: falcon_compute_public's scratch buffer holds NTT(f) (private-key
material) in its tail; wc_ForceZero it before both frees (the
f-not-invertible reject path and the success path). Also zeroize the
internally allocated hwork for consistency with the tmpbuf hardening.
- sampler: falcon_sampler_z's rejection loop never consulted the sticky
PRNG error flag, so a mid-signature SHAKE256 squeeze failure could make
berexp deterministically reject and the loop spin forever. Check p.err
each iteration and bail out; the returned value is discarded since
falcon_sign_core rejects the whole signature once p.err is set.
falcon_prng_init now frees the SHAKE256 context when a later init step
fails (plugs a device-context leak in WOLFSSL_ASYNC_CRYPT builds), and
falcon_prng_refill early-returns once the error is latched instead of
re-issuing failing squeezes.
- codec: guard the bits-dependent shifts in falcon_trim_i8_encode/decode
against out-of-range widths (defense in depth; callers only pass 5..8).
- check_key: implement the cryptographic private/public cross-check that
91ebd89d7 documented as a follow-up. New falcon_native_check_key decodes
(f, g) from the private key and h from the public key and verifies the
defining relation h*f == g (mod q, mod X^n + 1) slot-wise in the NTT
domain (falcon_ntt keeps values canonical in [0, q)); a slot with
NTT(f) == 0 is rejected too, as keygen only emits invertible f.
wc_falcon_check_key dispatches to it whenever the native signing core is
compiled in, and falls back to the presence check in verify-only /
callback-only builds. Doxygen updated to the actual contract, and a unit
test added: a mismatched pair (public half from a different key) must
fail with PUBLIC_KEY_E. This also strengthens the keypair validation
done via wc_falcon_check_key in asn.c.
- wc_falcon.c: replace the lazily-initialised, mutable NTT twiddle-table
cache with precomputed read-only const tables (falcon_zetas/izetas_l1/l5).
This removes the data race on the shared cache (a reader could see the
init flag set before all table entries were visible) and also drops the
now-unused falcon_brv / falcon_build_tables helpers. Verify stays fast
(const reads, no per-call rebuild); KAT confirms the values.
- falcon.c: correct the wc_falcon_sign_msg doc comment (signing needs the
*private* key; required buffer size is the active level's signature size,
not always FALCON_LEVEL1_SIG_SIZE) and validate rng != NULL in the
software path so the error is reported at the API boundary.
- wc_falcon_sign.c / wc_falcon_sign.h: route key->heap into
falcon_complete_private and falcon_expand_privkey (their large fpr scratch
allocations previously used a NULL heap hint, bypassing custom heap
routing); and fail fast in falcon_do_sign_tree on the sampler's sticky
PRNG error (passed in as samplerErr) instead of running to the restart
bound.
- wc_falcon_codec.h: use the project-standard WOLF_CRYPT_WC_FALCON_CODEC_H
include guard instead of the collision-prone generic FALCON_CODEC_H.
- doc/dox_comments (ssl.h + -ja): drop the stale "or HAVE_LIBOQS" from the
ML-KEM hybrid group requirements; liboqs is no longer supported.
Add doc/dox_comments/header_files/falcon.h covering every public wc_falcon_* /
wc_Falcon_* function (init/init_ex/init_id/init_label, set/get level, make_key,
sign/verify, import/export public+private, check_key, sizes, and the DER
encode/decode helpers), plus a Falcon \defgroup in doxygen_groups.h. List
Falcon in the INSTALL algorithm summary.
Docs refer to the algorithm only as "Falcon" (it is not standardized yet); the
temporary-name note and the configure --help / summary text are reworded to not
name a specific future standard.
Added support for encoding and decoding keys in ASN.1.
Added support for X.509 certificates and CSRs.
Generated certificates and CSRs. Not fo FrodoKEM-640 as is not in the specs.
RFC 9973 "TLS 1.3 Extension for Using Certificates with an External
Pre-Shared Key" is now published and obsoletes RFC 8773. The
cert_with_extern_psk implementation was written against the 8773bis
draft that became RFC 9973, so it is already compliant.
Update the textual references in comments, the configure help comment,
the extension codepoint comment, a test comment, and the Doxygen docs.
This is a documentation only change. The WOLFSSL_CERT_WITH_EXTERN_PSK
macro, the --enable-cert-with-extern-psk option, the public API names,
and the extension codepoint 33 (0x0021) are all unchanged, and no logic
is affected. Historical ChangeLog and README entries are left as they
shipped.