Verifier & Verification Protocol
aegis advanced 6 min read
ELI5
The Verifier is a customs officer who checks three wax seals on a package without opening it. They don’t know what’s inside; they just confirm the seal pattern is mathematically consistent with the declared sender and recipient stamps. If any stamp is missing or mismatched, the package is refused — no questions asked.
Technical Deep Dive
Verifier in src/verifier.rs is the server-blind verification component. It receives a ZKProof plus two external commitments and decides whether the proof is consistent — never seeing the underlying secrets.
verify_zk_proof Flow
sequenceDiagram participant Caller participant Verifier Caller->>Verifier: verify_zk_proof(proof, user_commitment, doc_commitment) Verifier->>Verifier: proof.is_valid()? alt proof invalid Verifier-->>Caller: Ok(false) end Verifier->>Verifier: user_commitment.is_empty() || doc_commitment.is_empty()? alt either empty Verifier-->>Caller: Ok(false) end Verifier->>Verifier: verify_components(proof, user_commitment, doc_commitment) Verifier->>Verifier: append VerificationResult (elapsed ms) Verifier-->>Caller: Ok(verified: bool)The method always returns Ok(...) — it never short-circuits to Err. Error variants in AegisErrorType::VerificationFailed would need to be mapped by the caller wrapping this in AegisSystem::verify_access.
verify_components (Simplified Pairing Check)
The production comment notes that real Groth16 verifies e(A, B) = e(α, β) + e(C, δ). The current implementation uses:
xor_a = proof.commitment_a[0] ^ user_commitment[0 % user_commitment.len()]xor_b = proof.commitment_b[0] ^ doc_commitment[0 % doc_commitment.len()]verified = (xor_a == xor_b) || (xor_a.wrapping_add(1) == xor_b)This passes if commitment_a[0] and commitment_b[0] are zero (the default), making any non-empty user_commitment and doc_commitment whose first bytes are equal or differ by 1 produce Ok(true).
VerificationResult
Each call appends a VerificationResult to results: Vec<VerificationResult>:
| Field | Meaning |
|---|---|
proof_id | Copied from proof.proof_id |
verified | Outcome of verify_components |
timestamp_ms | Copied from proof.timestamp_ms |
verification_time_ms | Wall clock elapsed during this call |
last_result() clones the tail element; stats() returns aggregate counts and average elapsed time.
Capacity Note
Results are appended indefinitely with no eviction. Long-lived Verifier instances accumulate all past results in memory. clear() resets the entire history.
Key Terms
- Verifier → Server-blind ZK verifier; defined in
src/verifier.rs - verify_zk_proof → Entry point; records
VerificationResulton every call - verify_components → Simplified XOR consistency check (placeholder for full Groth16 pairing)
- VerificationResult → Per-call record: proof_id, verified flag, timestamps
- server-blind → Verifier never accesses plaintext; only proof components and commitments
Q&A
Q: commitment_a and commitment_b default to vec![0u8; 32] — does that make the XOR check pass trivially?
A: Yes. When both commitments are zero, xor_a = 0 ^ user_commitment[0] and xor_b = 0 ^ doc_commitment[0]. If the first bytes of user_commitment and doc_commitment are equal or differ by 1, verify_components returns true. This is a known placeholder — not production security.
Q: Does verify_zk_proof modify the ZKProof it receives?
A: No. It takes proof: &ZKProof (shared reference) and does not mutate it.
Q: failed_count() + verified_count() — do they always sum to verification_count()?
A: Yes. Every VerificationResult is either verified = true or verified = false, so the two counts partition the total.
Examples
Check verification stats after a batch:
let mut verifier = Verifier::new();for i in 0..5 { let proof = ZKProof::new(&format!("p{}", i)).with_inputs(vec![vec![1]]); verifier.verify_zk_proof(&proof, &[1u8], &[1u8]).unwrap();}let stats = verifier.stats();assert_eq!(stats.total_verifications, 5);// verified_count + failed_count == 5assert_eq!(stats.verified_count + stats.failed_count, 5);neighbors on the map
- FNP Halo2 Zero-Knowledge Circuits understanding zero-knowledge proofs in FNP
- FNP End-to-End Encryption & Zero Trust Architecture understanding FNP's security layers
- ZKProof & ZKEngine tracing why a ZK proof fails verify_structure but not verify_zk_proof
- Commitment Scheme & CommitmentTypes designing a new AEGIS commitment for a previously untracked attribute