quantum_shield/lib.rs
1//! # Quantum Shield
2//!
3//! Hybrid post-quantum cryptography for Rust:
4//!
5//! - **Encryption**: X25519 + ML-KEM-1024 (FIPS 203) hybrid KEM feeding a
6//! SHA3-256 combiner, with AES-256-GCM payload encryption. Both key
7//! agreements enter one KDF, so an attacker must break *both* the
8//! classical and the post-quantum layer to recover a message.
9//! - **Signatures**: Ed25519 + ML-DSA-87 (FIPS 204), both always present
10//! and both required to verify — the post-quantum signature cannot be
11//! stripped.
12//!
13//! All algorithm implementations are pure Rust (RustCrypto and dalek
14//! crates); the crate builds and runs natively on Apple Silicon, x86-64,
15//! and other targets without a C toolchain.
16//!
17//! ## Security status
18//!
19//! **This library and the underlying `ml-kem`/`ml-dsa` crates have not been
20//! independently audited.** The library implements the FIPS 203/204
21//! algorithms via RustCrypto; the library itself is not FIPS-validated.
22//! Evaluate accordingly before using it to protect production data.
23//!
24//! Artifacts produced by quantum-shield 0.1.x use a cryptographically broken
25//! format and are rejected with [`Error::LegacyV1Artifact`].
26//!
27//! ## Example
28//!
29//! ```no_run
30//! use quantum_shield::{HybridCrypto, verify};
31//!
32//! # fn main() -> quantum_shield::Result<()> {
33//! let alice = HybridCrypto::generate()?;
34//! let bob = HybridCrypto::generate()?;
35//!
36//! // Alice encrypts a message for Bob.
37//! let envelope = alice.seal_for(b"Hybrid PQ message", bob.public_keys())?;
38//! let plaintext = bob.open(&envelope)?;
39//! assert_eq!(plaintext, b"Hybrid PQ message");
40//!
41//! // Alice signs a message; Bob verifies it.
42//! let signature = alice.sign(b"I agree to these terms", b"contract")?;
43//! verify(b"I agree to these terms", b"contract", &signature, alice.public_keys())?;
44//! # Ok(())
45//! # }
46//! ```
47//!
48//! Wire objects ([`Envelope`], [`HybridSignature`], [`PublicKeyBundle`])
49//! serialize to versioned binary formats via `to_bytes`/`from_bytes`; the
50//! format is specified in `docs/design.md`.
51
52#![forbid(unsafe_code)]
53#![warn(missing_docs, rust_2018_idioms)]
54#![cfg_attr(docsrs, feature(doc_cfg))]
55#![cfg_attr(not(feature = "std"), no_std)]
56
57extern crate alloc;
58
59mod api;
60mod constants;
61mod error;
62mod hybrid_kem;
63mod keys;
64mod multi;
65#[cfg(feature = "pem")]
66#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
67mod pem;
68mod rotate;
69mod seal;
70#[cfg(feature = "serde")]
71#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
72mod serde_impls;
73mod sign;
74mod stream;
75mod types;
76mod wire;
77
78pub use api::HybridCrypto;
79pub use constants::*;
80pub use error::{Error, Result};
81pub use keys::{KeyId, KeyPair, PublicKeyBundle};
82pub use multi::{open_multi, seal_multi, MultiRecipientEnvelope};
83pub use rotate::{verify_rotation, RotationAttestation};
84pub use seal::{open, seal};
85pub use stream::{StreamOpener, StreamSealer};
86pub use types::{Envelope, HybridSignature};
87pub use zeroize::Zeroizing;
88
89/// Verify a [`HybridSignature`] over `message` and `context` against the
90/// signer's [`PublicKeyBundle`].
91///
92/// Both the Ed25519 and the ML-DSA-87 component must be valid.
93///
94/// # Errors
95///
96/// Returns [`Error::VerificationFailed`] if either component is invalid, and
97/// [`Error::ContextTooLong`] if `context` exceeds 255 bytes.
98pub fn verify(
99 message: &[u8],
100 context: &[u8],
101 signature: &HybridSignature,
102 signer: &PublicKeyBundle,
103) -> Result<()> {
104 sign::verify(message, context, signature, signer)
105}
106
107/// Commonly used items.
108pub mod prelude {
109 pub use crate::{
110 seal, verify, Envelope, HybridCrypto, HybridSignature, KeyPair, PublicKeyBundle, Result,
111 };
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn end_to_end_encryption() {
120 let alice = HybridCrypto::generate().unwrap();
121 let bob = HybridCrypto::generate().unwrap();
122
123 let envelope = alice.seal_for(b"Test message", bob.public_keys()).unwrap();
124 let decrypted = bob.open(&envelope).unwrap();
125 assert_eq!(decrypted, b"Test message");
126 }
127
128 #[test]
129 fn end_to_end_signature() {
130 let alice = HybridCrypto::generate().unwrap();
131 let sig = alice.sign(b"Message to sign", b"").unwrap();
132 verify(b"Message to sign", b"", &sig, alice.public_keys()).unwrap();
133 }
134
135 #[test]
136 fn free_function_seal_matches_method() {
137 let bob = HybridCrypto::generate().unwrap();
138 let envelope = seal(b"via free function", bob.public_keys()).unwrap();
139 assert_eq!(bob.open(&envelope).unwrap(), b"via free function");
140 }
141}