1#![cfg_attr(not(feature = "std"), no_std)]
46#![allow(type_alias_bounds)]
49#![deny(unsafe_code)]
50#![deny(unused_qualifications)]
51#![allow(clippy::bool_assert_comparison)]
52#![allow(clippy::clone_on_copy)]
53#![allow(clippy::collapsible_if)]
54#![allow(clippy::get_first)]
55#![allow(clippy::iter_cloned_collect)]
56#![allow(clippy::manual_is_multiple_of)]
57#![allow(clippy::too_many_arguments)]
58#![allow(clippy::unnecessary_lazy_evaluations)]
59
60#[cfg(feature = "alloc")]
61extern crate alloc;
62
63#[cfg(feature = "alloc")]
65use alloc::boxed::Box;
66#[cfg(feature = "alloc")]
67use alloc::string::ToString;
68#[cfg(feature = "alloc")]
69use alloc::vec;
70#[cfg(feature = "alloc")]
71use alloc::vec::Vec;
72
73pub use lib_q_core::Result;
74#[cfg(any(
79 feature = "plonky",
80 feature = "plonky-keccak-air",
81 feature = "plonky-lookup",
82 feature = "plonky-uni-stark",
83 feature = "plonky-batch-stark",
84))]
85pub use lib_q_plonky as plonky;
86
87#[cfg(feature = "zkp")]
89pub mod stark;
90
91#[cfg(feature = "zkp")]
93pub mod circuit;
94
95#[cfg(feature = "zkp")]
97pub mod air;
98
99#[cfg(feature = "zkp")]
101pub mod aggregation;
102
103#[cfg(feature = "zkp")]
105pub mod ip;
106
107#[cfg(feature = "zkp")]
109pub mod merkle;
110
111#[cfg(feature = "zkp")]
113pub mod api;
114
115#[cfg(feature = "zkp")]
116pub use api::{
117 MerklePath,
118 build_merkle_tree,
119 prove_membership,
120 prove_membership_with_config,
121 prove_preimage,
122 prove_preimage_nist,
123 verify_membership,
124 verify_membership_with_config,
125 verify_membership_with_depth,
126 verify_membership_with_depth_and_config,
127 verify_preimage,
128 verify_preimage_nist,
129};
130
131#[cfg(feature = "wasm")]
132mod wasm;
133
134#[cfg(feature = "zkp")]
135pub use lib_q_stark::{
136 Proof as StarkProof,
137 StarkConfig,
138 StarkGenericConfig,
139 check_constraints,
140 prove,
141 verify,
142};
143#[cfg(feature = "zkp")]
144pub use lib_q_stark_air::Air;
145#[cfg(feature = "zkp")]
146use lib_q_stark_field::extension::Complex;
147#[cfg(feature = "zkp")]
148use lib_q_stark_matrix::dense::RowMajorMatrix;
149#[cfg(feature = "zkp")]
150use lib_q_stark_mersenne31::Mersenne31;
151#[cfg(feature = "zkp")]
152pub use merkle::PoseidonMerkleTree;
153#[cfg(feature = "zkp")]
154use serde::{
155 Deserialize,
156 Serialize,
157};
158
159#[cfg(feature = "zkp")]
160#[allow(unused_imports)]
161use crate::air::TraceGenerator;
162
163#[cfg(feature = "zkp")]
168pub type ZkpField = Complex<Mersenne31>;
169
170#[derive(Debug, Clone, PartialEq, Eq, Default)]
177#[cfg_attr(feature = "zkp", derive(Serialize, Deserialize))]
178pub enum ProofMetadata {
179 #[default]
181 None,
182 MerkleInclusion {
184 tree_depth: u8,
186 },
187 HashPreimage {
189 output_size: u16,
191 },
192 HashPreimageNist {
194 output_size: u16,
196 },
197 Circuit {
199 num_witnesses: u32,
201 num_public: u32,
203 },
204 Credential {
206 attribute_sizes: Vec<u16>,
208 reveal_mask: Vec<bool>,
210 },
211 Identity {
213 dsa_level: u8,
215 },
216}
217
218#[derive(Debug, Clone)]
220#[cfg_attr(feature = "zkp", derive(serde::Serialize, serde::Deserialize))]
221pub struct ZkpProof {
222 pub data: Vec<u8>,
224 pub proof_type: ProofType,
226 pub security_level: u32,
228 pub metadata: ProofMetadata,
230}
231
232#[cfg(feature = "zkp")]
233impl ZkpProof {
234 pub fn from_stark_proof<C: StarkGenericConfig>(
239 proof: &StarkProof<C>,
240 metadata: ProofMetadata,
241 ) -> Result<Self>
242 where
243 StarkProof<C>: Serialize,
244 {
245 let data = postcard::to_allocvec(proof).map_err(|_| lib_q_core::Error::InternalError {
246 operation: "ZKP proof serialization".to_string(),
247 details: "Failed to serialize STARK proof".to_string(),
248 })?;
249 Ok(Self {
250 data,
251 proof_type: ProofType::Stark,
252 security_level: 1,
253 metadata,
254 })
255 }
256
257 pub fn to_stark_proof<C: StarkGenericConfig>(&self) -> Result<StarkProof<C>>
259 where
260 StarkProof<C>: for<'de> Deserialize<'de>,
261 {
262 postcard::from_bytes(&self.data).map_err(|_| lib_q_core::Error::InternalError {
263 operation: "ZKP proof deserialization".to_string(),
264 details: "Failed to deserialize STARK proof".to_string(),
265 })
266 }
267
268 pub fn merkle_tree_depth(&self) -> Option<u8> {
273 match &self.metadata {
274 ProofMetadata::MerkleInclusion { tree_depth } => Some(*tree_depth),
275 _ => None,
276 }
277 }
278}
279
280#[derive(Debug, Clone, PartialEq, Eq)]
285#[cfg_attr(feature = "zkp", derive(serde::Serialize, serde::Deserialize))]
286pub enum ProofType {
287 Stark,
289}
290
291#[cfg(feature = "zkp")]
293pub struct ZkpProver {
294 }
296
297#[cfg(not(feature = "zkp"))]
298pub struct ZkpProver;
299
300#[cfg(feature = "zkp")]
302pub struct ZkpVerifier {
303 }
305
306#[cfg(not(feature = "zkp"))]
307pub struct ZkpVerifier;
308
309#[cfg(feature = "zkp")]
310impl ZkpProver {
311 pub fn new() -> Self {
313 Self {}
314 }
315
316 pub fn prove_secret_value(
344 &mut self,
345 secret_value: &[u8],
346 _public_statement: &[u8],
347 ) -> Result<ZkpProof> {
348 use crate::air::{
349 HashPreimageAir,
350 TraceGenerator,
351 };
352 use crate::stark::{
353 StarkProver,
354 default_config,
355 };
356
357 if secret_value.is_empty() {
359 return Err(lib_q_core::Error::InvalidState {
360 operation: "prove_secret_value".to_string(),
361 reason: "Secret value cannot be empty".to_string(),
362 });
363 }
364
365 if secret_value.len() > air::hash_preimage::MAX_PREIMAGE_SIZE {
366 return Err(lib_q_core::Error::InvalidState {
367 operation: "prove_secret_value".to_string(),
368 reason: "Secret value exceeds maximum size".to_string(),
369 });
370 }
371
372 let air = HashPreimageAir::new();
374
375 let input = secret_value.to_vec();
377 let trace: RowMajorMatrix<ZkpField> =
378 air.generate_trace(&input)
379 .map_err(|e| lib_q_core::Error::InternalError {
380 operation: "prove_secret_value".to_string(),
381 details: e.to_string(),
382 })?;
383
384 let public_values: Vec<ZkpField> = air.public_values(&input);
386
387 let config = default_config();
389 let prover = StarkProver::new(config);
390
391 let proof = prover.prove(&air, trace, &public_values).map_err(|e| {
393 lib_q_core::Error::InternalError {
394 operation: "STARK proof generation".to_string(),
395 details: e.to_string(),
396 }
397 })?;
398
399 let metadata = ProofMetadata::HashPreimage { output_size: 1u16 };
401
402 ZkpProof::from_stark_proof(&proof, metadata)
404 }
405
406 pub fn prove_secret_value_nist(
417 &mut self,
418 secret_value: &[u8],
419 _public_statement: &[u8],
420 ) -> Result<ZkpProof> {
421 use crate::air::{
422 HASH_OUTPUT_BYTES,
423 HashPreimageNistAir,
424 TraceGenerator,
425 };
426 use crate::stark::{
427 StarkProver,
428 default_config,
429 };
430
431 if secret_value.is_empty() {
432 return Err(lib_q_core::Error::InvalidState {
433 operation: "prove_secret_value_nist".to_string(),
434 reason: "Secret value cannot be empty".to_string(),
435 });
436 }
437 if secret_value.len() > air::hash_preimage_nist::MAX_PREIMAGE_SIZE {
438 return Err(lib_q_core::Error::InvalidState {
439 operation: "prove_secret_value_nist".to_string(),
440 reason: "Secret value exceeds maximum size".to_string(),
441 });
442 }
443
444 let air = HashPreimageNistAir::new();
445 let input = secret_value.to_vec();
446 let trace: RowMajorMatrix<ZkpField> =
447 air.generate_trace(&input)
448 .map_err(|e| lib_q_core::Error::InternalError {
449 operation: "prove_secret_value_nist".to_string(),
450 details: e.to_string(),
451 })?;
452
453 let public_values: Vec<ZkpField> = air.public_values(&input);
454 let config = default_config();
455 let prover = StarkProver::new(config);
456
457 let proof = prover.prove(&air, trace, &public_values).map_err(|e| {
458 lib_q_core::Error::InternalError {
459 operation: "STARK proof generation (NIST)".to_string(),
460 details: e.to_string(),
461 }
462 })?;
463
464 let metadata = ProofMetadata::HashPreimageNist {
465 output_size: HASH_OUTPUT_BYTES as u16,
466 };
467 ZkpProof::from_stark_proof(&proof, metadata)
468 }
469
470 pub fn prove_computation(
511 &mut self,
512 circuit: &circuit::ArithmeticCircuit<ZkpField>,
513 witness: &[ZkpField],
514 public: &[ZkpField],
515 ) -> Result<ZkpProof> {
516 use crate::circuit::CircuitAir;
517 use crate::stark::{
518 StarkProver,
519 default_config,
520 };
521
522 let air = CircuitAir::new(circuit.clone());
524
525 let trace = air.generate_trace(witness, public)?;
527
528 let config = default_config();
530 let prover = StarkProver::new(config);
531
532 let proof =
534 prover
535 .prove(&air, trace, public)
536 .map_err(|e| lib_q_core::Error::InternalError {
537 operation: "STARK proof generation".to_string(),
538 details: e.to_string(),
539 })?;
540
541 let metadata = ProofMetadata::Circuit {
543 num_witnesses: witness.len().min(u32::MAX as usize) as u32,
544 num_public: public.len().min(u32::MAX as usize) as u32,
545 };
546
547 ZkpProof::from_stark_proof(&proof, metadata)
549 }
550}
551
552#[cfg(not(feature = "zkp"))]
553impl ZkpProver {
554 pub fn new() -> Self {
556 Self {}
557 }
558
559 pub fn prove_secret_value(
561 &mut self,
562 _secret_value: &[u8],
563 _public_statement: &[u8],
564 ) -> Result<ZkpProof> {
565 Err(lib_q_core::Error::NotImplemented {
566 feature: "ZKP feature not enabled".to_string(),
567 })
568 }
569
570 pub fn prove_secret_value_nist(
572 &mut self,
573 _secret_value: &[u8],
574 _public_statement: &[u8],
575 ) -> Result<ZkpProof> {
576 Err(lib_q_core::Error::NotImplemented {
577 feature: "ZKP feature not enabled".to_string(),
578 })
579 }
580}
581
582#[cfg(feature = "zkp")]
585fn verify_secret_value_nist_impl(proof: &ZkpProof, expected_hash: &[u8]) -> Result<bool> {
586 use crate::air::{
587 HashPreimageNistAir,
588 expected_hash_to_public_values,
589 };
590 use crate::stark::{
591 StarkVerifier,
592 default_config,
593 };
594
595 if proof.proof_type != ProofType::Stark {
596 return Ok(false);
597 }
598 if proof.data.is_empty() {
599 return Ok(false);
600 }
601
602 let ProofMetadata::HashPreimageNist { .. } = &proof.metadata else {
603 return Ok(false);
604 };
605
606 let air = HashPreimageNistAir::new();
607 let public_values = expected_hash_to_public_values::<ZkpField>(expected_hash);
608
609 let stark_proof = match proof.to_stark_proof() {
610 Ok(p) => p,
611 Err(_) => return Ok(false),
612 };
613
614 let config = default_config();
615 let verifier = StarkVerifier::new(config);
616
617 match verifier.verify(&air, &stark_proof, &public_values) {
618 Ok(()) => Ok(true),
619 Err(_) => Ok(false),
620 }
621}
622
623#[cfg(feature = "zkp")]
624impl ZkpVerifier {
625 pub fn new() -> Self {
627 Self {}
628 }
629
630 pub fn verify_secret_value(&self, proof: &ZkpProof, public_hash: &[u8]) -> Result<bool> {
645 use lib_q_poseidon::{
646 Poseidon,
647 Poseidon128,
648 };
649
650 use crate::air::{
651 HashPreimageAir,
652 bytes_to_poseidon_field,
653 poseidon_slice_to_field,
654 };
655 use crate::stark::{
656 StarkVerifier,
657 default_config,
658 };
659
660 if proof.proof_type != ProofType::Stark {
661 return Ok(false);
662 }
663
664 if proof.data.is_empty() {
665 return Ok(false);
666 }
667
668 let ProofMetadata::HashPreimage { output_size } = &proof.metadata else {
670 return Ok(false);
672 };
673
674 let _ = output_size;
676 let air = HashPreimageAir::new();
677
678 let expected_field_elements = bytes_to_poseidon_field(public_hash);
682 let poseidon_hash = Poseidon128.hash(&expected_field_elements);
683
684 let public_values = poseidon_slice_to_field(&poseidon_hash);
686
687 let stark_proof = proof.to_stark_proof()?;
689
690 let config = default_config();
692 let verifier = StarkVerifier::new(config);
693
694 match verifier.verify(&air, &stark_proof, &public_values) {
696 Ok(()) => Ok(true),
697 Err(_) => Ok(false),
698 }
699 }
700
701 pub fn verify_secret_value_nist(&self, proof: &ZkpProof, expected_hash: &[u8]) -> Result<bool> {
706 verify_secret_value_nist_impl(proof, expected_hash)
707 }
708
709 pub fn verify_computation(
723 &self,
724 proof: &ZkpProof,
725 circuit: &circuit::ArithmeticCircuit<ZkpField>,
726 public: &[ZkpField],
727 ) -> Result<bool> {
728 use crate::circuit::CircuitAir;
729 use crate::stark::{
730 StarkVerifier,
731 default_config,
732 };
733
734 if proof.proof_type != ProofType::Stark {
735 return Ok(false);
736 }
737
738 if proof.data.is_empty() {
739 return Ok(false);
740 }
741
742 let air = CircuitAir::new(circuit.clone());
744
745 let stark_proof = proof.to_stark_proof()?;
747
748 let config = default_config();
750 let verifier = StarkVerifier::new(config);
751
752 match verifier.verify(&air, &stark_proof, public) {
754 Ok(()) => Ok(true),
755 Err(_) => Ok(false),
756 }
757 }
758
759 pub fn verify(&self, proof: ZkpProof, public_statement: &[u8]) -> Result<bool> {
777 if proof.proof_type != ProofType::Stark {
778 return Ok(false);
779 }
780 if proof.data.is_empty() {
781 return Ok(false);
782 }
783 match &proof.metadata {
784 ProofMetadata::HashPreimage { .. } => {
785 self.verify_secret_value(&proof, public_statement)
786 }
787 ProofMetadata::HashPreimageNist { .. } => {
788 verify_secret_value_nist_impl(&proof, public_statement)
789 }
790 ProofMetadata::MerkleInclusion { .. } => verify_membership(&proof, public_statement),
791 _ => Ok(false),
792 }
793 }
794
795 pub fn batch_verify(&self, proofs: &[ZkpProof], publics: &[&[u8]]) -> Result<bool> {
806 if proofs.len() != publics.len() {
807 return Err(lib_q_core::Error::InvalidState {
808 operation: "batch_verify".to_string(),
809 reason: "Number of proofs must match number of public statements".to_string(),
810 });
811 }
812
813 for (proof, public) in proofs.iter().zip(publics.iter()) {
814 match self.verify(proof.clone(), public) {
815 Ok(true) => continue,
816 Ok(false) => return Ok(false),
817 Err(e) => return Err(e),
818 }
819 }
820
821 Ok(true)
822 }
823}
824
825#[cfg(not(feature = "zkp"))]
826impl ZkpVerifier {
827 pub fn new() -> Self {
829 Self {}
830 }
831
832 pub fn verify(&self, _proof: ZkpProof, _public_statement: &[u8]) -> Result<bool> {
834 Err(lib_q_core::Error::NotImplemented {
835 feature: "ZKP feature not enabled".to_string(),
836 })
837 }
838
839 pub fn verify_secret_value_nist(
841 &self,
842 _proof: &ZkpProof,
843 _expected_hash: &[u8],
844 ) -> Result<bool> {
845 Err(lib_q_core::Error::NotImplemented {
846 feature: "ZKP feature not enabled".to_string(),
847 })
848 }
849
850 pub fn batch_verify(&self, _proofs: &[ZkpProof], _publics: &[&[u8]]) -> Result<bool> {
852 Err(lib_q_core::Error::NotImplemented {
853 feature: "ZKP feature not enabled".to_string(),
854 })
855 }
856}
857
858impl Default for ZkpProver {
859 fn default() -> Self {
860 Self::new()
861 }
862}
863
864impl Default for ZkpVerifier {
865 fn default() -> Self {
866 Self::new()
867 }
868}
869
870pub fn available_algorithms() -> Vec<&'static str> {
872 let algorithms = vec![
873 #[cfg(feature = "zkp")]
874 "stark",
875 ];
876
877 algorithms
878}
879
880pub fn create_zkp(algorithm: &str) -> Result<Box<dyn core::any::Any>> {
882 match algorithm {
883 #[cfg(feature = "zkp")]
884 "stark" => Ok(Box::new(ZkpProver::new())),
885
886 _ => Err(lib_q_core::Error::InvalidAlgorithm {
887 algorithm: "Unknown ZKP algorithm",
888 }),
889 }
890}
891
892#[cfg(test)]
893mod tests {
894 use super::*;
895
896 #[test]
897 fn test_zkp_prover_creation() {
898 let _prover = ZkpProver::new();
899 }
901
902 #[test]
903 fn test_zkp_verifier_creation() {
904 let _verifier = ZkpVerifier::new();
905 }
907
908 #[test]
909 fn test_zkp_proof_creation() {
910 let mut prover = ZkpProver::new();
911 let secret_value = b"secret_value";
912 let public_statement = b"public_statement";
913
914 let result = prover.prove_secret_value(secret_value, public_statement);
916 assert!(
918 result.is_ok(),
919 "Proof generation should succeed: {:?}",
920 result.err()
921 );
922 }
923
924 #[cfg(feature = "zkp")]
925 #[test]
926 fn test_nist_secret_value_round_trip() {
927 use digest::{
928 ExtendableOutput,
929 Update,
930 };
931 use lib_q_sha3::CShake256;
932
933 use crate::air::hash_preimage_nist::CSHAKE_DOMAIN;
934
935 let secret = b"nist_secret_value";
936 let mut prover = ZkpProver::new();
937 let proof = prover
938 .prove_secret_value_nist(secret, b"")
939 .expect("prove_secret_value_nist");
940 let mut expected_hash = [0u8; 32];
941 {
942 let mut hasher = CShake256::new_with_function_name(&[], CSHAKE_DOMAIN);
943 hasher.update(secret);
944 hasher.finalize_xof_into(&mut expected_hash);
945 }
946 let verifier = ZkpVerifier::new();
947 assert!(
948 verifier
949 .verify_secret_value_nist(&proof, &expected_hash)
950 .unwrap(),
951 "NIST proof should verify with correct expected hash"
952 );
953 assert!(
955 verifier.verify(proof.clone(), &expected_hash).unwrap(),
956 "verify() must accept NIST proof with correct expected hash"
957 );
958 }
959
960 #[cfg(feature = "zkp")]
961 #[test]
962 fn test_nist_proof_rejected_by_poseidon_verifier() {
963 use digest::{
964 ExtendableOutput,
965 Update,
966 };
967 use lib_q_sha3::CShake256;
968
969 use crate::air::hash_preimage_nist::CSHAKE_DOMAIN;
970
971 let secret = b"nist_only";
972 let mut prover = ZkpProver::new();
973 let proof = prover
974 .prove_secret_value_nist(secret, b"")
975 .expect("NIST prove");
976 let mut expected_hash = [0u8; 32];
977 let mut hasher = CShake256::new_with_function_name(&[], CSHAKE_DOMAIN);
978 hasher.update(secret);
979 hasher.finalize_xof_into(&mut expected_hash);
980 let verifier = ZkpVerifier::new();
981 assert!(
982 !verifier
983 .verify_secret_value(&proof, &expected_hash)
984 .unwrap(),
985 "NIST proof must not be accepted by Poseidon verifier"
986 );
987 }
988
989 #[cfg(feature = "zkp")]
990 #[test]
991 fn test_poseidon_proof_rejected_by_nist_verifier() {
992 let secret = b"poseidon_only";
993 let mut prover = ZkpProver::new();
994 let proof = prover
995 .prove_secret_value(secret, b"")
996 .expect("Poseidon prove");
997 let verifier = ZkpVerifier::new();
998 assert!(
999 !verifier
1000 .verify_secret_value_nist(&proof, &[0u8; 32])
1001 .unwrap(),
1002 "Poseidon proof must not be accepted by NIST verifier"
1003 );
1004 }
1005
1006 #[cfg(feature = "zkp")]
1007 #[test]
1008 fn test_verify_rejects_unknown_metadata() {
1009 use lib_q_stark_field::PrimeCharacteristicRing;
1010 use lib_q_stark_mersenne31::Mersenne31;
1011
1012 use crate::air::{
1013 ArithmeticAir,
1014 TraceGenerator,
1015 };
1016 use crate::stark::{
1017 StarkProver,
1018 default_config,
1019 };
1020
1021 let air = ArithmeticAir::new(1).expect("ArithmeticAir");
1022 let one = <ZkpField as PrimeCharacteristicRing>::ONE;
1023 let seven = ZkpField::from(Mersenne31::new(7));
1024 let input = alloc::vec![(one, seven)];
1025 let trace = air.generate_trace(&input).expect("trace generation");
1026 let public_values = air.public_values(&input);
1027 let proof_inner = StarkProver::new(default_config())
1028 .prove(&air, trace, &public_values)
1029 .expect("prove");
1030 let proof_bytes = postcard::to_allocvec(&proof_inner).expect("serialize STARK proof");
1031
1032 let proof = ZkpProof {
1033 data: proof_bytes,
1034 proof_type: ProofType::Stark,
1035 security_level: 1,
1036 metadata: ProofMetadata::None,
1037 };
1038
1039 let verifier = ZkpVerifier::new();
1040 assert_eq!(
1041 verifier.verify(proof, b"public_statement").unwrap(),
1042 false,
1043 "ProofMetadata::None must return false -- use a type-specific verifier"
1044 );
1045 }
1046
1047 #[test]
1048 fn test_batch_verify_mismatched_lengths() {
1049 let verifier = ZkpVerifier::new();
1050 let proofs = vec![ZkpProof {
1051 data: vec![0u8; 64],
1052 proof_type: ProofType::Stark,
1053 security_level: 1,
1054 metadata: ProofMetadata::None,
1055 }];
1056 let publics: &[&[u8]] = &[b"public1" as &[u8], b"public2" as &[u8]];
1057
1058 let result = verifier.batch_verify(&proofs, publics);
1059 assert!(result.is_err());
1060 if let Err(lib_q_core::Error::InvalidState { .. }) = result {
1061 } else {
1063 panic!("Expected InvalidState error");
1064 }
1065 }
1066
1067 #[cfg(feature = "zkp")]
1068 #[test]
1069 fn test_proof_metadata_merkle() {
1070 let metadata = ProofMetadata::MerkleInclusion { tree_depth: 8 };
1071 let proof = ZkpProof {
1072 data: vec![0u8; 64],
1073 proof_type: ProofType::Stark,
1074 security_level: 1,
1075 metadata,
1076 };
1077 assert_eq!(proof.merkle_tree_depth(), Some(8));
1078 }
1079
1080 #[cfg(feature = "zkp")]
1081 #[test]
1082 fn test_proof_metadata_none() {
1083 let proof = ZkpProof {
1084 data: vec![0u8; 64],
1085 proof_type: ProofType::Stark,
1086 security_level: 1,
1087 metadata: ProofMetadata::None,
1088 };
1089 assert_eq!(proof.merkle_tree_depth(), None);
1090 }
1091
1092 #[test]
1093 fn test_available_algorithms() {
1094 let algorithms = available_algorithms();
1095 #[cfg(feature = "zkp")]
1096 assert!(!algorithms.is_empty(), "zkp feature enables STARK");
1097 #[cfg(not(feature = "zkp"))]
1098 let _ = algorithms;
1099 }
1100
1101 #[cfg(feature = "zkp")]
1102 #[test]
1103 fn test_create_zkp() {
1104 let algorithms = available_algorithms();
1105 assert!(!algorithms.is_empty());
1106 let algorithm = algorithms[0];
1107 assert!(create_zkp(algorithm).is_ok());
1108 }
1109
1110 #[cfg(feature = "zkp")]
1111 #[test]
1112 fn test_verify_rejects_forged_proof_with_hash_preimage_metadata() {
1113 let proof = ZkpProof {
1114 data: alloc::vec![
1115 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD,
1116 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF,
1117 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA,
1118 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD,
1119 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF,
1120 ],
1121 proof_type: ProofType::Stark,
1122 security_level: 1,
1123 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1124 };
1125 let verifier = ZkpVerifier::new();
1126 let result = verifier.verify(proof, b"expected_hash");
1127 assert!(
1128 matches!(result, Ok(false) | Err(_)),
1129 "forged HashPreimage proof must not return Ok(true)"
1130 );
1131 }
1132
1133 #[cfg(feature = "zkp")]
1134 #[test]
1135 fn test_verify_rejects_forged_proof_with_merkle_metadata() {
1136 let proof = ZkpProof {
1137 data: alloc::vec![
1138 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE,
1139 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
1140 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE,
1141 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
1142 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
1143 ],
1144 proof_type: ProofType::Stark,
1145 security_level: 1,
1146 metadata: ProofMetadata::MerkleInclusion { tree_depth: 4 },
1147 };
1148 let verifier = ZkpVerifier::new();
1149 let result = verifier.verify(proof, b"wrong_root");
1150 assert!(
1151 matches!(result, Ok(false) | Err(_)),
1152 "forged MerkleInclusion proof must not return Ok(true)"
1153 );
1154 }
1155
1156 #[cfg(feature = "zkp")]
1157 #[test]
1158 fn test_verify_rejects_circuit_metadata_proof() {
1159 let proof = ZkpProof {
1160 data: alloc::vec![0u8; 64],
1161 proof_type: ProofType::Stark,
1162 security_level: 1,
1163 metadata: ProofMetadata::Circuit {
1164 num_witnesses: 2,
1165 num_public: 1,
1166 },
1167 };
1168 let verifier = ZkpVerifier::new();
1169 assert_eq!(
1170 verifier.verify(proof, b"anything").unwrap(),
1171 false,
1172 "Circuit proofs must be rejected by generic verify; use verify_computation"
1173 );
1174 }
1175
1176 #[cfg(feature = "zkp")]
1177 #[test]
1178 fn test_verify_rejects_credential_metadata_proof() {
1179 let proof = ZkpProof {
1180 data: alloc::vec![0u8; 64],
1181 proof_type: ProofType::Stark,
1182 security_level: 1,
1183 metadata: ProofMetadata::Credential {
1184 attribute_sizes: alloc::vec![8, 4],
1185 reveal_mask: alloc::vec![true, false],
1186 },
1187 };
1188 let verifier = ZkpVerifier::new();
1189 assert_eq!(
1190 verifier.verify(proof, b"anything").unwrap(),
1191 false,
1192 "Credential proofs must be rejected by generic verify; use ip::verify_credential_proof"
1193 );
1194 }
1195
1196 #[cfg(feature = "zkp")]
1197 #[test]
1198 fn test_verify_rejects_identity_metadata_proof() {
1199 let proof = ZkpProof {
1200 data: alloc::vec![0u8; 64],
1201 proof_type: ProofType::Stark,
1202 security_level: 1,
1203 metadata: ProofMetadata::Identity { dsa_level: 65 },
1204 };
1205 let verifier = ZkpVerifier::new();
1206 assert_eq!(
1207 verifier.verify(proof, b"anything").unwrap(),
1208 false,
1209 "Identity proofs must be rejected by generic verify; use ip::verify_it_ownership"
1210 );
1211 }
1212
1213 #[cfg(feature = "zkp")]
1214 #[test]
1215 fn test_verify_empty_data_is_rejected() {
1216 let proof = ZkpProof {
1217 data: alloc::vec![],
1218 proof_type: ProofType::Stark,
1219 security_level: 1,
1220 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1221 };
1222 let verifier = ZkpVerifier::new();
1223 assert_eq!(
1224 verifier.verify(proof, b"anything").unwrap(),
1225 false,
1226 "empty proof data must be rejected regardless of metadata"
1227 );
1228 }
1229
1230 #[cfg(feature = "zkp")]
1231 #[test]
1232 fn test_proof_type_only_stark_exists() {
1233 let _stark = ProofType::Stark;
1234 }
1237
1238 #[cfg(feature = "zkp")]
1239 #[test]
1240 fn test_batch_verify_rejects_forged_hash_preimage_proof() {
1241 let forged = ZkpProof {
1242 data: alloc::vec![0xFF; 64],
1243 proof_type: ProofType::Stark,
1244 security_level: 1,
1245 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1246 };
1247 let verifier = ZkpVerifier::new();
1248 let proofs = alloc::vec![forged];
1249 let publics: &[&[u8]] = &[b"anything"];
1250 let result = verifier.batch_verify(&proofs, publics);
1251 assert!(
1252 matches!(result, Ok(false) | Err(_)),
1253 "batch_verify must not accept a forged proof"
1254 );
1255 }
1256
1257 #[cfg(feature = "zkp")]
1258 #[test]
1259 fn test_prove_secret_value_rejects_empty_and_oversized_input() {
1260 let mut prover = ZkpProver::new();
1261 let empty = prover.prove_secret_value(b"", b"");
1262 assert!(empty.is_err());
1263
1264 let oversized = vec![0u8; air::hash_preimage::MAX_PREIMAGE_SIZE + 1];
1265 let too_large = prover.prove_secret_value(&oversized, b"");
1266 assert!(too_large.is_err());
1267 }
1268
1269 #[cfg(feature = "zkp")]
1270 #[test]
1271 fn test_prove_secret_value_nist_rejects_empty_and_oversized_input() {
1272 let mut prover = ZkpProver::new();
1273 let empty = prover.prove_secret_value_nist(b"", b"");
1274 assert!(empty.is_err());
1275
1276 let oversized = vec![0u8; air::hash_preimage_nist::MAX_PREIMAGE_SIZE + 1];
1277 let too_large = prover.prove_secret_value_nist(&oversized, b"");
1278 assert!(too_large.is_err());
1279 }
1280
1281 #[cfg(feature = "zkp")]
1282 #[test]
1283 fn test_verify_secret_value_rejects_invalid_proof_shape_inputs() {
1284 let verifier = ZkpVerifier::new();
1285 let non_stark = ZkpProof {
1286 data: vec![1u8; 16],
1287 proof_type: ProofType::Stark,
1288 security_level: 1,
1289 metadata: ProofMetadata::HashPreimageNist { output_size: 32 },
1290 };
1291 assert!(!verifier.verify_secret_value(&non_stark, b"hash").unwrap());
1292
1293 let empty = ZkpProof {
1294 data: vec![],
1295 proof_type: ProofType::Stark,
1296 security_level: 1,
1297 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1298 };
1299 assert!(!verifier.verify_secret_value(&empty, b"hash").unwrap());
1300 }
1301
1302 #[cfg(feature = "zkp")]
1303 #[test]
1304 fn test_verify_secret_value_nist_rejects_wrong_metadata_and_bad_bytes() {
1305 let verifier = ZkpVerifier::new();
1306 let wrong_meta = ZkpProof {
1307 data: vec![1u8; 16],
1308 proof_type: ProofType::Stark,
1309 security_level: 1,
1310 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1311 };
1312 assert!(
1313 !verifier
1314 .verify_secret_value_nist(&wrong_meta, &[0u8; 32])
1315 .unwrap()
1316 );
1317
1318 let malformed_stark = ZkpProof {
1319 data: vec![0xAA; 16],
1320 proof_type: ProofType::Stark,
1321 security_level: 1,
1322 metadata: ProofMetadata::HashPreimageNist { output_size: 32 },
1323 };
1324 assert!(
1325 !verifier
1326 .verify_secret_value_nist(&malformed_stark, &[0u8; 32])
1327 .unwrap()
1328 );
1329 }
1330
1331 #[cfg(feature = "zkp")]
1332 #[test]
1333 fn test_verify_computation_rejects_empty_or_non_stark_data() {
1334 use crate::circuit::CircuitBuilder;
1335
1336 let verifier = ZkpVerifier::new();
1337 let circuit = CircuitBuilder::<ZkpField>::new(1, 0).build();
1338
1339 let empty = ZkpProof {
1340 data: vec![],
1341 proof_type: ProofType::Stark,
1342 security_level: 1,
1343 metadata: ProofMetadata::Circuit {
1344 num_witnesses: 1,
1345 num_public: 0,
1346 },
1347 };
1348 assert!(!verifier.verify_computation(&empty, &circuit, &[]).unwrap());
1349 }
1350}