CRUMB a card from devarno-cloud

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>:

FieldMeaning
proof_idCopied from proof.proof_id
verifiedOutcome of verify_components
timestamp_msCopied from proof.timestamp_ms
verification_time_msWall 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 VerificationResult on 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 == 5
assert_eq!(stats.verified_count + stats.failed_count, 5);

neighbors on the map