Skip to main content
Library

2025ZKsyncZK CircuitsPrivacy

Privacy-Preserving Account Recovery with Zero-Knowledge Proofs

Recover a ZKsync smart account by signing in with Google, proven in zero knowledge. Circuits and contracts audited by OpenZeppelin.

Role
Technical Product Manager
Team
Moonsong Labs

01Snapshot

  • Focus: Google-based account recovery for ZKsync smart accounts, using zero-knowledge proofs to verify ownership without exposing the user's identity.
  • Context: ZKsync SSO gave users passkey-based smart accounts with no seed phrases, but it needed a recovery method that stayed private and didn't turn the OpenID provider into a custodian.
  • Who it's for: Matter Labs, the team behind ZKsync, building user-friendly wallet infrastructure for their Layer 2 ecosystem.

Outcomes

  • OidcRecoveryValidator, a new validator module that lets users register a new passkey by proving, in zero knowledge, that they hold a valid Google-signed token.
  • A custom Circom circuit that verifies the token's signature and claims while keeping the user's data private, with proofs generated in the browser.
  • Independent OpenZeppelin audits of the circuit and the recovery contracts, with no critical or high-severity issues in either.

02Context

ZKsync SSO (Smart Sign-On) is Matter Labs' modular smart account implementation for ZKsync. At the time it offered ERC-7579 modular accounts, passkey authentication with no seed phrases, configurable sessions, paymaster support, a TypeScript SDK, and an open-source auth server.

Passkeys solved onboarding, but users still needed a way back in after losing a device. We had already shipped social recovery with guardians, which works well but needs several people to coordinate. Mainstream users expect Web2-style recovery: sign in with Google and regain access.

03The Challenge

Google sign-in had to work as a recovery method without weakening the security and privacy properties that make smart accounts valuable:

  • Privacy without custody: Google shouldn't be able to link an on-chain address to a user's identity, or use its signing authority to take over accounts and drain funds.
  • On-chain verification limits: Verifying a zero-knowledge proof costs a lot of gas, and account abstraction restricts what a transaction's validation step can do.
  • Provider key rotation: Google rotates its signing keys several times a month. The contracts had to track the current keys securely, without a manual update step.
  • Security first: Recovery is the most sensitive flow in a wallet: whoever controls it controls the account. Every step had to hold up against replayed tokens and front-run transactions.
  • Familiar UX: All of this had to stay invisible. To the user, recovery should feel like a normal Google sign-in.

04Our Approach

We started with research: the recovery designs available on the market, weighed against the SSO architecture. We scoped and estimated the candidate solutions and walked Matter Labs through the trade-offs before committing to one.

The core insight was to use OAuth's nonce parameter as a commitment. The nonce accepts arbitrary data, and Google returns it signed inside the ID token (a JWT). Put a hash of the recovery transaction there, and Google's signature ties that token to one specific recovery. Salting and blinding keep the link private. The design builds on Sui's zkLogin.

The architecture is extensible. The implementation targets Google, but the circuit works on standard JWT structures, so any OIDC-compliant provider could plug in with minor contract changes for its key format and rotation schedule.

05Execution

Two components carry the design:

  • OidcRecoveryValidator: a new validator module that stores each SSO account's recovery data and validates the transactions that use it.
  • The recovery circuit: a Circom circuit, proven with Groth16, that shows the user holds a Google-signed JWT for the registered account and that the JWT commits to this exact recovery, without revealing which account it is.
Structure of the recovery circuit. Inputs: the JWT, Google's public key, the account digest, and the nonce for validation; a blinding factor and a salt for privacy and ownership; indexes for optimization. The circuit proves that the user owns a JWT issued for a specific Google account and intends to perform the recovery transaction, through four checks: JwtVerify validates the JWT, JwtData extracts iss, sub, aud, and nonce, VerifyOidcDigest checks the account digest, and VerifyNonce checks the nonce content. Output: validation success or failure.
A conceptual view of the recovery circuit.

Setup

To the user, setup is the familiar Sign in with Google pop-up. Behind it, the account registers a digest with OidcRecoveryValidator: a hash of the user's Google ID, the app's client ID, the issuer, and a salt. The digest pins one Google account without revealing which.

Recovery

The user creates a new passkey and signs in with Google. The nonce sent to Google commits to the account, the new passkey, the wallet submitting the transaction, a per-account counter, and a deadline, all blinded with a random factor. Google signs it without learning anything about the account.

The browser then generates a zero-knowledge proof that the JWT is valid, matches the registered digest, and carries that nonce. Proof generation runs entirely in the browser, in about 30 seconds on consumer hardware, with no proving service involved.

Because proof verification is gas-heavy, recovery takes 2 transactions. The first verifies the proof and marks the account ready to recover. The second registers the new passkey, and the validator only allows it for the passkey committed in the proof, within 10 minutes.

Sequence diagram of the recovery flow between the user, the auth server, Google, the salt service, OidcRecoveryValidator, and the smart account. The user starts recovery with a new passkey and connects a wallet. The auth server reads the recovery nonce, builds a blinded nonce, and sends the user through Google sign-in. Google returns a signed JWT containing the nonce, the auth server fetches the salt, and the browser generates the ZK proof. Transaction 1 calls startRecovery with the proof and the new passkey hash, and the validator verifies the proof. Transaction 2 adds the new passkey, which the validator approves because it matches the committed passkey within 10 minutes, restoring access.
The full recovery flow, from the first click to the new passkey.

Key rotation and account management

  • Key rotation: OidcKeyRegistry keeps Google's recent signing keys on-chain (the last 8 per provider), and a monitoring system updates it as Google rotates them, so proofs verify against current keys without manual updates.
  • Account management: Users can view their linked Google account in the SSO settings dashboard and remove it with passkey authentication.

06Results

The engagement delivered OIDC account recovery for ZKsync SSO: Web2-style recovery that keeps the self-custody and privacy properties separating smart accounts from custodial solutions.

OpenZeppelin audited the Circom circuit and the Solidity contracts independently, covering the cryptographic implementation, the contract logic, and how the two interact. Neither audit found a critical or high-severity issue in the recovery code, and every medium-severity finding was resolved. Few ZK circuits get an independent audit; this one did, alongside the contracts that verify it.

  • Circuit audit: OpenZeppelin, March to April 2025. 9 findings, none critical or high, all resolved.
  • Contract audit: OpenZeppelin, April 2025. No critical or high-severity issues in the recovery contracts, and all 3 medium-severity issues resolved. The report's one high was in an account signature handler, not the recovery modules.
  • Circuit source: the Circom circuit and browser proving tools, on GitHub.
  • zkLogin: the Sui research this design builds on (Baldimtsi et al., 2024).

The broader lesson: an on-chain verifier can confirm a fact about off-chain data, here a token signed by Google, without that data ever reaching the chain.