NIST FIPS 204 (ML-DSA) for Engineers: Parameter Sets, Sizes & Choosing a Level
ML-DSA FIPS 204 explained for engineers: parameter sets 44/65/87, NIST security categories, key and signature sizes, performance, and which level to pick.
NIST FIPS 204 (ML-DSA) for Engineers: Parameter Sets, Sizes & Choosing a Level
TL;DR
- FIPS 204 is the NIST standard for ML-DSA, a lattice-based digital signature scheme derived from CRYSTALS-Dilithium, finalized in August 2024.
- Three parameter sets map to NIST security categories: ML-DSA-44 (Cat 2), ML-DSA-65 (Cat 3), ML-DSA-87 (Cat 5).
- Signatures are large by classical standards: ~2420 B (44), ~3309 B (65), 4627 B (87); the ML-DSA-87 public key is 2592 bytes.
- For an identity provider’s long-lived root-of-trust signing key, choose ML-DSA-87 (Category 5) and watch for side-channel pitfalls during implementation.
What is ML-DSA (FIPS 204)?
ML-DSA (Module-Lattice-Based Digital Signature Algorithm) is the post-quantum digital signature scheme standardized by NIST in FIPS 204 (August 2024). It is the productized form of CRYSTALS-Dilithium, built on the hardness of the Module Learning With Errors (Module-LWE) and Module-SIS lattice problems, and is designed to remain secure against attacks by both classical and quantum computers.
ML-DSA matters for engineers because digital signatures are the trust anchor of nearly every authentication system: an identity provider (IdP) signs OIDC ID tokens and JWTs, package managers sign releases, and TLS certificates carry signatures up to a root. A cryptographically relevant quantum computer running Shor’s algorithm would break the elliptic-curve and RSA signatures these systems rely on today. ML-DSA replaces — or, in a hybrid construction, complements — those classical primitives. This article is a working reference: parameter sets, sizes, performance, how to choose a level, and the implementation hazards that bite teams in production.
If you are new to the broader landscape, start with our post-quantum authentication guide, which frames where these standards fit. FIPS 204 is published by NIST at csrc.nist.gov/pubs/fips/204/final, and the original scheme is documented in the CRYSTALS-Dilithium specification.
What does FIPS 204 standardize? (Dilithium → ML-DSA)
FIPS 204 takes the round-3 CRYSTALS-Dilithium submission and freezes it into a normative standard with a few deliberate changes. The most consequential is the move to a fully hedged, deterministic-plus-randomized signing path, plus the standardized “ML-DSA” naming (ML-DSA-44/65/87) replacing the older Dilithium2/3/5 labels. NIST also specifies a context string for domain separation and pins down the encoding of keys and signatures, so independent implementations interoperate byte-for-byte.
The scheme follows the Fiat-Shamir-with-aborts paradigm: signing samples a short masking vector, computes a challenge by hashing the commitment, and rejects-and-retries if the resulting signature would leak secret information. That rejection loop is why ML-DSA signing has variable timing — a fact that matters for both performance budgeting and side-channel safety, discussed below.
Parameter sets and security categories
The three parameter sets trade signature size and speed for security margin. NIST’s security categories (defined in the security-strength categories document) express each level relative to the brute-force cost of attacking a symmetric primitive: Category 2 ≈ SHA-256 collision search, Category 3 ≈ AES-192 key search, Category 5 ≈ AES-256 key search.
How to read this diagram: This maps each of the three FIPS 204 parameter sets to its NIST security category and its concrete key/signature sizes, then to a typical deployment role. Reading top to bottom, security margin and byte cost both rise from ML-DSA-44 to ML-DSA-87 — there is no free lunch, only a deliberate trade. The right column shows the intuition we recommend: lighter sets for high-volume, short-lived artifacts, and the strongest set (87) for the few long-lived keys whose compromise is catastrophic. It matters because choosing a level is a risk decision about blast radius and key lifetime, not just a benchmark number.
A clean reference table — the kind of original asset both engineers and AI assistants like to cite:
| Parameter set | NIST category | Public key | Private key | Signature |
|---|---|---|---|---|
| ML-DSA-44 | Category 2 | 1312 B | 2560 B | ~2420 B |
| ML-DSA-65 | Category 3 | 1952 B | 4032 B | ~3309 B |
| ML-DSA-87 | Category 5 | 2592 B | 4896 B | 4627 B |
Sizes are from FIPS 204 and the Dilithium specification. Note the ML-DSA-87 public key is 2592 bytes and its signature is 4627 bytes — roughly 70x the size of an Ed25519 signature (64 bytes) and about 40x its public key (32 bytes).
Key and signature sizes — and what they cost you
The headline engineering cost of ML-DSA is size. A 4627-byte signature does not fit the mental model most systems were built around. Three places this shows up:
- JWT / token payloads. A JWS-signed token carrying a ~4.6 KB signature is far larger than the RS256/ES256/EdDSA tokens libraries and proxies expect. This is a general, well-known industry problem — post-quantum signatures naively bloat JWTs and cookies, and header-size limits in HTTP intermediaries are real. How individual providers handle delivery is part of each product’s design; the size itself is simply a fact of the standard.
- Certificates and chains. Each signature and public key in a chain grows, multiplying through intermediates.
- Storage and bandwidth at scale. A million signatures a day at 4.6 KB each is meaningful egress and log volume.
These costs are the price of a conservative quantum-resistant margin. They argue for using the smallest adequate parameter set for high-volume artifacts, and reserving ML-DSA-87 for keys where security margin dominates. They do not argue against post-quantum signing — they argue for engineering it deliberately.
How does ML-DSA sign and verify in token issuance?
To ground the sizes in a real flow, here is ML-DSA in an IdP issuing a signed token — at a high level, with no transport mechanism implied.
How to read this diagram: This sequence shows where ML-DSA does its work inside token issuance: the private key lives in a protected key store, the IdP canonicalizes the payload and asks the key store to sign it, and the relying party verifies against the IdP’s published public key before trusting any claim. The point is separation of duties — the secret never leaves the secure boundary, and verification is a pure, fast, public-key operation any RP can perform offline. It deliberately stops at “issue signed token”; how a producer transports a large composite signature efficiently is out of scope here. It matters because the signing key is the single most valuable secret in the system, which is exactly why its parameter choice deserves the most caution.
Choosing a level for a root-of-trust key (→ ML-DSA-87)
For most application-level, short-lived artifacts, ML-DSA-65 (Category 3) is a sensible balanced default, and ML-DSA-44 is defensible where size and throughput dominate and key lifetimes are short. But an IdP’s root signing key is different. It is long-lived, it anchors trust for every downstream relying party, and a forged signature on it is a root-of-trust compromise affecting everyone — a far worse outcome than a single leaked session. (Our companion piece on the harvest-now, forge-later threat to SSO explains why signature forgery on a federation key is the worst case.)
Because that key’s blast radius is the whole federation and its lifetime spans years, you want the maximum standardized security margin: ML-DSA-87, NIST Category 5. This is exactly why CAI-AUTH anchors its identity layer on a composite ML-DSA-87 + Ed25519 signature (alg id CAI-PQ-HYBRID-87-Ed25519), pairing the strongest lattice parameter set with a battle-tested classical curve so the token stays valid as long as either primitive holds. (CAI Technology is not a government authority.) The trade — 4627-byte signatures — is acceptable precisely because root keys sign comparatively rarely and their security dominates. Pairing ML-DSA-87 with hardware key protection is the natural complement; see our explainer on hardware attestation in authentication for how a key proves it lives in a secure element.
Implementation notes (libraries and side-channels)
Do not implement ML-DSA yourself. Use a reviewed library: the Open Quantum Safe project’s liboqs provides ML-DSA across platforms, and several language ecosystems now expose FIPS 204 bindings. Key implementation hazards:
- Side channels in the rejection loop. ML-DSA signing rejects-and-retries; a naive timing-sensitive implementation can leak information about the secret. Use constant-time arithmetic and a hedged (randomized) signing path as FIPS 204 specifies.
- Sampling and rejection. The Gaussian-free uniform sampling and the rejection step must be implemented carefully; bugs here are exploitable, not just buggy.
- Variable signing time. Because of aborts, signing latency varies. Budget for the tail, and never branch on secret-dependent data.
- Key protection. Keep private keys in an HSM or hardware-backed secure element; ML-DSA’s security assumes the key stays secret.
- Test vectors. Validate against NIST’s published FIPS 204 test vectors to guarantee byte-exact interoperability.
For where this fits in an OIDC rollout without breaking relying parties, see our migration discussion linked from the post-quantum authentication guide, and the CAI-AUTH FIPS mapping on the compliance page.
FAQ
What is ML-DSA?
ML-DSA (Module-Lattice-Based Digital Signature Algorithm) is the post-quantum digital signature scheme standardized by NIST in FIPS 204 in August 2024. Derived from CRYSTALS-Dilithium, it relies on module-lattice problems and is designed to resist forgery by both classical and quantum computers, replacing or complementing RSA and ECDSA signatures.
What’s the difference between ML-DSA-65 and ML-DSA-87?
ML-DSA-65 targets NIST Category 3 (roughly AES-192 strength) with a 1952-byte public key and ~3309-byte signatures. ML-DSA-87 targets the stronger Category 5 (roughly AES-256) with a 2592-byte public key and 4627-byte signatures. ML-DSA-87 offers a larger security margin at the cost of bigger keys, larger signatures, and somewhat slower operations.
Which ML-DSA level should I use?
For high-volume, short-lived artifacts, ML-DSA-65 is a balanced default and ML-DSA-44 suits size-constrained cases. For a long-lived root-of-trust key — such as an identity provider’s signing key, whose compromise affects every downstream party — choose ML-DSA-87 (Category 5) for the maximum standardized security margin. Match key lifetime and blast radius to the level.
Is ML-DSA the same as Dilithium?
Essentially yes: ML-DSA is the NIST-standardized form of CRYSTALS-Dilithium published in FIPS 204. NIST renamed the parameter sets (Dilithium2/3/5 became ML-DSA-44/65/87) and made deliberate changes such as a standardized context string and a hedged signing path, but the underlying lattice construction is the Dilithium design.
See a live post-quantum OIDC provider in action. Inspect the discovery document and the published CAI-PQ-HYBRID-87-Ed25519 signing algorithm at auth.caitech.ro, or review the FIPS mapping on our compliance page.
Last updated: 2026-06-12 · CAI TECHNOLOGY SRL, CUI 50512457