Conversation
9695426 to
a46a2f6
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #10102
Scan targets checked: src, src-bugs, src-compliance, wolfcrypt-api_misuse, wolfcrypt-bugs, wolfcrypt-compliance, wolfcrypt-concurrency, wolfcrypt-portability, wolfcrypt-src
Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #10102
Scan targets checked: src, src-bugs, src-compliance, wolfcrypt-api_misuse, wolfcrypt-bugs, wolfcrypt-compliance, wolfcrypt-concurrency, wolfcrypt-portability, wolfcrypt-src
Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
9f0c33c to
2ab737b
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #10102
Scan targets checked: src, src-bugs, src-compliance, wolfcrypt-api_misuse, wolfcrypt-bugs, wolfcrypt-compliance, wolfcrypt-concurrency, wolfcrypt-portability, wolfcrypt-src
Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
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)
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)
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_ecc_import_x963_ex2 only checked whether an imported public point lies on the intended curve when both USE_ECC_B_PARAM was compiled in and the caller passed untrusted=1. In a default ./configure build, USE_ECC_B_PARAM is not defined, so the check was compiled out entirely. Additionally, the legacy wrapper wc_ecc_import_x963_ex unconditionally passed untrusted=0, meaning ECIES (wc_ecc_decrypt), PKCS#7 KARI, and the EVP ECDH layer never triggered the check even when the macro was present. wc_ecc_shared_secret performed no on-curve validation at all. An attacker who can supply an EC public key (e.g. via an ECIES ciphertext, PKCS#7 enveloped-data, or EVP_PKEY_derive) can choose a point on a twist of the target curve with a smooth-order subgroup. Each ECDH query leaks the victim's static private scalar modulo a small prime; CRT reconstruction across enough queries recovers the full key (Biehl-Meyer-Müller invalid-curve attack). Static-key ECIES and PKCS#7 KARI are directly affected; TLS is affected in default builds because the USE_ECC_B_PARAM gate defeated the untrusted=1 flag that the handshake does pass. Three changes close the attack: 1. Remove the USE_ECC_B_PARAM gate completely in the code base so that wc_ecc_point_is_on_curve() is compiled in all builds, not only those with HAVE_COMP_KEY or OPENSSL_EXTRA (only set for legacy FIPS builds in settings.h). 2. wc_ecc_import_x963_ex: pass untrusted=1 to wc_ecc_import_x963_ex2 so that ECIES, PKCS#7 KARI, and EVP callers that go through the four-argument wrapper always validate the imported point. 3. wc_ecc_shared_secret: add defense-in-depth on-curve check before scalar multiplication, catching any import path that bypassed the import-time validation (e.g. direct wc_ecc_import_x963_ex2 with untrusted=0). Both new validation sites dispatch to sp_ecc_check_key_NNN for SP-supported curves (P-256/384/521, SM2) when WOLFSSL_HAVE_SP_ECC is defined, keeping the mp_int stack cost off embedded targets. Non-SP curves fall back to wc_ecc_point_is_on_curve. Reported by: Nicholas Carlini (Anthropic) & Thai Duong (Calif.io) ecc
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)
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)
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 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 929dd99 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.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #10102
Scan targets checked: src, src-bugs, src-compliance, wolfcrypt-api_misuse, wolfcrypt-bugs, wolfcrypt-compliance, wolfcrypt-concurrency, wolfcrypt-portability, wolfcrypt-src
No new issues found in the changed files. ✅
|
only failing subtest is multi-test PRB |
zd21460