Post-Quantum Signature Lab: ML-DSA, SLH-DSA, Falcon
Generate ML-DSA, SLH-DSA or Falcon keys, sign a message and verify a signature with the exact FIPS 204 and FIPS 205 options: context string, pure or pre-hash interface, hedged or deterministic signing. Keys are read and written as PEM, JWK and COSE_Key, and NIST test vectors replay byte for byte.
100% local — keys, messages and signatures stay in your browser: the engine runs in a Web Worker loaded from this site and sends nothing anywhere
ML-DSA-65 at a glance
| Algorithm | Size |
|---|---|
| ML-DSA-65 | 1,952 bytes61× Ed25519 |
| ECDSA P-256 | 65 bytes |
| Ed25519 | 32 bytes |
| RSA-2048 | 270 bytes |
| Algorithm | Size |
|---|---|
| ML-DSA-65 | 3,309 bytes51.7× Ed25519 |
| ECDSA P-256 | ≤ 72 bytes |
| Ed25519 | 64 bytes |
| RSA-2048 | 256 bytes |
Signatures always have the same length. Raw encodings: an uncompressed point and a DER signature of at most 72 bytes for ECDSA P-256, a PKCS #1 key for RSA-2048.
Keys
Signature options
The standard interface: the message itself is signed, after a domain separator and the context.
What the lab does
The lab generates keys, signs and verifies with the three post-quantum signature families NIST has chosen, entirely in your browser. ML-DSA (formerly CRYSTALS-Dilithium) is specified in FIPS 204 and SLH-DSA (formerly SPHINCS+) in FIPS 205; both became final standards on 13 August 2024. Falcon is the third: NIST is turning it into FN-DSA, planned as FIPS 206, which is not a final standard as of 20 October 2026. The lab therefore runs Falcon exactly as submitted to the third round, and labels it that way, because FN-DSA will not be byte-compatible with it.
Pick a parameter set, generate a key pair or paste keys, and sign or verify. The panel next to the form shows what the choice costs: an ML-DSA-65 public key is 1,952 bytes and its signature 3,309 bytes, against 32 and 64 bytes for Ed25519. SLH-DSA keys are tiny, but its signatures run from 7,856 to 49,856 bytes. Falcon has the smallest public key and signature together, at the price of floating-point arithmetic that is hard to make constant-time.
The options that change a signature
FIPS 204 and FIPS 205 define more than one way to sign, and a verifier has to use the same one. That is the first thing to check when an interoperability test fails, and the lab tries the other variants for you when a signature does not verify.
- Context string. Up to 255 bytes, empty by default, mixed into what is signed so that a signature made for one purpose cannot be replayed for another. Signer and verifier must agree on it byte for byte. Falcon round 3 has no context.
- Pure or pre-hash. The pure interface signs the message itself. The pre-hash variants, HashML-DSA and HashSLH-DSA, sign a hash of it together with the hash function's OID. They are separate algorithms with their own identifiers, and RFC 9881 forbids HashML-DSA in X.509; for large messages it recommends "external μ" instead, which the lab can compute.
- Hedged or deterministic. By default ML-DSA and SLH-DSA mix fresh randomness into every signature (hedged signing), which protects against fault and side-channel attacks. Deterministic signing always gives the same bytes for the same key and message, which is what makes the NIST test vectors reproducible.
- Internal interface.
ML-DSA.Sign_internalandslh_sign_internalskip the domain separator and context. They exist for test vectors; signatures made this way do not verify with the standard functions.
Keys and where they are written down
A post-quantum private key comes in more than one form. ML-DSA keys are generated from a 32-byte seed, and
RFC 9881 lets a PKCS #8 file hold the seed, the 2,560 to 4,896-byte expanded key, or both. Keep the seed: it
cannot be recovered from the expanded key. When you import a key, the lab checks that the parts belong together —
the seed regenerates the expanded key, the stored public-key hash matches, and for ML-DSA even that the stored
t0 is consistent, the defect behind the inconsistent example keys of RFC 9881 Appendix C.4.
Public keys are read and written as SubjectPublicKeyInfo (RFC 9881 for ML-DSA,
RFC 9909 for SLH-DSA), as JSON Web Keys of type AKP and as
COSE_Key (RFC 9964, final for ML-DSA). SLH-DSA has JOSE names
only for the two 128-bit "small" sets, in draft-ietf-cose-sphincs-plus, and Falcon has no JOSE or COSE
encoding yet; its X.509 identifiers are the experimental ones of Open Quantum Safe, because FN-DSA's OIDs are
still to be assigned in draft-ietf-lamps-fn-dsa-certificates.
Known-answer examples
Every example comes from a published test vector: NIST ACVP-Server "sigGen" tests for ML-DSA and SLH-DSA, the Falcon round-3 KAT files and the PQClean padded-Falcon KATs, and the JOSE example of RFC 9964 Appendix A. The ML-DSA, SLH-DSA and RFC examples are deterministic, so pressing Sign reproduces the expected signature byte for byte and the lab says so. Falcon always signs with fresh randomness: Verify accepts the vector's signature, and Sign produces a different signature that verifies just as well.
Which one to use
For most systems the answer in 2026 is ML-DSA-65, with ML-DSA-87 where a policy such as CNSA 2.0 requires category 5. SLH-DSA fits long-lived roots of trust and firmware, where signing is rare and its security rests on hash functions alone. The trade-offs are compared in ML-DSA vs SLH-DSA, ML-DSA vs ECDSA and FN-DSA vs ML-DSA; tested code for OpenSSL, Go, Java and .NET is in ML-DSA for developers, and the deadlines in Post-quantum migration in 2026. To see how these sets compare with the other hundred algorithms of liboqs on your own device, open the algorithm zoo; to decode a certificate signed with them, the post-quantum certificate inspector.