Prototypes BallotP36 · Verifiable election

Vote

The Harbour Rowing Club elects its board. Your ballot is encrypted in this browser before it leaves it. The server checks that your code is on the list and has not voted, and that the ballot is well formed, then adds it to the public board. It cannot see whom you picked.

Loading

Your ballot

Each code votes once. Codes for visitors to use:

Pick one

In this browser

Each step runs in this page, with the time it took.

    What the server sees

    It sees

    • The public key that belongs to your code, so it can check the code is on the list and has not voted.
    • The ballot holds one encrypted number per candidate, each a pair of points on the curve P-256.
    • Proofs that each number is 0 or 1 and that they add up to exactly one, and your code’s signature over the ballot.

    It cannot see

    • Whom you picked. Opening one number needs the election’s secret key, which no one holds: it is split between three trustees, and two of them are needed. They only ever open the total.
    • Your code itself, which never leaves this browser. Only its public key is sent.
    • How to change your ballot. A change breaks your signature, and the verifier finds it.
    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.