#![cfg_attr(not(feature = "std"), no_std)]
#![allow(type_alias_bounds)]
#![deny(unsafe_code)]
#![deny(unused_qualifications)]
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::collapsible_if)]
#![allow(clippy::get_first)]
#![allow(clippy::iter_cloned_collect)]
#![allow(clippy::manual_is_multiple_of)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::unnecessary_lazy_evaluations)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::string::ToString;
#[cfg(feature = "alloc")]
use alloc::vec;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
pub use lib_q_core::Result;
#[cfg(any(
feature = "plonky",
feature = "plonky-keccak-air",
feature = "plonky-lookup",
feature = "plonky-uni-stark",
feature = "plonky-batch-stark",
))]
pub use lib_q_plonky as plonky;
#[cfg(feature = "zkp")]
pub mod stark;
#[cfg(feature = "zkp")]
pub mod circuit;
#[cfg(feature = "zkp")]
pub mod air;
#[cfg(feature = "zkp")]
pub mod aggregation;
#[cfg(feature = "zkp")]
pub mod ip;
#[cfg(feature = "zkp")]
pub mod merkle;
#[cfg(feature = "zkp")]
pub mod api;
#[cfg(feature = "zkp")]
pub use api::{
MerklePath,
build_merkle_tree,
prove_membership,
prove_membership_with_config,
prove_preimage,
prove_preimage_nist,
verify_membership,
verify_membership_with_config,
verify_membership_with_depth,
verify_membership_with_depth_and_config,
verify_preimage,
verify_preimage_nist,
};
#[cfg(feature = "wasm")]
mod wasm;
#[cfg(feature = "zkp")]
pub use lib_q_stark::{
Proof as StarkProof,
StarkConfig,
StarkGenericConfig,
check_constraints,
prove,
verify,
};
#[cfg(feature = "zkp")]
pub use lib_q_stark_air::Air;
#[cfg(feature = "zkp")]
use lib_q_stark_field::extension::Complex;
#[cfg(feature = "zkp")]
use lib_q_stark_matrix::dense::RowMajorMatrix;
#[cfg(feature = "zkp")]
use lib_q_stark_mersenne31::Mersenne31;
#[cfg(feature = "zkp")]
pub use merkle::PoseidonMerkleTree;
#[cfg(feature = "zkp")]
use serde::{
Deserialize,
Serialize,
};
#[cfg(feature = "zkp")]
#[allow(unused_imports)]
use crate::air::TraceGenerator;
#[cfg(feature = "zkp")]
pub type ZkpField = Complex<Mersenne31>;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "zkp", derive(Serialize, Deserialize))]
pub enum ProofMetadata {
#[default]
None,
MerkleInclusion {
tree_depth: u8,
},
HashPreimage {
output_size: u16,
},
HashPreimageNist {
output_size: u16,
},
Circuit {
num_witnesses: u32,
num_public: u32,
},
Credential {
attribute_sizes: Vec<u16>,
reveal_mask: Vec<bool>,
},
Identity {
dsa_level: u8,
},
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "zkp", derive(serde::Serialize, serde::Deserialize))]
pub struct ZkpProof {
pub data: Vec<u8>,
pub proof_type: ProofType,
pub security_level: u32,
pub metadata: ProofMetadata,
}
#[cfg(feature = "zkp")]
impl ZkpProof {
pub fn from_stark_proof<C: StarkGenericConfig>(
proof: &StarkProof<C>,
metadata: ProofMetadata,
) -> Result<Self>
where
StarkProof<C>: Serialize,
{
let data = postcard::to_allocvec(proof).map_err(|_| lib_q_core::Error::InternalError {
operation: "ZKP proof serialization".to_string(),
details: "Failed to serialize STARK proof".to_string(),
})?;
Ok(Self {
data,
proof_type: ProofType::Stark,
security_level: 1,
metadata,
})
}
pub fn to_stark_proof<C: StarkGenericConfig>(&self) -> Result<StarkProof<C>>
where
StarkProof<C>: for<'de> Deserialize<'de>,
{
postcard::from_bytes(&self.data).map_err(|_| lib_q_core::Error::InternalError {
operation: "ZKP proof deserialization".to_string(),
details: "Failed to deserialize STARK proof".to_string(),
})
}
pub fn merkle_tree_depth(&self) -> Option<u8> {
match &self.metadata {
ProofMetadata::MerkleInclusion { tree_depth } => Some(*tree_depth),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "zkp", derive(serde::Serialize, serde::Deserialize))]
pub enum ProofType {
Stark,
}
#[cfg(feature = "zkp")]
pub struct ZkpProver {
}
#[cfg(not(feature = "zkp"))]
pub struct ZkpProver;
#[cfg(feature = "zkp")]
pub struct ZkpVerifier {
}
#[cfg(not(feature = "zkp"))]
pub struct ZkpVerifier;
#[cfg(feature = "zkp")]
impl ZkpProver {
pub fn new() -> Self {
Self {}
}
pub fn prove_secret_value(
&mut self,
secret_value: &[u8],
_public_statement: &[u8],
) -> Result<ZkpProof> {
use crate::air::{
HashPreimageAir,
TraceGenerator,
};
use crate::stark::{
StarkProver,
default_config,
};
if secret_value.is_empty() {
return Err(lib_q_core::Error::InvalidState {
operation: "prove_secret_value".to_string(),
reason: "Secret value cannot be empty".to_string(),
});
}
if secret_value.len() > air::hash_preimage::MAX_PREIMAGE_SIZE {
return Err(lib_q_core::Error::InvalidState {
operation: "prove_secret_value".to_string(),
reason: "Secret value exceeds maximum size".to_string(),
});
}
let air = HashPreimageAir::new();
let input = secret_value.to_vec();
let trace: RowMajorMatrix<ZkpField> =
air.generate_trace(&input)
.map_err(|e| lib_q_core::Error::InternalError {
operation: "prove_secret_value".to_string(),
details: e.to_string(),
})?;
let public_values: Vec<ZkpField> = air.public_values(&input);
let config = default_config();
let prover = StarkProver::new(config);
let proof = prover.prove(&air, trace, &public_values).map_err(|e| {
lib_q_core::Error::InternalError {
operation: "STARK proof generation".to_string(),
details: e.to_string(),
}
})?;
let metadata = ProofMetadata::HashPreimage { output_size: 1u16 };
ZkpProof::from_stark_proof(&proof, metadata)
}
pub fn prove_secret_value_nist(
&mut self,
secret_value: &[u8],
_public_statement: &[u8],
) -> Result<ZkpProof> {
use crate::air::{
HASH_OUTPUT_BYTES,
HashPreimageNistAir,
TraceGenerator,
};
use crate::stark::{
StarkProver,
default_config,
};
if secret_value.is_empty() {
return Err(lib_q_core::Error::InvalidState {
operation: "prove_secret_value_nist".to_string(),
reason: "Secret value cannot be empty".to_string(),
});
}
if secret_value.len() > air::hash_preimage_nist::MAX_PREIMAGE_SIZE {
return Err(lib_q_core::Error::InvalidState {
operation: "prove_secret_value_nist".to_string(),
reason: "Secret value exceeds maximum size".to_string(),
});
}
let air = HashPreimageNistAir::new();
let input = secret_value.to_vec();
let trace: RowMajorMatrix<ZkpField> =
air.generate_trace(&input)
.map_err(|e| lib_q_core::Error::InternalError {
operation: "prove_secret_value_nist".to_string(),
details: e.to_string(),
})?;
let public_values: Vec<ZkpField> = air.public_values(&input);
let config = default_config();
let prover = StarkProver::new(config);
let proof = prover.prove(&air, trace, &public_values).map_err(|e| {
lib_q_core::Error::InternalError {
operation: "STARK proof generation (NIST)".to_string(),
details: e.to_string(),
}
})?;
let metadata = ProofMetadata::HashPreimageNist {
output_size: HASH_OUTPUT_BYTES as u16,
};
ZkpProof::from_stark_proof(&proof, metadata)
}
pub fn prove_computation(
&mut self,
circuit: &circuit::ArithmeticCircuit<ZkpField>,
witness: &[ZkpField],
public: &[ZkpField],
) -> Result<ZkpProof> {
use crate::circuit::CircuitAir;
use crate::stark::{
StarkProver,
default_config,
};
let air = CircuitAir::new(circuit.clone());
let trace = air.generate_trace(witness, public)?;
let config = default_config();
let prover = StarkProver::new(config);
let proof =
prover
.prove(&air, trace, public)
.map_err(|e| lib_q_core::Error::InternalError {
operation: "STARK proof generation".to_string(),
details: e.to_string(),
})?;
let metadata = ProofMetadata::Circuit {
num_witnesses: witness.len().min(u32::MAX as usize) as u32,
num_public: public.len().min(u32::MAX as usize) as u32,
};
ZkpProof::from_stark_proof(&proof, metadata)
}
}
#[cfg(not(feature = "zkp"))]
impl ZkpProver {
pub fn new() -> Self {
Self {}
}
pub fn prove_secret_value(
&mut self,
_secret_value: &[u8],
_public_statement: &[u8],
) -> Result<ZkpProof> {
Err(lib_q_core::Error::NotImplemented {
feature: "ZKP feature not enabled".to_string(),
})
}
pub fn prove_secret_value_nist(
&mut self,
_secret_value: &[u8],
_public_statement: &[u8],
) -> Result<ZkpProof> {
Err(lib_q_core::Error::NotImplemented {
feature: "ZKP feature not enabled".to_string(),
})
}
}
#[cfg(feature = "zkp")]
fn verify_secret_value_nist_impl(proof: &ZkpProof, expected_hash: &[u8]) -> Result<bool> {
use crate::air::{
HashPreimageNistAir,
expected_hash_to_public_values,
};
use crate::stark::{
StarkVerifier,
default_config,
};
if proof.proof_type != ProofType::Stark {
return Ok(false);
}
if proof.data.is_empty() {
return Ok(false);
}
let ProofMetadata::HashPreimageNist { .. } = &proof.metadata else {
return Ok(false);
};
let air = HashPreimageNistAir::new();
let public_values = expected_hash_to_public_values::<ZkpField>(expected_hash);
let stark_proof = match proof.to_stark_proof() {
Ok(p) => p,
Err(_) => return Ok(false),
};
let config = default_config();
let verifier = StarkVerifier::new(config);
match verifier.verify(&air, &stark_proof, &public_values) {
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
#[cfg(feature = "zkp")]
impl ZkpVerifier {
pub fn new() -> Self {
Self {}
}
pub fn verify_secret_value(&self, proof: &ZkpProof, public_hash: &[u8]) -> Result<bool> {
use lib_q_poseidon::{
Poseidon,
Poseidon128,
};
use crate::air::{
HashPreimageAir,
bytes_to_poseidon_field,
poseidon_slice_to_field,
};
use crate::stark::{
StarkVerifier,
default_config,
};
if proof.proof_type != ProofType::Stark {
return Ok(false);
}
if proof.data.is_empty() {
return Ok(false);
}
let ProofMetadata::HashPreimage { output_size } = &proof.metadata else {
return Ok(false);
};
let _ = output_size;
let air = HashPreimageAir::new();
let expected_field_elements = bytes_to_poseidon_field(public_hash);
let poseidon_hash = Poseidon128.hash(&expected_field_elements);
let public_values = poseidon_slice_to_field(&poseidon_hash);
let stark_proof = proof.to_stark_proof()?;
let config = default_config();
let verifier = StarkVerifier::new(config);
match verifier.verify(&air, &stark_proof, &public_values) {
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
pub fn verify_secret_value_nist(&self, proof: &ZkpProof, expected_hash: &[u8]) -> Result<bool> {
verify_secret_value_nist_impl(proof, expected_hash)
}
pub fn verify_computation(
&self,
proof: &ZkpProof,
circuit: &circuit::ArithmeticCircuit<ZkpField>,
public: &[ZkpField],
) -> Result<bool> {
use crate::circuit::CircuitAir;
use crate::stark::{
StarkVerifier,
default_config,
};
if proof.proof_type != ProofType::Stark {
return Ok(false);
}
if proof.data.is_empty() {
return Ok(false);
}
let air = CircuitAir::new(circuit.clone());
let stark_proof = proof.to_stark_proof()?;
let config = default_config();
let verifier = StarkVerifier::new(config);
match verifier.verify(&air, &stark_proof, public) {
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
pub fn verify(&self, proof: ZkpProof, public_statement: &[u8]) -> Result<bool> {
if proof.proof_type != ProofType::Stark {
return Ok(false);
}
if proof.data.is_empty() {
return Ok(false);
}
match &proof.metadata {
ProofMetadata::HashPreimage { .. } => {
self.verify_secret_value(&proof, public_statement)
}
ProofMetadata::HashPreimageNist { .. } => {
verify_secret_value_nist_impl(&proof, public_statement)
}
ProofMetadata::MerkleInclusion { .. } => verify_membership(&proof, public_statement),
_ => Ok(false),
}
}
pub fn batch_verify(&self, proofs: &[ZkpProof], publics: &[&[u8]]) -> Result<bool> {
if proofs.len() != publics.len() {
return Err(lib_q_core::Error::InvalidState {
operation: "batch_verify".to_string(),
reason: "Number of proofs must match number of public statements".to_string(),
});
}
for (proof, public) in proofs.iter().zip(publics.iter()) {
match self.verify(proof.clone(), public) {
Ok(true) => continue,
Ok(false) => return Ok(false),
Err(e) => return Err(e),
}
}
Ok(true)
}
}
#[cfg(not(feature = "zkp"))]
impl ZkpVerifier {
pub fn new() -> Self {
Self {}
}
pub fn verify(&self, _proof: ZkpProof, _public_statement: &[u8]) -> Result<bool> {
Err(lib_q_core::Error::NotImplemented {
feature: "ZKP feature not enabled".to_string(),
})
}
pub fn verify_secret_value_nist(
&self,
_proof: &ZkpProof,
_expected_hash: &[u8],
) -> Result<bool> {
Err(lib_q_core::Error::NotImplemented {
feature: "ZKP feature not enabled".to_string(),
})
}
pub fn batch_verify(&self, _proofs: &[ZkpProof], _publics: &[&[u8]]) -> Result<bool> {
Err(lib_q_core::Error::NotImplemented {
feature: "ZKP feature not enabled".to_string(),
})
}
}
impl Default for ZkpProver {
fn default() -> Self {
Self::new()
}
}
impl Default for ZkpVerifier {
fn default() -> Self {
Self::new()
}
}
pub fn available_algorithms() -> Vec<&'static str> {
let algorithms = vec![
#[cfg(feature = "zkp")]
"stark",
];
algorithms
}
pub fn create_zkp(algorithm: &str) -> Result<Box<dyn core::any::Any>> {
match algorithm {
#[cfg(feature = "zkp")]
"stark" => Ok(Box::new(ZkpProver::new())),
_ => Err(lib_q_core::Error::InvalidAlgorithm {
algorithm: "Unknown ZKP algorithm",
}),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zkp_prover_creation() {
let _prover = ZkpProver::new();
}
#[test]
fn test_zkp_verifier_creation() {
let _verifier = ZkpVerifier::new();
}
#[test]
fn test_zkp_proof_creation() {
let mut prover = ZkpProver::new();
let secret_value = b"secret_value";
let public_statement = b"public_statement";
let result = prover.prove_secret_value(secret_value, public_statement);
assert!(
result.is_ok(),
"Proof generation should succeed: {:?}",
result.err()
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_nist_secret_value_round_trip() {
use digest::{
ExtendableOutput,
Update,
};
use lib_q_sha3::CShake256;
use crate::air::hash_preimage_nist::CSHAKE_DOMAIN;
let secret = b"nist_secret_value";
let mut prover = ZkpProver::new();
let proof = prover
.prove_secret_value_nist(secret, b"")
.expect("prove_secret_value_nist");
let mut expected_hash = [0u8; 32];
{
let mut hasher = CShake256::new_with_function_name(&[], CSHAKE_DOMAIN);
hasher.update(secret);
hasher.finalize_xof_into(&mut expected_hash);
}
let verifier = ZkpVerifier::new();
assert!(
verifier
.verify_secret_value_nist(&proof, &expected_hash)
.unwrap(),
"NIST proof should verify with correct expected hash"
);
assert!(
verifier.verify(proof.clone(), &expected_hash).unwrap(),
"verify() must accept NIST proof with correct expected hash"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_nist_proof_rejected_by_poseidon_verifier() {
use digest::{
ExtendableOutput,
Update,
};
use lib_q_sha3::CShake256;
use crate::air::hash_preimage_nist::CSHAKE_DOMAIN;
let secret = b"nist_only";
let mut prover = ZkpProver::new();
let proof = prover
.prove_secret_value_nist(secret, b"")
.expect("NIST prove");
let mut expected_hash = [0u8; 32];
let mut hasher = CShake256::new_with_function_name(&[], CSHAKE_DOMAIN);
hasher.update(secret);
hasher.finalize_xof_into(&mut expected_hash);
let verifier = ZkpVerifier::new();
assert!(
!verifier
.verify_secret_value(&proof, &expected_hash)
.unwrap(),
"NIST proof must not be accepted by Poseidon verifier"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_poseidon_proof_rejected_by_nist_verifier() {
let secret = b"poseidon_only";
let mut prover = ZkpProver::new();
let proof = prover
.prove_secret_value(secret, b"")
.expect("Poseidon prove");
let verifier = ZkpVerifier::new();
assert!(
!verifier
.verify_secret_value_nist(&proof, &[0u8; 32])
.unwrap(),
"Poseidon proof must not be accepted by NIST verifier"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_rejects_unknown_metadata() {
use lib_q_stark_field::PrimeCharacteristicRing;
use lib_q_stark_mersenne31::Mersenne31;
use crate::air::{
ArithmeticAir,
TraceGenerator,
};
use crate::stark::{
StarkProver,
default_config,
};
let air = ArithmeticAir::new(1).expect("ArithmeticAir");
let one = <ZkpField as PrimeCharacteristicRing>::ONE;
let seven = ZkpField::from(Mersenne31::new(7));
let input = alloc::vec![(one, seven)];
let trace = air.generate_trace(&input).expect("trace generation");
let public_values = air.public_values(&input);
let proof_inner = StarkProver::new(default_config())
.prove(&air, trace, &public_values)
.expect("prove");
let proof_bytes = postcard::to_allocvec(&proof_inner).expect("serialize STARK proof");
let proof = ZkpProof {
data: proof_bytes,
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::None,
};
let verifier = ZkpVerifier::new();
assert_eq!(
verifier.verify(proof, b"public_statement").unwrap(),
false,
"ProofMetadata::None must return false -- use a type-specific verifier"
);
}
#[test]
fn test_batch_verify_mismatched_lengths() {
let verifier = ZkpVerifier::new();
let proofs = vec![ZkpProof {
data: vec![0u8; 64],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::None,
}];
let publics: &[&[u8]] = &[b"public1" as &[u8], b"public2" as &[u8]];
let result = verifier.batch_verify(&proofs, publics);
assert!(result.is_err());
if let Err(lib_q_core::Error::InvalidState { .. }) = result {
} else {
panic!("Expected InvalidState error");
}
}
#[cfg(feature = "zkp")]
#[test]
fn test_proof_metadata_merkle() {
let metadata = ProofMetadata::MerkleInclusion { tree_depth: 8 };
let proof = ZkpProof {
data: vec![0u8; 64],
proof_type: ProofType::Stark,
security_level: 1,
metadata,
};
assert_eq!(proof.merkle_tree_depth(), Some(8));
}
#[cfg(feature = "zkp")]
#[test]
fn test_proof_metadata_none() {
let proof = ZkpProof {
data: vec![0u8; 64],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::None,
};
assert_eq!(proof.merkle_tree_depth(), None);
}
#[test]
fn test_available_algorithms() {
let algorithms = available_algorithms();
#[cfg(feature = "zkp")]
assert!(!algorithms.is_empty(), "zkp feature enables STARK");
#[cfg(not(feature = "zkp"))]
let _ = algorithms;
}
#[cfg(feature = "zkp")]
#[test]
fn test_create_zkp() {
let algorithms = available_algorithms();
assert!(!algorithms.is_empty());
let algorithm = algorithms[0];
assert!(create_zkp(algorithm).is_ok());
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_rejects_forged_proof_with_hash_preimage_metadata() {
let proof = ZkpProof {
data: alloc::vec![
0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD,
0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF,
0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA,
0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD,
0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xAA, 0xDE, 0xAD, 0xBE, 0xEF,
],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::HashPreimage { output_size: 1 },
};
let verifier = ZkpVerifier::new();
let result = verifier.verify(proof, b"expected_hash");
assert!(
matches!(result, Ok(false) | Err(_)),
"forged HashPreimage proof must not return Ok(true)"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_rejects_forged_proof_with_merkle_metadata() {
let proof = ZkpProof {
data: alloc::vec![
0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE,
0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE,
0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE, 0xCA, 0xFE, 0xBA, 0xBE,
],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::MerkleInclusion { tree_depth: 4 },
};
let verifier = ZkpVerifier::new();
let result = verifier.verify(proof, b"wrong_root");
assert!(
matches!(result, Ok(false) | Err(_)),
"forged MerkleInclusion proof must not return Ok(true)"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_rejects_circuit_metadata_proof() {
let proof = ZkpProof {
data: alloc::vec![0u8; 64],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::Circuit {
num_witnesses: 2,
num_public: 1,
},
};
let verifier = ZkpVerifier::new();
assert_eq!(
verifier.verify(proof, b"anything").unwrap(),
false,
"Circuit proofs must be rejected by generic verify; use verify_computation"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_rejects_credential_metadata_proof() {
let proof = ZkpProof {
data: alloc::vec![0u8; 64],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::Credential {
attribute_sizes: alloc::vec![8, 4],
reveal_mask: alloc::vec![true, false],
},
};
let verifier = ZkpVerifier::new();
assert_eq!(
verifier.verify(proof, b"anything").unwrap(),
false,
"Credential proofs must be rejected by generic verify; use ip::verify_credential_proof"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_rejects_identity_metadata_proof() {
let proof = ZkpProof {
data: alloc::vec![0u8; 64],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::Identity { dsa_level: 65 },
};
let verifier = ZkpVerifier::new();
assert_eq!(
verifier.verify(proof, b"anything").unwrap(),
false,
"Identity proofs must be rejected by generic verify; use ip::verify_it_ownership"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_empty_data_is_rejected() {
let proof = ZkpProof {
data: alloc::vec![],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::HashPreimage { output_size: 1 },
};
let verifier = ZkpVerifier::new();
assert_eq!(
verifier.verify(proof, b"anything").unwrap(),
false,
"empty proof data must be rejected regardless of metadata"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_proof_type_only_stark_exists() {
let _stark = ProofType::Stark;
}
#[cfg(feature = "zkp")]
#[test]
fn test_batch_verify_rejects_forged_hash_preimage_proof() {
let forged = ZkpProof {
data: alloc::vec![0xFF; 64],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::HashPreimage { output_size: 1 },
};
let verifier = ZkpVerifier::new();
let proofs = alloc::vec![forged];
let publics: &[&[u8]] = &[b"anything"];
let result = verifier.batch_verify(&proofs, publics);
assert!(
matches!(result, Ok(false) | Err(_)),
"batch_verify must not accept a forged proof"
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_prove_secret_value_rejects_empty_and_oversized_input() {
let mut prover = ZkpProver::new();
let empty = prover.prove_secret_value(b"", b"");
assert!(empty.is_err());
let oversized = vec![0u8; air::hash_preimage::MAX_PREIMAGE_SIZE + 1];
let too_large = prover.prove_secret_value(&oversized, b"");
assert!(too_large.is_err());
}
#[cfg(feature = "zkp")]
#[test]
fn test_prove_secret_value_nist_rejects_empty_and_oversized_input() {
let mut prover = ZkpProver::new();
let empty = prover.prove_secret_value_nist(b"", b"");
assert!(empty.is_err());
let oversized = vec![0u8; air::hash_preimage_nist::MAX_PREIMAGE_SIZE + 1];
let too_large = prover.prove_secret_value_nist(&oversized, b"");
assert!(too_large.is_err());
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_secret_value_rejects_invalid_proof_shape_inputs() {
let verifier = ZkpVerifier::new();
let non_stark = ZkpProof {
data: vec![1u8; 16],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::HashPreimageNist { output_size: 32 },
};
assert!(!verifier.verify_secret_value(&non_stark, b"hash").unwrap());
let empty = ZkpProof {
data: vec![],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::HashPreimage { output_size: 1 },
};
assert!(!verifier.verify_secret_value(&empty, b"hash").unwrap());
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_secret_value_nist_rejects_wrong_metadata_and_bad_bytes() {
let verifier = ZkpVerifier::new();
let wrong_meta = ZkpProof {
data: vec![1u8; 16],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::HashPreimage { output_size: 1 },
};
assert!(
!verifier
.verify_secret_value_nist(&wrong_meta, &[0u8; 32])
.unwrap()
);
let malformed_stark = ZkpProof {
data: vec![0xAA; 16],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::HashPreimageNist { output_size: 32 },
};
assert!(
!verifier
.verify_secret_value_nist(&malformed_stark, &[0u8; 32])
.unwrap()
);
}
#[cfg(feature = "zkp")]
#[test]
fn test_verify_computation_rejects_empty_or_non_stark_data() {
use crate::circuit::CircuitBuilder;
let verifier = ZkpVerifier::new();
let circuit = CircuitBuilder::<ZkpField>::new(1, 0).build();
let empty = ZkpProof {
data: vec![],
proof_type: ProofType::Stark,
security_level: 1,
metadata: ProofMetadata::Circuit {
num_witnesses: 1,
num_public: 0,
},
};
assert!(!verifier.verify_computation(&empty, &circuit, &[]).unwrap());
}
}