# JWT and PQC: Algorithm Identifiers in JOSE

**Source**: https://quantumsequrity.com/blog/jwt-pqc-algorithms
**Category**: Implementations

---

[← Back to Blog](../../blog.html) Implementations

# JWT and PQC: Algorithm Identifiers in JOSE

11 min read

A JSON Web Token (JWT) is the swiss army knife of stateless web authentication. It is a base64url-encoded blob with three parts separated by dots: a header, a payload, and a signature. Every modern API gateway speaks JWT. Every modern SSO flow ends in a JWT. Every microservice mesh that does anything more than mutual TLS uses JWTs to carry identity.

JWT itself is defined in RFC 7519. The signature format under the hood is JSON Web Signature (JWS), defined in RFC 7515. The encryption format is JSON Web Encryption (JWE), defined in RFC 7516. The key format is JSON Web Key (JWK), defined in RFC 7517. Together this family is called JOSE — JSON Object Signing and Encryption.

When NIST finalized FIPS 203 (ML-KEM) and FIPS 204 (ML-DSA) in August 2024, the JOSE Working Group at IETF began the careful process of integrating post-quantum algorithms into this family. This blog post walks through what the integration looks like, why algorithm identifiers matter, and what the operational consequences are for token issuers and verifiers.

## What An Algorithm Identifier Actually Is

In a JWT header, the `alg` field is a short string that names which cryptographic algorithm produced the signature. Today you see values like:

- `RS256` — RSA-2048 with SHA-256, padding PKCS#1 v1.5
- `RS384`, `RS512` — RSA with SHA-384/SHA-512
- `PS256`, `PS384`, `PS512` — RSA-PSS variants
- `ES256`, `ES384`, `ES512` — ECDSA over P-256/P-384/P-521
- `EdDSA` — Edwards-curve digital signature (Ed25519 by default)
- `HS256`, `HS384`, `HS512` — HMAC with SHA-256/384/512

Every one of these short strings is registered with IANA in the JOSE Algorithms Registry. The registry is the authoritative list. A library that sees a string not in the registry will (or should) reject the token.

Post-quantum algorithm identifiers are being added to this registry through standard IETF processes. The current direction is to use the FIPS 204 names directly: `ML-DSA-44`, `ML-DSA-65`, `ML-DSA-87`. For SLH-DSA (FIPS 205), the names follow the FIPS 205 parameter set names.

## Why Identifiers Matter More Than They Look Like They Do

There is a well-known JWT vulnerability called "alg=none" where a token is presented with the `alg` field set to `"none"`, indicating no signature, and a buggy verifier accepts it. There is another well-known vulnerability where a token is presented with `alg: "HS256"` but verified against an RSA public key as if it were an HMAC secret, allowing forgery.

Both classes of attack exist because the algorithm identifier is part of the trust decision. A verifier must know exactly which algorithm to apply, and which key to use, before deciding whether the signature is valid.

For PQC, the same lessons apply. If a verifier sees `alg: "ML-DSA-65"` it must know to call the FIPS 204 verification routine with the correct parameter set. If the verifier supports both ML-DSA-44 and ML-DSA-65, it must check that the algorithm in the header matches the parameter set of the key — otherwise an attacker could swap parameter sets and trick the verifier.

The JOSE WG is being careful to specify exactly how this binding works. Each algorithm identifier maps to a specific FIPS 204 parameter set, and JWK key types must encode the parameter set so the verifier cannot be tricked.

## The JWK Format For ML-DSA Keys

A JWK (JSON Web Key) is a JSON object that describes a cryptographic key. For an Ed25519 key, a JWK looks like:

```
{
  "kty": "OKP",
  "crv": "Ed25519",
  "x": "<base64url-encoded public key>"
}
```

For ML-DSA, the IETF draft proposes a similar shape with new identifiers. The exact field names depend on which draft revision is current, but the conceptual model is:

```
{
  "kty": "ML-DSA",
  "alg": "ML-DSA-65",
  "x": "<base64url-encoded 1952-byte public key>"
}
```

Note that the public key is large — 1,952 bytes for ML-DSA-65. Base64url-encoded, it becomes roughly 2,600 characters. A JWKS document with even a handful of ML-DSA keys is hefty.

## Header Bloat: The Practical Number

A typical RS256-signed JWT looks like this:

```
eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYyJ9.<payload>.<256-byte signature in base64url>
```

The total length is the payload size plus a fixed overhead of about 350 bytes for header and signature.

For ML-DSA-65, the same JWT looks like:

```
eyJhbGciOiJNTC1EU0EtNjUiLCJraWQiOiJhYmMifQ.<payload>.<3309-byte signature in base64url>
```

The fixed overhead jumps from 350 bytes to roughly 4,400 bytes. The header is slightly larger (because `ML-DSA-65` is a few characters longer than `RS256`), but the bulk of the increase is the signature.

For a JWT carrying typical claims (sub, iat, exp, aud, iss, plus a few custom claims totaling 200 bytes), the total transitions from about 550 bytes to about 4,650 bytes. That is roughly an 8x increase.

What does this break in practice? See our [OAuth 2.0 PQC article](../../blog/oauth-2-pqc.html) for the full breakdown, but in summary:

- Cookies become impractical (4 KB browser limit).
- HTTP headers can hit server limits (often 8 KB total).
- URL fragments and query strings can hit browser limits (varies by browser).
- API logs grow proportionally.

## Compact Vs Flattened JWS

Most people only ever see JWTs in the compact serialization: three base64url segments separated by dots. There is also a flattened JSON serialization, where the same data is wrapped in a JSON object:

```
{
  "protected": "<base64url header>",
  "payload": "<base64url payload>",
  "signature": "<base64url signature>"
}
```

For PQC, the compact format remains usable but inefficient. The JSON format adds overhead but enables some optimizations like detached payloads (where the payload is sent separately and only the signature is in the JWS).

For very large signatures, detached payloads can be useful: the signature object stays small, the payload travels by another channel.

## SLH-DSA In JWT Contexts

We have a [whole article on ML-DSA versus SLH-DSA](../../blog/mldsa-vs-slhdsa.html), but the JWT-specific note is that SLH-DSA signatures are huge. SLH-DSA-128f (the small-and-fast variant) produces 17,088-byte signatures. SLH-DSA-128s (the slow-but-smaller variant) produces 7,856-byte signatures. SLH-DSA-256s is 29,792 bytes; SLH-DSA-256f is 49,856 bytes.

A JWT with a 49,856-byte signature is, charitably, a curiosity. It does not fit anywhere reasonable in a normal HTTP exchange. SLH-DSA in JOSE is more interesting for offline use cases (signing a log file, signing a release artifact) than for inline tokens.

## The IETF Draft Status

The relevant IETF draft is `draft-ietf-cose-dilithium`, which started in COSE (CBOR Object Signing and Encryption, RFC 8152) and was renamed to track the FIPS 204 final names. The JOSE-specific document is `draft-ietf-jose-pqc-mldsa` or similar (draft names shift over time; check the IETF datatracker for current status).

These documents are normally adopted at multiple IETF meetings and go through Working Group Last Call before being submitted to the IESG for publication as RFCs. The full process from working group adoption to RFC typically takes 18–36 months. As of late 2025, the COSE document is closer to publication than the JOSE document.

## Hybrid Identifiers

Just as in TLS where hybrid groups like `X25519MLKEM768` are deployed today, JOSE is exploring hybrid signature identifiers. The conceptual approach is a single `alg` value that names a composite scheme: for example, `ECDSA-ML-DSA-65` would indicate that the signature is the concatenation of an ECDSA-P256 signature and an ML-DSA-65 signature, and verification requires both to pass.

The exact scheme is still in flight. The downside of hybrid identifiers in JOSE is that a single token now carries two signatures, ballooning size further. The upside is conservative: even if either algorithm is broken, the other still protects integrity.

For now, deployments wanting hybrid security can dual-sign tokens — issue both an `RS256`-signed and an `ML-DSA-65`-signed JWT — and have the relying party verify whichever it can.

## Practical Migration For JWT-Heavy Applications

If you operate a JWT-issuing service today:

1. **Inventory token sizes.** Measure your typical JWT size in bytes. Multiply by 8. That is your post-PQC size if you switch to ML-DSA-65 alone.
2. **Check downstream limits.** Look at your reverse proxy header limits, your CDN limits, your client cookie usage, your mobile app token storage.
3. **Plan for opaque session tokens.** If your JWTs cannot grow, consider issuing opaque session identifiers and looking up claims server-side. This is operationally heavier but eliminates the size problem.
4. **Implement dual signing.** Sign every JWT with both your classical key and an ML-DSA key during the transition window. Set the `alg` based on what the relying party can verify.
5. **Monitor library support.** Track when your JWT library ships ML-DSA verification. Major Node, Python, Java, and Go libraries are tracking the standardization.

## CBOR Object Signing And Encryption (COSE)

Closely related to JOSE is COSE (CBOR Object Signing and Encryption, RFC 8152 and RFC 9052), which uses CBOR (Concise Binary Object Representation) instead of JSON. COSE is preferred in IoT, embedded, and constrained environments where binary efficiency matters.

The COSE WG has been ahead of JOSE on PQC integration. The relevant document, `draft-ietf-cose-dilithium`, has been adopted and is closer to final RFC publication than the analogous JOSE document.

For developers working on IoT-style identity (FIDO2 attestation, WebAuthn, COSE-based device certificates), the PQC story is the same conceptually: ML-DSA-65 algorithm identifier, larger signatures, eventually deployable. The CBOR format is slightly more byte-efficient than JSON (no base64 encoding), so signature size impact is somewhat less brutal but still significant.

## Why JWT Is Still The Right Format

A reasonable question is: given how badly PQC scales for JWT, should we consider abandoning JWT for some other token format?

The answer is generally no. JWT's ubiquity, library support, and operational tooling are massive. The world has invested in JWT. Replacing it would require coordinated migration across thousands of systems.

Better answers:

- Use opaque session identifiers and look up claims server-side, eliminating the need for large JWTs in client-server traffic.
- Use compressed JWT variants (JWE with compression) to mitigate size.
- Use mTLS-bound tokens to reduce the frequency of token transmission.

These are operational mitigations on top of JWT, not replacements for it.

## What QNSQY Does

QNSQY's billing API issues short-lived bearer tokens for authenticated API access. Today these are JWTs signed with Ed25519. Our PQC migration plan is to dual-sign with ML-DSA-65, with the dual-signing infrastructure already in place internally. We default to Ed25519 because most clients only verify Ed25519, and we will flip the default once major HTTP client libraries verify ML-DSA-65 by default.

For files encrypted with QNSQY (the actual core product), the JWT/JOSE story does not apply directly — file signatures are inside the QSPG v2 file format, not in JWTs. But the algorithm identifier discipline is the same: every file carries explicit metadata about which algorithm signed it, and verification refuses anything not on the allowlist.

## Frequently Asked Questions

**Q: Is JWT obsolete because PQC signatures are too big?**
No. JWT is a serialization format. The format itself is fine. The challenge is operational: large signatures stress real systems. The format will stay; deployments will adapt.

**Q: Can I use HMAC instead of an asymmetric signature?**
HMAC (HS256, HS384, HS512) does not have a quantum vulnerability the way RSA and ECDSA do — Grover's algorithm only halves the security, leaving HMAC-SHA-256 with 128-bit security. So HMAC-signed JWTs are quantum-safe. The catch: HMAC requires the verifier to know the secret key, which limits HMAC to symmetric scenarios where one party signs and verifies. You cannot do federated identity with HMAC.

**Q: Will my JWT library reject `alg: "ML-DSA-65"` today?**
Probably yes, because most libraries have an allowlist of supported algorithms. As ML-DSA-65 lands in major library releases, this will change. Until then, JWT clients should keep using ECDSA or Ed25519.

**Q: What about JWE (JSON Web Encryption)?**
JWE for PQC is also being worked on through the same JOSE WG. The encryption side uses ML-KEM for key encapsulation, paired with AES-GCM for the actual content encryption. The integration is similar in concept to TLS hybrid groups but specific to the JWE container format.

**Q: Are there any JWT libraries that support ML-DSA today?**
A handful of experimental forks and academic implementations exist. As of late 2025, no major production library has shipped a stable release with ML-DSA verification. Check Node `jose` (panva/jose) and Java `jose4j` as bellwethers.

## Sources

1. RFC 7519 — JSON Web Token. https://datatracker.ietf.org/doc/html/rfc7519
2. RFC 7515 — JSON Web Signature. https://datatracker.ietf.org/doc/html/rfc7515
3. RFC 7517 — JSON Web Key. https://datatracker.ietf.org/doc/html/rfc7517
4. NIST FIPS 204 — Module-Lattice-Based Digital Signature. https://csrc.nist.gov/pubs/fips/204/final
5. NIST FIPS 205 — Stateless Hash-Based Digital Signature. https://csrc.nist.gov/pubs/fips/205/final
6. IANA JOSE Algorithms Registry. https://www.iana.org/assignments/jose

## Related Articles

- [OAuth 2.0 and PQC](../../blog/oauth-2-pqc.html)
- [What Is Post-Quantum Cryptography?](../../blog/what-is-post-quantum-cryptography.html)
- [ML-DSA vs SLH-DSA](../../blog/mldsa-vs-slhdsa.html)
- [NIST FIPS Guide](../../blog/nist-fips-guide.html)
- [Hybrid Encryption](../../blog/hybrid-encryption.html)

---

### Protect Your Data Before Q-Day Arrives

QNSQY's NIST-standardized post-quantum encryption protects files against both current and quantum-era threats.

[Try QNSQY](../../pricing.html)
