-
Notifications
You must be signed in to change notification settings - Fork 3
ML DSA
Module-Lattice-Based Digital Signature Standard — formerly
CRYSTALS-Dilithium, standardized by NIST as
FIPS 204. ML-DSA proves a message's
authenticity and integrity. pqcrypto supports all three parameter sets and is
byte-exact against the NIST KAT corpus across every signing mode and flavour.
import 'dart:convert';
import 'dart:typed_data';
import 'package:pqcrypto/pqcrypto.dart';
final params = DilithiumParams.mlDsa65; // or mlDsa44 / mlDsa87
final (publicKey, secretKey) = MlDsa.generateKeyPair(params);
final message = Uint8List.fromList(utf8.encode('message bytes'));
final ctx = Uint8List.fromList(utf8.encode('myapp/purpose/v1')); // <= 255 bytes
final signature = MlDsa.sign(secretKey, message, params, ctx: ctx);
final ok = MlDsa.verify(publicKey, message, signature, params, ctx: ctx);final signature = MlDsa.hashSign(secretKey, largePayload, params, ctx: ctx);
final ok = MlDsa.hashVerify(publicKey, largePayload, signature, params, ctx: ctx);hashSign/hashVerify pre-hash the message internally with the FIPS 204
approved hash for the security level — SHA-256 for ML-DSA-44, SHA-384 for
ML-DSA-65, SHA-512 for ML-DSA-87. Pass the message, not a digest.
-
MlDsa.signDeterministic(sk, m, params, ctx: ...)— deterministic (rnd = 0). Explicit opt-in; discouraged because it is harder to protect against fault/side-channel attacks. Prefer the hedged defaultsign. -
MlDsa.generateKeyPairSeeded(params, seed32)— deterministic keygen from a 32-byte seed (backup/interop/KAT).
sequenceDiagram
participant Si as Signer
participant V as Verifier
Si->>Si: (pk, sk) = generateKeyPair(params)
Si->>Si: sig = sign(sk, msg, params, ctx)
Si->>V: send msg + sig (+ pk if not already trusted)
V->>V: verify(pk, msg, sig, params, ctx) -> bool
ML-DSA uses "Fiat–Shamir with Aborts": signing samples a masking vector from a SHAKE XOF and rejection-samples until the signature leaks nothing about the secret key. The rejection loop streams an incremental XOF (no fixed buffer to exhaust), and the infinity-norm check evaluates all 256 coefficients with no early exit to reduce timing leakage.
-
Hedged signing injects fresh entropy from
Random.secure()on every signature — the recommended, fault/side-channel-friendlier default. - The context string (
ctx, up to 255 bytes) provides domain separation. Use a distinct, versioned context per purpose so a signature minted for one use cannot be replayed as another. The samectxmust be used to verify.
| Parameter set | NIST category | Public key | Secret key | Signature | Pre-hash (HashML-DSA) |
|---|---|---|---|---|---|
| ML-DSA-44 | 2 | 1312 | 2560 | 2420 | SHA-256 |
| ML-DSA-65 | 3 | 1952 | 4032 | 3309 | SHA-384 |
| ML-DSA-87 | 5 | 2592 | 4896 | 4627 | SHA-512 |
Sizes are in bytes. Default to ML-DSA-65. Validate exact lengths before verifying.
-
verify/hashVerifyreturnfalse(never throw) on any malformed, wrong-length, or forged input — branch on the boolean. - A public key is only an identity once you trust it (enrollment, attestation, certificate). A self-provided key proves nothing on its own — see the Cookbook "Identity enrollment" recipe.
- This is KAT/regression evidence, not a CMVP/FIPS 140 validation; zeroization and side-channel resistance are best-effort in Dart. See Security Posture.
- Cryptographic Algorithms · ML-KEM
- Cookbook — signed tokens, updates, logs, documents, identities.
- Canonical: MLDSA_FIPS204_RELEASE_GUIDE.md
pqcrypto — pure Dart, zero-dependency post-quantum cryptography (ML-KEM FIPS 203 · ML-DSA FIPS 204) for Dart, Flutter, and the web · MIT License · pub.dev · Repository · Documentation Index
Algorithm/KAT-conformance and interoperability evidence — not a CMVP/FIPS 140 module validation.
pqcrypto Wiki
Getting started
Algorithms
Design & internals
Assurance
Integration
Project
Links