falcon-rust
Native Rust implementation of the Falcon post-quantum digital signature scheme, ported from the C reference implementation by Thomas Pornin.
Status
✅ Bit-for-bit compatible with the C reference — passes all NIST Known Answer Tests for both Falcon-512 and Falcon-1024.
Features
- NIST PQC standard — Falcon is selected for standardization by NIST
no_stdsupport — works in embedded and WASM environments- WASM ready — compiles to
wasm32-unknown-unknownout of the box - Security hardening — PRNG state is zeroized on drop via
write_volatile - Pure Rust — no C dependencies, no assembly
- Full SDK — high-level API with key/signature serialization
- Serde support — optional
Serialize/Deserializefor keys and signatures - Performance optimized — bounds-check-free NTT, FFT, and ChaCha20 hot paths
- Fuzz tested — 3 cargo-fuzz targets for verify, sign+verify, and codec
Quick Start
use ;
// Generate a Falcon-512 key pair
let kp = generate.unwrap;
// Sign a message
let sig = kp.sign.unwrap;
// Verify the signature
verify.unwrap;
Key Serialization
use FalconKeyPair;
let kp = generate.unwrap;
// Export keys to bytes (for storage, transmission, etc.)
let private_key: = kp.private_key.to_vec; // 1281 bytes
let public_key: = kp.public_key.to_vec; // 897 bytes
// Import from both keys
let restored = from_keys.unwrap;
// Import from private key only (recomputes public key)
let restored2 = from_private_key.unwrap;
assert_eq!;
// Extract public key without creating a full key pair
let pk = public_key_from_private.unwrap;
Signature Serialization
use ;
let kp = generate.unwrap;
let sig = kp.sign.unwrap;
// Export
let sig_bytes: = sig.into_bytes;
// Import
let sig2 = from_bytes;
Serde Support
Enable the serde feature for JSON/bincode/etc. serialization:
[]
= { = "0.1.0", = ["serde"] }
FalconKeyPair, FalconSignature, and FalconError all implement Serialize/Deserialize when enabled.
Security Levels
| Variant | logn |
NIST Level | Private Key | Public Key | Signature |
|---|---|---|---|---|---|
| Falcon-512 | 9 | I | 1281 B | 897 B | ~666 B |
| Falcon-1024 | 10 | V | 2305 B | 1793 B | ~1280 B |
Benchmarks — C vs Rust
Measured on Apple M-series (ARM64), single-threaded, release builds.
C compiled with clang -O3, Rust with cargo --release (opt-level 3).
Falcon-512
| Operation | C (ref) | Rust | Ratio |
|---|---|---|---|
| keygen | 5.55 ms | 4.60 ms | 0.83× ✅ |
| sign | 213 µs | 272 µs | 1.28× |
| verify | 14.3 µs | 14.3 µs | 1.00× ✅ |
Falcon-1024
| Operation | C (ref) | Rust | Ratio |
|---|---|---|---|
| keygen | 18.6 ms | 15.9 ms | 0.86× ✅ |
| sign | 434 µs | 542 µs | 1.25× |
| verify | 27.8 µs | 27.2 µs | 0.98× ✅ |
Notes: Keygen and verify are at or below C performance. Sign is ~1.25× slower, primarily because the C reference uses AVX2/NEON-optimized ChaCha20 PRNG and hand-tuned NTT. All measurements via Criterion.
Run benchmarks yourself:
# Criterion (statistical, recommended)
# Quick ad-hoc benchmarks
API Overview
High-Level SDK (safe_api)
| Type | Description |
|---|---|
FalconKeyPair |
Key generation, signing, import/export |
FalconSignature |
Verification, serialization |
FalconError |
Error codes (RandomError, FormatError, etc.) |
Low-Level (falcon)
For advanced use cases — streamed signing, expanded keys, custom signature formats:
use falcon as falcon_api;
use InnerShake256Context;
// Streamed signing (hash-then-sign for large messages)
let mut hash = new;
falcon_sign_start;
shake256_inject;
shake256_inject;
falcon_sign_dyn_finish;
// Expanded key (amortized cost for multiple signatures)
falcon_expand_privkey;
falcon_sign_tree;
Examples
Building
Testing
# Full test suite (58 tests)
# NIST KAT validation
# Benchmarks
Fuzz Testing
Three cargo-fuzz targets are included:
# Install cargo-fuzz (one-time)
# Fuzz verify rejection (random data should never verify)
# Fuzz sign+verify roundtrip (must always succeed)
# Fuzz codec encode/decode roundtrip
WASM
Falcon-RS compiles to WebAssembly out of the box:
# Install the WASM target (one-time)
# Build for WASM (no_std, no OS entropy)
In no_std / WASM environments, use deterministic key generation with your own entropy:
use FalconKeyPair;
let seed: = /* your entropy source */;
let kp = generate_deterministic.unwrap;
Documentation
License
MIT — matching the C reference implementation.