#![cfg_attr(not(feature = "std"), no_std)]
#![deny(unsafe_code)]
#![deny(unused_qualifications)]
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
pub mod aead;
#[cfg(feature = "cb-kem")]
pub use lib_q_cb_kem::LibQCbKemProvider;
pub use lib_q_core::{
AeadContext,
AeadDecryptSemantic,
Algorithm,
AlgorithmCategory,
DecryptSemanticOutcome,
Error,
HashContext,
KemContext,
LibQCryptoProvider as CoreLibQCryptoProvider,
Result,
SecurityLevel,
SecurityValidator,
SignatureContext,
VERSION,
algorithms_by_category,
algorithms_by_security_level,
init,
supported_algorithms,
version,
};
pub use lib_q_core::{
LibQCryptoProvider,
Utils,
create_kem_context,
};
#[cfg(feature = "alloc")]
pub use lib_q_hash::LibQHashProvider;
#[cfg(feature = "hqc")]
pub use lib_q_hqc::LibQHqcProvider;
#[cfg(any(feature = "ml-kem", feature = "hqc"))]
pub use lib_q_kem::{
LibQKemProvider,
available_algorithms,
};
#[cfg(feature = "random")]
pub use lib_q_random::{
EntropyQuality,
EntropyValidator,
LibQRng,
new_custom_rng,
new_deterministic_rng,
new_secure_rng,
};
#[cfg(feature = "random-custom-entropy")]
pub use lib_q_random::{
custom_entropy::{
CustomEntropyConfig,
CustomEntropySource,
EntropyContext,
EntropyQuality as CustomEntropyQuality,
},
get_custom_entropy_source_info,
has_custom_entropy_source,
register_custom_entropy_source,
unregister_custom_entropy_source,
};
#[cfg(feature = "std")]
pub use lib_q_sig::create_signature;
pub use lib_q_sig::{
LibQSignatureProvider,
available_algorithms as sig_available_algorithms,
};
#[cfg(feature = "alloc")]
pub fn create_signature_context() -> SignatureContext {
let provider = LibQSignatureProvider::new()
.expect("lib-q-sig LibQSignatureProvider / SecurityValidator initialization");
SignatureContext::with_provider(Box::new(provider))
}
#[cfg(feature = "alloc")]
pub fn create_hash_context() -> HashContext {
let provider = LibQHashProvider::new()
.expect("lib-q-hash LibQHashProvider / SecurityValidator initialization");
HashContext::with_provider(Box::new(provider))
}
#[cfg(feature = "zkp")]
pub mod zkp {
pub use lib_q_zkp::api::{
MerklePath,
prove_membership,
prove_preimage,
verify_membership,
verify_membership_with_depth,
verify_preimage,
};
pub use lib_q_zkp::circuit::{
ArithmeticCircuit,
CircuitAir,
CircuitBuilder,
};
pub use lib_q_zkp::ip::credential::{
IpCredential,
compute_credential_commitment,
prove_credential_attributes,
verify_credential_proof,
};
#[cfg(any(
feature = "zkp-plonky",
feature = "zkp-plonky-keccak-air",
feature = "zkp-plonky-lookup",
feature = "zkp-plonky-uni-stark",
feature = "zkp-plonky-batch-stark",
))]
pub use lib_q_zkp::plonky;
pub use lib_q_zkp::stark::{
StarkProver,
StarkVerifier,
default_config,
};
pub use lib_q_zkp::{
ProofMetadata,
ProofType,
ZkpField,
ZkpProof,
ZkpProver,
ZkpVerifier,
};
}
#[cfg(feature = "wasm")]
pub mod wasm {
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::string::{
String,
ToString,
};
#[cfg(feature = "std")]
use std::string::{
String,
ToString,
};
pub use lib_q_core::wasm::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn init_wasm() -> Result<(), JsValue> {
lib_q_core::init().map_err(|e| lib_q_core::wasm_common::wasm_js_error("LIB_Q_INIT", e))
}
#[wasm_bindgen]
pub fn get_version() -> String {
lib_q_core::version().to_string()
}
#[wasm_bindgen]
pub fn is_algorithm_supported_wasm(algorithm: &str) -> bool {
let manager = WasmProviderManager::new();
manager.is_algorithm_supported(algorithm)
}
#[wasm_bindgen]
pub fn get_supported_algorithms_wasm() -> JsValue {
let manager = WasmProviderManager::new();
let algorithms = manager.get_all_algorithms();
JsValue::from_str(&algorithms)
}
#[wasm_bindgen]
pub fn get_library_info_wasm() -> String {
get_library_info()
}
#[wasm_bindgen]
pub fn get_security_recommendations_wasm() -> String {
let manager = WasmProviderManager::new();
manager.get_security_recommendations()
}
#[wasm_bindgen]
pub fn get_performance_benchmarks_wasm() -> String {
let manager = WasmProviderManager::new();
manager.get_performance_benchmarks()
}
#[wasm_bindgen]
pub fn create_kem_context() -> WasmKemContext {
WasmKemContext::new()
}
#[wasm_bindgen]
pub fn create_signature_context() -> WasmSignatureContext {
let provider = lib_q_sig::LibQSignatureProvider::new()
.expect("lib-q-sig LibQSignatureProvider / SecurityValidator initialization");
WasmSignatureContext::from_signature_context(lib_q_core::SignatureContext::with_provider(
Box::new(provider),
))
}
#[wasm_bindgen]
pub fn create_hash_context() -> WasmHashContext {
let provider = lib_q_hash::LibQHashProvider::new()
.expect("lib-q-hash LibQHashProvider / SecurityValidator initialization");
WasmHashContext::from_hash_context(lib_q_core::HashContext::with_provider(Box::new(
provider,
)))
}
#[wasm_bindgen]
pub fn create_aead_context() -> WasmAeadContext {
WasmAeadContext::from_aead_context(lib_q_core::AeadContext::with_aead_operations(Box::new(
lib_q_aead::LibQAeadProvider::new()
.expect("lib-q-aead LibQAeadProvider / SecurityValidator initialization"),
)))
}
#[wasm_bindgen]
pub fn create_provider_manager() -> WasmProviderManager {
WasmProviderManager::new()
}
#[wasm_bindgen]
pub fn generate_random_bytes(length: usize) -> Result<js_sys::Uint8Array, JsValue> {
random_bytes(length).map_err(|e| lib_q_core::wasm_common::wasm_js_error("LIB_Q_RANDOM", e))
}
#[wasm_bindgen]
pub fn bytes_to_hex_wasm(data: &js_sys::Uint8Array) -> String {
bytes_to_hex(data)
}
#[wasm_bindgen]
pub fn hex_to_bytes_wasm(hex: &str) -> Result<js_sys::Uint8Array, JsValue> {
hex_to_bytes(hex).map_err(|e| lib_q_core::wasm_common::wasm_js_error("LIB_Q_HEX", e))
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use lib_q_core::{
CryptoProvider,
KemOperations, };
#[cfg(feature = "hqc")]
use lib_q_hqc::HqcParams;
use super::*;
#[cfg(feature = "alloc")]
use crate::aead;
#[test]
fn test_init() {
assert!(init().is_ok());
}
#[test]
fn test_version() {
assert!(!version().is_empty());
assert_eq!(version(), VERSION);
}
#[test]
fn test_supported_algorithms() {
let algorithms = algorithms_by_category(AlgorithmCategory::Hash);
assert!(
!algorithms.is_empty(),
"Should have at least one hash algorithm"
);
}
#[cfg(feature = "alloc")]
#[test]
fn test_signature_context_pre_wired_ml_dsa_roundtrip() {
let mut ctx = create_signature_context();
let keypair = ctx
.generate_keypair(Algorithm::MlDsa65, None)
.expect("ML-DSA-65 keygen with pre-wired provider");
let message = b"lib-q umbrella signature integration";
let signature = ctx
.sign(Algorithm::MlDsa65, keypair.secret_key(), message, None)
.expect("sign");
assert!(
ctx.verify(
Algorithm::MlDsa65,
keypair.public_key(),
message,
signature.as_slice(),
)
.expect("verify")
);
}
#[cfg(all(feature = "alloc", feature = "fn-dsa"))]
#[test]
fn test_signature_context_fn_dsa512_roundtrip() {
let mut ctx = create_signature_context();
let keypair = ctx
.generate_keypair(Algorithm::FnDsa512, None)
.expect("FN-DSA-512 keygen");
let message = b"fn-dsa umbrella path";
let signature = ctx
.sign(Algorithm::FnDsa512, keypair.secret_key(), message, None)
.expect("sign");
assert!(
ctx.verify(
Algorithm::FnDsa512,
keypair.public_key(),
message,
signature.as_slice(),
)
.expect("verify")
);
}
#[test]
fn test_algorithms_by_security_level() {
let level_1_algorithms = algorithms_by_security_level(SecurityLevel::Level1 as u32);
assert!(
!level_1_algorithms.is_empty(),
"Should have at least one Level 1 algorithm"
);
}
#[test]
fn test_unified_api() {
let provider = LibQCryptoProvider::new();
assert!(provider.is_ok(), "Provider should be created successfully");
let provider = provider.unwrap();
let kem_result = provider
.kem()
.unwrap()
.generate_keypair(Algorithm::MlKem512, None);
assert!(kem_result.is_err());
if let Err(Error::NotImplemented { feature }) = kem_result {
assert!(
feature.contains("ML-KEM implementations are provided by the main lib-q crate")
);
} else {
panic!("Expected NotImplemented error for KEM operations");
}
#[cfg(feature = "ml-kem")]
{
let kem_provider = LibQKemProvider::new().unwrap();
let kem_result = kem_provider.generate_keypair(Algorithm::MlKem512, None);
assert!(
kem_result.is_ok(),
"ML-KEM key generation should succeed with lib-q-kem provider"
);
let keypair = kem_result.unwrap();
assert!(!keypair.public_key().as_bytes().is_empty());
assert!(!keypair.secret_key().as_bytes().is_empty());
}
#[cfg(feature = "hqc")]
{
let kem_provider = LibQKemProvider::new().unwrap();
let keypair = kem_provider
.generate_keypair(Algorithm::Hqc128, None)
.expect("HQC-128 key generation with lib-q-kem provider");
assert_eq!(
keypair.public_key().as_bytes().len(),
lib_q_hqc::Hqc1Params::PUBLIC_KEY_BYTES
);
assert_eq!(
keypair.secret_key().as_bytes().len(),
lib_q_hqc::Hqc1Params::SECRET_KEY_BYTES
);
let (ciphertext, shared1) = kem_provider
.encapsulate(Algorithm::Hqc128, &keypair.public_key, None)
.expect("HQC-128 encapsulate");
assert_eq!(ciphertext.len(), lib_q_hqc::Hqc1Params::CIPHERTEXT_BYTES);
let shared2 = kem_provider
.decapsulate(Algorithm::Hqc128, &keypair.secret_key, &ciphertext)
.expect("HQC-128 decapsulate");
assert_eq!(shared1, shared2);
}
let sig_result = provider
.signature()
.unwrap()
.generate_keypair(Algorithm::MlDsa65, None);
#[cfg(feature = "ml-dsa")]
{
assert!(sig_result.is_err());
if let Err(Error::NotImplemented { feature }) = sig_result {
assert!(
feature.contains("ML-DSA implementations are provided by the main lib-q crate")
);
} else {
panic!("Expected NotImplemented error for ML-DSA in core provider");
}
}
#[cfg(not(feature = "ml-dsa"))]
{
assert!(sig_result.is_err());
if let Err(Error::NotImplemented { feature }) = sig_result {
assert!(
feature.contains("ML-DSA implementations are provided by the main lib-q crate")
);
} else {
panic!("Expected NotImplemented error for ML-DSA without feature flag");
}
}
let hash_result = provider
.hash()
.unwrap()
.hash(Algorithm::Sha3_256, b"test data");
assert!(hash_result.is_err());
if let Err(Error::NotImplemented { feature }) = hash_result {
assert!(feature.contains("SHA3 implementations are provided by the main lib-q crate"));
} else {
panic!("Expected NotImplemented error for hash operations");
}
#[cfg(not(feature = "std"))]
use alloc::vec;
use lib_q_core::traits::{
AeadKey,
Nonce,
};
let mut key_bytes = vec![0u8; 32];
let mut nonce_bytes = vec![0u8; 16];
for (i, byte) in key_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x1F).wrapping_add(0x2B);
}
for (i, byte) in nonce_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x3D).wrapping_add(0x7E);
}
let key = AeadKey::new(key_bytes);
let nonce = Nonce::new(nonce_bytes);
let aead_result = provider.aead().unwrap().encrypt(
Algorithm::Saturnin,
&key,
&nonce,
b"plaintext",
Some(b"associated data"),
);
assert!(aead_result.is_err());
match aead_result {
Err(Error::NotImplemented { feature }) => {
assert!(
feature.contains("LibQAeadProvider") || feature.contains("libq::aead::context")
);
}
Err(e) => {
panic!(
"Expected NotImplemented error for AEAD operations, got: {:?}",
e
);
}
Ok(_) => {
panic!("Expected error for AEAD operations, but got success");
}
}
}
#[cfg(feature = "alloc")]
#[test]
fn test_create_hash_context_sha3_256_roundtrip() {
let mut ctx = create_hash_context();
let out = ctx
.hash(Algorithm::Sha3_256, b"lib-q umbrella hash")
.expect("SHA3-256 with pre-wired LibQHashProvider");
assert_eq!(out.len(), 32);
}
#[cfg(feature = "alloc")]
#[test]
fn test_create_aead_context_shake256_roundtrip() {
use lib_q_core::traits::{
AeadKey,
Nonce,
};
let mut key_bytes = vec![0u8; 32];
let mut nonce_bytes = vec![0u8; 16];
for (i, byte) in key_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x1F).wrapping_add(0x2B);
}
for (i, byte) in nonce_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x3D).wrapping_add(0x7E);
}
let key = AeadKey::new(key_bytes);
let nonce = Nonce::new(nonce_bytes);
let plaintext = b"hello lib-q aead bridge";
let ad = b"associated data";
let mut ctx = aead::context();
let ciphertext = ctx
.encrypt(
Algorithm::Shake256Aead,
&key,
&nonce,
plaintext.as_slice(),
Some(ad.as_slice()),
)
.expect("SHAKE256-AEAD encrypt");
let recovered = ctx
.decrypt(
Algorithm::Shake256Aead,
&key,
&nonce,
&ciphertext,
Some(ad.as_slice()),
)
.expect("SHAKE256-AEAD decrypt");
assert_eq!(recovered.as_slice(), plaintext.as_slice());
}
#[cfg(all(feature = "alloc", feature = "duplex-sponge-aead"))]
#[test]
fn test_create_aead_context_duplex_sponge_roundtrip() {
use lib_q_core::traits::{
AeadKey,
Nonce,
};
let mut key_bytes = vec![0u8; 32];
let mut nonce_bytes = vec![0u8; 16];
for (i, byte) in key_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x11).wrapping_add(0x3C);
}
for (i, byte) in nonce_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x29).wrapping_add(0x71);
}
let key = AeadKey::new(key_bytes);
let nonce = Nonce::new(nonce_bytes);
let plaintext = b"duplex-sponge roundtrip";
let ad = b"ad";
let mut ctx = aead::context();
let ciphertext = ctx
.encrypt(
Algorithm::DuplexSpongeAead,
&key,
&nonce,
plaintext.as_slice(),
Some(ad.as_slice()),
)
.expect("Duplex-Sponge-AEAD encrypt");
let recovered = ctx
.decrypt(
Algorithm::DuplexSpongeAead,
&key,
&nonce,
&ciphertext,
Some(ad.as_slice()),
)
.expect("Duplex-Sponge-AEAD decrypt");
assert_eq!(recovered.as_slice(), plaintext.as_slice());
}
#[cfg(all(feature = "alloc", feature = "tweak-aead"))]
#[test]
fn test_create_aead_context_tweak_aead_roundtrip() {
use lib_q_core::traits::{
AeadKey,
Nonce,
};
let mut key_bytes = vec![0u8; 32];
let mut nonce_bytes = vec![0u8; 16];
for (i, byte) in key_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x13).wrapping_add(0x2E);
}
for (i, byte) in nonce_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x2B).wrapping_add(0x6D);
}
let key = AeadKey::new(key_bytes);
let nonce = Nonce::new(nonce_bytes);
let plaintext = b"tweak aead roundtrip";
let ad = b"ad2";
let mut ctx = aead::context();
let ciphertext = ctx
.encrypt(
Algorithm::TweakAead,
&key,
&nonce,
plaintext.as_slice(),
Some(ad.as_slice()),
)
.expect("Tweak-AEAD encrypt");
let recovered = ctx
.decrypt(
Algorithm::TweakAead,
&key,
&nonce,
&ciphertext,
Some(ad.as_slice()),
)
.expect("Tweak-AEAD decrypt");
assert_eq!(recovered.as_slice(), plaintext.as_slice());
}
#[cfg(all(feature = "alloc", feature = "romulus"))]
#[test]
fn test_create_aead_context_romulus_n_roundtrip() {
use lib_q_core::traits::{
AeadKey,
Nonce,
};
let mut key_bytes = vec![0u8; 16];
let mut nonce_bytes = vec![0u8; 16];
for (i, byte) in key_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x17).wrapping_add(0x31);
}
for (i, byte) in nonce_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x2Du8).wrapping_add(0x6Au8);
}
let key = AeadKey::new(key_bytes);
let nonce = Nonce::new(nonce_bytes);
let plaintext = b"romulus-n roundtrip";
let ad = b"ad-rn";
let mut ctx = aead::context();
let ciphertext = ctx
.encrypt(
Algorithm::RomulusN,
&key,
&nonce,
plaintext.as_slice(),
Some(ad.as_slice()),
)
.expect("Romulus-N encrypt");
let recovered = ctx
.decrypt(
Algorithm::RomulusN,
&key,
&nonce,
&ciphertext,
Some(ad.as_slice()),
)
.expect("Romulus-N decrypt");
assert_eq!(recovered.as_slice(), plaintext.as_slice());
}
#[cfg(all(feature = "alloc", feature = "romulus"))]
#[test]
fn test_create_aead_context_romulus_m_roundtrip() {
use lib_q_core::traits::{
AeadKey,
Nonce,
};
let mut key_bytes = vec![0u8; 16];
let mut nonce_bytes = vec![0u8; 16];
for (i, byte) in key_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x19).wrapping_add(0x2Fu8);
}
for (i, byte) in nonce_bytes.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x2Fu8).wrapping_add(0x68u8);
}
let key = AeadKey::new(key_bytes);
let nonce = Nonce::new(nonce_bytes);
let plaintext = b"romulus-m roundtrip";
let ad = b"ad-rm";
let mut ctx = aead::context();
let ciphertext = ctx
.encrypt(
Algorithm::RomulusM,
&key,
&nonce,
plaintext.as_slice(),
Some(ad.as_slice()),
)
.expect("Romulus-M encrypt");
let recovered = ctx
.decrypt(
Algorithm::RomulusM,
&key,
&nonce,
&ciphertext,
Some(ad.as_slice()),
)
.expect("Romulus-M decrypt");
assert_eq!(recovered.as_slice(), plaintext.as_slice());
}
}