Quantum Security Programme · Technical brief

ML-DSA (FIPS 204) — Dilithium signatures, deployment in TLS, S/MIME, code-signing

← Q-Security overview

1. The exact problem this technology solves

ML-DSA exists because every classical signature primitive in production today — RSA-2048, RSA-3072, ECDSA-P256, Ed25519 — falls to a sufficiently large fault-tolerant quantum computer running Shor's algorithm. The integer factorisation problem and the elliptic curve discrete logarithm problem both collapse to polynomial time. For a signature scheme this means an adversary with quantum capability can recover the private key from any published public key.

The attack model is not "decrypt traffic later". Signatures are identity. If RSA-2048 in your code-signing CA falls, an attacker forges a Microsoft Authenticode signature, an Apple notarisation, an APT package, a firmware update for a HSM. If the CA chain inside X.509 certificates falls, every TLS server identity in the country is forgeable. If Ed25519 SSH host keys fall, MITM against every administrative session becomes trivial.

The criticality difference between confidentiality (KEMs, ML-KEM) and authenticity (signatures, ML-DSA) is timing. Harvest-now-decrypt-later affects ciphertext that exists today. A signature attack only matters at the moment of forgery, which means we have until Q-Day rather than retroactively. But the artifacts being signed today — root CA certificates with 20-year validity, firmware that ships in devices with 15-year service lives, long-lived S/MIME archives — must verify under post-quantum trust by the time the attacker is capable. For root CAs issued in 2026, that is well within scope.

2. The cryptographic primitive

ML-DSA is the NIST-standardised form of CRYSTALS-Dilithium, finalised as FIPS 204 on 2024-08-13. Security rests on the hardness of Module Learning With Errors (MLWE) and Module Short Integer Solution (MSIS) over the polynomial ring R_q = Z_q[X]/(X^256 + 1) with q = 8380417. The construction is Fiat–Shamir with aborts applied to a lattice identification scheme.

The standard defines three parameter sets:

  • ML-DSA-44 — NIST level 2, public key 1312 B, signature 2420 B
  • ML-DSA-65 — NIST level 3, public key 1952 B, signature 3309 B
  • ML-DSA-87 — NIST level 5, public key 2592 B, signature 4627 B

The security target is EUF-CMA (existential unforgeability under chosen message attack) with the additional property of SUF-CMA (strong unforgeability) in the random oracle model, and EUF-CMA in the quantum random oracle model under standard MLWE/MSIS assumptions. There is no IND-CCA2 framing — that applies to KEMs (ML-KEM/Kyber), not signatures.

FIPS 204 specifies two signing modes: the deterministic mode (RFC-style, derives the per-signature randomness from the message and a secret seed) and the hedged mode which additionally mixes 32 bytes of fresh entropy. Use hedged. Deterministic Dilithium has known fault-injection and side-channel concerns that hedged mode reduces. The signing algorithm uses rejection sampling — signatures fail and retry — so signing latency has a long tail. Verification is a single deterministic pass.

3. Reference implementations, state of readiness in 2026

  • liboqs 0.12 — Open Quantum Safe project. Provides OQS_SIG_ml_dsa_44/65/87 with constant-time AVX2 paths. Production-ready for embedding; this is the upstream that nearly every other implementation tracks.
  • OpenSSL 3.5 LTS — native ML-DSA via EVP_PKEY with algorithm names ML-DSA-44, ML-DSA-65, ML-DSA-87. The 3.4 oqs-provider bridge is deprecated in favour of native.
  • BoringSSL — ML-DSA-65 supported for Chrome's internal use; hybrid Ed25519+ML-DSA-65 in experimental TLS 1.3 certificate paths.
  • BIND 9.20 — DNSSEC signing with ML-DSA-44 and ML-DSA-65 per draft-ietf-dnsop-pqc-dnssec. Zone-size inflation is the real operational issue.
  • OpenSSH 9.9 — host-key and user-key signing with ssh-mldsa65. Hybrid ssh-ed25519-mldsa65@openssh.com recommended for transition.
  • Cloudflare — ML-DSA in their internal PKI; PQ TLS handshake telemetry public since 2024.
  • AWS KMS / GCP Cloud KMS / Azure Key Vault — all three offer ML-DSA-65 and ML-DSA-87 signing keys in 2026, with FIPS 140-3 validation for the underlying HSMs in progress (varies by region).
  • GnuPG 2.6 — experimental ML-DSA support; not yet recommended for long-lived key material.

4. Deployment pattern

For a TLS 1.3 server certificate using ML-DSA-65 with OpenSSL 3.5:

# 1. Generate the CA key and self-signed root
openssl genpkey -algorithm ML-DSA-87 -out root-ca.key
openssl req -new -x509 -key root-ca.key -out root-ca.crt \
    -days 7300 -subj "/CN=IQS Root CA 2026"

# 2. Generate the leaf key and CSR
openssl genpkey -algorithm ML-DSA-65 -out server.key
openssl req -new -key server.key -out server.csr \
    -subj "/CN=api.example.ie"

# 3. Issue
openssl x509 -req -in server.csr \
    -CA root-ca.crt -CAkey root-ca.key -CAcreateserial \
    -out server.crt -days 365 \
    -extfile <(printf "subjectAltName=DNS:api.example.ie")

# 4. Verify the chain
openssl verify -CAfile root-ca.crt server.crt

For code-signing, the CMS path:

openssl cms -sign -in firmware.bin -binary \
    -signer signer.crt -inkey signer.key \
    -md sha512 -outform DER -out firmware.bin.p7s \
    -nodetach

# Verify
openssl cms -verify -in firmware.bin.p7s -inform DER \
    -CAfile root-ca.crt -out /dev/null

Always issue with a hybrid composite where the relying-party population is mixed. RFC 9763 (composite signatures) defines id-MLDSA65-ECDSA-P256-SHA256 and friends; OpenSSL 3.5 supports these natively as ML-DSA-65-ECDSA-P256.

5. What goes wrong in production

  • Certificate size. An ML-DSA-65 leaf certificate is roughly 5.5 KB. A full chain — leaf, intermediate, root — pushes the TLS Certificate message past 16 KB and into multiple records. Embedded TLS stacks with 8 KB handshake buffers fail silently. Audit every device.
  • QUIC initial packet amplification. The server's first flight no longer fits in the anti-amplification window. Either disable PQ on the QUIC listener or accept the extra round-trips until the client validates path.
  • HSM signing latency. ML-DSA-87 signing on a Thales Luna 7 in 2026 is around 4–6 ms versus ~1 ms for RSA-2048. For a CA that issues 50,000 OCSP responses per minute, that matters.
  • Deterministic-mode fault attacks. If you signed with deterministic ML-DSA on a device exposed to glitching, treat the key as compromised. Switch to hedged.
  • S/MIME archives. Outlook, Thunderbird and most MTAs in 2026 still cannot verify ML-DSA signatures. Sign with composite RSA-3072 + ML-DSA-65 for any inbox you do not control.
  • DNSSEC zone bloat. A zone signed with ML-DSA-65 is 6–10× the size of one signed with ECDSAP256SHA256. UDP responses fragment; some recursive resolvers in front of corporate firewalls drop them. TCP fallback must work.
  • Algorithm rollback. Verifiers that accept both classical and PQ must not allow an attacker to negotiate down. Pin the policy at the verifier.

6. How to test it works

Inspect the certificate's SubjectPublicKeyInfo OID:

openssl x509 -in server.crt -text -noout | grep -A2 "Public Key Algorithm"
#

Schedule a sovereign-cryptography assessment

Book a security call →