Every time you install a software update, connect to a website over HTTPS, sign a financial transaction, or verify an identity token, digital signatures are doing the work behind the scenes. They are the mathematical proof that a message is authentic—that it came from who it claims to come from, and that it hasn't been tampered with. Without digital signatures, there is no trust on the internet.
For decades, RSA and ECDSA have served as the backbone of digital signature infrastructure. They secure TLS certificate chains, code signing pipelines, blockchain consensus, JWT authentication tokens, and virtually every protocol that requires non-repudiation. But both algorithms rest on mathematical foundations—integer factorization and the elliptic curve discrete logarithm problem—that Shor's algorithm will shatter the moment a cryptographically relevant quantum computer (CRQC) comes online.
CRYSTALS-Dilithium—now standardized as ML-DSA under NIST FIPS 204—is the primary replacement. It is the post-quantum digital signature algorithm that NIST selected as its recommended standard, built on lattice mathematics that no known quantum algorithm can break efficiently. This article covers how it works, why it won, how it performs, and how to implement it.
CRYSTALS-Dilithium (ML-DSA) is the NIST-recommended post-quantum digital signature algorithm under FIPS 204. It replaces RSA and ECDSA for all applications requiring quantum resistance. The standards are final, the deadlines are set, and production-grade implementations exist today.
What Digital Signatures Are and Why They Matter
A digital signature is a cryptographic primitive that provides three guarantees:
- Authentication: The message was produced by the holder of a specific private key
- Integrity: The message has not been altered since it was signed
- Non-repudiation: The signer cannot deny having signed the message
These properties are fundamental to modern infrastructure. Consider the scope of what depends on them:
Where Digital Signatures Are Used
- TLS/HTTPS certificates—Every website you visit. Certificate authorities sign certificates with RSA or ECDSA. Your browser verifies the signature to establish trust.
- Code signing—Operating systems verify that software updates, drivers, and applications were signed by their publisher before executing them.
- Blockchain and cryptocurrency—Every transaction on Bitcoin, Ethereum, and other chains is authorized by an ECDSA or EdDSA signature.
- Authentication tokens—JWTs, SAML assertions, and OAuth tokens are signed to prevent forgery.
- Email (S/MIME, DKIM)—Email authentication relies on signature verification to prevent spoofing.
- Document signing—Legal documents, contracts, and government filings use digital signatures for legally binding proof of authorship.
When Shor's algorithm breaks RSA and ECDSA, every one of these systems becomes vulnerable. An attacker with a CRQC could forge TLS certificates, sign malicious software updates that appear legitimate, authorize fraudulent blockchain transactions, or mint authentication tokens for any identity. The entire trust model of the internet collapses.
This is why post-quantum signature migration is not optional. It is a prerequisite for maintaining digital trust in the quantum era.
How CRYSTALS-Dilithium Works
Dilithium is a lattice-based digital signature scheme that uses the Fiat-Shamir with Aborts paradigm, with its security derived from the hardness of the Module Learning With Errors (MLWE) problem. Let's unpack each of these components.
Module Learning With Errors (MLWE)
The security of Dilithium rests on the difficulty of the MLWE problem. In simplified terms, given a system of "noisy" linear equations over a polynomial ring, it is computationally infeasible to recover the secret values—even with a quantum computer.
More precisely, MLWE works over the polynomial ring R_q = Z_q[X]/(X^n + 1), where n = 256 and q = 8380417 (a specific prime chosen for NTT-friendliness). The secret key consists of short polynomials (with small coefficients), and the public key is constructed as t = A * s1 + s2, where A is a public random matrix, s1 is the secret signing key, and s2 is a secret error term. Recovering s1 from A and t is the MLWE problem—and no known quantum algorithm solves it efficiently.
Shor's algorithm exploits the hidden subgroup structure in factoring and discrete logarithm problems. Lattice problems like MLWE have no such structure. The best known quantum algorithms for lattice problems (based on quantum variants of BKZ) provide only a modest polynomial speedup over classical algorithms—nowhere near the exponential speedup Shor provides against RSA/ECDSA. This is why the entire NIST PQC portfolio is dominated by lattice-based constructions.
The Fiat-Shamir with Aborts Paradigm
Dilithium's signing process is based on the Fiat-Shamir transform applied to an identification scheme, but with a critical twist: the abort mechanism. Here is how the three core operations work:
Key Generation
- Generate a random seed and expand it into a public matrix
A(ak x lmatrix of polynomials) - Sample short secret vectors
s1(lengthl) ands2(lengthk) with coefficients bounded byη - Compute
t = A * s1 + s2 - The public key is
(A, t); the private key is(A, t, s1, s2)
Signing (with Aborts)
- Sample a random masking vector
ywith coefficients bounded byγ1 - Compute
w = A * yand extract its high-order bitsw1 - Compute the challenge hash:
c = H(w1 || message) - Compute the response:
z = y + c * s1 - Rejection check: If
zhas any coefficient with absolute value ≥γ1 - β, ABORT and restart from step 1 - Second check: Verify that the low-order bits of
A*z - c*tdon't exceed a bound. If they do, ABORT and restart. - If both checks pass, output signature
(z, c)
Verification
- Check that all coefficients of
zare within bounds - Compute
w1' = HighBits(A*z - c*t) - Verify that
H(w1' || message) = c
Without rejection sampling (the abort step), the distribution of signatures would leak information about the secret key s1. Each signature z = y + c*s1 is shifted by c*s1, which would gradually reveal s1 over many signatures. The abort mechanism ensures that only signatures where z follows a near-uniform distribution are output—completely masking the secret key. On average, signing requires 4–7 iterations before producing a valid signature, but each iteration is fast (microseconds), so the overhead is minimal.
Security Levels: ML-DSA-44, ML-DSA-65, ML-DSA-87
Dilithium defines three parameter sets, each targeting a different NIST security level. Under the FIPS 204 standard, these are designated ML-DSA-44, ML-DSA-65, and ML-DSA-87 (the numbers refer to the (k, l) matrix dimensions).
| Parameter | ML-DSA-44 | ML-DSA-65 | ML-DSA-87 |
|---|---|---|---|
| NIST Security Level | 2 | 3 | 5 |
| Classical Equivalent | ~AES-128 | ~AES-192 | ~AES-256 |
| Matrix (k × l) | 4 × 4 | 6 × 5 | 8 × 7 |
| Public Key | 1,312 bytes | 1,952 bytes | 2,592 bytes |
| Signature Size | 2,420 bytes | 3,293 bytes | 4,595 bytes |
| Secret Key | 2,560 bytes | 4,032 bytes | 4,896 bytes |
| η (secret bound) | 2 | 4 | 2 |
| γ1 (masking range) | 217 | 219 | 219 |
| CNSA 2.0 Compliant | No | Acceptable | Required |
ML-DSA-65 (Dilithium3) is the recommended parameter set for most applications. It provides NIST Security Level 3 (~AES-192 equivalent) with a good balance of key size, signature size, and performance. For national security systems, ML-DSA-87 (Dilithium5) is required by CNSA 2.0.
Performance: Dilithium vs. RSA vs. ECDSA
One of Dilithium's greatest strengths is its performance. Unlike some post-quantum candidates that impose severe computational overhead, Dilithium is faster than RSA for signing and verification, and competitive with ECDSA in most operations.
Key Size and Signature Size Comparison
| Algorithm | Security | Public Key | Signature | Secret Key |
|---|---|---|---|---|
| RSA-2048 | ~112-bit | 256 B | 256 B | ~1,200 B |
| RSA-3072 | ~128-bit | 384 B | 384 B | ~1,700 B |
| ECDSA P-256 | ~128-bit | 64 B | 64 B | 32 B |
| Ed25519 | ~128-bit | 32 B | 64 B | 32 B |
| ML-DSA-44 | ~128-bit PQ | 1,312 B | 2,420 B | 2,560 B |
| ML-DSA-65 | ~192-bit PQ | 1,952 B | 3,293 B | 4,032 B |
| ML-DSA-87 | ~256-bit PQ | 2,592 B | 4,595 B | 4,896 B |
Yes, Dilithium keys and signatures are larger than classical algorithms. An ML-DSA-65 public key is 1,952 bytes versus 64 bytes for ECDSA P-256—a 30x increase. But 1,952 bytes is still small in absolute terms. For context, a single TCP packet can carry up to 1,460 bytes of payload, so a Dilithium public key fits in two packets. The signature at 3,293 bytes fits in three. For most applications (API authentication, code signing, certificate chains), this overhead is negligible.
Bandwidth-constrained environments are the exception. IoT devices with limited packet sizes, blockchain transactions where every byte costs gas, and DNS-based certificate distribution (DANE) may need careful consideration. For these use cases, NIST has standardized SLH-DSA (FIPS 205, hash-based) and is drafting FN-DSA (FIPS 206, from FALCON) which offers smaller signatures at the cost of implementation complexity.
Speed Benchmarks
On modern hardware, Dilithium is remarkably fast. Our February 2026 benchmarks on AWS Graviton4 (c8g.metal-48xl, 192 vCPUs, Neoverse V2, ARM NEON) show:
H33 Production Benchmarks — ML-DSA-65 (Graviton4, Feb 2026)
Dilithium signing at 45µs is 33x faster than RSA-2048 signing (~1,500µs). Dilithium verification at 36.9µs is competitive with RSA verification (~25µs) and significantly faster than ECDSA verification (~100µs). With our key pool architecture (pre-generated keys), we achieve an effective keygen cost of just 0.35µs, enabling 2.86M signature operations per second on a single 96-worker node.
NIST FIPS 204: From Dilithium to ML-DSA
Understanding the naming history helps avoid confusion in documentation and procurement:
The key point: CRYSTALS-Dilithium and ML-DSA are the same algorithm. FIPS 204 made minor tweaks to the specification (deterministic vs. hedged signing modes, domain separation), but the core math is identical. If you see "Dilithium3" in code or documentation, it corresponds to ML-DSA-65.
Why Dilithium Won Over FALCON
NIST had two lattice-based signature finalists: Dilithium and FALCON. Both are quantum-resistant. FALCON actually produces smaller signatures (666 bytes for FALCON-512 vs. 2,420 bytes for ML-DSA-44 at comparable security). So why did NIST choose Dilithium as the primary standard?
| Criterion | Dilithium (ML-DSA) | FALCON (FN-DSA) |
|---|---|---|
| Signature Size | 2,420–4,595 B | 666–1,280 B |
| Public Key Size | 1,312–2,592 B | 897–1,793 B |
| Implementation Simplicity | Simple (NTT + rejection sampling) | Complex (requires discrete Gaussian sampling over NTRU lattices) |
| Constant-Time Feasibility | Straightforward | Extremely difficult (floating-point Gaussian sampling is timing-dependent) |
| Floating-Point Dependency | None | Requires double-precision FP for Gaussian sampling |
| Side-Channel Risk | Low (integer arithmetic throughout) | High (FP timing, cache-line leaks in tree sampling) |
| Embedded/Constrained Devices | Portable (no FP required) | Problematic (many embedded platforms lack FP or have non-IEEE FP) |
| Signing Speed | Fast (~45 µs) | Fast (~40 µs) |
The decisive factors were implementation simplicity and constant-time safety:
- FALCON requires discrete Gaussian sampling over NTRU lattices, which involves double-precision floating-point arithmetic. Getting this right in constant time—without leaking the secret key through timing side channels—is extraordinarily difficult. Multiple research papers have demonstrated timing attacks against FALCON implementations.
- Dilithium uses only integer arithmetic (modular multiplication, NTT, simple rejection sampling). Writing a constant-time implementation is straightforward, and side-channel resistance is well understood.
- Portability: Many embedded processors (ARM Cortex-M, RISC-V) lack hardware floating-point units or have non-IEEE-754-compliant FP. Dilithium works everywhere. FALCON does not.
"CRYSTALS-Dilithium is recommended as the primary algorithm for digital signatures due to its strong security, excellent performance, and relative ease of implementation. Its use of Module-LWE aligns well with ML-KEM (FIPS 203), allowing shared infrastructure for both signatures and key encapsulation."
FALCON will still be standardized as FN-DSA (FIPS 206, expected ~2026) for applications where signature size is the dominant constraint. But for general-purpose use, Dilithium is the answer.
Use Cases: Where to Deploy ML-DSA
Dilithium is a drop-in replacement for RSA and ECDSA signatures in virtually every context. Here are the primary deployment scenarios:
TLS Certificates and PKI
Certificate authorities will need to issue ML-DSA certificates for TLS. The larger certificate size (public key + signature in the cert chain) adds a few kilobytes to the TLS handshake. Measurements show this adds less than 1ms of additional latency on typical connections. Chrome and Firefox already support ML-DSA in experimental builds, and the IETF has published drafts for ML-DSA in X.509 certificates.
Code Signing
Software publishers sign binaries to prove authenticity. ML-DSA replaces RSA/ECDSA in code signing pipelines with minimal changes. The 3,293-byte signature (ML-DSA-65) is negligible compared to typical binary sizes. Microsoft, Apple, and Linux distributions are all working on PQC code signing support.
Blockchain and Cryptocurrency
Blockchain consensus mechanisms rely on signatures for transaction authorization. The larger signature sizes increase on-chain storage costs, but several chains (Ethereum, Algorand) are actively researching ML-DSA integration. For blockchains where signature size is critical, FN-DSA (FALCON) may be preferred due to its 666-byte signatures.
Authentication Tokens (JWT, SAML)
JSON Web Tokens signed with ML-DSA (using the ML-DSA-65 algorithm identifier) are a direct replacement for RS256 or ES256. The token size increases by ~3KB, which is acceptable for API authentication but may matter for cookie-based sessions where header size is constrained.
Attestation and Zero-Knowledge Proofs
In H33's production stack, Dilithium signs attestation digests that bind FHE computation results to a verifiable proof. A single Dilithium sign+verify cycle (~240µs including SHA3 digest computation) attests an entire batch of 32 biometric authentications, providing cryptographic proof that the computation was performed correctly without revealing any biometric data.
CNSA 2.0 Deadlines for Signature Migration
The NSA's Commercial National Security Algorithm Suite 2.0 (CNSA 2.0) sets hard deadlines for migrating away from classical signatures. If your organization sells to the U.S. government or handles national security data, these deadlines are contractual requirements.
| Category | Prefer ML-DSA By | Exclusive ML-DSA By | Required Level |
|---|---|---|---|
| Software & firmware signing | 2025 | 2030 | ML-DSA-87 |
| Web servers, browsers, cloud | 2025 | 2033 | ML-DSA-87 |
| Traditional networking (VPN, routers) | 2026 | 2030 | ML-DSA-87 |
| Operating systems | 2027 | 2033 | ML-DSA-87 |
| Constrained devices, large PKI | 2030 | 2033 | ML-DSA-87 |
For national security systems, CNSA 2.0 mandates ML-DSA-87 (Dilithium5, Security Level 5)—not ML-DSA-65. This is a higher bar than NIST's general recommendation. If you are building for government or defense customers, plan for ML-DSA-87 from the start. Retrofitting from ML-DSA-65 to ML-DSA-87 later requires regenerating all keys and re-signing all existing artifacts.
Beyond CNSA 2.0, NIST IR 8547 (November 2024) sets the broader federal timeline: RSA and ECDSA deprecated after 2030, disallowed after 2035. These deadlines apply to all federal systems, not just national security systems. If your product touches federal infrastructure in any capacity, the clock is already ticking.
H33's Dilithium Implementation
H33 uses CRYSTALS-Dilithium (ML-DSA-65) as the attestation layer in its production authentication stack. Every biometric authentication request is attested with a Dilithium signature, providing post-quantum non-repudiation for the entire computation.
Production Architecture
H33 Single API Call — Signature Component
- FHE Batch—BFV inner product over 32 encrypted biometric templates (~1,109µs)
- ZKP—STARK lookup proof of computation integrity (~0.067µs)
- Attestation—SHA3-256 digest of results + 1 Dilithium sign+verify (~240µs)
Batch Attestation: 31x Efficiency Gain
A naive approach would sign each of the 32 user results individually, requiring 32 Dilithium sign+verify cycles (32 × 82µs = 2,624µs). Instead, H33 uses batch attestation: the SHA3-256 digest covers the entire batch of 32 results, and a single Dilithium signature attests all of them at once. This is a 31x reduction in attestation overhead (240µs vs. 2,624µs) while maintaining the same cryptographic guarantees—if any result in the batch is tampered with, the digest changes and the signature fails verification.
Rust Code Example: Sign and Verify with Dilithium
Here is a complete, production-grade example of Dilithium signing and verification in Rust, similar to the pattern used in H33's attestation pipeline:
use sha3::{Sha3_256, Digest}; use pqcrypto_dilithium::dilithium3; use pqcrypto_traits::sign::{PublicKey, SecretKey, SignedMessage, DetachedSignature}; /// Generate a Dilithium3 (ML-DSA-65) key pair fn generate_keypair() -> (dilithium3::PublicKey, dilithium3::SecretKey) { dilithium3::keypair() } /// Sign arbitrary data with SHA3-256 pre-hashing + Dilithium fn sign_data(data: &[u8], sk: &dilithium3::SecretKey) -> dilithium3::DetachedSignature { // Pre-hash with SHA3-256 for domain separation let mut hasher = Sha3_256::new(); hasher.update(data); let digest = hasher.finalize(); // Sign the digest (NOT the raw data) — standard practice dilithium3::detached_sign(&digest, sk) } /// Verify signature against data + public key fn verify_signature( data: &[u8], sig: &dilithium3::DetachedSignature, pk: &dilithium3::PublicKey, ) -> bool { let mut hasher = Sha3_256::new(); hasher.update(data); let digest = hasher.finalize(); dilithium3::verify_detached_signature(sig, &digest, pk).is_ok() } /// Batch attestation: sign a batch of results with one signature fn batch_attest( results: &[AuthResult], sk: &dilithium3::SecretKey, ) -> dilithium3::DetachedSignature { let mut hasher = Sha3_256::new(); for result in results { hasher.update(&result.user_id.to_le_bytes()); hasher.update(&[result.match_result as u8]); hasher.update(&result.timestamp.to_le_bytes()); } let batch_digest = hasher.finalize(); dilithium3::detached_sign(&batch_digest, sk) } fn main() { // Key generation (~36.6µs on Graviton4) let (pk, sk) = generate_keypair(); // Sign some data (~45µs on Graviton4) let message = b"Transfer $1,000 from account A to B"; let signature = sign_data(message, &sk); // Verify (~36.9µs on Graviton4) let valid = verify_signature(message, &signature, &pk); assert!(valid, "Signature must verify"); // Tampered message fails verification let tampered = b"Transfer $1,000,000 from account A to B"; let invalid = verify_signature(tampered, &signature, &pk); assert!(!invalid, "Tampered message must fail"); println!("Dilithium sign+verify: OK"); println!("Public key: {} bytes", pk.as_bytes().len()); println!("Signature: {} bytes", sig.as_bytes().len()); }
Implementation Considerations
Deploying Dilithium in production requires attention to several practical concerns beyond the core algorithm.
Constant-Time Implementation
All Dilithium operations must be implemented in constant time to prevent timing side-channel attacks. This means:
- No secret-dependent branches: The rejection sampling loop must not leak how many iterations were needed
- No secret-dependent memory access: NTT butterfly operations must use fixed memory access patterns
- No variable-time arithmetic: Modular reduction must use Barrett or Montgomery reduction (not division)
The good news: Dilithium is much easier to implement in constant time than FALCON, because it uses only integer arithmetic. No floating-point, no discrete Gaussian sampling, no tree-based secret operations. This was a major factor in NIST's selection.
Random Number Generation
Dilithium requires high-quality randomness in two places: key generation (sampling s1, s2) and signing (sampling the masking vector y). Using a weak RNG is catastrophic—if an attacker can predict y, they can recover the secret key from a single signature.
Always use a cryptographically secure RNG (e.g., /dev/urandom, getrandom(), or OsRng in Rust). FIPS 204 also supports hedged signing (combining randomness with the secret key and message hash) to provide resilience against RNG failures. In production, always use hedged mode.
Key Management
Dilithium key management follows the same principles as classical signature schemes, with a few differences:
- Key storage: Secret keys are 4,032 bytes (ML-DSA-65), roughly 3x larger than RSA-2048 private keys. Hardware security modules (HSMs) must support this size—most modern HSMs do.
- Key rotation: Because Dilithium keys are fast to generate (~36.6µs), aggressive key rotation is practical. H33 uses a pre-generated key pool with continuous rotation.
- Key distribution: Public keys at 1,952 bytes are larger than ECDSA (64 bytes) but smaller than RSA-3072 (384 bytes). Certificate chains with ML-DSA add roughly 5–8KB of overhead.
- Backward compatibility: During the migration period, systems may need to support both classical and PQ signatures simultaneously.
Hybrid Signatures: The Migration Bridge
A hybrid signature combines a classical signature (RSA or ECDSA) with a post-quantum signature (ML-DSA) on the same message. The verifier checks both. This provides two guarantees:
- If ML-DSA has an undiscovered weakness: The classical signature still provides security (until a CRQC arrives)
- If a CRQC arrives: The ML-DSA signature still provides security (the classical signature is broken but irrelevant)
Hybrid signatures are recommended by NIST, CNSA 2.0, and most migration guides as the transition approach. The overhead is manageable: a hybrid ML-DSA-65 + ECDSA P-256 signature is ~3,357 bytes (3,293 + 64), and verification takes ~137µs (36.9 + ~100).
/// Hybrid signature: ML-DSA-65 + ECDSA P-256 /// Both must verify for the signature to be accepted pub struct HybridSignature { dilithium_sig: dilithium3::DetachedSignature, ecdsa_sig: ecdsa::Signature, } pub fn hybrid_sign( data: &[u8], dil_sk: &dilithium3::SecretKey, ec_sk: &ecdsa::SigningKey, ) -> HybridSignature { let digest = Sha3_256::digest(data); HybridSignature { dilithium_sig: dilithium3::detached_sign(&digest, dil_sk), ecdsa_sig: ec_sk.sign(&digest), } } pub fn hybrid_verify( data: &[u8], sig: &HybridSignature, dil_pk: &dilithium3::PublicKey, ec_pk: &ecdsa::VerifyingKey, ) -> bool { let digest = Sha3_256::digest(data); // BOTH signatures must verify let dil_ok = dilithium3::verify_detached_signature( &sig.dilithium_sig, &digest, dil_pk ).is_ok(); let ec_ok = ec_pk.verify(&digest, &sig.ecdsa_sig).is_ok(); dil_ok && ec_ok }
The Complete PQC Signature Landscape
Dilithium is not the only post-quantum signature algorithm. Understanding the full landscape helps with architectural decisions:
| Standard | Algorithm | Family | Signature | PK Size | Status | Best For |
|---|---|---|---|---|---|---|
| FIPS 204 | ML-DSA (Dilithium) | Lattice (MLWE) | 2,420–4,595 B | 1,312–2,592 B | Final | General purpose |
| FIPS 205 | SLH-DSA (SPHINCS+) | Hash-based | 7,856–49,856 B | 32–64 B | Final | Conservative / tiny PK |
| FIPS 206 | FN-DSA (FALCON) | Lattice (NTRU) | 666–1,280 B | 897–1,793 B | Draft ~2026 | Small signatures |
SLH-DSA (FIPS 205) is the "conservative" choice. Its security relies only on the properties of hash functions (SHA-256/SHAKE), making it theoretically the safest bet if lattice problems turn out to be easier than expected. The tradeoff: signatures are enormous (7–50KB) and signing is slow. Use it for root-of-trust applications (root CA certificates, firmware signing keys) where you sign rarely and verify occasionally.
FN-DSA (FIPS 206) from FALCON offers the smallest signatures (666 bytes) at the cost of implementation complexity. Once standardized, it will be preferred for blockchain transactions and other bandwidth-critical applications.
ML-DSA (FIPS 204) from Dilithium is the default. If you're unsure which to use, use Dilithium.
Common Implementation Pitfalls
Based on H33's production experience and published vulnerability research, here are the most common mistakes when implementing Dilithium:
Pitfalls to Avoid
- Using deterministic signing without hedging. Pure deterministic signing (deriving
yfrom the message + secret key with no randomness) is vulnerable to fault attacks. If an attacker can induce a computational fault during signing, they can recover the secret key from two faulty signatures of the same message. FIPS 204 supports hedged mode (randomness + deterministic component) for exactly this reason. - Leaking the rejection count. The number of abort-and-retry iterations during signing must not be observable (via timing, power consumption, or electromagnetic emanations). A side-channel that reveals how many iterations occurred leaks information about the secret key.
- Reusing nonces / masking vectors. If the same masking vector
yis used for two different messages, the secret key can be trivially recovered (similar to the ECDSA nonce-reuse attack that broke the PlayStation 3). - Not validating public key format. Malformed public keys can cause undefined behavior in some implementations. Always validate that deserialized public keys have correct dimensions and coefficient bounds before use.
- Ignoring algorithm agility. Hardcoding ML-DSA-65 everywhere makes future migration to ML-DSA-87 (or a different algorithm entirely) painful. Design your systems with algorithm identifiers and pluggable signature backends from the start.
Migration Roadmap: From RSA/ECDSA to ML-DSA
Migrating a production system from classical signatures to Dilithium is a multi-phase effort. Here is the recommended approach:
Phase 1: Inventory and Assessment (Weeks 1–4)
- Identify every system that uses RSA or ECDSA signatures
- Classify by data sensitivity and Mosca's inequality (see our HNDL article)
- Determine which systems can support larger key/signature sizes
- Identify HSMs, key management systems, and certificate authorities that need PQC support
Phase 2: Hybrid Deployment (Months 2–6)
- Deploy hybrid signatures (ML-DSA + classical) in parallel
- Update certificate chains to include both signature types
- Modify verifiers to check both signatures (accept if both pass)
- Monitor for performance impact and size overhead
Phase 3: PQ-Primary (Months 6–12)
- Make ML-DSA the primary signature algorithm
- Classical signature becomes the fallback (for backward compatibility)
- Begin deprecation warnings for classical-only verifiers
Phase 4: PQ-Exclusive (Month 12+)
- Remove classical signatures entirely
- Regenerate all long-lived keys as ML-DSA
- Update all certificate chains to PQ-only
If you can only do one thing today, prioritize post-quantum key exchange (ML-KEM / FIPS 203) over post-quantum signatures. Key exchange protects data confidentiality—the thing HNDL attacks exploit. Signature migration protects authentication integrity, which is critical but not vulnerable to retroactive attacks in the same way. Both are necessary, but key exchange has a tighter deadline.
Frequently Asked Questions
Is Dilithium the same as ML-DSA?
Yes. CRYSTALS-Dilithium was renamed to ML-DSA (Module-Lattice Digital Signature Algorithm) during NIST's FIPS 204 standardization. The core algorithm is identical. Minor changes in the standard include updated domain separation labels and support for both deterministic and hedged signing modes.
Which parameter set should I use?
ML-DSA-65 for most applications. It provides NIST Security Level 3 (comparable to AES-192) with a good performance-size tradeoff. Use ML-DSA-87 if you need CNSA 2.0 compliance or Security Level 5. Use ML-DSA-44 only for highly constrained environments where you've confirmed Level 2 security meets your requirements.
How do Dilithium signatures compare in size to RSA?
ML-DSA-65 signatures are 3,293 bytes versus 256 bytes for RSA-2048—about 13x larger. But RSA-2048 is not quantum-safe, and comparing to the quantum-equivalent RSA-15360 (if such a thing existed), Dilithium signatures would actually be smaller. In practice, 3,293 bytes is a non-issue for most applications.
Can existing HSMs support Dilithium?
Major HSM vendors (Thales, Entrust, AWS CloudHSM, Azure Dedicated HSM) are adding ML-DSA support. Some already offer firmware updates with PQC algorithms. Check with your vendor for the current status. For software-based key management, production-grade Dilithium implementations exist in liboqs, pqcrypto (Rust), bouncy-castle (Java), and OpenSSL 3.x (via oqs-provider).
What about performance on constrained devices?
Dilithium is designed to be efficient on a wide range of hardware. Reference benchmarks on ARM Cortex-M4 (a common microcontroller) show signing in ~2ms and verification in ~1ms. These numbers are well within the requirements for IoT authentication, smart cards, and embedded systems.
The Bottom Line
CRYSTALS-Dilithium (ML-DSA, FIPS 204) is the post-quantum digital signature standard. The algorithm is mature, the standard is final, production implementations exist in every major language, and the regulatory deadlines are set. There is no ambiguity about what to do—only about when to start.
The performance is excellent: 45µs signing and 37µs verification on modern hardware, 33x faster than RSA signing, competitive with ECDSA. The key and signature sizes are larger than classical algorithms but manageable for virtually all applications. The security is well-understood, resting on the Module-LWE problem that has withstood decades of cryptanalytic scrutiny with no efficient quantum attack known.
H33's production stack demonstrates what a fully post-quantum signature deployment looks like: ML-DSA-65 attestation at ~240µs per batch, 2.17M authenticated operations per second, every signature quantum-resistant by construction. The technology works. The only remaining variable is organizational will.
If you are still signing with RSA or ECDSA, your clock started when FIPS 204 was published in August 2024. The CNSA 2.0 "prefer by" deadline for software signing was 2025. The "exclusive use" deadline is 2030. The quantum computer that will break your classical signatures is a matter of when, not if.
Start with hybrid signatures today. Move to PQ-exclusive as your ecosystem supports it. The math is clear, the standards are final, and the tools are ready.
H33 provides post-quantum authentication infrastructure with FHE biometric processing (BFV lattice-based), ML-DSA digital signatures, and ML-KEM key exchange—all in a single API call at sub-millisecond latency. Every component in the stack is quantum-resistant by construction, not by policy.