# Patch Analysis: CVE-2026-5466 — wolfSSL ECCSI Universal Signature Forgery Fix

## What the Fix Changes

Upstream commit `13a016367ff4b4d3cc4c9bc2bfdfe692a512dd81` ("eccsi: fix universal signature forgery via r=0/s=0") modifies a single file:

- **File**: `wolfcrypt/src/eccsi.c`
- **Lines added**: 35 insertions across three locations.

### Change 1: `s` range check in `eccsi_calc_j` (lines 2159–2171)

After `eccsi_decode_sig_s` decodes `s` from the signature buffer, the fix adds:

```c
if (mp_iszero(s)) {
    err = MP_ZERO_E;
}
else if (mp_cmp(s, &key->params.order) != MP_LT) {
    err = ECC_OUT_OF_RANGE_E;
}
```

This mirrors the `wc_ecc_check_r_s_range` helper already used by ECDSA. It ensures `1 <= s < q` before `eccsi_mulmod_point(key, s, j, j, 1)` is executed. Without this check, `s = 0` causes the scalar multiplication to yield the point at infinity, whose affine x-coordinate evaluates to 0, leading to the final `mp_cmp(0, 0) == MP_EQ` acceptance.

### Change 2: `r` range check in `wc_VerifyEccsiHash` (lines 2250–2262)

Immediately after `eccsi_decode_sig_r_pvt` decodes `r` and before any scalar multiplication takes place, the fix adds:

```c
if (mp_iszero(r)) {
    err = MP_ZERO_E;
}
else if (mp_cmp(r, &params->order) != MP_LT) {
    err = ECC_OUT_OF_RANGE_E;
}
```

This prevents `r = 0` from reaching the arithmetic that would otherwise produce `J_x = 0` and an unconditional `mp_cmp(0, 0)` match.

### Change 3: Point-at-infinity guard (lines 2298–2308)

Before the final `mp_cmp(jx, r)` comparison, the fix adds:

```c
if (wc_ecc_point_is_at_infinity(j)) {
    err = ECC_INF_E;
}
```

This is a defense-in-depth measure. It catches any future path that might reach this point with a neutral-element result (e.g., `s ≡ 0 (mod q)` for a non-zero encoded `s` that passes the zero check but is congruent to 0 modulo `q`).

## What Assumptions the Fix Makes

1. **Single verifier entry point**: The fix assumes `wc_VerifyEccsiHash` is the only caller of `eccsi_calc_j` and the only public API that performs ECCSI signature verification. This assumption holds in v5.9.1: `eccsi_calc_j` is `static` and is called exactly once, from `wc_VerifyEccsiHash` (line 2297).

2. **Signature buffer format is exact**: `eccsi_decode_sig_r_pvt` and `eccsi_decode_sig_s` both enforce `sigSz == sz * 4 + 1`. The fix assumes no other code path can inject an `r` or `s` value into the key state without going through these decoders.

3. **Curve order is loaded before the checks**: `eccsi_load_ecc_params` is called earlier in `wc_VerifyEccsiHash`, so `params->order` is guaranteed to be initialized when the checks run.

4. **No overflow in `mp_cmp` or `mp_iszero`**: The fix relies on wolfSSL’s `mp_int` implementation correctly identifying zero and comparing against the order. These primitives are well-tested across the library.

## What Code Paths / Inputs the Fix Does NOT Cover

1. **No `wc_ecc_check_r_s_range` reuse**: The fix inlines the range checks rather than calling the existing `wc_ecc_check_r_s_range` helper from `wolfcrypt/src/ecc.c`. While this is functionally equivalent, it means any future change to the helper (e.g., adding a constant-time comparison) will not automatically apply to ECCSI.

2. **No check in the signing path**: `wc_SignEccsiHash` / `eccsi_gen_sig` already has a retry loop that avoids `s = 0` and `s = HE`, but it does not enforce `r < q` after fitting `jx` to octets. However, `r` is derived from a cryptographically random ephemeral key, so the probability of `r = 0` is negligible. This is not a security gap because the signing path is not an attacker-controlled trust boundary.

3. **No change to key-pair validation**: `wc_ValidateEccsiPair` and `wc_ValidateEccsiPvt` are unchanged. These functions validate PVT and key pairs; they do not decode attacker-supplied signature scalars, so they are outside the scope of this vulnerability.

4. **SAKKE is untouched**: The closely related SAKKE implementation in `wolfcrypt/src/sakke.c` is not modified by this patch. SAKKE is a key-encapsulation mechanism, not a signature scheme, and does not share the same `r`/`s` scalar verification arithmetic.

5. **No upper-layer protocol checks**: wolfSSL does not expose ECCSI through any TLS/SSL layer; it is a raw `wolfcrypt` API. Therefore there are no higher-level protocol handlers that might bypass or re-encode signatures.

## Is the Fix Complete?

**Yes, for the reported vulnerability.** The three checks collectively ensure that:
- `r` cannot be 0 or >= q before any scalar multiplication.
- `s` cannot be 0 or >= q before the `[s](...)` multiplication.
- Even if an unexpected arithmetic path produces the point at infinity, the defense-in-depth guard catches it.

All tested variant inputs (zero scalars, boundary scalars, over-boundary scalars, malformed sizes, infinity PVT encodings, and different messages) are correctly rejected by the fixed version.

## Comparison: Behavior Before and After the Fix

| Input | Before Fix (v5.9.0) | After Fix (v5.9.1) |
|-------|---------------------|--------------------|
| `r=0, s=0` | **ACCEPTED** (universal forgery) | `MP_ZERO_E` (-121) |
| `r=q, s=q` | Rejected (verified=0) | `ECC_OUT_OF_RANGE_E` (-217) |
| `r=q+1, s=q+1` | Rejected (verified=0) | `ECC_OUT_OF_RANGE_E` (-217) |
| `r=2q, s=2q` | Rejected (verified=0) | Rejected (verified=0) |
| `r=0, s=q-1` | Rejected (verified=0) | `MP_ZERO_E` (-121) |
| `r=1, s=0` | Rejected (verified=0) | `MP_ZERO_E` (-121) |
| Wrong `sigSz` | `BAD_FUNC_ARG` (-173) | `BAD_FUNC_ARG` (-173) |
| PVT = infinity | `ECC_INF_E` (-140) | `ECC_INF_E` (-140) |

The fix is surgical and does not alter legitimate verification behavior: valid signatures continue to verify successfully.
