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#![allow(clippy::assign_op_pattern)]
62#![allow(clippy::needless_range_loop)]
63#![allow(clippy::manual_memcpy)]
64#![allow(clippy::double_must_use)]
65
66#[cfg(feature = "alloc")]
67extern crate alloc;
68
69#[cfg(feature = "alloc")]
71use alloc::boxed::Box;
72#[cfg(feature = "alloc")]
73use alloc::string::ToString;
74#[cfg(feature = "alloc")]
75use alloc::vec;
76#[cfg(feature = "alloc")]
77use alloc::vec::Vec;
78
79pub use lib_q_core::Result;
80#[cfg(any(
85 feature = "plonky",
86 feature = "plonky-keccak-air",
87 feature = "plonky-lookup",
88 feature = "plonky-uni-stark",
89 feature = "plonky-batch-stark",
90))]
91pub use lib_q_plonky as plonky;
92
93#[cfg(feature = "zkp")]
95pub mod stark;
96
97#[cfg(feature = "zkp")]
99pub mod stark_baby_bear;
100
101#[cfg(feature = "zkp")]
103pub mod circuit;
104
105#[cfg(feature = "zkp")]
107pub mod air;
108
109#[cfg(feature = "zkp")]
111pub mod aggregation;
112
113#[cfg(feature = "zkp")]
115pub mod ip;
116
117#[cfg(feature = "zkp")]
119pub mod merkle;
120
121#[cfg(feature = "zkp")]
123pub mod merkle_baby_bear;
124
125#[cfg(feature = "zkp")]
127pub mod api;
128
129#[cfg(feature = "zkp")]
130pub mod wire;
131
132#[cfg(feature = "zkp")]
135pub mod membership;
136
137#[cfg(feature = "zkp")]
138pub use api::{
139 MerklePath,
140 build_merkle_tree,
141 prove_membership,
142 prove_membership_with_config,
143 prove_preimage,
144 prove_preimage_nist,
145 verify_membership,
146 verify_membership_with_config,
147 verify_membership_with_depth,
148 verify_membership_with_depth_and_config,
149 verify_preimage,
150 verify_preimage_nist,
151};
152
153#[cfg(feature = "wasm")]
154mod wasm;
155
156#[cfg(feature = "zkp")]
157pub use lib_q_stark::{
158 Proof as StarkProof,
159 StarkConfig,
160 StarkGenericConfig,
161 check_constraints,
162 prove,
163 verify,
164};
165#[cfg(feature = "zkp")]
166pub use lib_q_stark_air::Air;
167#[cfg(feature = "zkp")]
168use lib_q_stark_field::extension::Complex;
169#[cfg(feature = "zkp")]
170use lib_q_stark_matrix::dense::RowMajorMatrix;
171#[cfg(feature = "zkp")]
172use lib_q_stark_mersenne31::Mersenne31;
173#[cfg(feature = "zkp")]
174pub use merkle::PoseidonMerkleTree;
175#[cfg(feature = "zkp")]
176use serde::{
177 Deserialize,
178 Serialize,
179};
180
181#[cfg(feature = "zkp")]
182#[allow(unused_imports)]
183use crate::air::TraceGenerator;
184
185#[cfg(feature = "zkp")]
190pub type ZkpField = Complex<Mersenne31>;
191
192#[derive(Debug, Clone, PartialEq, Eq, Default)]
199#[cfg_attr(feature = "zkp", derive(Serialize, Deserialize))]
200pub enum ProofMetadata {
201 #[default]
203 None,
204 MerkleInclusion {
206 tree_depth: u8,
208 },
209 HashPreimage {
211 output_size: u16,
213 },
214 HashPreimageNist {
216 output_size: u16,
218 },
219 Circuit {
221 num_witnesses: u32,
223 num_public: u32,
225 },
226 Credential {
228 attribute_sizes: Vec<u16>,
230 reveal_mask: Vec<bool>,
232 },
233 Identity {
235 dsa_level: u8,
237 },
238 RecoveryPolicy {
240 key_count: u32,
242 air_id: u8,
244 },
245 UnlinkableMembership {
247 tree_depth: u8,
251 digest_width: u8,
253 zk: bool,
257 },
258}
259
260#[derive(Debug, Clone)]
262#[cfg_attr(feature = "zkp", derive(serde::Serialize, serde::Deserialize))]
263pub struct ZkpProof {
264 pub data: Vec<u8>,
266 pub proof_type: ProofType,
268 pub security_level: u32,
270 pub metadata: ProofMetadata,
272}
273
274#[cfg(feature = "zkp")]
275impl ZkpProof {
276 pub fn from_stark_proof<C: StarkGenericConfig>(
281 proof: &StarkProof<C>,
282 metadata: ProofMetadata,
283 ) -> Result<Self>
284 where
285 StarkProof<C>: Serialize,
286 {
287 let data = postcard::to_allocvec(proof).map_err(|_| lib_q_core::Error::InternalError {
288 operation: "ZKP proof serialization".to_string(),
289 details: "Failed to serialize STARK proof".to_string(),
290 })?;
291 Ok(Self {
292 data,
293 proof_type: ProofType::Stark,
294 security_level: 1,
295 metadata,
296 })
297 }
298
299 pub fn to_stark_proof<C: StarkGenericConfig>(&self) -> Result<StarkProof<C>>
301 where
302 StarkProof<C>: for<'de> Deserialize<'de>,
303 {
304 postcard::from_bytes(&self.data).map_err(|_| lib_q_core::Error::InternalError {
305 operation: "ZKP proof deserialization".to_string(),
306 details: "Failed to deserialize STARK proof".to_string(),
307 })
308 }
309
310 pub fn merkle_tree_depth(&self) -> Option<u8> {
315 match &self.metadata {
316 ProofMetadata::MerkleInclusion { tree_depth } => Some(*tree_depth),
317 ProofMetadata::UnlinkableMembership { tree_depth, .. } => Some(*tree_depth),
318 _ => None,
319 }
320 }
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
328#[cfg_attr(feature = "zkp", derive(serde::Serialize, serde::Deserialize))]
329pub enum ProofType {
330 Stark,
332}
333
334#[cfg(feature = "zkp")]
336pub struct ZkpProver {
337 }
339
340#[cfg(not(feature = "zkp"))]
341pub struct ZkpProver;
342
343#[cfg(feature = "zkp")]
345pub struct ZkpVerifier {
346 }
348
349#[cfg(not(feature = "zkp"))]
350pub struct ZkpVerifier;
351
352#[cfg(feature = "zkp")]
353impl ZkpProver {
354 pub fn new() -> Self {
356 Self {}
357 }
358
359 pub fn prove_secret_value(
387 &mut self,
388 secret_value: &[u8],
389 _public_statement: &[u8],
390 ) -> Result<ZkpProof> {
391 use crate::air::{
392 HashPreimageAir,
393 TraceGenerator,
394 };
395 use crate::stark::{
396 StarkProver,
397 default_config,
398 };
399
400 if secret_value.is_empty() {
402 return Err(lib_q_core::Error::InvalidState {
403 operation: "prove_secret_value".to_string(),
404 reason: "Secret value cannot be empty".to_string(),
405 });
406 }
407
408 if secret_value.len() > air::hash_preimage::MAX_PREIMAGE_SIZE {
409 return Err(lib_q_core::Error::InvalidState {
410 operation: "prove_secret_value".to_string(),
411 reason: "Secret value exceeds maximum size".to_string(),
412 });
413 }
414
415 let air = HashPreimageAir::new();
417
418 let input = secret_value.to_vec();
420 let trace: RowMajorMatrix<ZkpField> =
421 air.generate_trace(&input)
422 .map_err(|e| lib_q_core::Error::InternalError {
423 operation: "prove_secret_value".to_string(),
424 details: e.to_string(),
425 })?;
426
427 let public_values: Vec<ZkpField> = air.public_values(&input);
429
430 let config = default_config();
432 let prover = StarkProver::new(config);
433
434 let proof = prover.prove(&air, trace, &public_values).map_err(|e| {
436 lib_q_core::Error::InternalError {
437 operation: "STARK proof generation".to_string(),
438 details: e.to_string(),
439 }
440 })?;
441
442 let metadata = ProofMetadata::HashPreimage { output_size: 1u16 };
444
445 ZkpProof::from_stark_proof(&proof, metadata)
447 }
448
449 pub fn prove_secret_value_nist(
467 &mut self,
468 secret_value: &[u8],
469 public_statement: &[u8],
470 ) -> Result<ZkpProof> {
471 let _ = (secret_value, public_statement);
477 Err(lib_q_core::Error::NotImplemented {
478 feature: "NIST (cSHAKE256) preimage proofs: Keccak-f AIR constraints not implemented"
479 .to_string(),
480 })
481 }
482
483 pub fn prove_computation(
524 &mut self,
525 circuit: &circuit::ArithmeticCircuit<ZkpField>,
526 witness: &[ZkpField],
527 public: &[ZkpField],
528 ) -> Result<ZkpProof> {
529 use crate::circuit::CircuitAir;
530 use crate::stark::{
531 StarkProver,
532 default_config,
533 };
534
535 let air = CircuitAir::new(circuit.clone());
537
538 let trace = air.generate_trace(witness, public)?;
540
541 let config = default_config();
543 let prover = StarkProver::new(config);
544
545 let proof =
547 prover
548 .prove(&air, trace, public)
549 .map_err(|e| lib_q_core::Error::InternalError {
550 operation: "STARK proof generation".to_string(),
551 details: e.to_string(),
552 })?;
553
554 let metadata = ProofMetadata::Circuit {
556 num_witnesses: witness.len().min(u32::MAX as usize) as u32,
557 num_public: public.len().min(u32::MAX as usize) as u32,
558 };
559
560 ZkpProof::from_stark_proof(&proof, metadata)
562 }
563}
564
565#[cfg(not(feature = "zkp"))]
566impl ZkpProver {
567 pub fn new() -> Self {
569 Self {}
570 }
571
572 pub fn prove_secret_value(
574 &mut self,
575 _secret_value: &[u8],
576 _public_statement: &[u8],
577 ) -> Result<ZkpProof> {
578 Err(lib_q_core::Error::NotImplemented {
579 feature: "ZKP feature not enabled".to_string(),
580 })
581 }
582
583 pub fn prove_secret_value_nist(
585 &mut self,
586 _secret_value: &[u8],
587 _public_statement: &[u8],
588 ) -> Result<ZkpProof> {
589 Err(lib_q_core::Error::NotImplemented {
590 feature: "ZKP feature not enabled".to_string(),
591 })
592 }
593}
594
595#[cfg(feature = "zkp")]
598fn verify_secret_value_nist_impl(proof: &ZkpProof, expected_hash: &[u8]) -> Result<bool> {
599 if proof.proof_type != ProofType::Stark {
600 return Ok(false);
601 }
602 if proof.data.is_empty() {
603 return Ok(false);
604 }
605
606 let ProofMetadata::HashPreimageNist { .. } = &proof.metadata else {
607 return Ok(false);
608 };
609
610 let _ = expected_hash;
615 Err(lib_q_core::Error::NotImplemented {
616 feature: "NIST (cSHAKE256) preimage proofs: Keccak-f AIR constraints not implemented"
617 .to_string(),
618 })
619}
620
621#[cfg(feature = "zkp")]
622impl ZkpVerifier {
623 pub fn new() -> Self {
625 Self {}
626 }
627
628 pub fn verify_secret_value(&self, proof: &ZkpProof, preimage: &[u8]) -> Result<bool> {
646 use crate::air::{
647 HashPreimageAir,
648 TraceGenerator,
649 };
650 use crate::stark::{
651 StarkVerifier,
652 default_config,
653 };
654
655 if proof.proof_type != ProofType::Stark {
656 return Ok(false);
657 }
658
659 if proof.data.is_empty() {
660 return Ok(false);
661 }
662
663 let ProofMetadata::HashPreimage { output_size } = &proof.metadata else {
665 return Ok(false);
667 };
668
669 let _ = output_size;
671 let air = HashPreimageAir::new();
672
673 let public_values: Vec<ZkpField> = air.public_values(&preimage.to_vec());
676
677 let stark_proof = proof.to_stark_proof()?;
679
680 let config = default_config();
682 let verifier = StarkVerifier::new(config);
683
684 match verifier.verify(&air, &stark_proof, &public_values) {
686 Ok(()) => Ok(true),
687 Err(_) => Ok(false),
688 }
689 }
690
691 pub fn verify_secret_value_nist(&self, proof: &ZkpProof, expected_hash: &[u8]) -> Result<bool> {
696 verify_secret_value_nist_impl(proof, expected_hash)
697 }
698
699 pub fn verify_computation(
713 &self,
714 proof: &ZkpProof,
715 circuit: &circuit::ArithmeticCircuit<ZkpField>,
716 public: &[ZkpField],
717 ) -> Result<bool> {
718 use crate::circuit::CircuitAir;
719 use crate::stark::{
720 StarkVerifier,
721 default_config,
722 };
723
724 if proof.proof_type != ProofType::Stark {
725 return Ok(false);
726 }
727
728 if proof.data.is_empty() {
729 return Ok(false);
730 }
731
732 let air = CircuitAir::new(circuit.clone());
734
735 let stark_proof = proof.to_stark_proof()?;
737
738 let config = default_config();
740 let verifier = StarkVerifier::new(config);
741
742 match verifier.verify(&air, &stark_proof, public) {
744 Ok(()) => Ok(true),
745 Err(_) => Ok(false),
746 }
747 }
748
749 pub fn verify(&self, proof: ZkpProof, public_statement: &[u8]) -> Result<bool> {
767 if proof.proof_type != ProofType::Stark {
768 return Ok(false);
769 }
770 if proof.data.is_empty() {
771 return Ok(false);
772 }
773 match &proof.metadata {
774 ProofMetadata::HashPreimage { .. } => {
775 self.verify_secret_value(&proof, public_statement)
776 }
777 ProofMetadata::HashPreimageNist { .. } => {
778 verify_secret_value_nist_impl(&proof, public_statement)
779 }
780 ProofMetadata::MerkleInclusion { .. } => verify_membership(&proof, public_statement),
781 ProofMetadata::UnlinkableMembership { .. } => {
782 membership::verify_unlinkable_membership_bytes(&proof, public_statement)
783 }
784 _ => Ok(false),
785 }
786 }
787
788 pub fn batch_verify(&self, proofs: &[ZkpProof], publics: &[&[u8]]) -> Result<bool> {
799 if proofs.len() != publics.len() {
800 return Err(lib_q_core::Error::InvalidState {
801 operation: "batch_verify".to_string(),
802 reason: "Number of proofs must match number of public statements".to_string(),
803 });
804 }
805
806 for (proof, public) in proofs.iter().zip(publics.iter()) {
807 match self.verify(proof.clone(), public) {
808 Ok(true) => continue,
809 Ok(false) => return Ok(false),
810 Err(e) => return Err(e),
811 }
812 }
813
814 Ok(true)
815 }
816}
817
818#[cfg(not(feature = "zkp"))]
819impl ZkpVerifier {
820 pub fn new() -> Self {
822 Self {}
823 }
824
825 pub fn verify(&self, _proof: ZkpProof, _public_statement: &[u8]) -> Result<bool> {
827 Err(lib_q_core::Error::NotImplemented {
828 feature: "ZKP feature not enabled".to_string(),
829 })
830 }
831
832 pub fn verify_secret_value_nist(
834 &self,
835 _proof: &ZkpProof,
836 _expected_hash: &[u8],
837 ) -> Result<bool> {
838 Err(lib_q_core::Error::NotImplemented {
839 feature: "ZKP feature not enabled".to_string(),
840 })
841 }
842
843 pub fn batch_verify(&self, _proofs: &[ZkpProof], _publics: &[&[u8]]) -> Result<bool> {
845 Err(lib_q_core::Error::NotImplemented {
846 feature: "ZKP feature not enabled".to_string(),
847 })
848 }
849}
850
851impl Default for ZkpProver {
852 fn default() -> Self {
853 Self::new()
854 }
855}
856
857impl Default for ZkpVerifier {
858 fn default() -> Self {
859 Self::new()
860 }
861}
862
863pub fn available_algorithms() -> Vec<&'static str> {
865 let algorithms = vec![
866 #[cfg(feature = "zkp")]
867 "stark",
868 ];
869
870 algorithms
871}
872
873pub fn create_zkp(algorithm: &str) -> Result<Box<dyn core::any::Any>> {
875 match algorithm {
876 #[cfg(feature = "zkp")]
877 "stark" => Ok(Box::new(ZkpProver::new())),
878
879 _ => Err(lib_q_core::Error::InvalidAlgorithm {
880 algorithm: "Unknown ZKP algorithm",
881 }),
882 }
883}
884
885#[cfg(test)]
886mod tests {
887 use super::*;
888
889 #[test]
890 fn test_zkp_prover_creation() {
891 let _prover = ZkpProver::new();
892 }
894
895 #[test]
896 fn test_zkp_verifier_creation() {
897 let _verifier = ZkpVerifier::new();
898 }
900
901 #[test]
902 fn test_zkp_proof_creation() {
903 let mut prover = ZkpProver::new();
904 let secret_value = b"secret_value";
905 let public_statement = b"public_statement";
906
907 let result = prover.prove_secret_value(secret_value, public_statement);
909 assert!(
911 result.is_ok(),
912 "Proof generation should succeed: {:?}",
913 result.err()
914 );
915 }
916
917 #[cfg(feature = "zkp")]
918 #[test]
919 fn test_nist_secret_value_not_implemented() {
920 let secret = b"nist_secret_value";
923 let mut prover = ZkpProver::new();
924 assert!(
925 matches!(
926 prover.prove_secret_value_nist(secret, b""),
927 Err(lib_q_core::Error::NotImplemented { .. })
928 ),
929 "NIST prove must return NotImplemented"
930 );
931
932 let verifier = ZkpVerifier::new();
933 let mut dummy = ZkpProof {
934 data: alloc::vec![1u8; 8],
935 proof_type: ProofType::Stark,
936 security_level: 1,
937 metadata: ProofMetadata::HashPreimageNist { output_size: 32 },
938 };
939 assert!(
940 matches!(
941 verifier.verify_secret_value_nist(&dummy, &[0u8; 32]),
942 Err(lib_q_core::Error::NotImplemented { .. })
943 ),
944 "NIST verify must return NotImplemented"
945 );
946 dummy.data = alloc::vec![1u8; 8];
948 assert!(
949 matches!(
950 verifier.verify(dummy, &[0u8; 32]),
951 Err(lib_q_core::Error::NotImplemented { .. })
952 ),
953 "verify() must return NotImplemented for NIST proofs"
954 );
955 }
956
957 #[cfg(feature = "zkp")]
958 #[test]
959 fn test_poseidon_proof_rejected_by_nist_verifier() {
960 let secret = b"poseidon_only";
961 let mut prover = ZkpProver::new();
962 let proof = prover
963 .prove_secret_value(secret, b"")
964 .expect("Poseidon prove");
965 let verifier = ZkpVerifier::new();
966 assert!(
967 !verifier
968 .verify_secret_value_nist(&proof, &[0u8; 32])
969 .unwrap(),
970 "Poseidon proof must not be accepted by NIST verifier"
971 );
972 }
973
974 #[cfg(feature = "zkp")]
975 #[test]
976 fn test_verify_rejects_unknown_metadata() {
977 use lib_q_stark_field::PrimeCharacteristicRing;
978 use lib_q_stark_mersenne31::Mersenne31;
979
980 use crate::air::{
981 ArithmeticAir,
982 TraceGenerator,
983 };
984 use crate::stark::{
985 StarkProver,
986 default_config,
987 };
988
989 let air = ArithmeticAir::new(1).expect("ArithmeticAir");
990 let one = <ZkpField as PrimeCharacteristicRing>::ONE;
991 let seven = ZkpField::from(Mersenne31::new(7));
992 let input = alloc::vec![(one, seven)];
993 let trace = air.generate_trace(&input).expect("trace generation");
994 let public_values = air.public_values(&input);
995 let proof_inner = StarkProver::new(default_config())
996 .prove(&air, trace, &public_values)
997 .expect("prove");
998 let proof_bytes = postcard::to_allocvec(&proof_inner).expect("serialize STARK proof");
999
1000 let proof = ZkpProof {
1001 data: proof_bytes,
1002 proof_type: ProofType::Stark,
1003 security_level: 1,
1004 metadata: ProofMetadata::None,
1005 };
1006
1007 let verifier = ZkpVerifier::new();
1008 assert_eq!(
1009 verifier.verify(proof, b"public_statement").unwrap(),
1010 false,
1011 "ProofMetadata::None must return false -- use a type-specific verifier"
1012 );
1013 }
1014
1015 #[test]
1016 fn test_batch_verify_mismatched_lengths() {
1017 let verifier = ZkpVerifier::new();
1018 let proofs = vec![ZkpProof {
1019 data: vec![0u8; 64],
1020 proof_type: ProofType::Stark,
1021 security_level: 1,
1022 metadata: ProofMetadata::None,
1023 }];
1024 let publics: &[&[u8]] = &[b"public1" as &[u8], b"public2" as &[u8]];
1025
1026 let result = verifier.batch_verify(&proofs, publics);
1027 assert!(result.is_err());
1028 if let Err(lib_q_core::Error::InvalidState { .. }) = result {
1029 } else {
1031 panic!("Expected InvalidState error");
1032 }
1033 }
1034
1035 #[cfg(feature = "zkp")]
1036 #[test]
1037 fn test_proof_metadata_merkle() {
1038 let metadata = ProofMetadata::MerkleInclusion { tree_depth: 8 };
1039 let proof = ZkpProof {
1040 data: vec![0u8; 64],
1041 proof_type: ProofType::Stark,
1042 security_level: 1,
1043 metadata,
1044 };
1045 assert_eq!(proof.merkle_tree_depth(), Some(8));
1046 }
1047
1048 #[cfg(feature = "zkp")]
1049 #[test]
1050 fn test_proof_metadata_none() {
1051 let proof = ZkpProof {
1052 data: vec![0u8; 64],
1053 proof_type: ProofType::Stark,
1054 security_level: 1,
1055 metadata: ProofMetadata::None,
1056 };
1057 assert_eq!(proof.merkle_tree_depth(), None);
1058 }
1059
1060 #[test]
1061 fn test_available_algorithms() {
1062 let algorithms = available_algorithms();
1063 #[cfg(feature = "zkp")]
1064 assert!(!algorithms.is_empty(), "zkp feature enables STARK");
1065 #[cfg(not(feature = "zkp"))]
1066 let _ = algorithms;
1067 }
1068
1069 #[cfg(feature = "zkp")]
1070 #[test]
1071 fn test_create_zkp() {
1072 let algorithms = available_algorithms();
1073 assert!(!algorithms.is_empty());
1074 let algorithm = algorithms[0];
1075 assert!(create_zkp(algorithm).is_ok());
1076 }
1077
1078 #[cfg(feature = "zkp")]
1079 #[test]
1080 fn test_verify_rejects_forged_proof_with_hash_preimage_metadata() {
1081 let proof = ZkpProof {
1082 data: alloc::vec![
1083 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD,
1084 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF,
1085 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA,
1086 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD,
1087 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF,
1088 ],
1089 proof_type: ProofType::Stark,
1090 security_level: 1,
1091 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1092 };
1093 let verifier = ZkpVerifier::new();
1094 let result = verifier.verify(proof, b"expected_hash");
1095 assert!(
1096 matches!(result, Ok(false) | Err(_)),
1097 "forged HashPreimage proof must not return Ok(true)"
1098 );
1099 }
1100
1101 #[cfg(feature = "zkp")]
1102 #[test]
1103 fn test_verify_rejects_forged_proof_with_merkle_metadata() {
1104 let proof = ZkpProof {
1105 data: alloc::vec![
1106 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE,
1107 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
1108 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE,
1109 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
1110 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
1111 ],
1112 proof_type: ProofType::Stark,
1113 security_level: 1,
1114 metadata: ProofMetadata::MerkleInclusion { tree_depth: 4 },
1115 };
1116 let verifier = ZkpVerifier::new();
1117 let result = verifier.verify(proof, b"wrong_root");
1118 assert!(
1119 matches!(result, Ok(false) | Err(_)),
1120 "forged MerkleInclusion proof must not return Ok(true)"
1121 );
1122 }
1123
1124 #[cfg(feature = "zkp")]
1125 #[test]
1126 fn test_verify_rejects_circuit_metadata_proof() {
1127 let proof = ZkpProof {
1128 data: alloc::vec![0u8; 64],
1129 proof_type: ProofType::Stark,
1130 security_level: 1,
1131 metadata: ProofMetadata::Circuit {
1132 num_witnesses: 2,
1133 num_public: 1,
1134 },
1135 };
1136 let verifier = ZkpVerifier::new();
1137 assert_eq!(
1138 verifier.verify(proof, b"anything").unwrap(),
1139 false,
1140 "Circuit proofs must be rejected by generic verify; use verify_computation"
1141 );
1142 }
1143
1144 #[cfg(feature = "zkp")]
1145 #[test]
1146 fn test_verify_rejects_credential_metadata_proof() {
1147 let proof = ZkpProof {
1148 data: alloc::vec![0u8; 64],
1149 proof_type: ProofType::Stark,
1150 security_level: 1,
1151 metadata: ProofMetadata::Credential {
1152 attribute_sizes: alloc::vec![8, 4],
1153 reveal_mask: alloc::vec![true, false],
1154 },
1155 };
1156 let verifier = ZkpVerifier::new();
1157 assert_eq!(
1158 verifier.verify(proof, b"anything").unwrap(),
1159 false,
1160 "Credential proofs must be rejected by generic verify; use ip::verify_credential_proof"
1161 );
1162 }
1163
1164 #[cfg(feature = "zkp")]
1165 #[test]
1166 fn test_verify_rejects_identity_metadata_proof() {
1167 let proof = ZkpProof {
1168 data: alloc::vec![0u8; 64],
1169 proof_type: ProofType::Stark,
1170 security_level: 1,
1171 metadata: ProofMetadata::Identity { dsa_level: 65 },
1172 };
1173 let verifier = ZkpVerifier::new();
1174 assert_eq!(
1175 verifier.verify(proof, b"anything").unwrap(),
1176 false,
1177 "Identity proofs must be rejected by generic verify; use ip::verify_it_ownership"
1178 );
1179 }
1180
1181 #[cfg(feature = "zkp")]
1182 #[test]
1183 fn test_verify_empty_data_is_rejected() {
1184 let proof = ZkpProof {
1185 data: alloc::vec![],
1186 proof_type: ProofType::Stark,
1187 security_level: 1,
1188 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1189 };
1190 let verifier = ZkpVerifier::new();
1191 assert_eq!(
1192 verifier.verify(proof, b"anything").unwrap(),
1193 false,
1194 "empty proof data must be rejected regardless of metadata"
1195 );
1196 }
1197
1198 #[cfg(feature = "zkp")]
1199 #[test]
1200 fn test_proof_type_only_stark_exists() {
1201 let _stark = ProofType::Stark;
1202 }
1205
1206 #[cfg(feature = "zkp")]
1207 #[test]
1208 fn test_batch_verify_rejects_forged_hash_preimage_proof() {
1209 let forged = ZkpProof {
1210 data: alloc::vec![0xFF; 64],
1211 proof_type: ProofType::Stark,
1212 security_level: 1,
1213 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1214 };
1215 let verifier = ZkpVerifier::new();
1216 let proofs = alloc::vec![forged];
1217 let publics: &[&[u8]] = &[b"anything"];
1218 let result = verifier.batch_verify(&proofs, publics);
1219 assert!(
1220 matches!(result, Ok(false) | Err(_)),
1221 "batch_verify must not accept a forged proof"
1222 );
1223 }
1224
1225 #[cfg(feature = "zkp")]
1226 #[test]
1227 fn test_prove_secret_value_rejects_empty_and_oversized_input() {
1228 let mut prover = ZkpProver::new();
1229 let empty = prover.prove_secret_value(b"", b"");
1230 assert!(empty.is_err());
1231
1232 let oversized = vec![0u8; air::hash_preimage::MAX_PREIMAGE_SIZE + 1];
1233 let too_large = prover.prove_secret_value(&oversized, b"");
1234 assert!(too_large.is_err());
1235 }
1236
1237 #[cfg(feature = "zkp")]
1238 #[test]
1239 fn test_prove_secret_value_nist_rejects_empty_and_oversized_input() {
1240 let mut prover = ZkpProver::new();
1241 let empty = prover.prove_secret_value_nist(b"", b"");
1242 assert!(empty.is_err());
1243
1244 let oversized = vec![0u8; air::hash_preimage_nist::MAX_PREIMAGE_SIZE + 1];
1245 let too_large = prover.prove_secret_value_nist(&oversized, b"");
1246 assert!(too_large.is_err());
1247 }
1248
1249 #[cfg(feature = "zkp")]
1250 #[test]
1251 fn test_verify_secret_value_rejects_invalid_proof_shape_inputs() {
1252 let verifier = ZkpVerifier::new();
1253 let non_stark = ZkpProof {
1254 data: vec![1u8; 16],
1255 proof_type: ProofType::Stark,
1256 security_level: 1,
1257 metadata: ProofMetadata::HashPreimageNist { output_size: 32 },
1258 };
1259 assert!(!verifier.verify_secret_value(&non_stark, b"hash").unwrap());
1260
1261 let empty = ZkpProof {
1262 data: vec![],
1263 proof_type: ProofType::Stark,
1264 security_level: 1,
1265 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1266 };
1267 assert!(!verifier.verify_secret_value(&empty, b"hash").unwrap());
1268 }
1269
1270 #[cfg(feature = "zkp")]
1271 #[test]
1272 fn test_verify_secret_value_nist_rejects_wrong_metadata_and_bad_bytes() {
1273 let verifier = ZkpVerifier::new();
1274 let wrong_meta = ZkpProof {
1275 data: vec![1u8; 16],
1276 proof_type: ProofType::Stark,
1277 security_level: 1,
1278 metadata: ProofMetadata::HashPreimage { output_size: 1 },
1279 };
1280 assert!(
1281 !verifier
1282 .verify_secret_value_nist(&wrong_meta, &[0u8; 32])
1283 .unwrap()
1284 );
1285
1286 let nist_meta = ZkpProof {
1289 data: vec![0xAA; 16],
1290 proof_type: ProofType::Stark,
1291 security_level: 1,
1292 metadata: ProofMetadata::HashPreimageNist { output_size: 32 },
1293 };
1294 assert!(matches!(
1295 verifier.verify_secret_value_nist(&nist_meta, &[0u8; 32]),
1296 Err(lib_q_core::Error::NotImplemented { .. })
1297 ));
1298 }
1299
1300 #[cfg(feature = "zkp")]
1301 #[test]
1302 fn test_verify_computation_rejects_empty_or_non_stark_data() {
1303 use crate::circuit::CircuitBuilder;
1304
1305 let verifier = ZkpVerifier::new();
1306 let circuit = CircuitBuilder::<ZkpField>::new(1, 0).build();
1307
1308 let empty = ZkpProof {
1309 data: vec![],
1310 proof_type: ProofType::Stark,
1311 security_level: 1,
1312 metadata: ProofMetadata::Circuit {
1313 num_witnesses: 1,
1314 num_public: 0,
1315 },
1316 };
1317 assert!(!verifier.verify_computation(&empty, &circuit, &[]).unwrap());
1318 }
1319}