NFC passport · Halo2 · Orchard
Research preview · Circuits in developmentProve who you are without saying who you are.
ZK ID reads the NFC chip of an electronic passport (ICAO 9303), generates a Halo2 proof of the attributes you choose to share — age, country, validity — and anchors the nullifier in an Orchard memo. Name, photo, and number stay in your pocket.
RFC published 2026-04-20. The MVP covers ECDSA P-256 and brainpoolP256r1 (~80% of post-2015 passports). RSA-2048 and private face match land in phase 2.
Claims
Four proofs, one authentic source
Each claim is an independent circuit composed with PassiveAuth by the holder. You pick what to reveal — the verifier accepts the proof without ever seeing the MRZ.
Proof of age
MVPProves age ≥ N (e.g., 18, 21) without revealing the birth date.
Private: MRZ. Public: min_age, current_date, nullifier.Proof of country
Phase 2Proves the issuing country ∈ allowlist without revealing which country.
Private: DSC issuer. Public: country_merkle_root, nullifier.Non-sanction
Phase 2Proves non-inclusion in an OFAC/UN list via Merkle proof.
Private: MRZ hash. Public: sanction_merkle_root, nullifier.Proof of uniqueness
MVPStable nullifier per document — the same passport always generates the same nullifier, different ones differ. Anti-sybil by construction.
Private: MRZ + DSC issuer. Public: nullifier.
How it works
From the NFC chip to the Orchard anchor
Five steps, three actors — holder, prover, and verifier — ending in an on-chain receipt any Zcash node can check.
- Holder
Scans the NFC chip
Mobile app reads DG1 (MRZ), DG2 (photo, optional), SOD (passport signature).
- Prover
Passive Authentication
Validates the CSCA → DSC → SOD chain. If the country hasn't signed, the circuit rejects.
- Holder
Picks the claim
Age ≥ 18? Nationality? Uniqueness? Each claim is a composable sub-circuit.
- Prover
Generates a Halo2 proof
AggregationCircuit combines PassiveAuth + chosen claim. Proof size ~8KB.
- Verifier
Anchors and validates
Publishes the nullifier in an Orchard memo; verifier dApp checks inclusion via lightwalletd.
Stack
Same base as ZK Credit — Halo2 + IPA + Orchard
Full reuse with the rest of the workspace: same zkcgz binary, same pasta_curves primitives, same Orchard anchor. No trusted setup.
- Proving systemHalo2 + IPA
pasta_curves, trustless, aligned with Orchard. zcash/halo2 fork already consumed in Cargo.toml.
- ECDSA in circuithalo2-lib (axiom-crypto)
secp256r1 (P-256) and brainpoolP256r1 in MVP. RSA-2048 via bignum in phase 2.
- On-chain anchorOrchard memo (512B)
Poseidon(nullifier, claim_hash, anchor_height) published as a shielded memo.
- Prover hostingCloud (MVP) → on-device (phase 2)
MVP: server sees MRZ in plaintext. Post-MVP: WASM halo2_proofs on phone, zero exposure.
Roadmap
From RFC to E2E demo
Eight phases, starting with the web route (this page) through a testnet demo with a real passport.
- I.1
RFC approved + /id route
- I.2
PassiveAuthCircuit ECDSA P-256 (halo2-lib)
- I.3
ProofOfAgeCircuit + composition
- I.4
Cloud prover + CSCA trust root seeding
- I.5
/id/scan and /id/verify surfaces + QR handshake
- I.6
Mobile app MVP (iOS + Android)
- I.7
Testnet E2E with a real passport
- I.8
Multi-country: BR, US, DE, PT
Out of scope
What the ZK ID MVP does not ship
Explicit RFC decisions to keep the MVP viable within hackathon time.
- Biometrics / private FaceMatch (requires TEE/MPC).
- Non-eMRTD IDs: driver's license, mDL ISO 18013-5 (separate RFC).
- gov.br / ICP-Brasil integration (future complement, not a replacement for NFC).
- BAC/PACE protocols in circuit — derived off-circuit via MRZ.