Prototypes BallotP36 · Verifiable election

The trustees

Three trustees hold the election’s key between them, and any two can open the total. The demo trustees are played in this page from seeds that anyone can read; in a real election each trustee is a person with their own device.

Loading

Close and decrypt

Closing adds up the encrypted ballots on the server, one total per candidate. Then two trustees each decrypt their part of the totals, with a proof that they used their real share. After the second, the server joins the two parts and the counts come out.

Rehearse the ceremony

A rehearsal is a new election on the server with three new trustees whose secrets are made in this page. Each publishes its commitments, sends a sealed share to the other two, and checks the shares it receives. Turn on the cheat to see a bad share caught and its sender named.

Not run yet. A rehearsal takes a second.
Step and trusteeResult
How Ballot is built
  1. Ballot is an election where the server never sees a vote. Each ballot is encrypted in the voter’s browser, the server adds the encrypted ballots together, and the trustees decrypt only the total. The cryptography is written for this prototype in plain JavaScript with BigInt, with no dependencies.
  2. The curve is NIST P-256, the one WebCrypto and most TLS use. Ballot has its own field arithmetic, point addition and doubling, scalar multiplication with a window that does the same work for every scalar, a precomputed table for the generator, and SEC1 encoding with the check that a point is on the curve.
  3. A vote for a candidate is 1 and every other candidate gets 0, each encrypted with exponential ElGamal under the election key. Adding two ciphertexts adds the votes inside them, so the server can total the ballots without opening one.
  4. The browser adds zero-knowledge proofs to each ballot, made non-interactive with Fiat-Shamir over SHA-256: each number is 0 or 1, and the numbers add up to exactly one. Every proof is bound to the election and to the voter’s code, so it cannot be copied into another ballot or another election.
  5. A voter code never leaves the browser. It gives a key whose public half is on the election’s list, and the ballot is signed with it. The server checks the code is listed and unused, but cannot change a ballot or make one for a code it does not hold.
  6. The election key is made by three trustees without a dealer: each shares a random line with Feldman commitments, checks the shares it receives, and names a trustee whose share does not match. Any two trustees decrypt the total by Lagrange interpolation, each with a proof that they used their real share.
  7. The board is a log chained with SHA-256. A voter’s receipt is the hash of their ballot, which they can find on the board. The verifier reads only the board and checks every proof, the total added up again, each partial decryption and the result. Four flawed servers show it catching a dropped, changed or added ballot and a wrong total.
  8. It is judged against other implementations: the unit tests compare its scalar multiplication and point encoding with node’s P-256 for hundreds of random scalars, and the verifier page compares it with this browser’s WebCrypto. A property test plays random elections that must verify, and flawed ones that must be caught.
  9. Its limits: Ballot is a demo, and no authority has certified it for elections. The vote is in the clear on the voter’s device until the browser encrypts it, so malware on that device could read or change it. The person who hands out the codes knows which code went to whom: they can see that a person voted, and the encryption hides the choice from them. Two trustees who work together could decrypt one ballot instead of the total. The prototype has no defense against a voter who is watched or paid while voting, and BigInt arithmetic is not constant time.