Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
When a VxSuite machine exports data to a USB for another VxSuite machine to import, the first machine digitally signs that data so that the second machine can verify its authenticity. We use this mechanism in two places in particular:
To authenticate election definitions/packages — These configuration bundles are exported by VxAdmin and used to configure VxCentralScan, VxMark, and VxScan.
To authenticate cast vote records — These are exported by VxCentralScan and VxScan and imported by VxAdmin for tabulation.
The exporting machine digitally signs the following message using its TPM private key:
MESSAGE_FORMAT_VERSION + “//” + ARTIFACT_TYPE + “//” + ARTIFACT_CONTENTS
It then outputs the following to a .vxsig file:
SIGNATURE_LENGTH + SIGNATURE + SIGNING_MACHINE_CERTIFICATE
The importing machine parses the above and verifies that the signing machine certificate in the .vxsig file is a valid certificate that is signed by VotingWorks, using the VotingWorks CA certificate installed on every machine. The importing machine then extracts the exporting/signing machine’s public key from this certificate, reconstructs the message, and verifies the signature. After this, the importing machine performs artifact-specific authentication checks, e.g., that the signing machine cert is a VxAdmin cert if the artifact is an election package.
If verification fails on the importing machine, the importing machine will refuse to import the artifact. This provides protection against data tampering and/or corruption as data is transferred from one machine to another via USB drive.
Refer to the following code links for more details:
https://github.com/votingworks/vxsuite/tree/v4.0.0-release-branch/libs/auth — VxSuite authentication lib, a good starting point for all things authentication
https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/auth/src/artifact_authentication.ts — Artifact authentication logic
https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/auth/src/cryptography.ts — OpenSSL commands underlying various authentication and signing operations
Beyond the application-level protections and system integrity assurances built to ensure that unauthorized access is thwarted and unauthorized software cannot be executed, VxSuite is also designed with defense-in-depth and least-privilege principles. This ensures that a failure of some defenses, due to unforeseen bugs or novel attacks, can be limited in its impact.
VxSuite runs a modern, stripped down Linux installation with the minimum number of packages to reduce the attack surface of any unexpected partial penetration. The set of packages is manually curated by our team and specified in our installation manifests when building a base Linux image.
The Linux OS makes use of data execution prevention (DEP) and address space layout randomization (ASLR). These exploit mitigation strategies provide a low-level layer of protection against a variety of vulnerabilities. We have verified that these mechanisms are active via the following commands:
Once a VxSuite image is built, access for the root user is shut down and that shutdown is locked in via secure-boot verification of the root partition.
When a VxSuite function requires superuser access, that access is captured as a minimal shell script, and the appropriate VxSuite Linux user is granted sudo permissions on that shell script alone. For example, on a VxSuite production machine, no user has the ability to call the mount
command. Instead, when mounting USB drives, we have crafted a shell program that is only capable of mounting and unmounting USB drives at the /media/usb-drive
mount point, and the vx-services
user is granted superuser privileges when calling that script only. The full script for this example is available at https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/usb-drive/scripts/mount.sh.
VxSuite runs a minimal window manager, Openbox, which is built to have very few capabilities. In addition, we configure Openbox so as to turn off all keyboard shortcuts and contextual menus. Even if an attacker were able to plug in a mouse or a keyboard, they would be unable to get very far.
All processes that a user can directly interact with in VxSuite run as user vx-ui
. The processes that access the smart card reader or perform data processing services run as user vx-services
.
The vx-services
Linux user has a set of permissions allowing it to access the scanner, printer, and smart card reader. It is configured, like the root user, to not have a password, so no interactive user can log in as vx-services
and utilize those privileges for nefarious purposes. The services that run under the vx-services
user account offer their services over local HTTP, with well-formed semantics as to what actions they allow. The authentication status is checked on the backend, by vx-services
, so actions can only be taken if a valid authentication session is open.
Meanwhile, if a user were to break out of the constraints we've placed on the window manager and obtain a shell, that shell would run as vx-ui
, which as the ability to call into services run as vx-services
, but does not itself have the direct privileges of vx-services
. Thus, an attacker that penetrates the first line of defense would remain severely thwarted in what they can do next.
VxSuite v4 contains four distinct cryptographic modules:
Smart cards
VxAdmin / VxCentralScan TPM (same underlying hardware)
VxMark TPM
VxScan TPM
OpenSSL software
All of the above are FIPS-compliant.
VxSuite v4 uses NXP JCOP 4 Java Cards, specifically this model: https://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic.
These smart cards are FIPS140-2-certified: https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/3746.
The applet that we run on the cards, OpenFIPS201, is an implementation of the NIST 201 PIV protocol, which sits on top of FIPS140 cryptography. In the applet, all cryptographic operations are handled by the Java Card operating system, implemented by the NXP JCOP 4 card.
The VxAdmin / VxCentralScan HP uses an NPCT75x TPM 2.0 chip by Nuovoton.
This chip is FIPS140-2-certified: https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4411.
The VxMark board uses an SLB 9665 TPM 2.0 chip by Infineon.
This chip is FIPS140-2-certified: https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/2959.
The VxScan board uses an SLB 9670 TPM 2.0 chip by Infineon.
This chip is FIPS140-2-certified: https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/3492.
We use OpenSSL 3.0.9 and install the FIPS provider, per these instructions. In the basic configuration wizard run on first boot after imaging, we run the mandatory openssl fipsinstall
command to ensure that the FIPS provider is configured correctly and OpenSSL is running in FIPS mode.
This provider is FIPS140-2-certified: https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4282.
FIPS-compliance of all OpenSSL operations is guaranteed as follows:
FIPS compliance via hardware modules: For relevant operations, OpenSSL outsources cryptographic operations to the TPM 2.0 chip, and as noted above, all of our TPM 2.0 chips are FIPS-compliant.
FIPS compliance via software modules: For all other operations, OpenSSL uses the FIPS provider. Our OpenSSL config guarantees this.
VxSuite does not use encryption to secure data (election definitions, CVRs) while in transit on USB drives because that data does not need to be confidential – and in fact trust in a voting system is better achieved by transparency of this data. In particular, CVRs stored on USB drives are not encrypted so that they can be viewed using any computer. This is by design.
On the other hand, VxSuite strongly authenticates all data, which is critical. Thus, election definitions and CVRs are in plaintext on the USB drives that transfer them, accompanied by strong digital signatures, generated by signing keys stored in hardware TPMs.
VxSuite does encrypt data at rest on machines' internal drives, specifically their /var
partitions, per #protecting-critical-read-write-data.
All digital signatures for authenticating election definitions and CVRs are generated using ECC 256-bit keys, that further use the NIST standard P-256 curve.
All hashes – for generating election IDs, digesting data before signing, constructing the Merkle-tree hash for CVRs, and constructing the dm-verity system integrity hash – are generated using the NIST standard SHA-256.
Encryption of disk partitions is done using AES with 256-bit keys in XTS mode.
Secure Boot code signing uses RSA 4096-bit keys.
There is a tension between the requirement that (a) voter privacy should be strongly protected, and (b) cast vote records should be continuously exported in order to ensure that they can be immediately available in the case of hardware failure. For example, if each scanned ballot naively results in the creation of a new CVR file on the USB drive with a timestamp, the order of CVRs is obviously preserved on the USB drive, as the file creation and modification timestamps reveal the order in which those CVRs were stored.
To meet both requirements, VxScan performs some amount of "shuffling" every time a CVR is saved to the USB drive. Every time a ballot is cast and its CVR is saved to the disk, VxScan picks one or two CVRs already stored on the USB drive and updates their creation and modification time on the USB drive. This is the digital equivalent of taking one or two random ballots from a pile and bringing them to the top of the pile. Thus, if an attacker were to view the CVRs on the USB drive, they would not be able to determine the order in which those ballots were cast, because of this constant shuffling.
Recall from Cast Vote Records that a single CVR is structured, on disk, as a directory that contains the JSON data of the CVR and the ballot images for that CVR. The VxScan operation performed to move a single CVR "up to the top of the pile" involves four steps:
Copy the CVR directory contents:
cp -r 4d6a9dad-e6d6-4a29-89bc-9ab915012b73/ 4d6a9dad-e6d6-4a29-89bc-9ab915012b73-temp/
Once copying completes, mark the new directory as complete:
mv 4d6a9dad-e6d6-4a29-89bc-9ab915012b73-temp/ 4d6a9dad-e6d6-4a29-89bc-9ab915012b73-temp-complete/
Delete the old directory:
rm -r 4d6a9dad-e6d6-4a29-89bc-9ab915012b73/
Rename the new directory:
mv 4d6a9dad-e6d6-4a29-89bc-9ab915012b73-temp-complete/ 4d6a9dad-e6d6-4a29-89bc-9ab915012b73/
This four-step operation ensures that:
All metadata for that CVR (parent directory and children files) is updated, leaving no trace as to when that CVR was first saved to disk.
If a failure occurs at any point, it is possible to recover completely without losing any data.
Refer to the following code links for more details:
All VxSuite components are blocked from connecting to any network. Thus, there is no networking in a VxSuite implementation. Our network design is secure by virtue of it being completely absent.
Networking is disabled through several layers of defense:
Network drivers and known network connections are purged in the software setup process. See these lines in the setup-machine.sh script.
Secure boot ensures that the hard drive is not modified, thus preventing software that isn’t part of the approved VotingWorks bundle from running.
The network stack is disabled in the BIOS.
Wi-fi or bluetooth hardware is not present on the machines.
Ethernet ports are blocked.
As a final layer of defense, a firewall configuration is defined to block any incoming or outgoing traffic in the event a connection was somehow created.
Because there is no networking, all electronic data transfer is air-gapped via USB drives.
The foundation of our system integrity solution is the TPM (described in greater detail under Cryptography).
Our design for system integrity maps closely to that of ChromeOS:
The hard drive is partitioned such that executable code lives on the read-only partition /
, and only /var
and /home
are mounted read/write for configuration, logs, and working data like scanned ballot images.
The read-only partition is verified by dm-verity, which generates a hash tree of all the raw partition blocks, culminating in a single hash root for the entire read-only filesystem.
The kernel boot command line includes the dm-verity hash root as an argument, ensuring that Linux halts if the read-only partitions are not successfully verified against this hash root.
The BIOS is configured to only boot a properly signed bootloader+kernel+command-line, with public keys managed by VotingWorks and configured in the BIOS as secure boot keys. The bootloader+kernel+command-line that we want is set up as the default boot option in UEFI.
The private key described previously in Access Control is generated in the TPM and bound to Platform Configuration Registers (PCRs) that include the BIOS, the Secure Boot public keys, and the bootloader+kernel+command-line. Thus, this private key is only usable if the machine boots appropriately sanctioned source code with a hard drive whose read-only partitions are unmodified.
As a diagram:
The integrity of the overall system is ensured because:
Any change in the executable portion of the hard drive will be detected when dm-verity checks the hard drive blocks against its stored hashes.
Any change in the hashes stored on disk will be detected against the mismatched root hash, present in the kernel boot command line.
Any live changes to the read-only portion of the drive, even while the machine is already booted up, will be detected the moment Linux tries to read those modified blocks, as all disk reads are live-checked against the hashes.
Any change in the kernel boot command line will be detected in two ways:
First, the TPM won’t unseal secrets since a change in the command line changes one of the PCR values, which means that the TPM’s policy check will fail.
Second, the system won’t even boot at all because the changed command line invalidates the signature on the bootloader+kernel+command-line, and UEFI will refuse to execute the now-improperly signed bootloader+kernel+command-line.
Thus, if the system boots, it means that the hard drive corresponds, by hashing, to the expected value that we baked into the bootloader+kernel+command-line and signed.
Our system is configured so that /var
, /tmp
, and /home
are mounted read-write, and /
for everything else is mounted read-only. This requires writing logs and other runtime-generated VotingWorks data, e.g., scanned ballot images, to /var
. Variable system configuration, e.g. timezone information, also needs to be redirected to /var
. A USB mount point is pre-created at /media/vx/usb-drive
.
In addition to locking down the /
partition, the /var
partition is encrypted and authenticated using dm-crypt
. This ensures that, even if an attacker removes the physical hard drive and connects it to a different CPU, they cannot read or modify the contents of the /var
partition in an attempt to make the VxSuite component behave differently.
Furthermore, as an extra layer of defense, every individual VxSuite component instance uses a unique random encryption key for its /var
partition. Thus, if an attacker were to break the encryption on one VxSuite machine, they would have gained no advantage in penetrating a second machine. This is ensured through rekeying of the /var
partition encryption on first boot of a VxSuite component.
With Secure Boot, UEFI passes control to the bootloader only if the bootloader is appropriately signed. Then, the bootloader passes control to the kernel. We choose to bundle the bootloader, kernel, and command line that is used to run the kernel all as one, and to present that to the UEFI as the single executable whose signature should be verified before running. The three are combined into a single object file, along with the dm-verity root hash. This single object file is signed by VotingWorks secure boot private keys.
Every VotingWorks machine’s BIOS is configured with VotingWorks secure boot public keys, and the BIOS is configured to require Secure Boot. Thus, only a properly signed object file can serve as bootloader.
When mounting USB drives, VxSuite always mounts them using standard Linux mount configuration options that makes files on the USB drive NOT executable. This further protects from any unauthorized software running, even in a transient fashion.
Our precinct scanners export cast vote records (CVRs) to USB drive continuously, as ballots are cast. Continuous export also allows for a speedy polls close, as cast vote records need not be exported all at once on polls close.
To perform continuous export while also keeping signatures, i.e., authenticity information, up-to-date, we have to be able to write both CVRs and authenticity information incrementally. (It isn’t enough to just hash and sign new data. We need an up-to-date root hash/signature.) We can use a Merkle tree structure to accomplish this efficiently.
Assuming every CVR has a random UUID, e.g. 4d6a9dad-e6d6-4a29-89bc-9ab915012b73, we can specifically use the following structure:
We don’t actually have to 1) use a nested directory structure on the USB or 2) store all intermediate hashes on the USB. We can store the structure and intermediate hashes on the machine in a database table, e.g.
-
-
-
Root hash
4
-
-
4 hash
4
4d
-
4d hash
4
4d
4d6a9dad-e6d6-4a29-89bc-9ab915012b73
4d6a9dad-e6d6-4a29-89bc-9ab915012b73 hash
…
Then on the USB, we can use a flat structure that’s much easier to reason about and iterate over:
The database table makes recomputing hashes easy, as retrieval of all hashes for a given ID prefix becomes a simple SQL query. This approach also decreases the chance that we accidentally depend on data written to the USB drive when computing hashes, protecting against compromised or faulty USB drives.
In the above examples, we’ve listed a root-hash.txt file and have signed that. In practice, we use a JSON file capable of storing other metadata and sign that:
Sample metadata.json:
Whenever a machine imports a CVR directory, it authenticates the CVRs by 1) verifying the signature on the metadata.json file and 2) recomputing the “castVoteRecordRootHash” in metadata.json from scratch to ensure that it’s correct.
Refer to the following code links for more details:
https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/auth/src/cast_vote_record_hashes.ts — CVR hashing logic, including the Merkle tree implementation
VxSuite is composed of individual machines, namely VxAdmin, VxCentralScan, VxMark, and VxScan, as well as smart cards for authentication. Each machine and each smart card has a unique 256-bit ECC private key (or keys in the case of smart cards) stored in tamper-resistant hardware, plus one or more X.509 certificates for the corresponding public key (or keys) with relevant attributes bound.
This means that every VxSuite component can prove its authenticity, notably to any other VxSuite component. For example, a smart card can prove to a VxAdmin that it is a valid election administrator card for a particular election, or a VxScan can export digitally signed cast vote records, which a VxAdmin can then verify are authentic before importing.
Certification relationships work as follows:
VotingWorks directly certifies the type of every component, i.e., VxAdmin machine, VxCentralScan machine, VxMark machine, VxScan machine, or smart card. This makes it such that only components that are “blessed” by VotingWorks can successfully communicate with each other. This is also important for defense-in-depth and separation of privileges, as one VotingWorks component type cannot act like another, e.g., a VxScan cannot sign an election configuration package like a VxAdmin can. Because machine certificates include a machine ID, it's also the case that, say, VxScan SC01 cannot sign CVRs as VxScan SC02.
VotingWorks further certifies every VxAdmin as bound to a particular jurisdiction, e.g., Warren County, Mississippi. This is important because VxAdmins program smart cards and should not be able to program cards for other jurisdictions.
Every smart card, in addition to being certified directly by VotingWorks, is certified by a VxAdmin during card programming. A card’s VxAdmin-issued certificate indicates 1) the jurisdiction that the card is bound to, 2) which user role the card has been programmed for, i.e. system administrator card, election manager card, or poll worker card, and 3) in the case of election manager and poll worker cards, the specific election that the card is bound to.
As a diagram:
VxCentralScan, VxMark, and VxScan, unlike VxAdmin, aren’t bound to a jurisdiction by VotingWorks. They’re bound and unbound to jurisdictions as they’re configured and unconfigured by election managers. When an election manager configures a VxCentralScan, VxMark, or VxScan for an election, the machine persists in its datastore the jurisdiction of the election manager card used to unlock it and then only accepts cards from that jurisdiction. When the machine is unconfigured for that election, the machine returns to a jurisdiction-agnostic state. This allows one jurisdiction to lend its unconfigured equipment to another, without giving a jurisdiction any power over machines configured for a different jurisdiction.
As described above, programmed smart cards have two certificates, one issued by VotingWorks and one issued by VxAdmin. We generate two key pairs on the card, one for each certificate. We could have chosen to certify the same key twice, but as will be described below, we wanted to have different usage policies for the two private keys, thus the issuance of certificates against two different keys. Smart cards also have PINs (with the exception of poll worker cards, unless an election official chooses to enable poll worker card PINs). These interface as follows:
The private key slot associated with the card’s VotingWorks-issued certificate is not PIN-gated, so anyone can immediately verify that a card is a VotingWorks-certified card.
The private key slot associated with the card’s VxAdmin-issued certificate is PIN-gated, so authentication requires PIN entry.
The card’s VxAdmin-issued certificate, on the other hand, is not PIN-gated, so it’s possible for a VotingWorks machine to know the card’s type and which election it’s bound to even before a PIN is entered. This allows the machine to display error messages like “election mismatch” immediately after card insertion and before PIN entry.
This does mean that an attacker could load a fake certificate onto a card such that the machine, at worst, displays an incorrect error message, but nothing more. Authentication would still fail as the public key in the certificate would not match the private key on the card. We take care not to fully trust the information in this certificate until we complete authentication.
We’ve taken inspiration from the NIST Personal Identity Verification (PIV) standard but haven’t strictly adhered to it. Our use case differs from the PIV standard in a number of ways, most notably in that cards are certified by two entities (VotingWorks and VxSuite), rather than just one. We also don’t need VxSuite smart cards to be interoperable with other PIV systems.
There's one additional card type, the vendor card, and it differs slightly in its cert structure from the other card types. VotingWorks uses vendor cards to access the vendor menu on machines, for low-level operations like machine key rotation. Vendor cards are programmed directly by VotingWorks and not by VxAdmin. Because of this, vendor cards do not have a VxAdmin-issued cert, but rather, a second VotingWorks-issued cert. The general term that we use for this second cert across all card types is the identity cert, as it confers user role, i.e., identity.
Vendor cards can be programmed to be either jurisdiction-specific or jurisdiction-agnostic.
We store our root VotingWorks certificate authority (CA) private key encrypted in a password vault, usable only by a few authorized engineers.
VxAdmin, VxCentralScan, VxMark, and VxScan are all built on devices with a Trusted Platform Module (TPM) 2.0, a chip that ships standard on modern Intel and AMD hardware. The TPM can keep cryptographic material secret inside its tamper-resistant boundary until a certain set of system conditions are met. Only once the TPM determines that the system meets a set of appropriate conditions—correct bootloader, kernel, kernel command line, etc.—does the TPM allow an application to ask it to perform signing operations with its contained secret key.
Our smart cards are JCOP 4 Java Cards, version 3.0.5. These Java Cards can generate 256-bit ECC key pairs such that the private key never leaves the card, while the public key is exported. The cards are capable of self-destructing if someone attempts to extract the private key at the hardware level.
Vendor cards, system administrator cards, and election manager cards always have PINs. Poll worker cards do not have PINs by default but can if the system administrator enables them.
PINs are auto-generated random 6-digit numbers, guaranteed not to be weak (e.g. not 11111, 123456, 121212).
After 5 incorrect PIN attempts, users have to wait 15 seconds before they can try again. For every incorrect PIN attempt after that, the wait time doubles (15 seconds → 30 seconds → 60 seconds and so on). After 15 incorrect PIN attempts, the card is completely locked and has to be reprogrammed. System administrators can adjust the number of incorrect PIN attempts allowed without lockout as well as the starting lockout duration. The number of incorrect PIN attempts is stored on the card in a tamper-resistant location (as opposed to on the machine), meaning that taking a card to another machine to gain extra attempts will not work. Removing and reinserting a card restarts the lockout timer.
The certificates that we use are effectively vouchers—a certificate authority (CA) vouches that a public key has certain properties, for example that it is the public key of an election manager card for a particular election. This vouching is done by signing data containing the public key and those properties. We use X.509 certificates as they are broadly used and well supported.
While we could use existing X.509 fields like Organization, Organizational Unit, and Serial Number, and wedge our data into those types, overloading existing fields that don’t quite match isn’t ideal and could lead to security issues from type confusion.
Instead, we use our own fields. We’ve registered with IANA to have a VotingWorks enterprise object identifier (OID), which is effectively a prefix for OIDs of the form 1.3.6.1.4.1.32473.123, where 1.3.6.1.4.1.32473 is the enterprise OID for an example organization, and 123 is the 123rd field defined by that organization.
Our IANA-assigned Private Enterprise Number (PEN) is 59817, which means that the enterprise OID, fully-prefixed, is 1.3.6.1.4.1.59817. See https://www.iana.org/assignments/enterprise-numbers/?q=59817.
Our custom fields are:
1.3.6.1.4.1.59817.1 — Component = admin, central-scan, mark-scan, scan, or card (the first four referring to machines)
1.3.6.1.4.1.59817.2 — Jurisdiction = {state-2-letter-abbreviation}.{county-or-town} (e.g., ms.warren or ca.los-angeles)
1.3.6.1.4.1.59817.3 — Card type = vendor, system-administrator, election-manager, poll-worker, or poll-worker-with-pin (vendor, system administrator, and election manager cards always have PINs)
1.3.6.1.4.1.59817.4 — Election ID = The ID of the election that an election card was programmed for*
1.3.6.1.4.1.59817.5 — Election date = The date of the election that an election card was programmed for
1.3.6.1.4.1.59817.6 — Machine ID = A VxAdmin, VxCentralScan, VxMark, or VxScan machine ID
*This is not the same election ID as displayed in machine footers. The election ID as it pertains to cards is the id
field in the election definition. The election ID as it pertains to machine footers is actually a hash, specifically <first-7-digits-of-hash-of-election-json>-<first-7-digits-of-hash-of-election-zip>
. The reason for not using the latter in card certificates is to avoid having to reprogram cards after election package edits for what is conceptually still the same election.
X.509 certificates eventually expire. We use the following expiry times:
Certificates directly issued by VotingWorks: 100 years
VxAdmin-issued system administrator card certificates: 5 years
VxAdmin-issued election manager and poll worker card certificates: 6 months
VotingWorks-issued vendor card certificates: 7 days
This means that system administrator cards will automatically expire after 5 years, election manager and poll worker cards will automatically expire after 6 months, and vendor cards will automatically expire after 7 days.
Configuration of VxSuite components begins at a secure VotingWorks facility. At this facility is a VotingWorks certification terminal, VxCertifier. VxCertifier is a fully offline air-gapped terminal with access to the root VotingWorks private key.
Using VxCertifier, VotingWorks prepares and certifies smart cards for use with VotingWorks machines:
VotingWorks installs the appropriate security module code on the card.
VotingWorks instructs the smart card to generate a key pair and export the public key.
The root VotingWorks CA certifies the card public key and saves the resulting certificate onto the card.
The card is now VotingWorks-certified but “blank” from the perspective of VotingWorks machines.
VotingWorks also certifies its machines at this facility. After a machine has been imaged with our latest software release, the machine boots into a configuration wizard. The software image includes the root VotingWorks CA certificate, so no extra work is required beyond imaging to “install” that certificate. Using the configuration wizard and VxCertifier, VotingWorks certifies machines:
VotingWorks machine code instructs the machine’s TPM to generate a key pair and export the public key.
VotingWorks machine code generates a certificate signing request (CSR) for that public key and writes the CSR to a USB drive.
On VxAdmin, VotingWorks additionally specifies a jurisdiction. That jurisdiction is included in the CSR.
The USB is plugged into VxCertifier. Through VxCertifier, the root VotingWorks CA certifies the machine public key and saves the resulting certificate onto the USB drive.
VxAdmin certificates are themselves CA certificates capable of creating additional certificates (necessary for smart card programming).
The USB drive is plugged back into the machine to be certified. The machine loads the certificate from the USB drive, verifies its correctness, and saves it onto its hard drive.
On VxAdmin, the configuration wizard also surfaces a prompt to program a first system administrator card to bootstrap the jurisdiction as, from here on out, the machine will need to be unlocked by a system administrator card in order to program any other cards.
Machines, a first system administrator card, and “blank” cards are shipped to jurisdictions.
In the field, election officials use their VxAdmin and their first system administrator card to program additional smart cards. Smart card programming involves the following steps:
VxAdmin retrieves the card’s VotingWorks-issued certificate and verifies that it was signed by VotingWorks using the VotingWorks CA certificate installed on every machine.
VxAdmin resets the card PIN, which further causes the card to clear all of its PIN-gated key slots, essentially unprogramming it if it was previously programmed.
VxAdmin instructs the card to generate a key pair and export the public key. This key pair is distinct from the key pair that VotingWorks generated.
The VxAdmin CA certifies the card public key, assigning relevant attributes like the jurisdiction, card type, and election if applicable, and saves the resulting certificate to the card.
VxAdmin also saves its own CA certificate onto the card (more on how this is used under Authentication).
Smart card authentication involves the following steps:
The machine retrieves the card’s VotingWorks-issued certificate and verifies that it was signed by VotingWorks using the VotingWorks CA certificate installed on every machine.
The machine retrieves 1) the card’s VxAdmin-issued certificate and 2) the certificate of the VxAdmin that programmed the card, which as noted in Smart Card Programming is also loaded onto the card. The machine verifies that the former (1) was signed by the latter (2). The machine also verifies that the latter is a valid VxAdmin certificate signed by VotingWorks using the VotingWorks CA certificate, establishing a chain of trust all the way up to a trusted root.
The machine verifies that the card has a private key that corresponds to the public key in the card’s VotingWorks-issued certificate by asking the card to sign a challenge with its private key and attempting to verify it with the public key.
The machine verifies that the card has a private key that corresponds to the public key in the card’s VxAdmin-issued certificate by asking the card to sign a challenge with its private key and attempting to verify it with the public key.
If the card has a PIN, the card will only proceed with this signature if provided the appropriate PIN. The VotingWorks machine thus asks the user for their PIN to perform this operation.
Throughout this process, the machine verifies that certificate fields are valid and consistent. It also verifies that the card jurisdiction and election match the machine jurisdiction and election, where relevant.
Machines automatically lock after they’ve been left idle for 30 minutes and also automatically lock after 12 hours, even if the user has been active. The user is given appropriate heads up. System administrators can select shorter time limits.
These time limits do not apply to unauthenticated screens like the “Insert Your Ballot” screen on VxScan.
Refer to the following code links for more details:
https://github.com/votingworks/vxsuite/tree/v4.0.0-release-branch/libs/auth— VxSuite authentication lib, a good starting point for all things authentication
https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/auth/src/dipped_smart_card_auth.ts — High-level authentication state management for VxAdmin and VxCentralScan
https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/auth/src/inserted_smart_card_auth.ts — High-level authentication state management for VxScan
https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/auth/src/java_card.ts — Java Card implementation
https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/auth/src/certs.ts — Certificate configuration
https://github.com/votingworks/vxsuite/blob/v4.0.0-release-branch/libs/auth/src/cryptography.ts — OpenSSL commands underlying various authentication and signing operations
https://github.com/votingworks/vxsuite-complete-system/blob/v4.0.0-rc2/config/vendor-functions/basic-configuration.sh — The production machine configuration wizard
https://github.com/votingworks/vxsuite/tree/v4.0.0-release-branch/libs/auth#scripts — A summary of the scripts in the authentication lib, many of which are used for production configuration
https://github.com/votingworks/openfips201 — The applet that we’re installing onto our Java Cards
This section describe the overall system security architecture of VxSuite and the measures taken to thwart attacks on the proper operation of elections on VxSuite.
On VxSuite machines, there are no passwords for election administrators to use – only smart cards as described previously. The smart cards all use 256-bit elliptic-curve digital signatures, which makes them cryptographically strong. In addition, the PINs that gate usage of those cards follow the NIST recommendations for authentication – 6 digits long, randomly generated (as opposed to chosen by the user who might use a birthdate), and never a weak PIN like 000000.