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