# REPRO-2026-00173: wolfSSL: ECCSI universal signature forgery via missing scalar range check ## Summary Status: published Severity: high Type: security Confidence: Unknown ## Identifiers REPRO ID: REPRO-2026-00173 CVE: CVE-2026-5466 ## Package Name: wolfssl Ecosystem: source Affected: < 5.9.1 Fixed: 5.9.1 ## Root Cause # Root Cause Analysis: CVE-2026-5466 — wolfSSL ECCSI Universal Signature Forgery ## Summary wolfSSL's implementation of ECCSI (RFC 6507 — Elliptic Curve-based Certificateless Signatures for Identity-based Encryption) contains a critical flaw in `wc_VerifyEccsiHash` where the scalar signature components `r` and `s` are decoded from the signature buffer using `mp_read_unsigned_bin` without validating that they lie in the mathematically required range `[1, q-1]` (where `q` is the curve order). When either scalar is zero, the subsequent verification arithmetic collapses to identities that the verifier mistakes for a valid signature. An attacker can craft a forged signature with `r = 0, s = 0` (or other trivial constants) that passes verification for **any message** under **any identity** without possessing any private key material. ## Impact - **Package/Component**: wolfSSL — `wolfcrypt/src/eccsi.c`, specifically the `wc_VerifyEccsiHash` verifier and `eccsi_calc_j` helper - **Affected Versions**: wolfSSL `< 5.9.1` (last vulnerable tag: `v5.9.0-stable`) - **Fixed Version**: `5.9.1-stable` (commit `13a016367ff4b4d3cc4c9bc2bfdfe692a512dd81`) - **Risk Level**: High (CVSS 3.1: 8.1, CVSS 4.0: 7.6) - **Consequences**: Universal signature forgery — any message can be authenticated as signed by any identity. ECCSI is used in MIKEY-SAKKE, 3GPP MCData/MCVideo, and other identity-based PKI deployments where this flaw effectively nullifies signature security. ## Root Cause The ECCSI verification algorithm (RFC 6507, Section 5.2.2) requires checking that signature scalars `r` and `s` are valid non-zero elements less than the curve order `q`. wolfSSL's `wc_VerifyEccsiHash` decodes `r` via `eccsi_decode_sig_r_pvt` and `s` via `eccsi_decode_sig_s`, but proceeds directly to the scalar multiplications and final comparison without range validation. The arithmetic collapse occurs as follows: 1. With `s = 0`, the scalar multiplication `[s]([HE]G + [r]Y)` in `eccsi_calc_j` evaluates to the point at infinity, whose affine x-coordinate `J_x` is `0`. 2. With `r = 0`, the final comparison `mp_cmp(J_x, r)` becomes `mp_cmp(0, 0)`, which returns `MP_EQ`. 3. The verifier interprets this equality as "signature valid," unconditionally accepting the forgery regardless of the message or identity. The fix (commit `13a01636`) adds three defensive checks: 1. In `wc_VerifyEccsiHash`: reject `r` if `mp_iszero(r)` or `mp_cmp(r, ¶ms->order) != MP_LT`, returning `MP_ZERO_E` or `ECC_OUT_OF_RANGE_E`. 2. In `eccsi_calc_j`: reject `s` with the same `[1, q-1]` range checks after decoding. 3. Defense-in-depth: before the final comparison, reject `J` if it is the point at infinity using `wc_ecc_point_is_at_infinity(j)`. ## Reproduction Steps The reproduction is fully automated by `repro/reproduction_steps.sh`, which: 1. Clones the wolfSSL repository (if not already present). 2. Builds vulnerable wolfSSL `v5.9.0-stable` with `--enable-eccsi --enable-ecc --enable-sha256` into `/tmp/wolfssl-vuln`. 3. Builds fixed wolfSSL `v5.9.1-stable` with the same flags into `/tmp/wolfssl-fixed`. 4. Compiles `repro/eccsi_forge.c` against both libraries. 5. Runs the forgery harness against both versions and captures output to `logs/vulnerable_forge.txt` and `logs/fixed_forge.txt`. The harness (`repro/eccsi_forge.c`): 1. Generates a valid ECCSI KMS key and identity pair on curve P-256. 2. Signs a real message to obtain a valid 129-byte signature (`r | s | PVT`). 3. Constructs multiple forged signatures by replacing `r` and `s` with: - `r = 0, s = 0` - `r = q, s = q` - `r = q, s = 0` - `r = q, s = 1` - `r = 2q, s = 2q` while keeping the original (valid) `PVT` point. 4. Calls `wc_VerifyEccsiHash` with each forged signature against an attacker-chosen message. 5. Also tests `r = 0, s = 0` against a completely different, never-signed message. **Expected evidence of reproduction**: - **Vulnerable build**: at least one forged scalar tuple returns `ret=0, verified=1`. The harness confirms `r=0, s=0` succeeds for both the original message and an unrelated message. - **Fixed build**: all forged tuples are rejected. `r=0, s=0` returns `MP_ZERO_E (-121)`; `r=q, s=q` and related out-of-range values return `ECC_OUT_OF_RANGE_E (-217)`. ## Evidence - **Vulnerable run log**: `logs/vulnerable_forge.txt` - Key excerpt: ``` Test: r=0, s=0 wc_VerifyEccsiHash ret=0, verified=1 *** FORGERY ACCEPTED *** Test: r=0, s=0 with DIFFERENT message wc_VerifyEccsiHash ret=0, verified=1 *** FORGERY ACCEPTED *** ``` - **Fixed run log**: `logs/fixed_forge.txt` - Key excerpt: ``` Test: r=0, s=0 wc_VerifyEccsiHash ret=-121, verified=0 Forgery rejected (expected) Test: r=q, s=q wc_VerifyEccsiHash ret=-217, verified=0 Forgery rejected (expected) ``` - **Validation verdict**: `repro/validation_verdict.json` contains `{"verdict": "confirmed"}` because the vulnerable verifier accepted a forgery and the fixed verifier rejected all tested forgeries. - **Environment**: Ubuntu-based container, wolfSSL built from source with `--enable-eccsi`, linked statically, compiled with GCC. ## Recommendations / Next Steps 1. **Upgrade immediately** to wolfSSL `>= 5.9.1-stable` (or apply commit `13a01636` as a patch). The fix is minimal (35 lines) and only touches `wolfcrypt/src/eccsi.c`. 2. **Validate all ECCSI signatures** processed by pre-5.9.1 systems as potentially forged. Because the attack requires no secret material and works universally, any signature received from an untrusted channel before the upgrade is suspect. 3. **Regression tests**: Ensure CI includes a test case that submits `r = 0, s = 0` and `r = q, s = q` to `wc_VerifyEccsiHash` and expects failure. This directly exercises the missing checks. 4. **Audit other ECCSI implementations** in the same codebase (or derived from wolfSSL) for the same missing range checks, especially any custom verifiers that may have been copied from the vulnerable code. ## Additional Notes - **Idempotency confirmed**: `repro/reproduction_steps.sh` was executed twice consecutively; both runs produced identical verdicts (`confirmed`) and the same error codes. - **Edge cases tested**: The harness tests not only `r=0, s=0` but also boundary cases (`r=q, s=q`, `r=2q, s=2q`). Only the zero-scalar case produced a successful universal forgery on the vulnerable build; the others were correctly rejected by the arithmetic or the point-at-infinity handling in the vulnerable code. This confirms the attack surface is specifically the `r=0, s=0` path. - **No ASAN / sanitizer required**: This is a pure logic bug in the cryptographic protocol; memory safety tools do not detect it. The reproduction demonstrates the actual security failure (universal forgery) rather than a crash or memory corruption. ## Reproduction Details Reproduced: 2026-05-28T12:51:58.945Z Duration: 988 seconds Tool calls: 159 Turns: 117 Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00173 pruva-verify CVE-2026-5466 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00173&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00173/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-5466 ## Artifacts - bundle/repro/rca_report.md (analysis, 6872 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 2897 bytes) - bundle/vuln_variant/rca_report.md (analysis, 7238 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 2775 bytes) - bundle/context.json (other, 3667 bytes) - bundle/metadata.json (other, 572 bytes) - bundle/ticket.md (ticket, 5653 bytes) - bundle/repro/validation_verdict.json (other, 200 bytes) - bundle/repro/eccsi_forge.c (other, 8280 bytes) - bundle/vuln_variant/eccsi_variant.c (other, 10259 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 1002 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 5734 bytes) - bundle/vuln_variant/variant_manifest.json (other, 2664 bytes) - bundle/vuln_variant/validation_verdict.json (other, 3159 bytes) - bundle/logs/variant_vulnerable.txt (other, 2560 bytes) - bundle/logs/variant_fixed.txt (other, 2566 bytes) - bundle/logs/vulnerable_forge.txt (other, 1960 bytes) - bundle/logs/fixed_forge.txt (other, 1966 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00173 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00173/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00173 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev