Skip to main content

Crate latticearc

Crate latticearc 

Source
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 LatticeArcWith LatticeArc
~50 lines for hybrid encrypt3 lines
Manage 4 key vectors manuallySingle EncryptKey::Hybrid
Research NIST parameter setsUseCase auto-selects
Manual secret zeroizationAutomatic via Zeroize

§Known limitations

  • No streaming AEAD API. All encrypt / decrypt entry 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_blocking is 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

AlgorithmStandardBackendFIPS Validated
ML-KEMFIPS 203aws-lc-rsYes
AES-256-GCMSP 800-38Daws-lc-rsYes
HKDF-SHA256SP 800-56Caws-lc-rsYes
SHA-256FIPS 180-4aws-lc-rsYes
ML-DSAFIPS 204fips204No
SLH-DSAFIPS 205fips205No
FN-DSAFIPS 206 (draft / Falcon)fn-dsaNo

§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.

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

FeatureDescription
fipsFIPS 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-testPower-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-initSubscriber/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-serdeSerialization support for ZKP types (enables serde_with for Schnorr/Sigma protocol structs).
secret-mlockOS-level memory locking for SecretVec (Secret Type Invariant I-10). Uses mlock(2) on Linux/macOS via region. No-op on WindowsVirtualLock 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-vectorsExposes AeadCipher::new_allow_weak_key (AES-GCM Test Cases 1/2 reproducer). Default-off so production builds cannot construct weak-key ciphers.
test-utilsExposes 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-replayExposes 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-verificationCompilation marker: enables formal verification harness code (Kani proofs). Does not run proofs — use cargo kani separately.
kaniCompilation marker: enables Kani bounded model checking proof harnesses. Requires cargo kani to execute proofs.
sawCompilation 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
Zeroizing is a a wrapper for any Z: Zeroize type which implements a Drop handler which zeroizes dropped values.