How zero-knowledge proofs enable voting without identity
The problem with conventional systems
In a conventional digital voting system, eligibility verification works like this: a central database holds a list of eligible voters, each identified by name, national ID, or email address. When you vote, the system checks your identity against the list — and then marks you as having voted.
The privacy problem is structural: the system necessarily holds a link between your identity and the fact that you voted. Even if the database never exposes which way you voted, it exposes whether you voted. Under coercion, that matters.
Zero-knowledge group membership
Zero-knowledge proofs allow one party to prove something to another party without revealing any information beyond the fact that the statement is true.
The Semaphore protocol, developed by the Privacy & Scaling Explorations team at the Ethereum Foundation, applies this to group membership. A voter can prove they are a member of the eligible-voter group without revealing which member they are.
The mechanism works roughly as follows:
- At registration, each eligible voter generates a private identity (a secret key). The corresponding public commitment (a hash) is added to a Merkle tree that represents the set of eligible voters.
- At vote time, the voter generates a zero-knowledge proof that they know the secret corresponding to one of the commitments in the tree — without revealing which one.
- The voter also computes a nullifier — a value derived from their identity and the specific election. The nullifier is deterministic: the same voter will always produce the same nullifier for the same election.
- The system records the nullifier. If a second vote arrives with the same nullifier, it's a duplicate — and it's rejected. But the nullifier cannot be traced back to the voter's identity.
The result: double-voting is publicly detectable, but the detection mechanism reveals nothing about the voter's identity.
What this means for the threat model
This design directly addresses two adversaries in our threat model:
A2 — Eligible voter attempts to vote more than once. The nullifier makes this publicly detectable without any identity lookup. The system doesn't need to know who tried to vote twice; it just rejects the second nullifier.
A3 — Corrupt operator attempts bulk fraud. Even if an operator issues fraudulent credentials, each credential produces a unique nullifier. Population-level reconciliation (comparing aggregate nullifier counts against the known eligible population) can flag anomalies statistically — without needing to inspect individual votes.
What we're building on
We're not implementing Semaphore from scratch. The PSE team's open-source library provides the core primitives, and prior work like ZkDemocracy provides reference voting implementations built on top of it.
Our contribution is the integration with the broader system: the credential issuance workflow (with identity proofing for Votercare Max), the nullifier storage and deduplication backend, the live provisional count, and the verified final tally.
We'll be publishing more technical detail on the integration as it develops. If you have a background in ZK cryptography and want to engage with the design, we'd like to hear from you.