Explainer 04
The passport trust chain (and where it can be broken)
This is the most important file in this folder. Everything else in the platform sits on top of one assumption: that the document a user scans is real, and that "real" is something we can verify without trusting the user, the phone, or any single company.
This file explains how we get to that assumption — and where it stops being true.
What an e-passport / INID is, physically
Modern biometric travel documents and Iran's national ID smart card contain a tiny computer (an "ICC", integrated-circuit chip) accessed over NFC. The chip stores:
- A set of Data Groups — DG1 (machine-readable zone), DG2 (face image), DG13 (personal data on INID), DG15 (active-authentication key on passports that have it), and others.
- A Security Object Document (SOD) — a structure that hashes every data group and is signed by a Document Signer (DS) certificate.
- For some cards, an authentication certificate with a private key, used in a challenge-response handshake to prove the chip is not a clone.
Reading the chip requires the MRZ (machine-readable zone, printed on page 2 of a passport / front of an INID) to derive the session keys (via BAC or PACE). That is why our app asks the user to first scan the page with their camera before tapping the chip.
The certificate chain — top to bottom
ICAO PKD (Public Key Directory)
- Maintained by the International Civil Aviation Organization, a UN specialized agency.
- Contains the public keys of every country's CSCA.
- We snapshot it into a
Merkletree. (~857 certs)
CSCA — Country Signing Certificate Authority
- One or more per country (Iran ~3, Germany 17)
- Long-lived (~10 years), kept in HSM by issuer
- Signs the DS certs
DS — Document Signer certificate
- Rotated every few months
- One DS cert signs thousands of passports
- Hash registered in our
CertificatesSMTonce
SODSOD — Security Object inside the chip
- Contains DG hashes; signed by DS
- This is what the ZK circuit ultimately verifies
For Iran specifically, the issuer is National Organization for Civil Registration; CSCAs are operated by the state, DS certificates rotate, and both passports and INIDs participate in the same ICAO PKI scheme.
For INIDs specifically, the chip is built and software-managed by Matiran Co. The chip exposes two certificates (signing + authentication), uses PACE-CAM rather than BAC, and proves it is not a clone via a challenge-response with a private key held inside the chip's secure element. There is no DG15 file, but the anti-clone property is preserved by the auth-certificate mechanism. (Source: our reverse-engineering work in INIDOSDK.)
What the ZK circuit actually checks
Inside the circuit (≈5 million constraints), the user's phone proves that all of the following hold simultaneously:
- The SOD signature is valid under the DS certificate's public key.
- The DS certificate's signature is valid under some CSCA's public key.
- That CSCA is present in our committed ICAO Merkle tree at root
0x8aebf998...(or whatever the contract has accepted). - The MRZ data (DG1) hashes match the SOD entry for DG1.
- The birth date in DG1 makes the user old enough.
- The expiry date in DG1 is in the future.
- The nullifier was correctly derived from the user's secret + event ID.
Only outputs (4 categories, no raw data) leave the proof: nullifier, ICAO root, citizenship code, dates.
What a user cannot fake
This is the strong-statement column. None of these attacks work against our system:
| Attack the user tries | Why it fails |
|---|---|
| Edit DG1 / DG2 / DG13 in the chip dump before scanning | SOD hash mismatch — circuit's hash check rejects. |
| Substitute their own SOD with handcrafted DG hashes | SOD signature fails under DS public key. |
| Forge a DS certificate | Not signed by any CSCA → step 2 of the circuit fails. |
| Submit a real document but lie about birth date | DG1 inputs to the circuit are bound to the SOD hashes — date is the real one. |
| Reuse another person's proof | Bound to a wallet/secret they don't control; nullifier mismatch on first attempt. |
| Vote twice on the same proposal | Same nullifier; contract refuses second vote. |
| Replay a vote across proposals | Different event ID → different nullifier; but the user is only "the same person" anonymously, by design. |
| Run a modified app that skips checks | The checks live in the circuit, not the app code. A modified app produces invalid proofs. |
| Patch the proof bytes in transit | Pairing equation breaks — verifier rejects. |
| Pay someone to register on their behalf | Possible socially, but no different from selling your real-world passport — not solvable by software. |
The cryptographic guarantee is strong: everything that ends up on-chain is something the issuer signed. Nothing the user could invent.
What an attacker can do — the honest section
This is the column we have to be honest about. Three real attacks remain, and two of them are shared by every passport-ZK system in the world.
Attack 1: Government with CSCA private key mints fake identities
The Iranian state (or anyone with access to a CSCA HSM) can:
- Sign a fresh DS certificate.
- Use that DS to sign a fake SOD with arbitrary DG1 content (made-up name, made-up birth date, made-up citizenship).
- Hand that fake document to a paid actor whose phone produces a perfectly valid ZK proof.
The proof will pass our circuit and our contract because the math is sound: the document is signed by a real CSCA. The ICAO tree itself doesn't distinguish "real human" from "valid signature on a made-up record."
This is a limit shared by every passport-ZK system. ZKPassport, Rarimo / FreedomTool, World ID's passport flow, and ours — all of us inherit this. The state is the root of trust, and if it cheats at the root, no downstream cryptography can detect it.
What we can do about it, ranked from cheap to expensive:
| Mitigation | Status | What it does |
|---|---|---|
| Time-locked ICAO snapshot | Plan for M6 | Pin a CSCA-tree root from a date before adversarial behaviour is suspected. New CSCAs added by the regime after that date are simply not in our tree. |
| Multi-issuer cross-check | Future | For high-stakes votes, require two distinct issuers (e.g., passport + INID, or Iranian + another country for diaspora). Both issuers would have to collude. |
| Statistical anomaly detection | Future | Track DS certificate issuance volume. Sudden spikes are a signal. Off-chain analysis, on-chain transparency. |
| Public transparency log | Plan | Every registered DS cert is on-chain. A coordinated bulk-mint becomes visible. |
| On-device face match + liveness (Bionetta-style ZKML) | M7 (planned) | Force the prover to bind registration to a live biometric. Fake documents would require real faces, which scales poorly for an adversary. |
We name this limit publicly because credibility requires it. Nobody else in the space is willing to put it this plainly; we will be.
Attack 2: Coordinated faction of legitimate users
A regime-aligned (or otherwise organized) group of real Iranian citizens with real passports / INIDs registers honestly and then floods the platform with content that:
- incites violence,
- denies documented atrocities,
- dehumanizes minorities,
- normalizes torture or enforced disappearance.
This is not a cryptographic problem. Identity proofs alone don't stop it, because everyone involved has a legitimate identity. This is exactly the attack the normative compliance layer is designed to resist — see two gates: identity and speech. We use an LLM that evaluates content against 11 UN human-rights conventions before publication. It is not a toxicity filter; it is a structural defense against capture.
Attack 3: Compromised user device
If an attacker has root on the user's phone — through malware, a custom flashed OS, or physical access — they can:
- read the wallet's private key,
- generate proofs as the user,
- vote as the user.
This is the same threat model as banking apps, WhatsApp, or any other device-bound credential. Mitigations are platform-level:
- iOS App Attest / Android Play Integrity gate, mandatory in production
- secure-storage (Keychain / Keystore) for the wallet key
- biometric / PIN unlock before signing
- session-bound nonces with short expiry
We don't claim to solve this. We claim that any attacker willing to individually compromise devices cannot scale beyond a small number of users.
What the platform does not protect against — explicit list
This is the same content as the table above, restated as plain claims, so it appears verbatim in our public materials:
- A state actor with the CSCA signing key can mint fake identities that our system cannot distinguish from real ones. We mitigate operationally; we don't eliminate.
- Real people voting with real documents according to coordinated instructions is something identity systems cannot solve. The normative compliance layer addresses content; vote choice itself is — and must be — free.
- A rooted or malware-infected user device is outside cryptographic scope. We rely on Apple's / Google's attestation, not on our own crypto.
- An attacker with physical access to the user's unlocked phone can act as that user during that session.
How the trust chain comes together with M6
Today, the ICAO tree, the CertificatesSMT, the RegistrationSMT, and the dispatchers that decode certificate types are all deployed by Rarimo. We use their contracts. That is fine for now, but it means someone else admins our trust roots.
After M6, we deploy our own Registration2, our own StateKeeper, our own SMTs, and our own dispatchers — still on Rarimo L2 — with our own ICAO snapshot. This gives us:
- the ability to pin the CSCA tree at a date we choose,
- the ability to add or remove CSCAs based on our own diligence,
- independence from Rarimo's governance decisions on dispatcher registry,
- a clean security boundary: Jomhoor is the sole admin of Jomhoor's trust.
We keep the chain (Rarimo L2). We replace the contracts.
Glossary
| Term | Meaning |
|---|---|
| MRZ | Machine-readable zone — the OCR-friendly text on a passport / ID. Used to derive NFC session keys. |
| BAC / PACE | Two protocols for establishing an encrypted session with the chip. PACE is newer; PACE-CAM is the INID variant. |
| SOD | Security Object Document — signed hash list of all data groups in the chip. |
| DG1, DG2, DG13, DG15 | Specific data files inside the chip. DG1 = MRZ data, DG2 = face image, DG13 = INID personal info, DG15 = active-authentication public key. |
| CSCA | Country Signing Certificate Authority — top of a country's e-passport PKI. |
| DS | Document Signer — short-lived cert that signs each individual passport's SOD. |
| ICAO PKD | Public Key Directory maintained by the UN ICAO agency — the global list of CSCAs. |
| CertificatesSMT | On-chain Sparse Merkle Tree of registered DS certificate hashes. |
| RegistrationSMT | On-chain Sparse Merkle Tree of identity commitments. |
| Nullifier | Anti-replay value, per event. See zero-knowledge proofs. |