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:

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 Merkle tree. (~857 certs)
root hash committed on-chain

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
signs

DS Document Signer certificate

  • Rotated every few months
  • One DS cert signs thousands of passports
  • Hash registered in our CertificatesSMT once
signs SOD

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

  1. The SOD signature is valid under the DS certificate's public key.
  2. The DS certificate's signature is valid under some CSCA's public key.
  3. That CSCA is present in our committed ICAO Merkle tree at root 0x8aebf998... (or whatever the contract has accepted).
  4. The MRZ data (DG1) hashes match the SOD entry for DG1.
  5. The birth date in DG1 makes the user old enough.
  6. The expiry date in DG1 is in the future.
  7. 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 triesWhy it fails
Edit DG1 / DG2 / DG13 in the chip dump before scanningSOD hash mismatch — circuit's hash check rejects.
Substitute their own SOD with handcrafted DG hashesSOD signature fails under DS public key.
Forge a DS certificateNot signed by any CSCA → step 2 of the circuit fails.
Submit a real document but lie about birth dateDG1 inputs to the circuit are bound to the SOD hashes — date is the real one.
Reuse another person's proofBound to a wallet/secret they don't control; nullifier mismatch on first attempt.
Vote twice on the same proposalSame nullifier; contract refuses second vote.
Replay a vote across proposalsDifferent event ID → different nullifier; but the user is only "the same person" anonymously, by design.
Run a modified app that skips checksThe checks live in the circuit, not the app code. A modified app produces invalid proofs.
Patch the proof bytes in transitPairing equation breaks — verifier rejects.
Pay someone to register on their behalfPossible 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:

  1. Sign a fresh DS certificate.
  2. Use that DS to sign a fake SOD with arbitrary DG1 content (made-up name, made-up birth date, made-up citizenship).
  3. 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:

MitigationStatusWhat it does
Time-locked ICAO snapshotPlan for M6Pin 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-checkFutureFor 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 detectionFutureTrack DS certificate issuance volume. Sudden spikes are a signal. Off-chain analysis, on-chain transparency.
Public transparency logPlanEvery 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:

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:

This is the same threat model as banking apps, WhatsApp, or any other device-bound credential. Mitigations are platform-level:

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:

  1. 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.
  2. 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.
  3. A rooted or malware-infected user device is outside cryptographic scope. We rely on Apple's / Google's attestation, not on our own crypto.
  4. 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:

We keep the chain (Rarimo L2). We replace the contracts.

Glossary

TermMeaning
MRZMachine-readable zone — the OCR-friendly text on a passport / ID. Used to derive NFC session keys.
BAC / PACETwo protocols for establishing an encrypted session with the chip. PACE is newer; PACE-CAM is the INID variant.
SODSecurity Object Document — signed hash list of all data groups in the chip.
DG1, DG2, DG13, DG15Specific data files inside the chip. DG1 = MRZ data, DG2 = face image, DG13 = INID personal info, DG15 = active-authentication public key.
CSCACountry Signing Certificate Authority — top of a country's e-passport PKI.
DSDocument Signer — short-lived cert that signs each individual passport's SOD.
ICAO PKDPublic Key Directory maintained by the UN ICAO agency — the global list of CSCAs.
CertificatesSMTOn-chain Sparse Merkle Tree of registered DS certificate hashes.
RegistrationSMTOn-chain Sparse Merkle Tree of identity commitments.
NullifierAnti-replay value, per event. See zero-knowledge proofs.