The voter casts his vote on a voting machine. Before allowing him to vote, the machine insists that he insert the voter card. The machine interacts with the voter card, and if it likes the interaction, it disables the voter card and allows the voter to cast a vote.
After the voter is done voting, the machine ejects the now-disabled voter card, and the voter gives the voter card back to the election workers at the front desk before leaving the polling place. (The voter card may later be re-enabled for use by another voter.)
The idea of this scheme is that an enabled voter card allows one vote to be cast. Unfortunately, some real implementations of this scheme are insecure, allowing an adversary to re-enable his own voter card as often as he likes, and thereby to cast as many votes as he likes. Your job is to fix this.
The voting machine is a computer. It plugs into the wall and looks like an oversized laptop. It has a slot into which a voter card can be inserted. When a voter card has been inserted, the voting machine interacts with the card and then decides whether to allow a vote to be cast. If necessary, it interacts with the voter card to tell the voter card to disable itself.
When the voter card is inserted into another device, the voter card can communicate with that device using a bidirectional serial channel. For example, the voter card can send a byte to the other device, and the other device can receive that byte; or vice versa. This is the only way the devices can interact.
It is important to recognize that the voter card is a computer in its own right. Other devices, such as the encoder and voting machine, can never read or write the voter card's memory directly. All they can do is communicate with the voter card and rely on it to change its state.
Another important fact about the smartcard is that it does not have its own power source--it gets power only when it is inserted into another device (here, an encoder or voting machine). When the device ejects the smartcard, the smartcard immediately loses power and stops running. The smartcard has a "persistent memory" which can store up to 65536 bytes of data, and is preserved even when the smartcard is without power. The persistent memory is the only way for the smartcard to remember anything when it is not running.
The starter code is a bit tricky to understand. We'll start by discussing the communication protocol used in the starter code. The communication protocol can be thought of as a contract between the smartcard, voting machine, and encoder. This protocol must work no matter what sequence of devices the smartcard is inserted into (e.g. if the smartcard is inserted into an encoder 8 times before being inserted into a voting machine, it should still work).
Protocol used by Starter Code
The protocol used by the starter code is very simple. For a visual picture, see the smartcard-encoder protocol schematic, and the smartcard-machine protocol schematic.
Note that the smartcard (in our implementation) has no idea which type of device it's talking to. Your design may or may not have this feature. As a reminder, Cross-group collaboration -- including discussion of the merits/flaws of different protocol designs -- is not allowed.
The Roles of Each Class
Running the code. To simulate insertion of a smartcard into an encoder, one uses the command java Encoder [filename], where filename is a 65536 byte file on your computer's disk which holds the contents of a smartcard's memory, and likewise for a voting machine. To generate a 65536 byte smartcard memory file, java CreateCard [filename], which simulates the process of a brand new smartcard being manufactured in the bowels of the smartcard factory. Try it out, see how the [file] storing the memory changes (first byte should switch from 0x00 to 0x01 and back).
List of classes. The starter code is divided into 8 classes, summarized in the table below. The columns should be self-explanatory, except perhaps Usable by student code -- if this column is "no", you should not write any code which uses any methods from this class or any code which instantiates an instance of this class.
Class name | Role | Edit | Classes used by | Usable by student code | Run from command line | Special Constraints |
---|---|---|---|---|---|---|
SmartCard | SmartCard protocol | Yes: execute() | InsertedSmartCard | No | No | N/A |
Encoder | Encoder protocol | Yes: execute() | None | No | Yes | N/A |
Machine | Machine protocol | Yes: execute() | None | No | Yes | N/A |
CreateCard | Creates files that represent smartcard memory contents | Yes: initPersistentMemory() | None | No | Yes | N/A |
EncoderSecrets | Stores encoder secrets | Yes: constants | Encoder, CreateCard | Yes | No | Constants only. Cannot be used in Machine or SmartCard |
MachineSecrets | Stores machine secrets | Yes: constants | Machine, CreateCard | Yes | No | Constants only. Cannot be used in Encoder or SmartCard |
PersistentMemory | Reads/writes SmartCard memory to files on your computer | No | CreateCard, SmartCard | Yes | No | N/A |
InsertedSmartCard | Wrapper class for executing SmartCard class | No | Encoder, Machine | No | No | N/A |
The first three classes Encoder, Machine, SmartCard implement the protocol described above. The fourth class CreateCard simulates the process of creating SmartCards at the factory. EncoderSecrets and MachineSecrets may contain any constants you need for security, but the respective secrets can only be used by the respective device class and by CreateCard. You will use PersistentMemory in your SmartCard class to store/receive data in the smartcard's memory. InsertedSmartCard is a utility class that you will not use or edit.
Class details. The Encoder program's main function is invoked whenever an election worker inserts a smartcard into the encoder. Encoder takes one commandline argument, which is the name of a file containing (the persistent memory of) the smartcard that has been inserted into the encoder device.
The Encoder program does some initial setup steps in its main method, which you are not allowed to change. One of these steps is to launch a separate program that represents the inserted smartcard. These two programs run simultaneously, communicating via a serial connection, just as the real encoder would communicate with a real smartcard that is inserted into it.
Having set up an Encoder object, Encoder's main() method then calls the Encoder's execute() method, which does the main work of the encoder. This is what you will modify.
The voting machine program, called Machine, works similarly to the encoder. It takes one commandline argument (the name of a file containing a smartcard's persistent memory). It does some setup, which includes launching a program to represent the smartcard. Then it calls a method called execute, which does the main work of the voting machine and which you will modify.
The final program is the SmartCard program, which is invoked whenever a smartcard is inserted into a device. (By "device" we mean an encoder or a voting machine.) The SmartCard program does some initial setup steps, which we have provided and you may not modify. Then it calls the execute method, which you may modify. This procedure is invoked whenever the smartcard is inserted into another device. The smartcard runs until execute returns, or until the device ejects the smartcard, whichever comes first. Read the comments in SmartCard.java for more information.
The smartcard has a persistent memory, implemented in PersistentMemory.java. You may not modify PersistentMemory.java. The persistent memory is the only storage available to the smartcard which is preserved when the smartcard is not running.
Normally, you will not launch SmartCard directly. Instead, it will be launched automatically when you run Encoder or Machine.
The CreateCard program creates a new smartcard. Its job is to initialize the persistent memory of the new smartcard. When you invoke CreateCard, you give it a filename; CreateCard creates a new file with that name, and stores the smartcard's persistent memory in that file. You can have as many smartcards as you want -- there will be one file for each card, to hold the contents of that card's persistent memory.
We have provided working (but insecure) code for all of these programs. It's probably a good idea to read the code for these programs, and to run them after adding "hello world" code to the execute procedures, to get a better handle on how the pieces fit together.
The encoder and machine programs we have given you do not use any persistent storage (i.e. they have no persistent state). You can give them persistent storage if you want. If you do this, the persistent storage should be kept in a file, and of course one program's persistent storage should never by accessed by another program.
You may not use any crypto libraries except: PRF, TrueRandomness, or code from assignments 1, 2, or 3.
Assignment Tips and Tricks. This list will definitely grow in response to Piazza questions.
The adversary's goal is for the number of votes cast on the legitimate voting machine to exceed the number of times that a smartcard was inserted into the legitimate encoder.
Again, we reiterate all work should be within a single group. Cross-group collaboration -- including discussion of the merits/flaws of different protocol designs -- is not allowed.
You should submit any source code files that you were allowed to edit (see table above or the dropbox link) -- even if you did not edit them (e.g. if you do not use MachineSecrets, you must still submit a MachineSecrets file). You should also submit any additional class files that your program uses. For your README, if submitting in plaintext format, use the filename README.txt. If submitting in pdf format, use the filename README.pdf.
You must work in a group on this assignment. You may not collaborate with anyone outside your group [clarification!]
You can submit your code using this link.