dilithium-rs
Pure Rust implementation of ML-DSA (FIPS 204) / CRYSTALS-Dilithium
Post-quantum digital signature scheme —
no_std+ WASM ready.
Features
| Feature | Status |
|---|---|
| ML-DSA-44 / ML-DSA-65 / ML-DSA-87 | ✅ |
| Pure ML-DSA signing (§6.1) | ✅ |
| HashML-DSA pre-hash mode (§6.2) | ✅ |
| Key validation (§7.1) | ✅ |
| Constant-time verification | ✅ |
| Zeroization of secrets | ✅ |
no_std / WASM compatible |
✅ |
Optional serde support |
✅ |
| NIST KAT vectors — bit-for-bit match with C reference | ✅ |
| SIMD acceleration (AVX2 + NEON) | ✅ |
0 unsafe blocks (core library) |
✅ |
Quick Start
use ;
// Generate a key pair
let kp = generate.unwrap;
// Sign a message
let sig = kp.sign.unwrap;
// Verify
assert!;
Security Levels
| FIPS 204 Name | NIST Level | Public Key | Secret Key | Signature |
|---|---|---|---|---|
| ML-DSA-44 | 2 | 1,312 B | 2,560 B | 2,420 B |
| ML-DSA-65 | 3 | 1,952 B | 4,032 B | 3,309 B |
| ML-DSA-87 | 5 | 2,592 B | 4,896 B | 4,627 B |
API Reference
Key Generation
use ;
// Random key pair (OS entropy)
let kp = generate.unwrap;
// Deterministic key pair (from seed)
let seed = ;
let kp = generate_deterministic;
Signing & Verification
// Pure ML-DSA (§6.1)
let sig = kp.sign.unwrap;
let ok = verify;
// HashML-DSA pre-hash (§6.2) — message is SHA-512 hashed internally
let sig = kp.sign_prehash.unwrap;
let ok = verify_prehash;
Serialization
// Key pair round-trip
let bytes = kp.to_bytes; // [mode_tag | pk | sk]
let kp2 = from_bytes.unwrap;
// Public key export (for distribution)
let pk_bytes = kp.public_key_bytes; // [mode_tag | pk]
let = from_public_key.unwrap;
// Signature round-trip
let sig_bytes = sig.as_bytes.to_vec;
let sig2 = from_bytes;
// Import raw keys with validation (FIPS 204 §7.1)
let kp = from_keys.unwrap;
Serde (optional)
[]
= { = "0.1", = ["serde"] }
let json = to_string.unwrap;
let kp: DilithiumKeyPair = from_str.unwrap;
no_std / WASM
[]
= { = "0.1", = false }
All dependencies support no_std and wasm32-unknown-unknown:
sha3,sha2— SHAKE/SHA hashingsubtle— constant-time comparisonzeroize— secret material cleanupgetrandom— OS entropy (usescrypto.getRandomValuesin WASM)
Benchmarks
Measured on Apple Silicon (M-series), --release, 10,000 iterations.
Rust uses Criterion; C reference compiled with cc -O3.
| Operation | Mode | Rust (µs) | C ref (µs) | Ratio |
|---|---|---|---|---|
| keygen | ML-DSA-44 | 48.9 | 50.8 | 0.96× ✅ |
| keygen | ML-DSA-65 | 79.2 | 94.8 | 0.84× ✅ |
| keygen | ML-DSA-87 | 130.3 | 135.9 | 0.96× ✅ |
| sign | ML-DSA-44 | 126.1 | 215.7 | 0.58× ✅ |
| sign | ML-DSA-65 | 529.9 | 354.3 | 1.50× |
| sign | ML-DSA-87 | 284.2 | 442.6 | 0.64× ✅ |
| verify | ML-DSA-44 | 53.2 | 53.8 | 0.99× ✅ |
| verify | ML-DSA-65 | 85.1 | 84.6 | 1.01× |
| verify | ML-DSA-87 | 135.3 | 143.5 | 0.94× ✅ |
Sign times have high variance due to rejection sampling. Ratios <1.0 mean Rust is faster.
Security
- 0
unsafeblocks in core library (SIMD modules useunsafebehindsimdfeature) - Constant-time verification via
subtle::ConstantTimeEq - Zeroize — private keys auto-zeroed on drop, seeds/rnd zeroed after use
- NIST KAT — bit-for-bit match with C reference (pk, sk, sig hashes)
- Key validation —
from_keys()validates rho consistency +tr = H(pk) - Fuzz tested — 3 targets, 41M+ executions, 0 crashes
See SECURITY.md for responsible disclosure and scope.
Test Suite
cargo test # all 73 tests
cargo test --features serde # with serde
cargo test --features simd # with SIMD
cargo clippy -- -W clippy::pedantic # 0 warnings
| Suite | Tests | What |
|---|---|---|
| Unit | 30 | NTT, reduce, rounding, symmetric, poly, SIMD |
| Round-trip | 17 | Sign/verify all modes, HashML-DSA, key validation |
| Coverage | 17 | Edge cases, error paths, boundaries |
| KAT | 4 | Bit-for-bit match with C reference (all 3 modes) |
| Multi-vector KAT | 3 | 100 vectors × 3 modes accumulated hash |
| Doc-tests | 2 | Code examples compile and run |
Feature Flags
| Feature | Default | Description |
|---|---|---|
std |
✅ | OS entropy for generate, sign, sign_prehash |
serde |
❌ | Serialize/Deserialize for key pairs and signatures |
simd |
❌ | AVX2 (x86_64) and NEON (AArch64) NTT acceleration |
js |
❌ | getrandom/js for WASM browser targets |
License
MIT