Cryptography / August 9, 2026 · Updated August 11, 2026
Authentication that never sees the password
How a ceremony-bound proof replaces the password in a ZKAuth login request, plus the risks that zero knowledge does not remove.

Mohith · 6 min read

A password database is dangerous even when every record is salted and deliberately expensive to guess. ZKAuth tests a different protocol: the client derives a commitment during registration, then proves knowledge of the matching password during login. The password itself is absent from both requests.
The difference begins in the wire format. Here is the request body the engine receives when someone registers:
{ "email": "ada@example.com", "salt": "0x8f3a...", "commitment": "0x2c17...", "deviceInfo": { "deviceName": "Chrome on Mac", "deviceType": "desktop" }}No password field. Before the request leaves the device, the zkauth-client SDK stretches the password with Argon2id, then binds the result to the user’s salt, the project, and the email with a Poseidon hash. The output is a commitment: a number the server can store that is not directly reusable as a login. As with any stored password verifier, a database compromise can still enable offline guessing, so password strength and Argon2id cost remain important.
Client device
Verifier
Login proves knowledge in a fresh ceremony
At login, the SDK re-derives the same material on the device and generates a Groth16 zero-knowledge proof that says I know a password whose commitment matches the one you stored for this email, in this project, without revealing the password or even its hash. The login request looks like this:
{ "email": "ada@example.com", "proof": { "pi_a": ["..."], "pi_b": [["..."]], "pi_c": ["..."] }, "publicSignals": ["...12 canonical V2 field values..."], "proofProtocol": "zkauth-groth16-v2", "policyVersion": 1, "circuitVersion": "auth-v2", "provingKeyVersion": "auth-v2-dev-0000", "ceremonyId": "123e4567-e89b-42d3-a456-426614174000", "challenge": "...server-issued 32-byte base64url challenge...", "deviceInfo": { "deviceName": "Chrome on Mac", "deviceType": "desktop" }}The engine loads the short-lived ceremony from its database, derives every expected public signal from trusted context, verifies the Groth16 proof, and atomically consumes the challenge when it creates the session. The statement binds the commitment, tenant, email, purpose, versions, challenge, ceremony, and exact expiry. Reuse, expiry, or any context mismatch is rejected.
What the protocol changes
- A stored commitment is not a bearer credential. It cannot be submitted directly as a login, although a database compromise still creates offline password-guessing risk.
- The proof is tenant-bound. A valid proof for one project cannot be replayed against another; the tenant hash is a public input to the circuit itself.
- Intercepting the server request is less useful. There is no password in transit to steal from the ZKAuth request; device malware and fake login pages are still separate risks.
What zero knowledge does not solve
Malware on the user’s device is out of scope: if the device is compromised, the password can be captured before any cryptography runs. Groth16 over BN128 is not post-quantum. The current V2 proving keys are development artifacts, so this password-proof path is experimental and disabled by default in production.
Experimental production status
The Proof V2 Lab documents the unpublished SDK checkout required to run the flow. Exact measurements and verification scope live in the evidence record; broader risks remain on the limitations page.

Mohith
Founder, ZKAuth
August 9, 2026
Next post
When replay protection is unavailable, login stops