Skip to main content

quantica/
lib.rs

1//! # quantica — post-quantum cryptography for the `krypteia` workspace
2//!
3//! Pure-Rust implementations of the three NIST post-quantum standards,
4//! sharing the same side-channel countermeasure toolkit
5//! ([`silentops`](https://docs.rs/silentops)) used by the classical
6//! side of the workspace ([`arcana`](https://docs.rs/arcana)).
7//!
8//! | Module       | Standard | Algorithm                              | Validation                                     |
9//! |--------------|----------|----------------------------------------|------------------------------------------------|
10//! | [`ml_kem`]   | FIPS 203 | ML-KEM (key encapsulation)             | ACVP + Wycheproof                              |
11//! | [`ml_dsa`]   | FIPS 204 | ML-DSA (digital signature)             | ACVP + Wycheproof                              |
12//! | [`slh_dsa`]  | FIPS 205 | SLH-DSA (hash-based digital signature) | ACVP (Wycheproof has no SLH-DSA corpus yet)    |
13//!
14//! # Cargo features
15//!
16//! | Feature | Default | Effect |
17//! |---------|:-------:|--------|
18//! | `std`   | **on**  | Pulls in the standard library. Enables [`OsRng`](ml_kem::OsRng) and `std::error::Error` impls. |
19//! | `ml-kem` | **on** | Compiles the FIPS 203 module ([`ml_kem`]). |
20//! | `ml-dsa` | **on** | Compiles the FIPS 204 module ([`ml_dsa`]). |
21//! | `slh-dsa` | **on** | Compiles the FIPS 205 module ([`slh_dsa`]). |
22//! | `sca-protected` | **on** | Activates the masking + shuffled-NTT defences inside ML-KEM **and** ML-DSA (see below). |
23//!
24//! Disable `std` for `no_std` builds: pass `--no-default-features`
25//! then re-enable the algorithms you need. The crate still requires
26//! `alloc` (keys, ciphertexts and signatures are `Vec<u8>`-backed).
27//!
28//! # Quick start
29//!
30//! ```no_run
31//! use quantica::ml_kem::*;
32//!
33//! let mut rng = OsRng;
34//! let (ek, dk) = MlKem::<MlKem768>::keygen(&mut rng).unwrap();
35//! let (ss_a, ct) = MlKem::<MlKem768>::encaps(&ek, &mut rng).unwrap();
36//! let ss_b = MlKem::<MlKem768>::decaps(&dk, &ct, &mut rng).unwrap();
37//! assert_eq!(ss_a, ss_b);
38//! // Both shared secrets auto-zeroize when they drop.
39//! ```
40//!
41//! # Typed key wrappers (Zeroize-on-Drop)
42//!
43//! The public API never returns raw `Vec<u8>` for secret material.
44//! Each algorithm exposes parameter-set-tagged wrapper types backed
45//! by the shared [`secret`] module:
46//!
47//! | Module     | Public (not zeroized)                       | Secret (Drop-zeroizes)                      |
48//! |------------|---------------------------------------------|----------------------------------------------|
49//! | [`ml_kem`] | [`EncapsulationKey<P>`](ml_kem::EncapsulationKey), [`Ciphertext<P>`](ml_kem::Ciphertext) | [`DecapsulationKey<P>`](ml_kem::DecapsulationKey), [`SharedSecret`](ml_kem::SharedSecret) |
50//! | [`ml_dsa`] | [`VerifyingKey<P>`](ml_dsa::VerifyingKey), [`Signature<P>`](ml_dsa::Signature) | [`SigningKey<P>`](ml_dsa::SigningKey) |
51//! | [`slh_dsa`]| [`VerifyingKey<P>`](slh_dsa::VerifyingKey), [`Signature<P>`](slh_dsa::Signature) | [`SigningKey<P>`](slh_dsa::SigningKey) |
52//!
53//! All wrappers implement `from_bytes(&[u8])` (length-validated),
54//! `as_bytes()`, `Deref<Target=[u8]>`, `AsRef<[u8]>` and `Clone`.
55//! Secret variants have a **redacted `Debug`** impl
56//! (`<redacted; len=N>`) so stray logging cannot leak key material.
57//!
58//! The internal byte-slice API (`ml_kem::kem::*`, `ml_dsa::dsa::*`,
59//! `slh_dsa::slh::*`) is still public for ACVP/CAVP testing and
60//! for the C FFI.
61//!
62//! # Side-channel countermeasures
63//!
64//! ## Always-on (every build)
65//!
66//! | Defence               | Algorithms          | Threat addressed               |
67//! |-----------------------|---------------------|---------------------------------|
68//! | Constant-time arith   | all three           | Timing / cache / basic SPA     |
69//! | Zeroize-on-Drop       | all three           | Cold boot, memory dumps, UAF   |
70//! | Volatile zeroization  | all three           | Same, on intermediates         |
71//! | Double Decaps         | ML-KEM              | DFA on FO comparison           |
72//! | dk integrity check    | ML-KEM              | DFA on stored key material     |
73//! | Hedged signing        | ML-DSA, SLH-DSA     | Fault-induced nonce reuse      |
74//!
75//! ## `sca-protected` (on by default)
76//!
77//! | Defence                       | Algorithms      | Module                                |
78//! |-------------------------------|-----------------|---------------------------------------|
79//! | First-order additive masking  | ML-KEM, ML-DSA  | [`ml_kem::masked`], [`ml_dsa::masked`] |
80//! | Shuffled NTT (Fisher-Yates)   | ML-KEM, ML-DSA  | [`ml_kem::shuffle`], [`ml_dsa::shuffle`] |
81//! | Mask refresh between rounds   | ML-DSA          | `MaskedPoly::refresh()` per rejection iteration |
82//!
83//! The masking layer is mathematically transparent: `unmask(op(mask(s), public)) ≡ op(s, public)`.
84//! All NIST ACVP vectors pass unchanged with `sca-protected` enabled — the masked path produces
85//! **bit-identical** signatures and shared secrets.
86//!
87//! SLH-DSA is purely hash-based and has no algebraic structure to
88//! mask; the always-on defences are the relevant layer there.
89//!
90//! # `no_std`
91//!
92//! `quantica` is `std`-by-default, but the standard library is gated
93//! behind the `std` feature. Building with
94//! `--no-default-features --features ml-kem,ml-dsa,slh-dsa,sca-protected`
95//! produces a `no_std` crate that still depends on `alloc` (because
96//! the algorithms allocate heap buffers for keys, ciphertexts and
97//! signatures). In `no_std` mode:
98//!
99//! * The OS-backed `OsRng` is not available — callers must provide
100//!   their own [`CryptoRng`](ml_kem::CryptoRng) implementation
101//!   (typically wrapping a hardware RNG on embedded targets).
102//! * The error enums no longer implement [`std::error::Error`].
103//!
104//! Cross-compile validated on `thumbv7em-none-eabihf` (Cortex-M4),
105//! `thumbv6m-none-eabi` (Cortex-M0), and `riscv32imc-unknown-none-elf`
106//! (ESP32-C3) with all three algorithms + `sca-protected` enabled.
107//!
108//! # Examples
109//!
110//! ```bash
111//! cargo run --release -p quantica --example ml_kem_roundtrip
112//! cargo run --release -p quantica --example ml_dsa_sign_verify
113//! cargo run --release -p quantica --example slh_dsa_sign_verify
114//! ```
115
116#![cfg_attr(not(feature = "std"), no_std)]
117
118#[macro_use]
119extern crate alloc;
120
121// Bring `Vec` into scope for all submodules without forcing a per-file
122// `use alloc::vec::Vec;`. The `vec!` macro is brought in via the
123// `#[macro_use] extern crate alloc;` above.
124#[allow(unused_imports)]
125pub(crate) use alloc::vec::Vec;
126
127// Shared Keccak-f[1600] core used by every algorithm. Crate-private:
128// each algorithm exposes its own thin `sha3` wrapper module on top.
129#[cfg(any(feature = "ml-kem", feature = "ml-dsa", feature = "slh-dsa"))]
130mod sha3;
131
132/// Zeroize-on-Drop wrappers for secret key material
133/// ([`SecretBytes`](secret::SecretBytes), [`SecretArray`](secret::SecretArray)).
134#[cfg(any(feature = "ml-kem", feature = "ml-dsa", feature = "slh-dsa"))]
135pub mod secret;
136
137#[cfg(feature = "ml-kem")]
138pub mod ml_kem;
139
140#[cfg(feature = "ml-dsa")]
141pub mod ml_dsa;
142
143#[cfg(feature = "slh-dsa")]
144pub mod slh_dsa;