What if you could vote the next president of your country using an app on your phone?
In this article I’m going to build an online voting system. It is mostly based on this paper, but it’ll be slightly modified. Also, in order to fully understand this article, you need to know the basics of RSA. For this, I recommend my last article.
There will be 3 programs:
The validator server software: the validator server is responsible for verifying that a client is an eligible voter and blindly signing the masked vote.
The counter server software: this server just stores anonymous votes associated with valid signatures. In a real life scenario, it is recommended that voters mask their IP before sending their vote to the storage server in order to maximize anonymity.
The client software: the client will have hard-coded voting options. Once the user voted, it will handle vote encryption, blinding, signing, unblinding and submission.
If we do everything right, the voting process will be anonymous. Also, we will weight the pros and cons of online voting at the end of this article.
0.5. Is this approach anonymous?
The validator knows your identity and that you voted – but can’t unblind your vote, so it has no knowledge of whom you voted for.
The counter only knows that you have a vote signed by the validator, so it doesn’t know who you are.
If you have a hard time understanding, leave a comment or PM me on Twitter.
1. Generating the RSA key
We are going to need a RSA keypair to sign all the votes. The public key will be hardcoded into the client and storage server software, and the private key will only be held by the validator server. We can generate it with the following Python script:
I’ll paste the generated public and private keys below (of course, yours will be different):
Also, here’s a useful one-liner that will generate the variable declarations of the key:
2. The Validator Server
As I said before, the validator server will blindly sign one vote for every eligible voter. The validator cannot tell a blind vote apart from a random number, so a voter that sends an invalid vote will loose his/her ability to vote.
I believe the code is pretty readable and easy to understand, so I’ll just paste it below:
3. The Counter Server
The counter server is even simpler than the validator server. After it receives a signed vote, it verifies the signature and add it to an array if it is valid. I also implemented a stats function that returns the current umber of votes for every option:
4. The Voter (Client) Software
In a real-life scenario, this would be a website or a mobile app. However, in order to keep thing simple, we will code the client software in Python.
First, we need to import the required libraries:
Then, we need to hard-code the voting options. This is more secure than fetching them from another server every time (and is also more simple to code!).
We also need to set the addresses of the API point we will be using In my case, the validator and counter servers will be running on localhost, or 127.0.0.1.
The getSignedVote() function will take the voter name and choice as arguments and will return the signed vote. This means that it needs to blond the vote, talk to the validator server and then unblind the signature:
We are also going to need a function that will submit the data to the counter server:
Also, it would be nice if we could print the vote count to the user after he/she voted:
The next function makes sure that the integer representation of a vote is not the same between participants by adding a seed:
Now that we have all the required methods, let’s see the main code:
5. Testing the System
Now that we have a working system, let’s test it. First, we need to start the validator and counter servers. By default, they will listen on localhost on ports 1111 and 2222.
Now, let’s go to vote! Remember, only usernames that start with yakuhito and end with a number from 0 to 99 are allowed to vote. Let’s first try an invalid name:
The validator server rejected our vote because our username wasn’t whitelisted. Let’s now try voting with a valid username:
Our vote was recorded. Let’s try to use the same username again:
The validator server rejects our vote again. This is because no voter is allowed to change his vote or vote twice.
Moving to production
Here’s a short list I made of things that need to change in order to go into production (a.k.a. use the system in a real-world scenario):
Make the voter program work on multiple platforms. I would go with a website, but stand-alone apps for Android and iPhone could also work.
Connect the validator & counter servers to back-end databases. Test for SQL injections!
BUY HTTPS CERTIFICATES. This doesn’t need additional explanation.
Buy servers, hire sysadmins/programmers to set everything up.
Conduct at least one PENTEST and request the opinion of someone with a PhD in cryptography. I’m just an enthusiast and there might be a fatal flaw I cannot see because of my limited understanding of cryptography.
Someone might steal the RSA key and sign a lot of votes. This could be solved by increasing security and verifying that the number of people that were verified by the validator is equal to the number of signed votes.
The holder of the validator server might sign votes to support a party of his choice. This is the equivalent of inserting paper votes into a ballot to influence the outcome of the vote. This can be solved by having multiple validators/counters with different keys.
The counter server might report false data. This can be solved by publishing the list of signed votes at the end of election. If you have a signed vote that is not on the list, you can prove you voted in the election, and you could request a vote re-count.
While using software to vote might be less secure, it’s still an option to consider. In this article, we saw that it is relatively easy to implement such system. However, it is also hard to protect it against viruses. The program works in theory, but what if malware intercepts your request and modifies them so you vote for another party? Making it available on mobile phones only is more secure, however, it is still insecure.
EDIT: You can find the code here and ask me questions here.