Crates.io | saorsa-pqc |
lib.rs | saorsa-pqc |
version | 0.3.11 |
created_at | 2025-08-17 21:40:50.635127+00 |
updated_at | 2025-08-29 10:33:46.524705+00 |
description | Post-Quantum Cryptography library for Saorsa Labs projects |
homepage | https://github.com/dirvine/saorsa-pqc |
repository | https://github.com/dirvine/saorsa-pqc |
max_upload_size | |
id | 1799726 |
size | 943,475 |
A comprehensive, production-ready Post-Quantum Cryptography library providing a complete quantum-secure cryptographic suite. Implements NIST FIPS 203, 204, and 205 standardized algorithms for asymmetric cryptography, plus comprehensive cryptographic primitives including BLAKE3, SHA3, HMAC, HKDF, AES-256-GCM, and ChaCha20-Poly1305. This library provides high-performance, thoroughly tested implementations with a clean, safe API, all validated against official NIST ACVP, RFC, and specification test vectors.
[dependencies]
saorsa-pqc = "0.3"
BLAKE3: Modern cryptographic hash with tree hashing
SHA3-256/SHA3-512: NIST FIPS 202 Keccak-based hash functions
AES-256-GCM: Hardware-accelerated authenticated encryption
ChaCha20-Poly1305: Software-optimized authenticated encryption
12 variants covering all combinations of:
use saorsa_pqc::api::ChaCha20Poly1305;
use saorsa_pqc::api::symmetric::{generate_key, generate_nonce};
// Generate a random 256-bit key (quantum-secure)
let key = generate_key();
let cipher = ChaCha20Poly1305::new(&key);
// Encrypt data with authenticated encryption
let nonce = generate_nonce(); // 96-bit nonce
let plaintext = b"Secret quantum-secure message";
let aad = b"Additional authenticated data";
// Encrypt with associated data (AEAD)
let ciphertext = cipher.encrypt_with_aad(&nonce, plaintext, aad)?;
// Decrypt and verify authenticity
let decrypted = cipher.decrypt_with_aad(&nonce, &ciphertext, aad)?;
assert_eq!(&decrypted[..], plaintext);
// Simple encryption without AAD
let ciphertext2 = cipher.encrypt(&nonce, plaintext)?;
let decrypted2 = cipher.decrypt(&nonce, &ciphertext2)?;
assert_eq!(&decrypted2[..], plaintext);
use saorsa_pqc::api::{ml_kem_768, MlKemPublicKey, MlKemSecretKey};
// Generate keypair (RNG handled internally)
let kem = ml_kem_768();
let (public_key, secret_key) = kem.generate_keypair()?;
// Encapsulate - creates shared secret and ciphertext
let (shared_secret, ciphertext) = kem.encapsulate(&public_key)?;
// Decapsulate - recovers shared secret from ciphertext
let recovered_secret = kem.decapsulate(&secret_key, &ciphertext)?;
assert_eq!(shared_secret.to_bytes(), recovered_secret.to_bytes());
use saorsa_pqc::api::{ml_dsa_65, MlDsaPublicKey, MlDsaSecretKey};
// Generate keypair
let dsa = ml_dsa_65();
let (public_key, secret_key) = dsa.generate_keypair()?;
// Sign message
let message = b"Authenticate this message";
let signature = dsa.sign(&secret_key, message)?;
// Verify signature
let is_valid = dsa.verify(&public_key, message, &signature)?;
assert!(is_valid);
use saorsa_pqc::api::{slh_dsa_sha2_128s, SlhDsaPublicKey, SlhDsaSecretKey};
// Generate keypair (note: SLH-DSA keygen is slow)
let slh = slh_dsa_sha2_128s();
let (public_key, secret_key) = slh.generate_keypair()?;
// Sign and verify
let message = b"Quantum-resistant message";
let signature = slh.sign(&secret_key, message)?;
let is_valid = slh.verify(&public_key, message, &signature)?;
assert!(is_valid);
This library has been extensively tested against official test vectors from multiple authoritative sources:
# Run all tests
cargo test --all-features
# Run specific algorithm tests
cargo test --test nist_official_vectors
cargo test --test extended_crypto_vectors
# Run with release optimizations (faster)
cargo test --release
Run comprehensive benchmarks:
cargo bench --bench comprehensive_benchmarks
Algorithm | Operation | Time | Throughput |
---|---|---|---|
ML-KEM-768 | KeyGen | ~50 ฮผs | - |
ML-KEM-768 | Encapsulate | ~55 ฮผs | - |
ML-KEM-768 | Decapsulate | ~65 ฮผs | - |
ML-DSA-65 | KeyGen | ~120 ฮผs | - |
ML-DSA-65 | Sign | ~350 ฮผs | - |
ML-DSA-65 | Verify | ~130 ฮผs | - |
SLH-DSA-SHA2-128f | KeyGen | ~3 ms | - |
SLH-DSA-SHA2-128f | Sign | ~90 ms | - |
SLH-DSA-SHA2-128f | Verify | ~4 ms | - |
ChaCha20-Poly1305 | Encrypt (1KB) | ~0.8 ฮผs | 1.25 GB/s |
ChaCha20-Poly1305 | Encrypt (64KB) | ~12 ฮผs | 5.3 GB/s |
ChaCha20-Poly1305 | Decrypt (64KB) | ~12 ฮผs | 5.3 GB/s |
AES-256-GCM | Encrypt (1KB) | ~0.6 ฮผs | 1.67 GB/s |
AES-256-GCM | Encrypt (64KB) | ~8 ฮผs | 8.0 GB/s |
BLAKE3 | Hash (1KB) | ~0.4 ฮผs | 2.5 GB/s |
SHA3-256 | Hash (1KB) | ~1.2 ฮผs | 833 MB/s |
HMAC-SHA3-256 | MAC (1KB) | ~1.3 ฮผs | 769 MB/s |
Note: Performance varies by hardware. AES-GCM benefits from AES-NI acceleration. ChaCha20-Poly1305 and BLAKE3 benefit from SIMD acceleration (AVX2/NEON).
Full API documentation is available at docs.rs/saorsa-pqc
MlKemPublicKey
, MlKemSecretKey
, MlKemCiphertext
, MlKemSharedSecret
MlDsaPublicKey
, MlDsaSecretKey
, MlDsaSignature
SlhDsaPublicKey
, SlhDsaSecretKey
, SlhDsaSignature
ml_kem_512()
, ml_kem_768()
, ml_kem_1024()
ml_dsa_44()
, ml_dsa_65()
, ml_dsa_87()
slh_dsa_sha2_128s()
, slh_dsa_sha2_128f()
, etc.Choose the right cryptographic primitives for your use case:
use saorsa_pqc::api::hash::{Blake3Hasher, Sha3_256Hasher};
use saorsa_pqc::api::traits::Hash;
// High performance: BLAKE3
let mut hasher = Blake3Hasher::new();
hasher.update(b"data to hash");
let hash = hasher.finalize();
// NIST standard: SHA3-256
let mut hasher = Sha3_256Hasher::new();
hasher.update(b"data to hash");
let hash = hasher.finalize();
use saorsa_pqc::api::aead::{Aes256GcmAead, AeadCipher, GcmNonce};
use saorsa_pqc::api::traits::Aead;
// Hardware accelerated: AES-256-GCM
let key = [0u8; 32]; // Use proper key generation
let aead = Aes256GcmAead::new(&key)?;
let nonce = GcmNonce::generate();
let ciphertext = aead.encrypt(&nonce, b"plaintext", b"aad")?;
// Software optimized: ChaCha20-Poly1305 (via enum)
let ciphertext = AeadCipher::ChaCha20Poly1305
.encrypt(&key, nonce.as_ref(), b"plaintext", b"aad")?;
use saorsa_pqc::api::kdf::HkdfSha3_256;
use saorsa_pqc::api::traits::Kdf;
// Derive encryption key from shared secret
let shared_secret = b"shared secret from ML-KEM";
let info = b"application context";
let mut derived_key = [0u8; 32];
HkdfSha3_256::derive(shared_secret, None, info, &mut derived_key)?;
use saorsa_pqc::api::hpke::{HpkeConfig, seal, open};
use saorsa_pqc::api::{MlKem, MlKemVariant, kdf::KdfAlgorithm, aead::AeadCipher};
// Configure HPKE with ML-KEM + AES-GCM
let config = HpkeConfig {
kem: MlKemVariant::MlKem768,
kdf: KdfAlgorithm::HkdfSha3_256,
aead: AeadCipher::Aes256Gcm,
};
// Generate recipient keypair
let kem = MlKem::new(MlKemVariant::MlKem768);
let (pk, sk) = kem.generate_keypair()?;
// Encrypt
let (enc_key, ciphertext) = seal(
config,
&pk.to_bytes(),
b"context info",
b"secret message",
b"associated data"
)?;
// Decrypt
let plaintext = open(
config,
&sk.to_bytes(),
&enc_key,
b"context info",
&ciphertext,
b"associated data"
)?;
Combine ML-KEM key exchange with symmetric primitives:
use saorsa_pqc::api::{ml_kem_768, ChaCha20Poly1305};
use saorsa_pqc::api::symmetric::generate_nonce;
// Alice generates ML-KEM keypair
let kem = ml_kem_768();
let (alice_pk, alice_sk) = kem.generate_keypair()?;
// Bob encapsulates a shared secret using Alice's public key
let (shared_secret, ciphertext) = kem.encapsulate(&alice_pk)?;
// Alice decapsulates to get the same shared secret
let recovered_secret = kem.decapsulate(&alice_sk, &ciphertext)?;
// Derive proper encryption key from shared secret using HKDF
use saorsa_pqc::api::kdf::HkdfSha3_256;
use saorsa_pqc::api::traits::Kdf;
let mut encryption_key = [0u8; 32];
HkdfSha3_256::derive(
&shared_secret.to_bytes(),
None,
b"saorsa-pqc encryption key",
&mut encryption_key
)?;
// Create cipher with derived key
let cipher = ChaCha20Poly1305::new(&encryption_key);
// Now Bob can encrypt messages to Alice
let nonce = generate_nonce();
let message = b"Quantum-secure message";
let encrypted = cipher.encrypt(&nonce, message)?;
// Alice decrypts using the same key
let decrypted = cipher.decrypt(&nonce, &encrypted)?;
assert_eq!(decrypted, message);
// All keys and signatures support serialization
let pk_bytes = public_key.to_bytes();
let restored_pk = MlKemPublicKey::from_bytes(
MlKemVariant::MlKem768,
&pk_bytes
)?;
// ML-DSA supports domain separation via context
let context = b"application-specific-context";
let signature = dsa.sign_with_context(&secret_key, message, context)?;
let is_valid = dsa.verify_with_context(&public_key, message, &signature, context)?;
// Generate keys from seed (for testing/reproducibility)
// Uses FIPS 203 deterministic generation with two 32-byte seeds
let d_seed = [0u8; 32]; // First seed value
let z_seed = [1u8; 32]; // Second seed value
let kem = ml_kem_768();
let (pk, sk) = kem.generate_keypair_from_seed(&d_seed, &z_seed);
// Deterministic generation produces identical keys
let (pk2, sk2) = kem.generate_keypair_from_seed(&d_seed, &z_seed);
assert_eq!(pk.to_bytes(), pk2.to_bytes());
Contributions are welcome! Please feel free to submit issues and pull requests.
# Clone the repository
git clone https://github.com/dirvine/saorsa-pqc
cd saorsa-pqc
# Run tests
cargo test --all-features
# Run benchmarks
cargo bench
# Check code quality
cargo clippy --all-features
cargo fmt --check
This project is dual-licensed under:
Choose whichever license works best for your use case.
This library builds upon the excellent work of:
This library incorporates the latest NIST standards released in 2024:
Note: This library is under active development. While the underlying FIPS implementations are certified, always perform your own security audit before production use.