{"repro_id":"REPRO-2026-00173","version":7,"title":"wolfSSL: ECCSI universal signature forgery via missing scalar range check","repro_type":"security","status":"published","severity":"high","root_cause":"# Root Cause Analysis: CVE-2026-5466 — wolfSSL ECCSI Universal Signature Forgery\n\n## Summary\n\nwolfSSL'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.\n\n## Impact\n\n- **Package/Component**: wolfSSL — `wolfcrypt/src/eccsi.c`, specifically the `wc_VerifyEccsiHash` verifier and `eccsi_calc_j` helper\n- **Affected Versions**: wolfSSL `< 5.9.1` (last vulnerable tag: `v5.9.0-stable`)\n- **Fixed Version**: `5.9.1-stable` (commit `13a016367ff4b4d3cc4c9bc2bfdfe692a512dd81`)\n- **Risk Level**: High (CVSS 3.1: 8.1, CVSS 4.0: 7.6)\n- **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.\n\n## Root Cause\n\nThe 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.\n\nThe arithmetic collapse occurs as follows:\n\n1. 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`.\n2. With `r = 0`, the final comparison `mp_cmp(J_x, r)` becomes `mp_cmp(0, 0)`, which returns `MP_EQ`.\n3. The verifier interprets this equality as \"signature valid,\" unconditionally accepting the forgery regardless of the message or identity.\n\nThe fix (commit `13a01636`) adds three defensive checks:\n1. In `wc_VerifyEccsiHash`: reject `r` if `mp_iszero(r)` or `mp_cmp(r, &params->order) != MP_LT`, returning `MP_ZERO_E` or `ECC_OUT_OF_RANGE_E`.\n2. In `eccsi_calc_j`: reject `s` with the same `[1, q-1]` range checks after decoding.\n3. Defense-in-depth: before the final comparison, reject `J` if it is the point at infinity using `wc_ecc_point_is_at_infinity(j)`.\n\n## Reproduction Steps\n\nThe reproduction is fully automated by `repro/reproduction_steps.sh`, which:\n\n1. Clones the wolfSSL repository (if not already present).\n2. Builds vulnerable wolfSSL `v5.9.0-stable` with `--enable-eccsi --enable-ecc --enable-sha256` into `/tmp/wolfssl-vuln`.\n3. Builds fixed wolfSSL `v5.9.1-stable` with the same flags into `/tmp/wolfssl-fixed`.\n4. Compiles `repro/eccsi_forge.c` against both libraries.\n5. Runs the forgery harness against both versions and captures output to `logs/vulnerable_forge.txt` and `logs/fixed_forge.txt`.\n\nThe harness (`repro/eccsi_forge.c`):\n1. Generates a valid ECCSI KMS key and identity pair on curve P-256.\n2. Signs a real message to obtain a valid 129-byte signature (`r | s | PVT`).\n3. Constructs multiple forged signatures by replacing `r` and `s` with:\n   - `r = 0, s = 0`\n   - `r = q, s = q`\n   - `r = q, s = 0`\n   - `r = q, s = 1`\n   - `r = 2q, s = 2q`\n   while keeping the original (valid) `PVT` point.\n4. Calls `wc_VerifyEccsiHash` with each forged signature against an attacker-chosen message.\n5. Also tests `r = 0, s = 0` against a completely different, never-signed message.\n\n**Expected evidence of reproduction**:\n- **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.\n- **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)`.\n\n## Evidence\n\n- **Vulnerable run log**: `logs/vulnerable_forge.txt`\n  - Key excerpt:\n    ```\n    Test: r=0, s=0\n      wc_VerifyEccsiHash ret=0, verified=1\n      *** FORGERY ACCEPTED ***\n    \n    Test: r=0, s=0 with DIFFERENT message\n      wc_VerifyEccsiHash ret=0, verified=1\n      *** FORGERY ACCEPTED ***\n    ```\n\n- **Fixed run log**: `logs/fixed_forge.txt`\n  - Key excerpt:\n    ```\n    Test: r=0, s=0\n      wc_VerifyEccsiHash ret=-121, verified=0\n      Forgery rejected (expected)\n    \n    Test: r=q, s=q\n      wc_VerifyEccsiHash ret=-217, verified=0\n      Forgery rejected (expected)\n    ```\n\n- **Validation verdict**: `repro/validation_verdict.json` contains `{\"verdict\": \"confirmed\"}` because the vulnerable verifier accepted a forgery and the fixed verifier rejected all tested forgeries.\n\n- **Environment**: Ubuntu-based container, wolfSSL built from source with `--enable-eccsi`, linked statically, compiled with GCC.\n\n## Recommendations / Next Steps\n\n1. **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`.\n2. **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.\n3. **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.\n4. **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.\n\n## Additional Notes\n\n- **Idempotency confirmed**: `repro/reproduction_steps.sh` was executed twice consecutively; both runs produced identical verdicts (`confirmed`) and the same error codes.\n- **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.\n- **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.\n","cve_id":"CVE-2026-5466","package":{"name":"wolfssl","ecosystem":"source","affected_versions":"< 5.9.1","fixed_version":"5.9.1"},"reproduced_at":"2026-05-28T12:51:58.945602+00:00","duration_secs":988.0996057987212,"tool_calls":159,"turns":117,"handoffs":2,"total_cost_usd":1.2527528600000004,"agent_costs":{"repro":0.36404300999999994,"support":0.018205850000000003,"vuln_variant":0.8705040000000001},"cost_breakdown":{"repro":{"accounts/fireworks/models/kimi-k2p6":0.36404300999999994},"support":{"accounts/fireworks/models/kimi-k2p6":0.018205850000000003},"vuln_variant":{"accounts/fireworks/models/kimi-k2p6":0.8705040000000001}},"quality":{"idempotent_verified":false,"community_verifications":0},"published_at":"2026-05-28T12:52:01.341141+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":6872,"category":"analysis"},{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":2897,"category":"reproduction_script"},{"path":"bundle/vuln_variant/rca_report.md","filename":"rca_report.md","size":7238,"category":"analysis"},{"path":"bundle/vuln_variant/reproduction_steps.sh","filename":"reproduction_steps.sh","size":2775,"category":"reproduction_script"},{"path":"bundle/context.json","filename":"context.json","size":3667,"category":"other"},{"path":"bundle/metadata.json","filename":"metadata.json","size":572,"category":"other"},{"path":"bundle/ticket.md","filename":"ticket.md","size":5653,"category":"ticket"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":200,"category":"other"},{"path":"bundle/repro/eccsi_forge.c","filename":"eccsi_forge.c","size":8280,"category":"other"},{"path":"bundle/vuln_variant/eccsi_variant.c","filename":"eccsi_variant.c","size":10259,"category":"other"},{"path":"bundle/vuln_variant/root_cause_equivalence.json","filename":"root_cause_equivalence.json","size":1002,"category":"other"},{"path":"bundle/vuln_variant/patch_analysis.md","filename":"patch_analysis.md","size":5734,"category":"documentation"},{"path":"bundle/vuln_variant/variant_manifest.json","filename":"variant_manifest.json","size":2664,"category":"other"},{"path":"bundle/vuln_variant/validation_verdict.json","filename":"validation_verdict.json","size":3159,"category":"other"},{"path":"bundle/logs/variant_vulnerable.txt","filename":"variant_vulnerable.txt","size":2560,"category":"other"},{"path":"bundle/logs/variant_fixed.txt","filename":"variant_fixed.txt","size":2566,"category":"other"},{"path":"bundle/logs/vulnerable_forge.txt","filename":"vulnerable_forge.txt","size":1960,"category":"other"},{"path":"bundle/logs/fixed_forge.txt","filename":"fixed_forge.txt","size":1966,"category":"other"}]}