* add WC_FIPS_186_4, WC_FIPS_186_4_PLUS, WC_FIPS_186_5, and WC_FIPS_186_5_PLUS feature macros.
* add support for WC_HASH_CUSTOM_MIN_DIGEST_SIZE, WC_HASH_CUSTOM_MAX_DIGEST_SIZE, and
WC_HASH_CUSTOM_MAX_BLOCK_SIZE, for use with custom digest algorithms.
* add SigOidMatchesKeyOid() helper function and WC_MIN_DIGEST_SIZE macro.
* add additional size and OID agreement checks for sig gen and verify ops.
* update ecc_test_vector() with FIPS 186-5 vectors.
Co-authored-by: Tobias Frauenschläger <tobias@wolfssl.com>
When an EC_KEY is created via EC_KEY_new + EC_KEY_set_group +
EC_KEY_set_private_key (no public point set), SetECKeyInternal
incorrectly marks the internal ecc_key as ECC_PRIVATEKEY (instead of
ECC_PRIVATEKEY_ONLY) because pub_key is always non-NULL — EC_KEY_new
always allocates it as an empty, zero-initialised EC_POINT.
ECC_populate_EVP_PKEY only calls wc_ecc_make_pub for ECC_PRIVATEKEY_ONLY
keys, so the zero public-key point was serialised into the DER stored in
pkey->pkey.ptr. After commit 929dd9913 made wc_ecc_import_x963_ex always
pass untrusted=1, the re-decode inside wolfSSL_EVP_PKEY2PKCS8 →
wolfSSL_d2i_PrivateKey_EVP correctly rejected that zero point with an
on-curve failure, causing EVP_PKEY2PKCS8 to return NULL.
Fix: in ECC_populate_EVP_PKEY, also call wc_ecc_make_pub when the key
type is ECC_PRIVATEKEY but pubkey.x is zero (meaning the public key was
never actually populated). This reconstructs the public key from the
private scalar so that the encoded DER contains a valid on-curve point.
In TLSX_EchChangeSNI, the ctx->extensions branch set extensions
unconditionally even when TLSX_Find returned NULL. This caused
TLSX_UseSNI to attach the attacker-controlled publicName to the shared
WOLFSSL_CTX when no inner SNI was configured. TLSX_EchRestoreSNI then
failed to clean it up because its removal was gated on serverNameX !=
NULL. The inner ClientHello was sized before the pollution but written
after it, causing TLSX_SNI_Write to memcpy 255 bytes past the
allocation boundary.
Fix by mirroring the guarded pattern of the ssl->extensions branch:
only set extensions when TLSX_Find returns non-NULL, and only perform
the SNI swap when extensions is non-NULL. Also move TLSX_Remove in
TLSX_EchRestoreSNI outside the serverNameX guard so any injected
publicName SNI is always cleaned up.
Also return BAD_FUNC_ARG when ECH is used without an inner SNI,
preventing ECH ClientHello construction in an invalid configuration.
Reported by: Nicholas Carlini (Anthropic) & Thai Duong (Calif.io)
When an untrusted issuer has CA:FALSE and no verify_cb is registered,
the !isCa branch now fails closed (ret=WOLFSSL_FAILURE, goto exit)
instead of falling through and skipping X509StoreVerifyCert for the
leaf. SetupStoreCtxError_ex is also hardened to never overwrite a
previously recorded error with success, preventing a later valid chain
link from clobbering ctx->error back to X509_V_OK. Tests added for
both the no-callback rejection and the error-preservation cases.
Reported by: Nicholas Carlini (Anthropic) & Thai Duong (Calif.io)
wc_PKCS7_DecodeAuthEnvelopedData() accepted an attacker-controlled GCM tag
length from the mac OCTET STRING and did not validate it against the
parsed aes-ICVlen parameter. In parallel, wc_AesGcmDecrypt() accepted
very short tags on decrypt while encrypt enforced WOLFSSL_MIN_AUTH_TAG_SZ.
This made short-tag verification reachable through CMS AuthEnvelopedData
and weakened integrity checks by allowing tag truncation.
Fixes:
- validate parsed macSz range in AuthEnvelopedData decode
- require authTagSz to match parsed macSz
- reject undersized GCM tags in PKCS7 decode
- enforce WOLFSSL_MIN_AUTH_TAG_SZ in wc_AesGcmDecrypt() and
wc_AesGcmDecryptFinal()
Also add a regression test in pkcs7authenveloped vectors that truncates
the final MAC OCTET STRING length from 16 to 1 and verifies decode fails.
Reported by: Nicholas Carlini (Anthropic) & Thai Duong (Calif.io)
EVP_DecryptFinal_ex() called wc_ChaCha20Poly1305_Final() which only
computes the Poly1305 tag, writing it into ctx->authTag and
overwriting the expected tag stored there by EVP_CTRL_AEAD_SET_TAG.
No comparison was ever performed, so any forged tag was accepted.
Fix: save the expected tag before calling Final(), then verify with
wc_ChaCha20Poly1305_CheckTag() on the decrypt path, mirroring the
existing AES-GCM branch. Add a regression test that asserts
EVP_DecryptFinal_ex() rejects an all-zero forged tag.
Reported-by: Nicholas Carlini (Anthropic) & Bronson Yen (Calif.io)
wc_VerifyEccsiHash did not validate that r and s lie in [1, q-1]
after decoding them from the signature buffer. With s=0 the scalar
multiplication [s](...) returns the point at infinity (J_x=0); with
r=0 the final mp_cmp(0,0)==MP_EQ check then accepts the forged
signature unconditionally against any message and any identity.
Add [1, q-1] range checks for r (in wc_VerifyEccsiHash, after params
are loaded) and for s (in eccsi_calc_j, after eccsi_decode_sig_s),
mirroring the checks already present in wc_ecc_check_r_s_range.
Add a defense-in-depth point-at-infinity guard on J before the final
comparison.
Reported-by: Nicholas Carlini (Anthropic) & Bronson Yen (Calif.io)
The guard `if (cmac->totalSz != 0)` was used to skip XOR-chaining on
the first block (where digest is all-zeros and the XOR is a no-op).
However, totalSz is word32 and wraps to zero after 2^28 block flushes
(4 GiB), causing the guard to erroneously fire again and discard the
live CBC-MAC chain state. Any two messages sharing a common suffix
beyond the 4 GiB mark then produce identical CMAC tags, enabling a
zero-work prefix-substitution forgery. The fix removes the guard,
making the XOR unconditional; the no-op property on the first block is
preserved because digest is zero-initialized by wc_InitCmac_ex.
Identified by: Nicholas Carlini (Anthropic) & Thai Duong (Calif.io)
- wolfSSL_i2d_OCSP_REQUEST_bio: save/restore pointer before i2d call
that advances it, preventing BIO_write from wrong offset and heap
corruption on free
- wolfSSL_d2i_OCSP_RESPONSE: remove (unsigned char) cast that truncated
pointer advance to 8 bits, breaking responses larger than 255 bytes
- wolfSSL_OCSP_CERTID_dup: deep-copy CertStatus to prevent double-free
when both original and duplicate are freed
- wolfSSL_i2d_OCSP_RESPONSE: add NULL check on response parameter
- wolfSSL_i2d_OCSP_REQUEST: advance *data pointer per i2d convention
- FreeOCSP: NULL-check ocsp->cm before dereferencing for heap
- Fix WOLFSSL_LEAVE strings to match actual function names in
wc_CheckCertOcspResponse, GetOcspEntry, GetOcspStatus,
CheckOcspResponse, CheckOcspRequest
Add test for CERTID dup (double-free confirmed under ASAN without fix)
and pointer advancement assertions for d2i_OCSP_RESPONSE callers.
Reported in: ZD21469