Expand description
LatticeArc - Post-Quantum Cryptography Library
Post-quantum cryptography on the aws-lc-rs FIPS 140-3 backend. Hybrid
ML-KEM+X25519 by default, all 4 NIST PQC standards (FIPS 203–206) — one
crate, zero unsafe. For PQ TLS, use rustls 0.23.37+ with aws-lc-rs
directly; this crate does not wrap rustls.
IMPORTANT — FIPS terminology: this crate distinguishes algorithm conformance (implementing the NIST specs FIPS 203/204/205/206) from module validation (FIPS 140-3 CMVP certification of a cryptographic module). LatticeArc implements the algorithms for all four standards but is not itself CMVP-validated. With
--features fips, AES-GCM, ML-KEM, HKDF, and SHA-2 are routed through the FIPS 140-3 validated aws-lc-rs backend. ML-DSA (fips204), SLH-DSA (fips205), and FN-DSA (fn-dsa) are NIST-conformant but use non-validated crate implementations regardless of feature flags.
§Why LatticeArc?
| Without LatticeArc | With LatticeArc |
|---|---|
| ~50 lines for hybrid encrypt | 3 lines |
| Manage 4 key vectors manually | Single EncryptKey::Hybrid |
| Research NIST parameter sets | UseCase auto-selects |
| Manual secret zeroization | Automatic via Zeroize |
§Known limitations
- No streaming AEAD API. All
encrypt/decryptentry points operate on whole-message buffers. For very large payloads (multi-GiB files, network streams), partition into chunks at the application layer and bind chunk-index + total-count into the AEAD additional authenticated data so the per-chunk tags compose into a single document tag. A native streaming API is on the roadmap; not in 0.8. - No async API. Cryptographic functions are blocking. They run in
microseconds-to-low-milliseconds and don’t interact with I/O, so
wrapping them in
tokio::task::spawn_blockingis the recommended integration pattern. A native async API is not planned because the added complexity is rarely worth the wins given the latency profile.
§Algorithm Validation Status
| Algorithm | Standard | Backend | FIPS Validated |
|---|---|---|---|
| ML-KEM | FIPS 203 | aws-lc-rs | Yes |
| AES-256-GCM | SP 800-38D | aws-lc-rs | Yes |
| HKDF-SHA256 | SP 800-56C | aws-lc-rs | Yes |
| SHA-256 | FIPS 180-4 | aws-lc-rs | Yes |
| ML-DSA | FIPS 204 | fips204 | No |
| SLH-DSA | FIPS 205 | fips205 | No |
| FN-DSA | FIPS 206 (draft / Falcon) | fn-dsa | No |
§Unified API with CryptoConfig
All cryptographic operations use CryptoConfig for configuration. This builder
pattern provides automatic algorithm selection based on use case or security level,
with optional Zero Trust session verification.
§Basic Usage (Hybrid — Recommended)
use latticearc::{encrypt, decrypt, CryptoConfig, EncryptKey, DecryptKey};
// Hybrid encryption: ML-KEM-768 + X25519 + HKDF + AES-256-GCM
let (pk, sk) = latticearc::generate_hybrid_keypair()?;
let encrypted = encrypt(b"secret", EncryptKey::Hybrid(&pk), CryptoConfig::new())?;
let decrypted = decrypt(&encrypted, DecryptKey::Hybrid(&sk), CryptoConfig::new())?;§PQ-Only Encryption (0.6.0+)
Use CryptoMode::PqOnly for pure post-quantum encryption without a classical
component. Required for CNSA 2.0 compliance.
use latticearc::{encrypt, decrypt, CryptoConfig, CryptoMode, EncryptKey, DecryptKey};
// PQ-only: ML-KEM-768 + HKDF + AES-256-GCM (no X25519)
let (pk, sk) = latticearc::generate_pq_keypair()
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
let config = CryptoConfig::new().crypto_mode(CryptoMode::PqOnly);
let encrypted = encrypt(b"secret", EncryptKey::PqOnly(&pk), config.clone())?;
let decrypted = decrypt(&encrypted, DecryptKey::PqOnly(&sk), config)?;For low-level access to the PQ-only ciphertext components, use
PqOnlyCiphertext getters or into_parts().
To convert a hybrid scheme to its PQ-only equivalent at the same NIST level:
EncryptionScheme::to_pq_equivalent().
§Digital Signatures
use latticearc::{generate_signing_keypair, sign_with_key, verify, CryptoConfig};
let message = b"Document to sign";
// Generate a persistent signing keypair (ML-DSA-65 + Ed25519 hybrid)
let (pk, sk, scheme) = generate_signing_keypair(CryptoConfig::new())?.into_parts();
// Sign with the persistent keypair
let signed = sign_with_key(message, &sk, &pk, CryptoConfig::new())?;
// Verify (uses public key embedded in SignedData)
let is_valid = verify(&signed, CryptoConfig::new())?;§Feature Flags
| Feature | Description |
|---|---|
fips | FIPS 140-3 validated backend via aws-lc-rs. Requires CMake + Go build tools. Without this feature, aws-lc-rs uses its default non-FIPS backend (C compiler only). Transitively enables fips-self-test. |
fips-self-test | Power-up KAT self-tests (ML-KEM via aws-lc-rs, AES-GCM, SHA-3, ML-DSA, SLH-DSA, FN-DSA). Pulled in transitively by fips; can be enabled standalone for non-FIPS builds that still want POST coverage. |
tracing-init | Subscriber/init helpers (init_tracing, init_tracing_with_file) for the tracing facade. Off by default so downstream binaries that wire their own subscriber don’t fight ours. The latticearc-cli binary enables this. |
zkp-serde | Serialization support for ZKP types (enables serde_with for Schnorr/Sigma protocol structs). |
secret-mlock | OS-level memory locking for SecretVec (Secret Type Invariant I-10). Uses mlock(2) on Linux/macOS via region. No-op on Windows — VirtualLock is documented as best-effort and VirtualUnlock’s ERROR_NOT_LOCKED path causes a panic = "abort" build to abort the process; the this feature off on Windows targets so the cfg evaluates false regardless of the feature flag. Default-off because RLIMIT_MEMLOCK can fail at constructor time on some deployments. |
kat-test-vectors | Exposes AeadCipher::new_allow_weak_key (AES-GCM Test Cases 1/2 reproducer). Default-off so production builds cannot construct weak-key ciphers. |
test-utils | Exposes FIPS module-error-state recovery helpers (clear_error_state, restore_operational_state) for negative tests that need to recover global state without re-running POST. FIPS 140-3 §9.6 state-machine bypass — must NOT be enabled in production builds. Default-off. (Note: earlier rounds listed SigmaProof::challenge_mut here. M3 narrowed it to #[cfg(test)] — strictly narrower than test-utils — so downstream consumers cannot acquire the soundness-bypass mutator by enabling this feature. The mutator is reachable only from in-tree lib unit tests.) |
kat-replay | Exposes legitimate KAT-replay entry points that bypass operator parameter floors but NOT cryptographic soundness checks. Currently: pbkdf2_kat (replays RFC 6070 / NIST CAVP fixtures with sub-OWASP iteration counts; DoS cap and PRF correctness still enforced). The CLI enables this for kdf --allow-weak-iterations. Distinct from test-utils; safe to ship to consumers that need vector replay. Default-off. |
formal-verification | Compilation marker: enables formal verification harness code (Kani proofs). Does not run proofs — use cargo kani separately. |
kani | Compilation marker: enables Kani bounded model checking proof harnesses. Requires cargo kani to execute proofs. |
saw | Compilation marker: enables SAW formal verification markers (inherited from aws-lc-rs). Does not run SAW proofs at build time. |
§More Examples
§Symmetric Encryption
use latticearc::{encrypt, decrypt, CryptoConfig, CryptoScheme, EncryptKey, DecryptKey};
use latticearc::primitives::rand::random_bytes;
// Generate a fresh 256-bit key from the OS CSPRNG. Never use a constant
// (e.g. `[0u8; 32]`) — AEAD constructors reject all-zero keys per the
// McGrew/Viega NIST AES-GCM Test Cases 1 and 2 weak-key check.
let key = random_bytes(32);
let encrypted = encrypt(b"secret", EncryptKey::Symmetric(&key),
CryptoConfig::new().force_scheme(CryptoScheme::Symmetric))?;
let decrypted = decrypt(&encrypted, DecryptKey::Symmetric(&key), CryptoConfig::new())?;§With Use Case Selection
use latticearc::{encrypt, CryptoConfig, UseCase, EncryptKey};
use latticearc::generate_hybrid_keypair_with_level;
use latticearc::primitives::kem::ml_kem::MlKemSecurityLevel;
// FileStorage resolves to ML-KEM-1024 (Level 5), so the keypair MUST
// be generated at the matching level. `generate_hybrid_keypair()`
// defaults to ML-KEM-768 and would be rejected by
// `validate_key_matches_scheme`.
let (pk, _sk) = generate_hybrid_keypair_with_level(MlKemSecurityLevel::MlKem1024)?;
let encrypted = encrypt(b"data", EncryptKey::Hybrid(&pk),
CryptoConfig::new().use_case(UseCase::FileStorage))?;§Zero Trust Session Verification
For production deployments, use VerifiedSession to enable Zero Trust
verification before each operation:
use latticearc::{
encrypt, decrypt, CryptoConfig, VerifiedSession, generate_keypair,
EncryptKey, DecryptKey,
};
// Step 1: Generate a keypair (done once, typically at provisioning)
let (pk, sk) = generate_keypair()?;
// Step 2: Establish a verified session (performs challenge-response)
let session = VerifiedSession::establish(pk.as_slice(), sk.expose_secret())?;
// Step 3: Hybrid encryption with session verification
let (enc_pk, enc_sk) = latticearc::generate_hybrid_keypair()?;
let encrypted = encrypt(b"secret", EncryptKey::Hybrid(&enc_pk),
CryptoConfig::new().session(&session))?;
let decrypted = decrypt(&encrypted, DecryptKey::Hybrid(&enc_sk),
CryptoConfig::new().session(&session))?;§Hybrid Encryption (ML-KEM-768 + X25519)
Use the unified API with EncryptKey::Hybrid / DecryptKey::Hybrid:
use latticearc::{encrypt, decrypt, CryptoConfig, EncryptKey, DecryptKey};
let (pk, sk) = latticearc::generate_hybrid_keypair()?;
let encrypted = encrypt(b"secret data", EncryptKey::Hybrid(&pk), CryptoConfig::new())?;
let decrypted = decrypt(&encrypted, DecryptKey::Hybrid(&sk), CryptoConfig::new())?;§Hybrid Signatures (ML-DSA-65 + Ed25519)
use latticearc::{generate_hybrid_signing_keypair, sign_hybrid, verify_hybrid_signature, SecurityMode};
// Generate a hybrid signing keypair (ML-DSA-65 + Ed25519)
let (pk, sk) = generate_hybrid_signing_keypair(SecurityMode::Unverified)?;
// Sign (both ML-DSA and Ed25519 signatures generated)
let signature = sign_hybrid(b"document", &sk, SecurityMode::Unverified)?;
// Verify (both must pass for signature to be valid)
let valid = verify_hybrid_signature(b"document", &signature, &pk, SecurityMode::Unverified)?;§Session Lifecycle
Sessions have a 30-minute default lifetime:
use latticearc::{encrypt, CryptoConfig, VerifiedSession, generate_keypair, CoreError, EncryptKey};
let (pk, sk) = generate_keypair()?;
let session = VerifiedSession::establish(pk.as_slice(), sk.expose_secret())?;
// Check session properties
assert!(session.is_valid()); // Not expired
let _ = session.session_id(); // Unique ID for audit
let _ = session.expires_at(); // Expiration time
// Validate before critical operations
session.verify_valid()?; // Returns Err(SessionExpired) if expired
// Refresh if expired
if !session.is_valid() {
let new_session = VerifiedSession::establish(pk.as_slice(), sk.expose_secret())?;
}§Complete Example
use latticearc::{
encrypt, decrypt, generate_signing_keypair, sign_with_key, verify,
generate_hybrid_keypair, CryptoConfig, CoreError,
EncryptKey, DecryptKey,
};
fn secure_workflow() -> Result<(), CoreError> {
// --- Hybrid Encryption (unified API) ---
let (enc_pk, enc_sk) = generate_hybrid_keypair()?;
let encrypted = encrypt(b"confidential", EncryptKey::Hybrid(&enc_pk),
CryptoConfig::new())?;
let decrypted = decrypt(&encrypted, DecryptKey::Hybrid(&enc_sk),
CryptoConfig::new())?;
// --- Digital Signatures ---
let (sign_pk, sign_sk, _scheme) = generate_signing_keypair(CryptoConfig::new())?.into_parts();
let signed = sign_with_key(b"important document", &sign_sk, &sign_pk, CryptoConfig::new())?;
let is_valid = verify(&signed, CryptoConfig::new())?;
assert!(is_valid);
Ok(())
}Re-exports§
pub use prelude::LatticeArcError;pub use unified_api::AlgorithmSelection;pub use unified_api::Challenge;pub use unified_api::ComplianceMode;pub use unified_api::ContinuousSession;pub use unified_api::ContinuousVerifiable;pub use unified_api::CoreError;pub use unified_api::CryptoConfig;pub use unified_api::CryptoContext;pub use unified_api::CryptoMode;pub use unified_api::CryptoPayload;pub use unified_api::CryptoScheme;pub use unified_api::DataCharacteristics;pub use unified_api::DecryptKey;pub use unified_api::EncryptKey;pub use unified_api::EncryptedData;pub use unified_api::EncryptedMetadata;pub use unified_api::EncryptedOutput;pub use unified_api::EncryptionScheme;pub use unified_api::HashOutput;pub use unified_api::HybridComponents;pub use unified_api::KeyPair;pub use unified_api::PatternType;pub use unified_api::PerformancePreference;pub use unified_api::PrivateKey;pub use unified_api::ProofOfPossession;pub use unified_api::ProofOfPossessionData;pub use unified_api::PublicKey;pub use unified_api::Result;pub use unified_api::SchemeSelector;pub use unified_api::SecretBytes;pub use unified_api::SecretVec;pub use unified_api::SecurityLevel;pub use unified_api::SecurityMode;pub use unified_api::SignedData;pub use unified_api::SignedMetadata;pub use unified_api::SymmetricKey;pub use unified_api::TrustLevel;pub use unified_api::UseCase;pub use unified_api::VERSION;pub use unified_api::VerificationStatus;pub use unified_api::VerifiedSession;pub use unified_api::ZeroKnowledgeProof;pub use unified_api::ZeroTrustAuth;pub use unified_api::ZeroTrustAuthenticable;pub use unified_api::ZeroTrustSession;pub use unified_api::fips_available;pub use unified_api::init;pub use unified_api::init_with_config;pub use hybrid::pq_only::PqOnlyPublicKey;pub use hybrid::pq_only::PqOnlySecretKey;pub use hybrid::pq_only::generate_pq_keypair;pub use hybrid::pq_only::generate_pq_keypair_with_level;pub use hybrid::encrypt_hybrid::DerivationBinding;pub use hybrid::encrypt_hybrid::HybridCiphertext;pub use hybrid::encrypt_hybrid::HybridEncryptionError;pub use hybrid::kem_hybrid::EncapsulatedKey;pub use hybrid::kem_hybrid::HybridKemError;pub use hybrid::kem_hybrid::HybridKemPublicKey;pub use hybrid::kem_hybrid::HybridKemSecretKey;pub use hybrid::sig_hybrid::HybridSigPublicKey;pub use hybrid::sig_hybrid::HybridSigSecretKey;pub use hybrid::sig_hybrid::HybridSignature;pub use hybrid::sig_hybrid::HybridSignatureError;pub use unified_api::SigningKeypair;pub use unified_api::decrypt;pub use unified_api::encrypt;pub use unified_api::generate_signing_keypair;pub use unified_api::sign_with_key;pub use unified_api::verify;pub use unified_api::generate_hybrid_keypair;pub use unified_api::generate_hybrid_keypair_with_level;pub use unified_api::generate_hybrid_signing_keypair;pub use unified_api::generate_hybrid_signing_keypair_with_config;pub use unified_api::sign_hybrid;pub use unified_api::sign_hybrid_with_config;pub use unified_api::verify_hybrid_signature;pub use unified_api::verify_hybrid_signature_with_config;pub use unified_api::serialization::deserialize_encrypted_output;pub use unified_api::serialization::deserialize_keypair;pub use unified_api::serialization::deserialize_signed_data;pub use unified_api::serialization::serialize_encrypted_output;pub use unified_api::serialization::serialize_keypair;pub use unified_api::serialization::serialize_signed_data;pub use unified_api::key_format::KeyAlgorithm;pub use unified_api::key_format::KeyData;pub use unified_api::key_format::KeyType;pub use unified_api::key_format::PortableKey;
Modules§
- hybrid
- Hybrid cryptography combining post-quantum and classical algorithms. Hybrid Cryptography for LatticeArc
- perf
- Performance monitoring and benchmarking utilities. LatticeArc Performance Primitives
- prelude
- Common prelude with error handling, domain constants, and testing infrastructure. LatticeArc Prelude
- primitives
- Core cryptographic primitives (KEM, signatures, AEAD, hash, KDF, MAC).
- types
- Pure-Rust domain types, traits, configuration, and policy engine.
- unified_
api - Unified cryptographic API with Zero-Trust security.
- zkp
- Zero-knowledge proof primitives (Schnorr, Sigma protocols, Pedersen commitments). Non-FIPS: uses non-approved EC operations. Basic Zero-Knowledge Proof Primitives
Structs§
- Zeroizing
Zeroizingis a a wrapper for anyZ: Zeroizetype which implements aDrophandler which zeroizes dropped values.