Ballot QR Code Data Format
Both hand-marked paper ballots and machine-marked ballots contain QR codes. For hand-marked paper ballots, the QR code contains only metadata about the ballot. For machine-marked ballots, the QR code contains both metadata and voter choices.
In order to make the information as compact as possible, information is coded as binary values that reference the election definition. For example, instead of encoding the precinct name (e.g. "Fire Station Precinct") the QR code contains the index of the precinct within the election definition. If the precinct were the first precinct listed in the election definition, the value would be 0. If it were the second precinct listed in the election definition, the value would be 1. The binary sequence is ultimately encoded into the QR code as Base64 for broad compatibility with devices that scan QR codes.
Decoding QR Codes Manually
Take following steps to decode a barcode manually and, in the case of a machine-marked ballot, manually verify the encoded selections are correct:
Scan the QR code with a smartphone camera or barcode reader. If your smartphone does not automatically detect a value, you may use any freely available QR code scanning app.
Copy and paste the scanned value into a Base64 to binary converter. There are many freely available online, such as cryptii.
Inspect the binary values and compare them to the encodings described below. In order to make sense of the values, you will need the associated election definition available for comparison.
You may use this process to verify that the encoded information in the ballot QR code matches the information presented on the ballot to human readers.
An easier, recommended approach is to simply scan the ballot(s) at VxScan or VxCentralScan. You can confirm the interpretation (and therefore in most cases, the QR code) is correct by looking at a results report. You can also inspect the Cast Vote Records exported from the scanner, which contain the images and interpretation for each ballot, to confirm that the metadata and voter selections were encoded and decoded correctly, similar to an Image Audit.
HMPB QR Code Data Format
HMPB Prelude
24 bits
The characters "VP2," which in binary appear as: 01010110 01010000 00000010
Ballot Hash
80 bits
See ballot hash documentation. The 80 bits represent 20 4-bit hexadecimal characters.
Padding
0 - 7 bits
To ensure that the value in the QR code is composed of whole bytes, 0 bits are added until the data ends in a whole byte.
Ballot Config Encoding
Precinct Index
13 bits
The index of the ballot's precinct in the election definition's list of precincts.
Ballot Style Index
13 bits
The index of the ballot's ballot style in the election definition's list of ballot styles.
Page Number
5 bits
HMPB only. Encodes the page number of the ballot, one-indexed.
Test Ballot?
1 bit
When true, indicates a test ballot. When false, indicates an official ballot.
Ballot Type
4 bits
Indicates the ballot type - absentee ballots are 0001
and precinct ballots are 0000
Ballot ID Set?
1 bit
When true, indicates that the encoded ballot has an assigned ID that follows this bit. When false, indicates there is no ID.
Ballot ID
Variable, max 256 bytes
Optional, HMPB only. Identifier for the ballot, UTF-8 encoding.
BMD QR Code Data Format
Unlike the HMPB QR code, the BMD QR code actually encodes votes.
BMD Prelude
24 bits
The characters "VX2," which in binary appear as: 01010110 01011000 00000010
Ballot Hash
80 bits
See ballot hash documentation. The election's ballot hash is visible on most authenticated screens on all apps. The 80 bits represent 20 4-bit hexadecimal characters.
Vote "Roll Call"
Variable, 1 bit per contest in the election definition
Each bit corresponds to one contest and indicates whether the voter made any selections. For example, with four contests, 1010
would indicate votes in the first and third contests but not the second and fourth.
Vote Data
Variable
In sequence, all the vote data for the contests indicated in the "roll call."
For yes-no contests, a set bit 1
indicates the "yes option" whereas an unset bit 0
indicates the "no option."
For candidate contests, includes a bit for each candidate in the contest definition in the order of the contest definition. 1
is a vote and 0
is a non-vote. If the contest allows write-ins (meaning allowWriteIns
is true in the election definition), there will be additional data here, described below.
Padding
0 - 7 bits
To ensure that the value in the QR code is composed of whole bytes, 0 bits are added until the data ends in a whole byte.
Write-In Encoding
If the contest does not allow write-ins (allowWriteIns = false
) then the candidate contest data ends after the bits for the list of candidates. If the contest does allow write-ins (allowWriteIns = true
), then write-in data will follow the candidate roll call and start with the number of write-ins. The maximum number of write-ins is the difference between the number of possible votes in the contest (e.g. is it a vote for one or vote for three contest) and the number of votes used on non-write-in candidates. The number of write-ins is written with the number of bits that would be required to write the maximum number of write-ins. For example, if a contest is "Vote for Five" and the ballot already contains two votes, the maximum number of votes is three. Three takes 2 bits to represent, so the number of write-ins will be 11
, 10
, 01
, or 00
for 3, 2, 1, or 0 respectively.
After the number of write-ins, the write-ins are written in series, with 6 bits containing the length of the write-in, in bytes, and then the write-in itself in however many bytes after that. The bytes are in UTF-8 format.
QR Code Specifications
HMPB and BMD QR codes are Model 2 QR codes with Level H error correction.
Source Code
The source code and further technical documentation of how ballot QR codes are encoded and decoded can be found in the ballot-encoder library.
Last updated