## Ticket: CVE-2026-44699 — libjwt JWT algorithm-confusion authentication bypass

**Advisory**: GHSA-q843-6q5f-w55g — https://github.com/benmcollins/libjwt/security/advisories/GHSA-q843-6q5f-w55g
**CVE**: CVE-2026-44699 | **CWE-347** (Improper Verification of Cryptographic Signature)
**Severity**: Critical — CVSS 3.1 base 9.1 (AV:N/AC:L/PR:N/UI:N)
**Package**: `libjwt` (C library) | **Repository**: https://github.com/benmcollins/libjwt

### Impact

`libjwt` parses JSON Web Keys (JWKs) into an internal union key structure. When
an RSA JWK that **lacks an `alg` parameter** is supplied as the verification key
for an HMAC-signed token (`HS256`/`HS384`/`HS512`), libjwt through `3.3.2`
accepts that RSA key for HMAC verification. The HMAC octet (secret) field of the
union is left **zero-length**, so HMAC verification runs with an **empty key**.

Because the public JWKS is, by definition, public, an attacker who knows only
the published RSA JWK can forge an HMAC token signed with an empty key and have
it verify successfully — a full **authentication bypass**.

### Affected / fixed versions

Affected: `libjwt` `3.0.0` → `3.3.2` (inclusive).
Fixed: **`3.3.3`**.

Reproduce on a vulnerable build (**`3.3.2`**) and verify the fix on
**`3.3.3`**.

### Where to look

Build both versions from the repository:

```bash
git clone https://github.com/benmcollins/libjwt.git
cd libjwt && git checkout v3.3.2   # vulnerable
# ... and v3.3.3 for the fixed build
```

Inspect the `3.3.2`→`3.3.3` history (`git log v3.3.2..v3.3.3`) to confirm the
root cause: the fix rejects an RSA JWK (or any key whose type does not match the
token algorithm family) when verifying an HMAC token, instead of falling through
to HMAC verification with a zero-length secret.

### Reproduction approach

No service, database, or browser is needed — the bug is observable purely
in-process from a small C harness.

1. Build the **vulnerable** `libjwt 3.3.2`.
2. Write a small C harness that:
   - constructs an RSA public JWK with **no `alg` field**,
   - forges an `HS256` JWT signed with an **empty HMAC key**,
   - calls libjwt's verification API with the RSA JWK as the verification key,
   - exits `0` if verification succeeds, non-zero if it fails.
3. Rebuild the **same harness** against the **fixed** `libjwt 3.3.3`.

### Expected result

| Build | Verify forged HS256 token against RSA JWK (no `alg`) | Harness exit |
|-------|------------------------------------------------------|--------------|
| `libjwt 3.3.2` (vulnerable) | RSA key accepted for HMAC; empty-key HMAC verifies the forgery | **0** (verification OK) |
| `libjwt 3.3.3` (fixed) | key/algorithm mismatch rejected | **non-zero** (verification fails) |

- **Vulnerable indicator**: the forged token verifies successfully on `3.3.2`
  (harness exits `0`).
- **Fixed indicator**: verification is rejected on `3.3.3` (harness exits
  non-zero).

### Expected artifacts

- `reproduction_steps.sh` — builds both libjwt versions, compiles the C
  harness against each, and runs it, printing the verification outcome.
- `validation_verdict.json` — `verdict: confirmed`, with the vulnerable and
  fixed indicators captured.
- Logs capturing the harness output for `3.3.2` and `3.3.3`.
