1#![cfg_attr(not(feature = "std"), no_std)]
104#![deny(unsafe_code)]
105#![deny(unused_qualifications)]
106
107#[cfg(not(feature = "std"))]
108extern crate alloc;
109
110#[cfg(all(feature = "alloc", not(feature = "std")))]
111use alloc::boxed::Box;
112
113#[cfg(feature = "alloc")]
114pub mod aead;
115
116#[cfg(feature = "cb-kem")]
120pub use lib_q_cb_kem::LibQCbKemProvider;
121pub use lib_q_core::{
122 AeadContext,
124 AeadDecryptSemantic,
125 Algorithm,
126 AlgorithmCategory,
127 DecryptSemanticOutcome,
128 Error,
129 HashContext,
130 KemContext,
131 LibQCryptoProvider as CoreLibQCryptoProvider,
133 Result,
134 SecurityLevel,
135 SecurityValidator,
137 SignatureContext,
138 VERSION,
140 algorithms_by_category,
142 algorithms_by_security_level,
143 init,
144 supported_algorithms,
145 version,
146};
147pub use lib_q_core::{
149 LibQCryptoProvider,
150 Utils,
151 create_kem_context,
152};
153#[cfg(feature = "alloc")]
154pub use lib_q_hash::LibQHashProvider;
155#[cfg(feature = "hqc")]
156pub use lib_q_hqc::LibQHqcProvider;
157#[cfg(any(feature = "ml-kem", feature = "hqc"))]
159pub use lib_q_kem::{
160 LibQKemProvider,
161 available_algorithms,
162};
163#[cfg(feature = "random")]
165pub use lib_q_random::{
166 EntropyQuality,
167 EntropyValidator,
168 LibQRng,
169 new_custom_rng,
170 new_deterministic_rng,
171 new_secure_rng,
172};
173#[cfg(feature = "random-custom-entropy")]
179pub use lib_q_random::{
180 custom_entropy::{
181 CustomEntropyConfig,
182 CustomEntropySource,
183 EntropyContext,
184 EntropyQuality as CustomEntropyQuality,
185 },
186 get_custom_entropy_source_info,
187 has_custom_entropy_source,
188 register_custom_entropy_source,
189 unregister_custom_entropy_source,
190};
191#[cfg(feature = "std")]
193pub use lib_q_sig::create_signature;
194pub use lib_q_sig::{
195 LibQSignatureProvider,
196 available_algorithms as sig_available_algorithms,
197};
198
199#[cfg(feature = "alloc")]
207pub fn create_signature_context() -> SignatureContext {
208 let provider = LibQSignatureProvider::new()
209 .expect("lib-q-sig LibQSignatureProvider / SecurityValidator initialization");
210 SignatureContext::with_provider(Box::new(provider))
211}
212
213#[cfg(feature = "alloc")]
221pub fn create_hash_context() -> HashContext {
222 let provider = LibQHashProvider::new()
223 .expect("lib-q-hash LibQHashProvider / SecurityValidator initialization");
224 HashContext::with_provider(Box::new(provider))
225}
226
227#[cfg(feature = "zkp")]
228pub mod zkp {
229 pub use lib_q_zkp::api::{
234 MerklePath,
235 prove_membership,
236 prove_preimage,
237 verify_membership,
238 verify_membership_with_depth,
239 verify_preimage,
240 };
241 pub use lib_q_zkp::circuit::{
242 ArithmeticCircuit,
243 CircuitAir,
244 CircuitBuilder,
245 };
246 pub use lib_q_zkp::ip::credential::{
247 IpCredential,
248 compute_credential_commitment,
249 prove_credential_attributes,
250 verify_credential_proof,
251 };
252 #[cfg(any(
256 feature = "zkp-plonky",
257 feature = "zkp-plonky-keccak-air",
258 feature = "zkp-plonky-lookup",
259 feature = "zkp-plonky-uni-stark",
260 feature = "zkp-plonky-batch-stark",
261 ))]
262 pub use lib_q_zkp::plonky;
263 pub use lib_q_zkp::stark::{
264 StarkProver,
265 StarkVerifier,
266 default_config,
267 };
268 pub use lib_q_zkp::{
269 ProofMetadata,
270 ProofType,
271 ZkpField,
272 ZkpProof,
273 ZkpProver,
274 ZkpVerifier,
275 };
276}
277
278#[cfg(feature = "wasm")]
283pub mod wasm {
284 #[cfg(not(feature = "std"))]
293 use alloc::boxed::Box;
294 #[cfg(not(feature = "std"))]
295 use alloc::string::{
296 String,
297 ToString,
298 };
299 #[cfg(feature = "std")]
300 use std::string::{
301 String,
302 ToString,
303 };
304
305 pub use lib_q_core::wasm::*;
306 use wasm_bindgen::prelude::*;
307
308 #[wasm_bindgen]
310 pub fn init_wasm() -> Result<(), JsValue> {
311 lib_q_core::init().map_err(|e| lib_q_core::wasm_common::wasm_js_error("LIB_Q_INIT", e))
312 }
313
314 #[wasm_bindgen]
316 pub fn get_version() -> String {
317 lib_q_core::version().to_string()
318 }
319
320 #[wasm_bindgen]
322 pub fn is_algorithm_supported_wasm(algorithm: &str) -> bool {
323 let manager = WasmProviderManager::new();
325 manager.is_algorithm_supported(algorithm)
326 }
327
328 #[wasm_bindgen]
330 pub fn get_supported_algorithms_wasm() -> JsValue {
331 let manager = WasmProviderManager::new();
333 let algorithms = manager.get_all_algorithms();
334 JsValue::from_str(&algorithms)
335 }
336
337 #[wasm_bindgen]
339 pub fn get_library_info_wasm() -> String {
340 get_library_info()
341 }
342
343 #[wasm_bindgen]
345 pub fn get_security_recommendations_wasm() -> String {
346 let manager = WasmProviderManager::new();
347 manager.get_security_recommendations()
348 }
349
350 #[wasm_bindgen]
352 pub fn get_performance_benchmarks_wasm() -> String {
353 let manager = WasmProviderManager::new();
354 manager.get_performance_benchmarks()
355 }
356
357 #[wasm_bindgen]
359 pub fn create_kem_context() -> WasmKemContext {
360 WasmKemContext::new()
361 }
362
363 #[wasm_bindgen]
366 pub fn create_signature_context() -> WasmSignatureContext {
367 let provider = lib_q_sig::LibQSignatureProvider::new()
368 .expect("lib-q-sig LibQSignatureProvider / SecurityValidator initialization");
369 WasmSignatureContext::from_signature_context(lib_q_core::SignatureContext::with_provider(
370 Box::new(provider),
371 ))
372 }
373
374 #[wasm_bindgen]
377 pub fn create_hash_context() -> WasmHashContext {
378 let provider = lib_q_hash::LibQHashProvider::new()
379 .expect("lib-q-hash LibQHashProvider / SecurityValidator initialization");
380 WasmHashContext::from_hash_context(lib_q_core::HashContext::with_provider(Box::new(
381 provider,
382 )))
383 }
384
385 #[wasm_bindgen]
387 pub fn create_aead_context() -> WasmAeadContext {
388 WasmAeadContext::from_aead_context(lib_q_core::AeadContext::with_aead_operations(Box::new(
389 lib_q_aead::LibQAeadProvider::new()
390 .expect("lib-q-aead LibQAeadProvider / SecurityValidator initialization"),
391 )))
392 }
393
394 #[wasm_bindgen]
396 pub fn create_provider_manager() -> WasmProviderManager {
397 WasmProviderManager::new()
398 }
399
400 #[wasm_bindgen]
402 pub fn generate_random_bytes(length: usize) -> Result<js_sys::Uint8Array, JsValue> {
403 random_bytes(length).map_err(|e| lib_q_core::wasm_common::wasm_js_error("LIB_Q_RANDOM", e))
404 }
405
406 #[wasm_bindgen]
408 pub fn bytes_to_hex_wasm(data: &js_sys::Uint8Array) -> String {
409 bytes_to_hex(data)
410 }
411
412 #[wasm_bindgen]
414 pub fn hex_to_bytes_wasm(hex: &str) -> Result<js_sys::Uint8Array, JsValue> {
415 hex_to_bytes(hex).map_err(|e| lib_q_core::wasm_common::wasm_js_error("LIB_Q_HEX", e))
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 #[allow(unused_imports)]
422 use lib_q_core::{
423 CryptoProvider,
424 KemOperations, };
426 #[cfg(feature = "hqc")]
427 use lib_q_hqc::HqcParams;
428
429 use super::*;
430 #[cfg(feature = "alloc")]
431 use crate::aead;
432
433 #[test]
434 fn test_init() {
435 assert!(init().is_ok());
436 }
437
438 #[test]
439 fn test_version() {
440 assert!(!version().is_empty());
441 assert_eq!(version(), VERSION);
442 }
443
444 #[test]
445 fn test_supported_algorithms() {
446 let algorithms = algorithms_by_category(AlgorithmCategory::Hash);
447 assert!(
448 !algorithms.is_empty(),
449 "Should have at least one hash algorithm"
450 );
451 }
452
453 #[cfg(feature = "alloc")]
456 #[test]
457 fn test_signature_context_pre_wired_ml_dsa_roundtrip() {
458 let mut ctx = create_signature_context();
459 let keypair = ctx
460 .generate_keypair(Algorithm::MlDsa65, None)
461 .expect("ML-DSA-65 keygen with pre-wired provider");
462 let message = b"lib-q umbrella signature integration";
463 let signature = ctx
464 .sign(Algorithm::MlDsa65, keypair.secret_key(), message, None)
465 .expect("sign");
466 assert!(
467 ctx.verify(
468 Algorithm::MlDsa65,
469 keypair.public_key(),
470 message,
471 signature.as_slice(),
472 )
473 .expect("verify")
474 );
475 }
476
477 #[cfg(all(feature = "alloc", feature = "fn-dsa"))]
478 #[test]
479 fn test_signature_context_fn_dsa512_roundtrip() {
480 let mut ctx = create_signature_context();
481 let keypair = ctx
482 .generate_keypair(Algorithm::FnDsa512, None)
483 .expect("FN-DSA-512 keygen");
484 let message = b"fn-dsa umbrella path";
485 let signature = ctx
486 .sign(Algorithm::FnDsa512, keypair.secret_key(), message, None)
487 .expect("sign");
488 assert!(
489 ctx.verify(
490 Algorithm::FnDsa512,
491 keypair.public_key(),
492 message,
493 signature.as_slice(),
494 )
495 .expect("verify")
496 );
497 }
498
499 #[test]
500 fn test_algorithms_by_security_level() {
501 let level_1_algorithms = algorithms_by_security_level(SecurityLevel::Level1 as u32);
502 assert!(
503 !level_1_algorithms.is_empty(),
504 "Should have at least one Level 1 algorithm"
505 );
506 }
507
508 #[test]
509 fn test_unified_api() {
510 let provider = LibQCryptoProvider::new();
512 assert!(provider.is_ok(), "Provider should be created successfully");
513
514 let provider = provider.unwrap();
515
516 let kem_result = provider
518 .kem()
519 .unwrap()
520 .generate_keypair(Algorithm::MlKem512, None);
521
522 assert!(kem_result.is_err());
524 if let Err(Error::NotImplemented { feature }) = kem_result {
525 assert!(
526 feature.contains("ML-KEM implementations are provided by the main lib-q crate")
527 );
528 } else {
529 panic!("Expected NotImplemented error for KEM operations");
530 }
531
532 #[cfg(feature = "ml-kem")]
534 {
535 let kem_provider = LibQKemProvider::new().unwrap();
536 let kem_result = kem_provider.generate_keypair(Algorithm::MlKem512, None);
537 assert!(
538 kem_result.is_ok(),
539 "ML-KEM key generation should succeed with lib-q-kem provider"
540 );
541 let keypair = kem_result.unwrap();
542 assert!(!keypair.public_key().as_bytes().is_empty());
543 assert!(!keypair.secret_key().as_bytes().is_empty());
544 }
545
546 #[cfg(feature = "hqc")]
547 {
548 let kem_provider = LibQKemProvider::new().unwrap();
549 let keypair = kem_provider
550 .generate_keypair(Algorithm::Hqc128, None)
551 .expect("HQC-128 key generation with lib-q-kem provider");
552 assert_eq!(
553 keypair.public_key().as_bytes().len(),
554 lib_q_hqc::Hqc1Params::PUBLIC_KEY_BYTES
555 );
556 assert_eq!(
557 keypair.secret_key().as_bytes().len(),
558 lib_q_hqc::Hqc1Params::SECRET_KEY_BYTES
559 );
560 let (ciphertext, shared1) = kem_provider
561 .encapsulate(Algorithm::Hqc128, &keypair.public_key, None)
562 .expect("HQC-128 encapsulate");
563 assert_eq!(ciphertext.len(), lib_q_hqc::Hqc1Params::CIPHERTEXT_BYTES);
564 let shared2 = kem_provider
565 .decapsulate(Algorithm::Hqc128, &keypair.secret_key, &ciphertext)
566 .expect("HQC-128 decapsulate");
567 assert_eq!(shared1, shared2);
568 }
569
570 let sig_result = provider
572 .signature()
573 .unwrap()
574 .generate_keypair(Algorithm::MlDsa65, None);
575 #[cfg(feature = "ml-dsa")]
576 {
577 assert!(sig_result.is_err());
580 if let Err(Error::NotImplemented { feature }) = sig_result {
581 assert!(
582 feature.contains("ML-DSA implementations are provided by the main lib-q crate")
583 );
584 } else {
585 panic!("Expected NotImplemented error for ML-DSA in core provider");
586 }
587 }
588 #[cfg(not(feature = "ml-dsa"))]
589 {
590 assert!(sig_result.is_err());
591 if let Err(Error::NotImplemented { feature }) = sig_result {
592 assert!(
593 feature.contains("ML-DSA implementations are provided by the main lib-q crate")
594 );
595 } else {
596 panic!("Expected NotImplemented error for ML-DSA without feature flag");
597 }
598 }
599
600 let hash_result = provider
602 .hash()
603 .unwrap()
604 .hash(Algorithm::Sha3_256, b"test data");
605 assert!(hash_result.is_err());
607 if let Err(Error::NotImplemented { feature }) = hash_result {
608 assert!(feature.contains("SHA3 implementations are provided by the main lib-q crate"));
609 } else {
610 panic!("Expected NotImplemented error for hash operations");
611 }
612
613 #[cfg(not(feature = "std"))]
615 use alloc::vec;
616
617 use lib_q_core::traits::{
618 AeadKey,
619 Nonce,
620 };
621 let mut key_bytes = vec![0u8; 32];
623 let mut nonce_bytes = vec![0u8; 16]; for (i, byte) in key_bytes.iter_mut().enumerate() {
627 *byte = (i as u8).wrapping_mul(0x1F).wrapping_add(0x2B);
628 }
629 for (i, byte) in nonce_bytes.iter_mut().enumerate() {
630 *byte = (i as u8).wrapping_mul(0x3D).wrapping_add(0x7E);
631 }
632
633 let key = AeadKey::new(key_bytes);
634 let nonce = Nonce::new(nonce_bytes);
635 let aead_result = provider.aead().unwrap().encrypt(
636 Algorithm::Saturnin,
637 &key,
638 &nonce,
639 b"plaintext",
640 Some(b"associated data"),
641 );
642 assert!(aead_result.is_err());
645 match aead_result {
646 Err(Error::NotImplemented { feature }) => {
647 assert!(
648 feature.contains("LibQAeadProvider") || feature.contains("libq::aead::context")
649 );
650 }
651 Err(e) => {
652 panic!(
653 "Expected NotImplemented error for AEAD operations, got: {:?}",
654 e
655 );
656 }
657 Ok(_) => {
658 panic!("Expected error for AEAD operations, but got success");
659 }
660 }
661 }
662
663 #[cfg(feature = "alloc")]
664 #[test]
665 fn test_create_hash_context_sha3_256_roundtrip() {
666 let mut ctx = create_hash_context();
667 let out = ctx
668 .hash(Algorithm::Sha3_256, b"lib-q umbrella hash")
669 .expect("SHA3-256 with pre-wired LibQHashProvider");
670 assert_eq!(out.len(), 32);
671 }
672
673 #[cfg(feature = "alloc")]
674 #[test]
675 fn test_create_aead_context_shake256_roundtrip() {
676 use lib_q_core::traits::{
677 AeadKey,
678 Nonce,
679 };
680
681 let mut key_bytes = vec![0u8; 32];
682 let mut nonce_bytes = vec![0u8; 16];
683 for (i, byte) in key_bytes.iter_mut().enumerate() {
684 *byte = (i as u8).wrapping_mul(0x1F).wrapping_add(0x2B);
685 }
686 for (i, byte) in nonce_bytes.iter_mut().enumerate() {
687 *byte = (i as u8).wrapping_mul(0x3D).wrapping_add(0x7E);
688 }
689
690 let key = AeadKey::new(key_bytes);
691 let nonce = Nonce::new(nonce_bytes);
692 let plaintext = b"hello lib-q aead bridge";
693 let ad = b"associated data";
694
695 let mut ctx = aead::context();
696 let ciphertext = ctx
697 .encrypt(
698 Algorithm::Shake256Aead,
699 &key,
700 &nonce,
701 plaintext.as_slice(),
702 Some(ad.as_slice()),
703 )
704 .expect("SHAKE256-AEAD encrypt");
705
706 let recovered = ctx
707 .decrypt(
708 Algorithm::Shake256Aead,
709 &key,
710 &nonce,
711 &ciphertext,
712 Some(ad.as_slice()),
713 )
714 .expect("SHAKE256-AEAD decrypt");
715
716 assert_eq!(recovered.as_slice(), plaintext.as_slice());
717 }
718
719 #[cfg(all(feature = "alloc", feature = "duplex-sponge-aead"))]
720 #[test]
721 fn test_create_aead_context_duplex_sponge_roundtrip() {
722 use lib_q_core::traits::{
723 AeadKey,
724 Nonce,
725 };
726
727 let mut key_bytes = vec![0u8; 32];
728 let mut nonce_bytes = vec![0u8; 16];
729 for (i, byte) in key_bytes.iter_mut().enumerate() {
730 *byte = (i as u8).wrapping_mul(0x11).wrapping_add(0x3C);
731 }
732 for (i, byte) in nonce_bytes.iter_mut().enumerate() {
733 *byte = (i as u8).wrapping_mul(0x29).wrapping_add(0x71);
734 }
735
736 let key = AeadKey::new(key_bytes);
737 let nonce = Nonce::new(nonce_bytes);
738 let plaintext = b"duplex-sponge roundtrip";
739 let ad = b"ad";
740
741 let mut ctx = aead::context();
742 let ciphertext = ctx
743 .encrypt(
744 Algorithm::DuplexSpongeAead,
745 &key,
746 &nonce,
747 plaintext.as_slice(),
748 Some(ad.as_slice()),
749 )
750 .expect("Duplex-Sponge-AEAD encrypt");
751
752 let recovered = ctx
753 .decrypt(
754 Algorithm::DuplexSpongeAead,
755 &key,
756 &nonce,
757 &ciphertext,
758 Some(ad.as_slice()),
759 )
760 .expect("Duplex-Sponge-AEAD decrypt");
761
762 assert_eq!(recovered.as_slice(), plaintext.as_slice());
763 }
764
765 #[cfg(all(feature = "alloc", feature = "tweak-aead"))]
766 #[test]
767 fn test_create_aead_context_tweak_aead_roundtrip() {
768 use lib_q_core::traits::{
769 AeadKey,
770 Nonce,
771 };
772
773 let mut key_bytes = vec![0u8; 32];
774 let mut nonce_bytes = vec![0u8; 16];
775 for (i, byte) in key_bytes.iter_mut().enumerate() {
776 *byte = (i as u8).wrapping_mul(0x13).wrapping_add(0x2E);
777 }
778 for (i, byte) in nonce_bytes.iter_mut().enumerate() {
779 *byte = (i as u8).wrapping_mul(0x2B).wrapping_add(0x6D);
780 }
781
782 let key = AeadKey::new(key_bytes);
783 let nonce = Nonce::new(nonce_bytes);
784 let plaintext = b"tweak aead roundtrip";
785 let ad = b"ad2";
786
787 let mut ctx = aead::context();
788 let ciphertext = ctx
789 .encrypt(
790 Algorithm::TweakAead,
791 &key,
792 &nonce,
793 plaintext.as_slice(),
794 Some(ad.as_slice()),
795 )
796 .expect("Tweak-AEAD encrypt");
797
798 let recovered = ctx
799 .decrypt(
800 Algorithm::TweakAead,
801 &key,
802 &nonce,
803 &ciphertext,
804 Some(ad.as_slice()),
805 )
806 .expect("Tweak-AEAD decrypt");
807
808 assert_eq!(recovered.as_slice(), plaintext.as_slice());
809 }
810
811 #[cfg(all(feature = "alloc", feature = "romulus"))]
812 #[test]
813 fn test_create_aead_context_romulus_n_roundtrip() {
814 use lib_q_core::traits::{
815 AeadKey,
816 Nonce,
817 };
818
819 let mut key_bytes = vec![0u8; 16];
820 let mut nonce_bytes = vec![0u8; 16];
821 for (i, byte) in key_bytes.iter_mut().enumerate() {
822 *byte = (i as u8).wrapping_mul(0x17).wrapping_add(0x31);
823 }
824 for (i, byte) in nonce_bytes.iter_mut().enumerate() {
825 *byte = (i as u8).wrapping_mul(0x2Du8).wrapping_add(0x6Au8);
826 }
827
828 let key = AeadKey::new(key_bytes);
829 let nonce = Nonce::new(nonce_bytes);
830 let plaintext = b"romulus-n roundtrip";
831 let ad = b"ad-rn";
832
833 let mut ctx = aead::context();
834 let ciphertext = ctx
835 .encrypt(
836 Algorithm::RomulusN,
837 &key,
838 &nonce,
839 plaintext.as_slice(),
840 Some(ad.as_slice()),
841 )
842 .expect("Romulus-N encrypt");
843
844 let recovered = ctx
845 .decrypt(
846 Algorithm::RomulusN,
847 &key,
848 &nonce,
849 &ciphertext,
850 Some(ad.as_slice()),
851 )
852 .expect("Romulus-N decrypt");
853
854 assert_eq!(recovered.as_slice(), plaintext.as_slice());
855 }
856
857 #[cfg(all(feature = "alloc", feature = "romulus"))]
858 #[test]
859 fn test_create_aead_context_romulus_m_roundtrip() {
860 use lib_q_core::traits::{
861 AeadKey,
862 Nonce,
863 };
864
865 let mut key_bytes = vec![0u8; 16];
866 let mut nonce_bytes = vec![0u8; 16];
867 for (i, byte) in key_bytes.iter_mut().enumerate() {
868 *byte = (i as u8).wrapping_mul(0x19).wrapping_add(0x2Fu8);
869 }
870 for (i, byte) in nonce_bytes.iter_mut().enumerate() {
871 *byte = (i as u8).wrapping_mul(0x2Fu8).wrapping_add(0x68u8);
872 }
873
874 let key = AeadKey::new(key_bytes);
875 let nonce = Nonce::new(nonce_bytes);
876 let plaintext = b"romulus-m roundtrip";
877 let ad = b"ad-rm";
878
879 let mut ctx = aead::context();
880 let ciphertext = ctx
881 .encrypt(
882 Algorithm::RomulusM,
883 &key,
884 &nonce,
885 plaintext.as_slice(),
886 Some(ad.as_slice()),
887 )
888 .expect("Romulus-M encrypt");
889
890 let recovered = ctx
891 .decrypt(
892 Algorithm::RomulusM,
893 &key,
894 &nonce,
895 &ciphertext,
896 Some(ad.as_slice()),
897 )
898 .expect("Romulus-M decrypt");
899
900 assert_eq!(recovered.as_slice(), plaintext.as_slice());
901 }
902}