# 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, &params->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.
