Skip to main content

FIPS 204 quick reference

A memory aid for ML-DSA — the post-quantum signature scheme used throughout this stack. This is not a substitute for FIPS 204.

What it is

ML-DSA ("Module-Lattice-Based Digital Signature Algorithm") is the NIST-standardized PQ successor to ECDSA / EdDSA. It is a lattice-based signature scheme whose security rests on the hardness of Module-LWE and Module-SIS, not on integer factorization or elliptic-curve discrete log. Finalized in FIPS 204 (August 2024).

Historical name: Dilithium. The NIST-selected variant is now formally ML-DSA.

Parameter sets

Parameter setNIST security category~Classical securityPublic keySecret keySignature
ML-DSA-44II~128 bits1,312 B2,560 B2,420 B
ML-DSA-65III~192 bits1,952 B4,032 B3,309 B
ML-DSA-87V~256 bits2,592 B4,896 B4,627 B

The number ("44", "65", "87") encodes internal matrix dimensions; it is not a security level. Compare against AES-128 / AES-192 / AES-256 using the NIST category, not the parameter-set number.

OIDs

ML-DSA-44 2.16.840.1.101.3.4.3.17
ML-DSA-65 2.16.840.1.101.3.4.3.18
ML-DSA-87 2.16.840.1.101.3.4.3.19

When to use which

  • ML-DSA-65 — default. Level 3, best balance of security and size for most deployments including on-chain verification where signature size is paid in calldata cost.
  • ML-DSA-44 — when signature size and signing latency dominate (client-side signing on constrained devices). Use only when Level 2 is acceptable in your threat model.
  • ML-DSA-87 — when Level 5 (256-bit equivalent) is required. Larger keys and signatures; rarely needed for transactional use cases.

Comparison with ECDSA / EdDSA

PropertyECDSA-P256 / Ed25519ML-DSA-65
Hardness assumptionElliptic-curve discrete logModule-LWE / Module-SIS
Quantum-vulnerableYes (Shor's algorithm)No
Public key size32–64 B1,952 B
Signature size64–72 B3,309 B
Verification gas (EVM)~3K (ecrecover)Millions in pure EVM; ~374K on Arbitrum Stylus
DeterministicEdDSA yes; ECDSA no by defaultDeterministic ("hedged" optional)

PQ signatures are larger by one to two orders of magnitude. That size appears at every layer: bandwidth, storage, calldata, certificate chains.

Test vectors

Implementations in this stack are validated against NIST ACVP test vectors (official keygen, siggen, sigver vectors from usnistgov/ACVP-Server) and the Wycheproof corpus. Any implementation change that affects ACVP outputs is treated as a regression.

Common mistakes

  • Mixing raw and hashed inputs. Sign a digest, not the raw message. In the smart-account stack, the signed input is userOpHash (a 32-byte keccak-256). Always be explicit about what is signed.
  • Omitting domain separation. When signing across multiple chains or sessions, include a domain separator (e.g. chainId, nonce) to prevent cross-context replay.
  • Confusing raw and encoded key sizes. The 1,952-byte ML-DSA-65 public key is the raw key. SPKI-wrapped or PKCS#8-wrapped versions are larger. pq-key-encoder handles this.
  • Rolling your own implementation. Use an audited library (ml-dsa in Rust, @noble/post-quantum in TypeScript). Side-channel-resistant lattice arithmetic is difficult to get right.