Zero-knowledge proofs have improved privacy by letting users prove facts about themselves without exposing raw data. ZKPassport technology applies this idea to the most widely trusted documents on earth: biometric passports and government IDs. The result is a reusable, privacy-preserving credential that can be verified onchain or offchain by decentralized applications.
This article analyzes how ZKPassport systems operate today and the trade‑offs they entail. It also documents a hands‑on demonstration using ZKPassport (via the ZKPassport app) to show the flow end‑to‑end. References include the ZKPassport intro and documentation and background on biometric passports and registry design.
Passports are globally standardized under ICAO’s Machine Readable Travel Documents framework. Each biometric passport embeds an NFC chip that stores the Machine‑Readable Zone (MRZ): identity fields, the expiration date, and the issuing authority’s digital signature.
The chip is divided into data groups, each storing specific categories of information according to the ICAO 9303 standard. The most relevant for this discussion is DG1, which contains identity information such as the holder’s name and date of birth.
A verifier can read the chip and validate the signature, ensuring the data has not been forged. ZKPassport projects build on that strong public‑key infrastructure to prove statements like “over 18” or “citizen of X” without revealing the full document.
At a high level, the user performs a one‑time setup in the mobile app, then produces lightweight proofs on demand:
First, during onboarding, the ZKPassport app scans the NFC chip, reads the MRZ, and generates a zero‑knowledge proof that the passport (or ID) is valid. Implementations typically register a hashed commitment in a shared onchain Merkle tree or registry. Because many users and dapps share this tree, it creates a “privacy network effect”: as the tree grows, it becomes harder to correlate any single hash to a specific real‑world person.
Later, when a dapp requests verification, the app constructs a new proof of inclusion in the Merkle tree (and optionally other predicates like age or nationality) and returns it for verification onchain or offchain. This avoids re‑examining the entire passport each time, keeping subsequent proofs fast and cheap.
Many implementations balance device limits with privacy by combining on‑device proof generation with efficient verification. Some systems describe two modes: a full mode that proves both issuer‑signature validity and MRZ hash integrity on‑device, and a light mode that proves hash integrity locally while delegating signature checking to a trusted verifier due to smartphone constraints. ZKPassport focuses on local proof generation and provides verifier details for onchain checks where needed.
The demonstration uses the ZKPassport app and SDK to gate Safe recovery in a decentralized application.
The process begins with a one‑time setup on a smartphone: the ZKPassport app is installed, the passport is tapped to the NFC reader, and the MRZ fields are read. The app produces an initial zero‑knowledge proof and registers the document’s commitment in a shared registry. Thereafter, proof requests can be satisfied without rescanning the document.
On the application side, the SDK generates a QR code (query URL). Scanning it in the ZKPassport app prompts the user to approve generation of a fresh proof of inclusion and the requested predicate (e.g., “proof of personhood” or “over 18”). The SDK exposes onchain verifier details for EVM networks (for example, a verifier contract on Ethereum Sepolia), which are used to verify proofs onchain. After verification, the recovery module’s register
function is invoked to bind the proof to a Safe, and later the recover
function rotates ownership to a new address upon successful verification. On modern phones, the round trip consistently completes in under a minute.
Smart‑Contract integration:ZKPassportSafeRecovery.sol
and personhood binding
The recovery module implements a minimal binding between a Safe and the personhood identifier produced by ZKPassport. ZKPassport provides a domain‑ and scope‑scoped unique identifier that is stable for the same ID yet reveals no personal attributes. The contract verifies a proof via the ZKPassport verifier and persists only this identifier.
Relevant source code: ZKPassportSafeRecovery.sol
Below is a flow-chart describing the 3 main phases of the recovery.
Minimal disclosure: persisting only a bytes32
identifier avoids onchain storage of personally identifiable information (PII) such as name, birth date, nationality, or document numbers.
Scope and domain scoping: the identifier is scoped to an application domain and (optionally) a request scope, reducing cross‑site linkability.
Dictionary‑attack resistance: storing raw or hashed MRZ fields invites correlation and dictionary attacks; a scoped, opaque identifier plus a growing shared registry improves plausible deniability.
Principle of least privilege: recovery requires a stable binding between an account and a personhood token, not full identity attributes. Any additional attribute should be proven at query time via selective disclosure circuits rather than recorded onchain.
In the referenced architecture, the passport is verified onchain (via ZK) once and a commitment to DG1 data is stored, a hash derived from fields such as full name and date of birth. Subsequent queries operate against that commitment and reveal only what is needed. For example, a circuit can prove “age ≥ 18” without exposing the actual birthday, or prove “is citizen of country X” without disclosing the entire MRZ.
This approach has important advantages. There is no single universal circuit that covers every passport format; instead, a set of heavier, one‑time verification circuits per format is maintained to normalize data into the same DG1 commitment. After that, lightweight query circuits can be reused across all participants. Commitments such as hash(full‑name + date‑of‑birth + user‑defined secret) are also feasible to bind a user‑chosen factor without leaking raw identity. Note that some attributes, for instance, place of birth are not recorded in DG1 and would require a different data group.
ZKPassport provide strong proof-of-personhood. But a naïve “proof of identity” that reveals or deterministically links full name, date of birth, or document number will effectively dox the user. The way to avoid this is to design custom query circuits that only disclose the minimum necessary predicate and never release the underlying DG1 values. The same caution applies to other projects in the space; without selective disclosure, identity proofs quickly become privacy disclosures.
In practice, you can bind a Safe to a scoped, opaque identifier derived from a passport commitment, allowing the same person to re-prove control later without revealing name, date of birth, or document numbers. Predicate proofs such as age, citizenship, or “same person in this domain” are feasible via selective-disclosure circuits against the DG1 commitment or registry inclusion.
The onchain verification path is straightforward:
The mobile app produces a proof.
The verifier contract validates it.
The recovery module checks the identifier and performs the owner rotation.
Designs that reveal raw MRZ fields or stable hashes enable tracking and doxxing and should be avoided. Proofs must not be reusable; each request needs a fresh nonce or challenge and single-use semantics to prevent replay.
Possession of a valid document is not liveness, so treat liveness as a separate factor when risk demands it. Finally, document renewal changes identifiers; without a linking flow, users risk lockout at renewal time.
Robust recovery hinges on selective-disclosure circuits that prove only the predicate needed (for example, a personhood token scoped to this app) and never expose DG1 values. Identifiers should be scoped by domain and, where appropriate, by request to prevent cross-app linkage.
Every proof must be:
Bound to a single-use nonce.
Protected against replay on-chain.
For higher-value recoveries, add liveness or a second factor such as a PIN, password, TOTP, or WebAuthn. Provide a user-initiated renewal or rotation flow that links old and new document identifiers while both remain valid.
Operationally, apply:
Timelocks or delays.
Clear revocation and update procedures.
Use personhood/predicate proofs to bind a Safe to “the same human in this domain,” not to their real-world identity. Never put MRZ fields (or hashes of them) on-chain. Combine scoped identifiers, one-time challenges, and optional liveness/MFA for recovery. Plan for document renewal with a linking flow.
This delivers practical, privacy-preserving recovery without doxxing.
The core components are a mobile wallet or app that scans the document, derives a commitment, and generates selective‑disclosure proofs; a shared registry or verifier that exposes on‑chain verification (contract and ABI) and provides helpers for bind/recover inputs; a recovery module that stores only a scoped personhood identifier and swaps owners once verification succeeds; a challenge issuer (backend or contract) that serves fresh nonces and enforces single‑use semantics; optional liveness or a second factor for high‑value recoveries; and a renewal or rotation flow that links old and new document identifiers while both are valid.
Registration begins when the Safe owner scans a QR code. The app proves personhood, the verifier validates the proof, and the recovery module stores the scoped identifier. For each recovery request, the dapp issues a fresh nonce bound to the Safe and the action. The app generates a predicate proof tied to that nonce and the scoped identifier. The contract then checks the identifier match, rejects replays, applies any required second factor or liveness, and executes the owner swap.
In production, add timelocks or delays with cancellation by the current owner, apply per‑identifier and per‑Safe rate limits, prevent replay of proofs, keep revocation and update procedures clear with non‑PII event logs, and present human‑readable UX that states exactly which predicate is being proven. In short, this architecture suits personhood‑based recovery and age or citizenship gating without leaking PII; for strong legal identity attestation, add a VC issuer and liveness.
The stacks below are summarized against the reference requirements above.
Project | Description | Advantages | Limitations | Fit with Reference Architecture |
---|---|---|---|---|
Mobile app + SDK for document scanning, local proof generation, and verifier integration (EVM). Exposes QR-based request flows and verifier ABIs. | Developer-centric integration (QR flows, verifier ABI exposure)- Compact onchain verification model | Device constraints for heavy proofs- No liveness by default- Query circuits must avoid identity leakage | Strong for scoped personhood + onchain verification; add liveness/MFA and renewal linking at app/contract layer | |
ZKPassport with global onchain Merkle registry; two verification modes: full (issuer sig + hash integrity on-device) and light (hash on-device, issuer sig by trusted service). Transitioning circuits to Noir. | Shared registry amortizes costs- Broad compatibility via light verification | Hardware constraints for full verification- No liveness- Cannot prevent multi-passport onboarding- Need curated circuits across formats | Good registry + modes; ensure nonces/nullifiers and add liveness/MFA and renewal linking | |
Privacy-first identity protocol focused on Sybil resistance + selective disclosure. Builder tools and contract recipes support airdrops, social proofs, quadratic funding, wallet recovery. | Streamlined builder experience- Explicit support for predicate disclosures- Guidance for onchain integrations | Sparse details about onchain registry- No liveness- Naïve attribute disclosures compromise privacy | Promising predicate focus; verify replay protection + renewal/rotation paths | |
Privado ID (Polygon ID) | Full VC stack (issuer, verifier, wallet) built on iden3. Supports offchain + onchain verification, state publication via Merkle proofs, revocation trees, nullifiers. SDKs, contracts, and wallets included. | Rich developer tooling | Operational overhead of issuers + credential lifecycle- Higher integration complexity vs “scan-and-prove” stacks | Strongest for issuer-anchored identity + nullifiers; complexity higher for simple personhood-only recovery |
There are well‑documented constraints. Full on‑device verification is still limited by mobile hardware in some cases. There are no built‑in biometric liveness checks, meaning a thief with a valid document could potentially generate proofs; ZK‑ML‑based liveness is an active area of research. The system cannot prevent a single person with multiple passports from onboarding multiple times. And for recovery, only documents with Active Authentication are widely supported today. Documentation also discusses using a shared Merkle tree to mitigate dictionary‑style correlation attacks by growing the anonymity set.
When implementing ZKPassport for wallet recovery mechanisms, several critical security considerations must be thoroughly evaluated and addressed to ensure the system remains robust against various attack vectors:
Threat / Risk | Description | Mitigation |
---|---|---|
Physical theft of documents | Malicious actors with stolen passports/IDs could generate valid proofs without owner consent. | - Sophisticated liveness verification to confirm the presenter is physically present. - Multi-factor authentication (e.g., password/PIN + document possession). |
Document renewal | New passports generate different cryptographic identifiers, risking disruption of recovery access. | - Robust linking protocols between old and new document identifiers. - Allow users to update recovery credentials seamlessly during transition. |
Replay attacks | Attackers may intercept and reuse proofs for unauthorized access. | - Challenge-response with single-use nonces. - Time-bound validity periods to ensure proofs cannot be reused. |
Document format vulnerabilities | Dependence on government PKI introduces risks if cryptographic standards are deprecated or issuing authority keys are compromised. | - Continuous monitoring of international cryptographic standards. - Implement graceful degradation paths to maintain integrity. |
ZKPassport is suitable for Safe recovery today if you frame it as personhood rather than legal identity. It reliably lets a user prove “I am the same human who registered this Safe in this domain” without revealing MRZ fields or other PII, and it supports fast, repeatable proofs once the initial commitment is made. Used this way, it delivers practical recovery while preserving privacy.
To reach production‑grade recovery, you still need a few concrete pieces: proofs must be bound to fresh nonces and rejected on reuse; the recovery flow should include optional liveness or a second factor for higher‑value accounts; and there must be a renewal pathway to link old and new document identifiers when passports change. Timelocks, rate limits, and clear revocation/update procedures round out the operational safeguards.
If your goal is strong legal identity attestation rather than personhood, this stack is not sufficient on its own. You would add a verifiable‑credentials issuer, enforce liveness to KYC standards, and accept the greater operational complexity that comes with issuer‑anchored identity. Otherwise, for personhood‑only recovery, integrate the verifier contract, implement the challenge service and replay checks, ship the renewal linking flow, and keep all onchain storage to scoped identifiers—not raw DG1 values.
Safe Research is the applied R&D arm of Safe, dedicated to advancing the self custody stack. Our work is grounded in the cypherpunk principles of security, censorship resistance, and privacy, and we focus on building trustless, user centric infrastructure for smart accounts and wallets.
Discuss ZKPassport in our forum
Learn more about Safe Research
Read our manifesto
Let’s make Ethereum’s original cypherpunk vision real, one commit at a time.
Project repository
Privado ID documentation: https://docs.privado.id/
©2023–2025 Safe Ecosystem Foundation