Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
VxSuite can tabulate a wide variety of ballot designs as long as they conform to the following requirements:
Ballot size must be one of the available system options
Ballot must include correctly formatted timing mark borders
Ballot must include an appropriately positioned metadata QR code
Ballot must use a specific bubble shape
Ballot must adhere to system limits
There are six valid ballot sizes. All are 8.5 inches in width, but vary in height:
Letter
11"
8.5"
Legal
14"
8.5"
Custom 17"
17"
8.5"
Custom 18"
18"
8.5"
Custom 21"
21"
8.5"
Custom 22"
22"
8.5"
These lengths correspond to the length specified in the Ballot Layout within the election definition.
Every ballot must have timing mark borders on both front and back. The timing marks and page margins are strictly defined:
Timing Mark Width
3/16 in.
Timing Mark Height
1/16 in.
Page Margin (Top, Bottom)
12pt (1/6 in.)
Page Margin (Left, Right)
5mm
As for number of timing marks, there are always 34 in the top and bottom borders. The number of marks in the left and right borders varies based on the length of the ballot, and should be calculated by the formula (# inches * 4) - 3. For example, a letter-sized ballot (11 inches) should have 41 left and right timing marks whereas a legal-sized ballot (14 inches) should have 53 left and right timing marks.
The timing marks must be aligned to the margins of the page and evenly spaced.
The ballot must include a QR code which contains key metadata about the ballot.
The QR code includes ballot metadata:
Precinct Index - corresponds to a precinct in the election definition
Ballot Style Index - corresponds to a ballot style in the election definition
Page Number - page number within the ballot style
Test Ballot Flag - indicates whether the ballot is a test ballot or an official ballot
Ballot Type (Precinct, Absentee, or Provisional)
In addition, the QR code includes the ballot hash. The ballot hash ensures that the ballot was generated from the same election definition that will be used to interpret the ballot.
For full specifications on how to generate readable QR codes, view the ballot encoder documentation.
The interpreter looks for ballots in the bottom-left corner of the ballot. The detection area is a square whose sides are 1/4 of the ballot width.
In order to be interpreted correctly, bubbles must meet the following dimensions:
Width
0.20"
Height
0.13"
Border Radius
0.7"
Line Thickness
1px (0.265mm)
The timing mark borders define an abstract grid that VxSuite uses to assign coordinates to positions on the ballot. In the election definition, bubble positions and other ballot regions are specified using these grid coordinates. The ballot grid is an XY grid with the origin in the top-left corner. For example, the coordinate (5, 3) would correspond to the following position on the ballot:
Notes:
Coordinates correspond to the center of each timing mark
Coordinates specify the center of each bubble
Fractional coordinates are allowed
Below is a complete ballot that, when paired with the appropriate election definition, could be interpreted within VxSuite:
The election package contains all of the information that defines an election. VxAdmin is configured by inserting a USB drive and selecting an election package from the drive.
After VxAdmin is configured, the election package can then be digitally signed and exported onto a USB drive in order to configure VxScan, VxCentralScan, and VxMark. The verifies that the election package is legitimate. The other devices require that the signature is present. As a result, an election package from outside the system cannot be used to directly configure VxScan, VxCentralScan, or VxMark.
The election package is a zip archive (a .zip
file). The zip archive contains 6 files:
Election Definition (election.json
)
App Strings (appStrings.json
)
Audio IDs (audioIds.json
)
Audio Clips (audioClips.jsonl
)
System Settings (systemSettings.json
)
Metadata (metadata.json
)
The election definition file includes the information that defines the election and ballots, including but not limited to definitions for contests, ballot styles, precincts, parties, multi-lingual ballot translations, and ballot layouts. The system accepts two formats - the format or the . When using the Ballot Definition CDF, some features are not available.
The election definition file is required to be present in the election package.
The app strings file contains all voter-facing strings in the user interface and their translations. For example, at the beginning of the voter flow in VxMark there is a button labelled, by default, "Start Voting." If the voter has set VxMark to another language, however, the button will display the translation for that language specified in the app strings file. Additionally, the default English text can be overridden by specifying a different English value in the app strings file.
In the case of specifying a Spanish translation and overriding the English translation for "Start Voting", which has an internal key of buttonStartVoting
, the following values and overall structure would appear in the app strings file:
Notes:
Voter-facing strings that appear on ballots (contest names, candidate names, the name of the jurisdiction, etc.) are not included in the app strings file because they are already included in the election definition.
The app strings file is optional. If not provided, default English strings will be used.
The audio IDs file is a JSON file structured identically to the app strings file except that instead of containing text values for each voter string, it contains audio IDs for each voter string. Each audio ID corresponds to an entry in the audio clips file.
Each row of the audio clips file contains one audio clip with relevant metadata. The audio itself appears as an MP3 file encoded in base 64. For example, a row representing an audio clip for the "Start Voting" button would appear as:
Together, the two files allow specifying audio playback for any text read to a voter in audio mode, including both non-English translations and English overrides.
The audio IDs and audio clips files are optional. If they are not provided, audio playback will be disabled.
The system settings file contains settings which are not specific to an election definition but do impact machine behavior.
Authentication Settings
arePollWorkerCardPinsEnabled
- When set to true
, poll worker cards are created with PINs which are then required when authenticating.
inactiveSessionTimeLimitMinutes
- Sets the number of minutes after which machines automatically lock due to inactivity.
numIncorrectPinAttemptsAllowedBeforeCardLockout
- Sets the number of times that a PIN can be entered incorrectly before the user has to wait extra time to retry.
overallSessionTimeLimitHours
- Sets the maximum number of hours for any session, regardless of activity.
startingCardLockoutDurationSeconds
- Sets the number of seconds that the user is locked out from retrying a PIN after the number of failed attempts specified by numIncorrectPinAttemptsAllowedBeforeCardLockout
. Each subsequent failed attempt triggers a lockout double the length of the previous lockout.
Scanning Thresholds
definite
- Specifies the percentage of a bubble that needs to be filled in for the tabulators to consider it a mark.
writeInTextArea
- Specifies the percentage of the write-in area that needs to be filled in for the tabulators to consider it a write-in. This is only relevant for jurisdictions that allow unmarked write-ins i.e write-ins without an accompanying mark.
Adjudication Reasons
precinctScanAdjudicationReasons
- Specifies the reasons that a ballot scanned at VxScan should be flagged for adjudication. Supported reasons are overvotes, undervotes, blank ballots, or unmarked write-ins.
centralScanAdjudicationReasons
- Specifies the reasons that a ballot scanned at VxCentralScan should be flagged for adjudication. Supported reasons are overvotes, undervotes, blank ballots, or unmarked write-ins.
disallowCastingOvervotes
- When set to true
, scanners will always reject overvoted ballots. When set to false
, VxScan will allow a voter to choose whether to reject or cast an overvoted ballot, and VxCentralScan will allow an election manager to choose whether to reject or tabulate an overvoted ballot.
allowOfficialBallotsInTestMode
- When set to true
, official ballots will not be rejected in test mode. The setting is for jurisdictions where testing must take place on official ballots.
Other
precinctScanEnableShoeshineMode
- When set to true
, VxScan will run in "shoeshine mode," which will scan the same ballot repeatedly. Instead of ejecting the ballot after scanning it, VxScan will move it back to the input tray and scan it again. This mode is used only for internal testing and certification testing.
castVoteRecordsIncludeOriginalSnapshots
- When set to true
, scanners will include "original" snapshots in the CVR export for each ballot. This includes extra information about the marks on the ballot before contest rules are applied as specified in the CVR CDF. This extra information increases the size of the CVR export, degrading performance.
castVoteRecordsIncludeRedundantMetadata
- When set to true
, scanners will include election and system metadata in each ballot's CVR as specified in the CVR CDF, rather than just including it once for the entire export. This extra information increases the size of the CVR export, degrading performance.
disableVerticalStreakDetection
- Disables the warning when streaks are detected when scanning ballots (which indicates that the scanner needs to be cleaned). Used as a failsafe in case of erroneous warnings.
The system settings file is optional. If not provided, the following default settings will be used:
The metadata file is a JSON file that contains a single key, version
. In the current software version, the value will only ever be "latest"
. In other words, the metadata file is a JSON file with the contents:
In future software versions, the "version"
value will be used to differentiate versions of the election package that correspond to different software versions. For example, if a user flow is updated, a new app string may be added and the "version"
value will indicate which app strings to expect.
There are two hashes for each election which are both critical to operation and security of each election.
The ballot hash is the SHA256 hash of the election definition file and represents a snapshot of all election-specific data which determines the ballot, including contest definitions and ballot layout. Ballots must include a machine-readable version of the ballot hash (e.g. a QR code or a pattern of timing marks). When a ballot is scanned, the scanner checks to make sure the ballot has the expected ballot hash. The ballot hash from the ballot must match the ballot hash from the scanner's configured election definition.
If any election content changes, the ballot hash will change, meaning the system will consider it to be a new election definition, and any ballots with a different ballot hash will not scan. The system's strict checking of the ballot hash prevents situations where a change to the election definition leads to ballots being miscounted (for example, if the order of two candidates is swapped). As a result, any changes to the election definition require reprinting all ballots.
The election package hash is the SHA256 hash of the entire election package, which includes both the election definition and all other files. The election package hash is displayed on screen to election managers and system administrators. It allows the user to ascertain which election package was used to configure a machine. The election package hash is not used during ballot scanning.
For example, imagine that an election administrator wants to change the audio clip for candidate's name. The audio clip is not a part of the election definition but it is a part of the election package, so the ballot hash remains the same while the election package hash changes. The election administrator can reconfigure VxMark with an updated election package but does not have to reprint ballots.
The election ID shown on screen and included in printed reports contains a shortened version of both the ballot hash and the election hash in the format: {ballot hash}-{election hash}
. For example, with ballot hash 083e2e0afbb19191a4d2850562ddef050ff860b0d61acee15d3bb26954932941
and election hash db5c379b71accbf991e42ab23d26202f88b2539b6c69b814a0d3c8dc9f4072dc
, the election ID would be: 083e2e0-db5c379
. This enables identification of both the specific election package and election definition that a machine is configured with.
The election definition within the election package can be a JSON Ballot Definition CDF Version 1.0 file. The full specification is defined by NIST and can be .
VxSuite accepts the Ballot Definition CDF Version 1.0 as defined by NIST without any data extensions. VxSuite requirements differ from the NIST CDF schema only in the following respects:
Some fields not required in the NIST CDF are required in VxSuite
Some enumeration values are more restricted in VxSuite than in the NIST CDF
Some classes and attributes are ignored in VxSuite
The exact VxSuite schema is defined as a which is derived from the . The differences between the two are documented in the below tables.
The Ballot Definition defines all geographies as GpUnit
s, whereas the VxSuite Election Definition defines various geographies.
Contest term descriptions (e.g. “2 years”) will always show up in English because there is no field in the CDF for internationalized term descriptions
The full party name will be displayed for each candidate on VxMark (e.g. “Democratic Party” instead of “Democrat”) because the CDF only has one attribute for party name. In contrast, the VxSuite format supports two separate fields, one for the full name and one for showing on the ballot.
When adjudicating write-ins, the highlighted contest option area will be set to a default because there is no field in the CDF for this parameter.
Seal images are not supported in the CDF, so no seal will be shown in association with the election.
After a voter is done making vote selections with VxMark, the machine will print a ballot representing the selections. The voter reviews the ballot, after which it is deposited into the attached ballot box. Eventually, those ballots are scanned at VxScan or VxCentralScan.
The ballot displays information about the election, metadata about the ballot, and the voter's selections. The selections are both displayed and encoded in the QR code.
The blank space at the top of the ballot is included so that all of the ballot content is visible when VxMark presents the ballot for the voter to review (since the top part of the ballot is held in the machine).
The language codes in the app strings file are the for supported VxSuite languages: English, Spanish, Simplified Chinese, and Traditional Chinese.
The keys for the various user interface app strings (e.g. buttonStartVoting
) are defined in VxSuite's .
When importing a Ballot Definition CDF file, attributes will be mapped to the and used across the system in the exact same way.
Most attributes in the Ballot Definition CDF that could require translation are defined with the CDF InternationalizedText
class which allows specifying variants for each language. InternationalizedText
maps directly onto VxSuite's . When an InternationalizedText
attribute is mapped to a ballot string, the English version is used as the default which will appear on reports and administrator interfaces while the translations are maintained in the election definition for multi-language experiences on voter devices.
Some geographies must exist in the Ballot Definition CDF file to be used in VxSuite. There must be one GpUnit
of type state
and one of type county
to populate the relevant metadata on the .
Any GpUnit
of type precinct
is mapped to a .
Any GpUnit
with an associated contest is mapped to a .
Because VxSuite does not utilize any data extensions to the Ballot Definition CDF, some information cannot be included when using the Ballot Definition CDF. The missing information creates some limitations when using the Ballot Definition CDF when compared to the format:
If VxMark is being used in a language other than English, the resulting ballot will be multi-lingual and feature the other language. All pieces of text that appear on the VxMark ballot may have translations specified in the , which will then be used when printing the ballot.
The QR codes on VxMark ballots are different than the in that they actually contain voter selections in addition to the ballot metadata. For full specifications on how metadata and selections are encoded, view the .
ElectionType
general, primary
ReportingUnitType
county, precinct, split-precinct, state, other
ActivationContest
ActivationOption
AnnotatedString
AnnotatedUri
BallotMeasureType
CandidatePreElectionStatus
Coalition
ContactInformation
DayType
ElectionAdministration
GeoSpatialFormat
Hours
LatLng
OfficeGroup
OfficeTermType
OrderedHeader
PartyContest
PartyOption
PartyPreferenceContest
PartyRegistration
Person
RetentionContest
Schedule
ShortString
SpatialDimension
SpatialExtent
StraightPartyContest
StraightPartyRuleset
TimeWithZone
VoteVariation
BallotDefinition
IsTest, Notes, OfficeGroup, Person, TestType
BallotMeasureContest
Abbreviation, BallotSubTitle, ContStatement, EffectOfAbstain, ExternalIdentifier, HasRotation, InfoUri, OtherType, OtherVoteVariation, PassageThreshold, ProStatement, SequenceOrder, SummaryText, TotalSubUnits, Type, VoteVariation
BallotMeasureOption
ExternalIdentifier, SequenceOrder
BallotStyle
ImageUri, Purpose
Candidate
CampaignSlogan, ContactInformation, ExternalIdentifier, FileDate, IsIncumbent, IsTopTicket, PartyId, PersonId, PreElectionStatus, ReadName
CandidateContest
Abbreviation, BallotSubTitle, ExternalIdentifier, HasRotation, NumberElected, NumberRunoff, OtherVoteVariation, RanksAllowed, SequenceOrder, TotalSubUnits, VoteVariation
CandidateOption
ExternalIdentifier, SequenceOrder
Election
ContactInformation, OtherType
Office
ContactInformation, Description, ElectionDistrictId, ExternalIdentifier, FilingDeadline, IsPartisan, OfficeHolderPersonIds
OrderedContest
OrderedContestOptionIds
Party
Color, ContactInformation, ExternalIdentifier, IsRecognizedParty, LeaderPersonIds, LogoUri, PartyScopeGpUnitIds, Slogan
ReportingUnit
AuthorityIds, ContactInformation, ElectionAdministration, ExternalIdentifier, IsDistricted, IsMailOnly, Number, OtherType, PartyRegistration, SpatialDimension, TotalSubUnits, VotersRegistered
Term
EndDate, StartDate, Type
Election.ExternalIdentifier
Election.id
Election.Type
Election.type
Election.Name
Election.title
Election.StartDate
Election.date
GpUnit.id
County.id, Precinct.id, District.id
GpUnit.Name
Election.state, County.name, Precinct.name, District.name
Party.id
Party.id
Party.Name
Party.name
Party.Name
Party.fullName
Party.Abbreviation
Party.abbrev
Contest.id
Contest.id
Contest.BallotTitle
Contest.title
Contest.ElectionDistrictId
Contest.districtId
CandidateContest.VotesAllowed
CandidateContest.seats
CandidateContest.ContestOption.IsWriteIn
CandidateContest.allowWriteIns
CandidateContest.PrimaryPartyIds
CandidateContest.partyId
Office.Term.Label
CandidateContest.termDescription
Candidate.id
Candidate.id
Candidate.BallotName
Candidate.name
CandidateOption.EndorsementPartyIds
Candidate.partyIds
BallotMeasureContest.FullText
YesNoContest.description
BallotMeasureContestOption.id
YesNoContestOption.id
BallotMeasureContestOption.Selection
YesNoContestOption.label
BallotStyle.ExternalIdentifier.Value
BallotStyle.id
BallotStyle.GpUnitIds
BallotStyle.districts, BallotStyle.precincts
BallotStyle.PartyIds
BallotStyle.partyId
BallotStyle.Language
BallotStyle.languages
BallotFormat.ShortEdge
BallotLayout.width
BallotFormat.LongEdge
BallotLayout.height
OrderedContest.ContestId
GridPosition.contestId
PhysicalContestOption.OptionPosition.Sheet
GridPosition.sheet
PhysicalContestOption.OptionPosition.Side
GridPosition.side
PhysicalContestOption.OptionPosition.X
GridPosition.column
PhysicalContestOption.OptionPosition.Y
GridPosition.row
PhysicalContestOption.ContestOptionId
GridPositionOption.optionId
PhysicalContestOption.ContestOptionId
GridPositionWriteIn.writeInIndex
PhysicalContestOption.WriteInPosition.X
GridPositionWriteIn.writeInArea.x
PhysicalContestOption.WriteInPosition.Y
GridPositionWriteIn.writeInArea.y
PhysicalContestOption.WriteInPosition.W
GridPositionWriteIn.writeInArea.width
PhysicalContestOption.WriteInPosition.H
GridPositionWriteIn.writeInArea.height
Election Seal
The seal included in the election definition
Ballot Title
The title is either "Unofficial Test Ballot" or "Official Ballot" depending whether VxMark is in test mode
Election Metadata
Includes the election title, election date, state, and county
Ballot Metadata
Includes the precinct name, the ballot style identifier, and the ballot identifier. The ballot identifier is a random UUID assigned by VxMark.
QR Code
See QR Code
Contest Title
The contest title
Selection
The name of the candidate or label of the yes-no contest option that the voter selected
Party
The short party name associated with the selected candidate
BallotDefinition
BallotFormat, Election, GpUnit, and Party are required
BallotFormat and Election must have a length of 1
GpUnit must have a minimum of length 1
BallotMeasureContest
BallotTitle, ContestOption, and FullText are required
ContestOption must have a length of 2
BallotStyle
ExternalIdentifier is required and must have a length of 1
Language must have a minimum length of 1
CandidateContest
BallotTitle and ContestOption are required
ContestOption must have a minimum of length 1
PrimaryPartyIds must have a length of 1 if it exists
CandidateOption
CandidateIds must have a length of 1
Election
BallotStyle, Contest, and ExternalIdentifier are required and all must have a minimum length of 1
GpUnit
At least one GpUnit must be defined with the type "state"
At least one GpUnit must be defined with the type "county"
Office
Term is required
OptionPosition
Sheet is required
OrderedContest
Physical is required and must have a minimum length of 1
Party
Abbreviation is required
PhysicalContest
PhysicalContestOption is required and must have a minimum length of 1
PhysicalContestOption
ContestOptionId is required
ReportingUnit
Name is required
Term
Label is required
VxAdmin supports a variety of results exports.
Tally reports contain contest results for the full election or a specific subset of ballots. They can be printed directly from VxAdmin, exported as PDFs, or exported in CSV format. In addition, full election results can be exported as an Election Results Reporting CDF JSON file.
Ballot count reports include only ballot counts, without contest results. They can also be printed directly from VxAdmin, exported as PDFs, or exported in CSV format.
The write-in adjudication report acts as a summary of all write-in adjudication activity or as a "scatter report." The tally reports consolidates write-in candidates whose vote totals are too small to affect the contest outcomes. The counts for those write-in candidates are available in the write-in adjudication report. The write-in adjudication report may be printed directly from VxAdmin or exported as a PDF.
For all VxAdmin reports, the report is either "Official" or "Unofficial." If the report is exported before the results are marked as official in the application, the results are "Unofficial." If the report is exported after the results are marked as official in the application, the results are "Official." All visual reports have "Official" or "Unofficial" included in the title and all exported files will have "official" or "unofficial" included in the filename.
When polls open or close on VxScan, a tally report is printed. The tally report contains the tally results (pre-adjudication) of all ballots scanned at the scanner.
The tally report header contains the following information:
Title - Includes the type of the polls report and the name of the precinct, or "All Precincts" if VxScan is configured to accept ballots from all precincts
Subtitle - For primary elections, includes the full party name or "Nonpartisan Contests"
Election Info - The title, date, and location of the election
Timestamps - The first timestamp indicates when the poll status changed and the second indicates when the report was printed. In most cases these times are the same, but in some cases the report may be printed later.
Election ID - The election ID on the report is a concatenation of the ballot hash and election package hash and specifies exactly which election definition and election settings that the report corresponds to
Certification Signatures - The space is provided for poll workers or election officials to sign the report in accordance with local statute.
The ballot counts table provides the count of hand marked vs. machine marked ballots. For elections with multi-sheet ballots, it provides counts per sheet.
Each candidate or contest option appears in a row below the contest header. Because results at VxScan are not yet adjudicated, all write-ins are grouped under the "Write-In" bucket (except unmarked write-ins, which are reported as undervotes until adjudicated at VxAdmin).
As a rule, the sum of the number of votes for all candidates, the number of undervotes, and the number of overvotes will equal the number of total possible votes for the contest, which is the number of ballots times the number of selections allowed.
When voting is paused or resumed, VxScan prints a report containing the ballot count. Tally results are never included. The header is structured in the same way as the polls opened and closed reports.
The VotingWorks voting system (a.k.a. VxSuite) consists of four primary components:
VxAdmin: election setup and election results manager
VxMark: ballot-marking device (BMD)
VxScan: precinct scanner
VxCentralScan: batch scanner
Using VxSuite for an election begins with generating an election package and hand marked ballots using an external system.
VxAdmin is where the election administrator performs election setup tasks and manages election results. At the beginning of an election, the user configures VxAdmin with an election package. Once configured, VxAdmin is used for two key election setup tasks:
Exporting a copy of the election package to USB drives with a digital signature. The election package is used to configure VxMark, VxScan, and VxCentralScan, and it must be digitally signed by VxAdmin.
Programming role-based smart cards that will be used to authenticate on all machines. While the "System Administrator" role is election-agnostic, the "Election Manager" and "Poll Worker" roles are election-specific and cards must be programmed for every election.
VxAdmin is later used to load, store, and tabulate cast vote records from the scanners. The results are available for review or export in several results formats. Once tabulation is complete, election administrators can mark results as official, after which no new results can be added.
VxMark is the system's ballot-marking device (BMD) that provides an accessible voting experience. At the beginning of an election, it is configured with an election package from VxAdmin. Once configured, a voter can make vote selections in various interaction modes according to their needs.
The following input modes are supported:
Touch, using the touchscreen
Tactile, using the accessible controller
Limited Dexterity, using a sip-and-puff device or other dual-switch input
The following output modes are supported:
Visual, with options to change color contrast and text size
Audio, with navigation instructions and contest details read to the user over headphones
The voter can also adjust the language based on translations included in the election package.
After the voter finishes their vote selections, VxMark prints a machine marked ballot and presents it to the voter. The ballot is scanned (but not cast) so the interpreted results can be presented to the voter on-screen. After reviewing the ballot and confirming their selections, the ballot is cast and ejected into the attached ballot box. At a later time, depending on election procedures, the ballot will be removed from the ballot box and tabulated at one of the system's scanners.
VxScan is the system's precinct scanner. At the beginning of an election, it is configured with an election package from VxAdmin. The election package specifies the ballot layouts. The polls are opened by a poll worker after which casting ballots is allowed. Opening polls prints a tally report which is empty because no ballots have been scanned, a.k.a. the zero report.
Voters cast ballots by inserting their ballots into the scanner in any orientation. After interpreting the scanned ballot, the scanner will drop the ballot into the ballot box and inform the voter that their ballot was successfully cast. During voting, VxScan continuously exports cast vote records to an attached USB drive.
If the election is configured to support second-chance voting and the ballot triggers a configured adjudication reason (e.g. it has an overvote), the scanner will hold the ballot while the voter is notified of the issues on their ballot. The voter then chooses whether to cast their ballot or return it to update or spoil.
When polls are closed, the CVR export is completed and a polls closed report prints. The polls closed report includes the vote tallies of all ballots cast at the scanner while polls were open. Unlike the vote tallies eventually exported from VxAdmin, the vote tallies at VxScan do not contain any post-voting adjudication information such as write-in adjudication. The CVR export on the USB drive is then taken to VxAdmin for adjudication, aggregation, and reporting.
VxCentralScan is the system's batch scanner, often used to scan absentee or provisional ballots. At the beginning of an election, it is configured with an election package from VxAdmin. The election package specifies the ballot layouts.
Ballots are inserted in the batch scanner's hopper and a batch scan is triggered from VxCentralScan. The ballots are scanned and interpreted in succession until the hopper is empty. If a ballot triggers a configured adjudication reason (e.g. it has an overvote), scanning will pause and the ballot will be displayed on screen, at which point the user can choose to tabulate the ballot anyway or remove it, untabulated.
After scanning is complete, the user can export the cast vote records to a USB drive and take the USB drive to VxAdmin for adjudication, aggregation, and reporting.
The VxSuite Election Definition is a data format for defining an election that is specific to VxSuite. It is a JSON file which defines the essential features of an election - metadata, contests, parties, precincts, districts, ballot styles, candidates, and more. In addition to defining that basic structure of the election, the format contains translations for any text which may appear on the ballots and ballot layouts to map the bubbles on each ballot to contest options.
The Election
entity is the top-level entity that contains all other entities.
ballotLayout
Physical ballot metadata
ballotStrings
ballotStyles
All ballot styles
contests
All contests
gridLayouts
county
County metadata
date
string
- YYYY-MM-DD
Date of the election
districts
All districts
id
string
Unique identifier
parties
All parties
precincts
All precincts
seal
string
- SVG file format
Seal for the election
state
string
Name of the state
title
string
Title of the election
type
string
- "general" or "primary"
Type of the election
The ballot layout entity includes basic information about the physical ballots used for the election.
paperSize
string
Indicates physical length of the ballot
metadataEncoding
string
Indicates how the ballot metadata will be encoded on the ballot
The paperSize
attribute accepts the following valid options:
The metadataEncoding
attribute must be "qr-code".
Each ballot style corresponds to a single- or multi-sheet ballot. The contests on a ballot style are determined by its associated districts - every contest belonging to an associated district is considered a part of the ballot style. A ballot style may be used in multiple precincts, one ballot style might correspond to multiple ballot PDFs that have identical contest layouts but different precinct labels.
id
string
Unique identifier
precincts
The IDs of all precincts which use the ballot style
districts
The IDs of all districts whose contests are included in the ballot style
partyId
Optional. The ID of the party to which the ballot belongs, if a primary
languages
array
- string
Optional. The language codes for the languages covered by the ballot style
There are two types of contests - candidate contests and yes-no contests. Both types share core attributes:
id
string
Unique identifier
districtId
The associated district of the contest such as a state, county, or ward
title
string
Title of the contest
type
string
- "candidate" or "yesno"
Type of the contest
In a candidate contest, the voter makes a selection between pre-defined candidates or write-in options. The following attributes extend the shared Contest attributes:
seats
number
The number of selections a voter can make
candidates
Candidate options for the contest
allowWriteIns
boolean
Whether the contest allows write-ins
partyId
Optional. The ID of the party to which the contest belongs, if a primary
termDescription
string
Optional. Description of the term of the position, such as "For three years"
id
string
Unique identifier
name
string
Name as it appears on the ballot
partyIds
Optional. The IDs of the parties associated with the candidate. The party name will appear next to the candidate
In a yes-no contest, also known as a ballot measure, the voter makes a selection between two options. The following attributes extend the shared Contest attributes:
id
string
Contest description
yesOption
"Yes" option
noOption
"No" option
id
string
Unique Identifier
label
string
Label e.g. "Yes" or "No"
One and only one county is associated with each election.
id
string
Unique Identifier
name
string
Name e.g. "Choctaw County"
Districts are used to define levels at which a contest takes place. For example, an election may have districts defined for the state, county, town, and ward levels. Different contests can be associated with each of those levels.
id
string
Unique Identifier
name
string
Name e.g. "State of Mississippi"
Parties are used in the data model either to associate candidates with a party, associate ballot styles with a party for a primary, associate contests with a party for a primary
id
string
Unique Identifier
name
string
Short name which will appear on the ballot besides candidates e.g. "Republican"
fullName
string
Full name which will appear in reports and in the titles of ballots e.g. "Democratic Party"
abbrev
string
Abbreviation for a party e.g. "R"
id
string
Unique Identifier
name
string
Name e.g. "Fire Station"
The ballotStrings
object contains the translations for any text which may appear on the ballot. The text falls into one of two categories: instructional text or election-specific text.
Examples of instructional ballot text include:
"To vote, completely fill in the oval next to your choice."
"Vote for up to 3",
"Official Absentee Ballot"
Although the system does not use the hand marked ballot instructional text for any purpose, it must be included in the election definition for security purposes. When included, it becomes a part of the ballot hash and cannot be changed without invalidating older ballots.
The core data model already includes names and labels. For example, the Precinct entity already has a name
attribute. The names within the data model are used by default in the system in reports and administrative menus. The translations for all of these names are within the ballotStrings
and are important for two main reasons:
The language-specific strings are used to accommodate multi-lingual voting on VxMark
Including the translations in the election definition means they are included in the ballot hash and cannot be changed without invalidating older ballots.
The election-specific ballotStrings
recognized by the system are the following:
Ballot Language
ballotLanguage
string
Ballot Style IDs
ballotStyleId
Candidate Names
candidateName
Contest Descriptions
contestDescription
Contest Option Labels
contestOptionLabel
Contest Terms
contestTerm
Contest Titles
contestTitle
County Name
countyName
string
District Names
districtName
Election Date
electionDate
string
Election Title
electionTitle
string
Party Full Names
partyFullName
Party Names
partyName
Precinct Names
precinctName
State Name
stateName
string
The language codes used are the IETF language tags for supported VxSuite languages: English, Spanish, Simplified Chinese, and Traditional Chinese.
Grid layouts describe where the bubbles and write-in areas exist on each ballot style. They must correspond exactly to the ballots used for the election in order for interpretation to succeed. For more information on the acceptable ballot format and its relationship to grid coordinates, see Hand Marked Ballots.
gridLayouts
is an array
which contains entities with the following attributes:
ballotStyleId
Identifies the ballot style
optionBoundsFromTargetMark
Describes where the entire contest option area sits relative to the bubble
gridPositions
Describes bubble and write-in positions on the ballot
The optionBoundsFromTargetMark
field specifies an Outset
entity. The purpose is to give the distance from the bubble to edges of the entire contest option e.g. the area that includes the bubble, the corresponding candidate's name, and some padding. The system uses this area during write-in-adjudication to highlight specific contest options for review.
top
number
Distance from center of bubble to top of option bounds
right
number
Distance from center of bubble to right of option bounds
bottom
number
Distance from center of bubble to bottom of option bounds
left
number
Distance between center of bubble to left of option bounds
Grid positions are defined positions on the ballot corresponding to bubbles that can be marked by a voter. The row
and column
coordinates of each grid position denote where the bubble is printed within the abstract "grid" created by the timing marks on the outer edges of the ballot. There are two types of positions - a standard option position corresponding to a candidate or option in the election definition or a write-in position corresponding to a write-in bubble and area on the ballot.
type
string
- "option"
Indicates this is a standard option position
sheetNumber
number
1-indexed sheet number within the ballot style
side
string
- "front" or "back"
Indicates side of the sheet
column
number
Column position of the bubble in the timing mark grid
row
number
Row position of the bubble in the timing mark grid
contestId
Contest identifier
optionId
Option identifier
type
string
- "write-in"
Indicates this is a write-in option position
sheetNumber
number
1-indexed sheet number within the ballot style
side
string
- "front" or "back"
Indicates side of the sheet
column
number
Column position of the bubble in the timing mark grid
row
number
Row position of the bubble in the timing mark grid
contestId
Contest identifier
writeInIndex
number
An index of the write-in position within the list of write-in positions for a contest (zero-indexed)
writeInArea
Area of the ballot to scan for a write-in
In some jurisdictions, a write-in can only be counted if the associated bubble is filled in as for any other option. In other jurisdictions, a write-in must be counted even if the bubble is not filled. In order to detect these write-ins, a writeInArea
is defined for each write-in grid position. The Rectangle
specifies the area.
x
number
Column start of the rectangle
y
number
Row start of the rectangle
width
number
Width of the rectangle
height
number
Height of the rectangle
Election definition examples are located in the vxsuite
repository, such as here.
The full election tally reports can be exported as JSON Election Results Reporting CDF results. The feature is only available for the full election results and not grouped or filtered results. The export follows the CDF specification without extensions. The CDF fields are used as follows:
The write-in adjudication report includes the results of all write-in adjudication organized by contest:
The values in the contest table above are as follows:
Before adjudication, write-ins are treated as votes for a generic write-in candidate and will appear in tally reports as "Unadjudicated Write-In." After adjudication, the new value will be reflected on tally reports:
When adjudicated for an official candidate, the write-in will be added to the official candidate count just as a regular vote for the candidate would be
When adjudicated for a write-in candidate, either:
The write-in will appear in the adjudicated write-in bucket if it is not relevant to the results
When invalid, the write-in becomes an undervote
Unmarked write-ins behave differently in relationship to the tally results because they cannot be considered a valid vote before adjudication. As a result, they are considered undervotes before adjudication. If an unmarked write-in is adjudicated as invalid, then tally results will not change. If an unmarked write-in is adjudicated for a candidate, the unmarked write-in is no longer an undervote and is added to the vote total for that candidate.
Also note that, because the write-in adjudication report includes unmarked write-ins, the "Unadjudicated Write-In" count on tally reports may be less than the "Not Adjudicated" count on the write-in adjudication report. The initial difference is the count of unmarked write-ins.
Both tally reports and ballot count reports can be exported in comma-separated values (CSV) format.
In the CSV tally report, the vote total for each candidate or contest option is listed in a single row. In addition, there are rows for overvote and undervote totals for a contest.
For example, here is an excerpt of a tally report including the results for one contest:
If manual results were entered, two additional columns will be added - "Scanned Votes" and "Manual Votes." These columns denote which votes for each selection came from the scanners vs manual entry.
The results of write-in adjudication are always included in the CSV exports. All write-in candidates will appear with their adjudicated name and a UUID assigned by VxAdmin. Unadjudicated write-in candidates will appear as "Unadjudicated Write-In" with the ID "write-in". Unlike in the printed reports, write-in candidates are never consolidated.
Any filters or groupings which apply to each row will be indicated in the row itself by metadata columns. For example, in the example export above, the "Precinct" column lists which precinct group each row represents.
If a row is filtered by a single attribute value (e.g. represents one precinct rather than multiple precincts) then the following basic metadata fields are used:
If a row is filtered by multiple attribute values, the following columns may be used:
In cases as above, where a row value includes comma-separated values, those values will be wrapped in quotation marks per typical CSV formatting in order to allow consumers to properly differentiate columns.
The basic structure of the tally reports printed from VxAdmin is similar to the printed from VxScan.
In addition to the full election report, VxAdmin exports reports filtered or grouped by the following dimensions:
Ballot Style
Precinct
Voting Method
Scanner
Batch
District (filtering only)
When a report is grouped, the report is broken up into multiple sections each with their own filter. For example, if you were to group by "Voting Method," the resulting report would have a section filtered for precinct ballots only and a section filtered for absentee ballots only:
The filter for a given report section is indicated in the title of the report. If the report has more than one filter applied to it, the full details of the filter will be specified in a box below the title:
Note that filters and groups can be combined.
If manual results have been added, they will be included in the tally report alongside the scanned results. For each contest, there will be three results columns: "scanned", "manual", and "total".
In order to prevent a long list of write-in candidates from making contest results difficult to read, VxAdmin tally reports consolidate write-in candidates if they're not relevant to the contest outcome. Relevant write-ins are included as "<Name> (Write-In)". Irrelevant write-ins are consolidated as "Write-In" or, if there are relevant write-ins in the list, "Other Write-In". Unadjudicated write-ins are always consolidated as "Unadjudicated Write-In."
A write-in is deemed relevant if it must be listed for the reader to determine the winner(s) of the contest. In the middle example below, the single adjudicated write-in is consolidated because Natalie Portman is the winner regardless of the identities of the write-ins. In the last example, however, there are 31 adjudicated write-ins so it's possible that a write-in got more votes than Natalie Portman. The report then enumerates the write-ins with the highest vote totals until the "Other Write-In" count is smaller than the counts for all winners.
VxAdmin and VxCentralScan are both laptops that connect to peripherals as needed. The laptop is a .
The relevant hardware interfaces are:
Smart Card Reader - Enables reading and writing to smart cards, which is the basis for authentication in VxSuite
USB Ports - Allows connecting to the peripherals for scanning or printing
While not strictly necessary, VxAdmin and VxCentralScan are bundled with a mouse and a USB hub for ease of use. A USB A-B cable is included to connect to the peripherals.
VxAdmin connects to a printer for the purposes of printing reports. The printer includes an RJ45 (ethernet) port which is blocked by a port blocker because network connectivity is not required by the system.
VxCentralScan connects to a batch scanner for batch scanning. The scanner is either the smaller Ricoh fi-8170 or the larger Ricoh fi-7600. Ballots are loaded into the hopper and then, after a scan is triggered from the application, the scanner processes the ballots one-by-one, sending images to the application for interpretation.
Both batch scanners can be used with Ricoh imprinters (not pictured) which allow printing an identifier on each ballot as it's exiting the scanner, which can be useful for certain types of post-election audits.
The batch scanner is powered through an Anker Solix C300 LFS (Lithium Ferrite System) UPS (Uninterruptible Power Supply) to ensure that the scanner can operate in variable power environments.
The fi-8170 includes an RJ45 (ethernet) port which is blocked by a port blocker because network connectivity is not required by the system.
VxCentralScan can be used with either the Ricoh fi-8170 or the Ricoh fi-7600, but does not need both. Each scanner can be paired with one of the two imprinters listed below.
The laptop is a custom configuration of the widely available HP Elitebook 840 14" G11 laptop. B43P6UP#ABA is the HP SKU of the VotingWorks configuration, which includes the following:
As listed in the bill of materials above, the high criticality components are as follows:
HP Elitebook 840 11" G11 - As the processor and data store for ballot data and other tallies, the laptops are one of the most critical components in the entire system. HP is the only manufacturer. VotingWorks is an official OEM partner of HP in order to work closely together to ensure the quality and configuration of the laptops.
Ricoh fi-7600 & fi-8170 - As the source of ballot images for VxCentralScan, the Ricoh scanners are highly sensitive and critical components.
The medium criticality components and the reasons for their classification are as follows:
Tripp Lite Ultra Slim USB Hub - Manages flow of data between laptop and peripherals.
Logitech B100 Mouse - Does not directly handle election data but does plug into a USB port, thus it could still pose a USB attack vector.
Monoprice USB A-B Cable - Manages flow of data between laptop and peripherals.
HP LaserJet Pro 4001dn - The VxAdmin printer is responsible for printing reports from VxAdmin and therefore handles highly sensitive election information. All reports from VxAdmin can be exported as PDFs and printed elsewhere, so it is not strictly necessary for the operation of the system and thus is not the highest criticality.
Ricoh fi-760PRB & fi-819PRB - The imprinters that pair with the Ricoh scanners are not involved in generating ballot data, so they cannot influence the interpretation of ballots when used according to instructions. The imprinted identifiers are important for post-election audits, however, so the imprinters are security-sensitive components.
VxSuite has four authenticated user roles. Authentication is performed with JCOP4 Java Cards, a security-oriented type of smart card. Each card is programmed with a single role and, in most cases, a PIN for two-factor authentication. Details of the authentication system can be found here: .
The poll worker role is the lowest privilege role for election day tasks at the precinct.
The poll worker role allows a user to manage the polls on the precinct equipment, VxScan and VxMark. On both devices, the poll worker card is used to open and close polls on election day. On VxMark, the poll worker card is used to enable voter sessions. There are a few other actions enabled by the poll worker card - reprinting reports, powering down the machine, or checking the software hash - but overall the role is quite limited.
All poll worker cards are programmed at VxAdmin and are programmed for a specific election. They must be reprogrammed for every election. Poll worker cards may or may not require PINs depending on the value of arePollWorkerCardPinsEnabled
flag in the file within the election package.
The poll worker role has no purpose on the central equipment, VxAdmin and VxCentralScan, and poll worker cards will be ignored on those devices.
The election manager role is a broad role for election officials to setup and operate the election.
On the precinct equipment, an election manager card is used to configure the machines at the beginning of each election. This includes loading the election package, selecting a precinct for the device, and checking on consumables such as thermal paper for VxScan. At the end of an election, the election manager card can be used to unconfigure machines, re-export results, and export logs. In addition, system functions like setting date and time, turning on and off certain features, and accessing system diagnostics are available to election managers. The election manager card is not usually required at the precinct on election day, however, where the poll worker card should be sufficient for all election day tasks.
On the central equipment, an election manager card is used to access core election management features. On VxAdmin, it is used to load, adjudicate, review, and export results. On VxCentralScan, it is used to operate the scanner and export results.
All election manager cards are programmed at VxAdmin and are programmed for a specific election. They must be reprogrammed for every election. They alway have PINs, which change after reprogramming the cards for a new election.
The system administrator role is a powerful role for initial election setup and machine management. Unlike the poll worker and election manager roles, they are not tied to specific elections.
On VxAdmin, a system administrator card is necessary to initially load an election package from an external system. System administrators can then program poll worker cards and election manager cards for a specific election. In that sense, the system administrator role is the gatekeeper for loading elections and creating users, and is strictly necessary for every election.
On all devices, the system administrator role allows accessing a menu with powerful system actions. Many actions are shared with election managers - unconfiguring a machine or setting the date and time. Others are unique to the system administrator. For example, on precinct devices a system administrator can reset polls from a "closed" state to a "paused" state in case a poll worker accidentally closes polls prematurely.
Additional system administrator cards can be programmed by a system administrator at VxAdmin. System administrator cards always have PINs. Because a system administrator card is necessary to program cards or set up elections to begin with, at least one pre-programmed card is initially provided with the voting system to bootstrap the jurisdiction's authentication.
The vendor role is a role that only VotingWorks itself has access to. It allows booting into an administrative menu with options to update various system configuration files.
While the authentication model only includes the four aforementioned roles, one can conceptually describe a "voter mode" on the precinct devices. On VxMark, a poll worker must authenticate in order to initiate a voting session. Once initiated, the voter is able to make selections, navigate menus, and print their ballot. Once done voting, the machine leaves voter mode and once again requires authentication to start a voting session. On VxScan, voter mode is any time the polls are open and no other role is authenticated, during which a ballot can be inserted for tabulation.
VotingWorks software is open-source, which means that the code is free and publicly available. All code written by VotingWorks and almost all dependencies are open-source, with the notable exception of third-party firmware for various hardware components.
All system components - VxAdmin, VxCentralScan, VxMark, VxScan - run different application code but have fundamentally the same software architecture. The rest of this document applies to all system components unless otherwise noted.
The system uses 12 as the base operating system. Debian is a free and open source Linux distribution developed by the Debian Project. The operating system is first installed with the minimally required dependencies and then any additional packages required by the system are installed during build time.
Due to the extensive security measures, users are limited to using the application software and will not have access to Debian's typical set of features.
All machines are completely disconnected from any network and have network capabilities disabled, but the frameworks and architecture employed are borrowed from web-based development. The user interacts with a restricted browser which communicates with a server that provides the web-content and another server that provides application data and hardware status.
The hardware peripherals are polled and managed through the application backends. For example when detecting the status of the card reader, the browser polls the backend server which in turn polls the hardware itself and returns a status to the browser. In order to manage changing states of more complex hardware such as scanners, the backends use state machines.
The exact layer between the application backend and the hardware varies by hardware. Some run in-process whereas others run as a separate process with which the backend communicates. For the accessibility peripherals - the accessible controller and the sip-and-puff interface - the backend starts and manages a daemon which surfaces user input directly to the browser as keyboard commands.
In many cases VotingWorks has written custom drivers that interface directly with the USB device. In other cases, VotingWorks leverages open-source middleware layers installed as Debian packages:
Firmware for embedded devices such as screens and speakers is bundled with the operating system.
On hand marked paper ballots, voters fill in bubbles next to their selections. Because voter indications are made and reviewed directly by the voter, they are inherently voter-verified. The software interprets the image of the ballot for tabulation but, of course, the paper ballot persists after scanning.
For machine marked paper ballots, voters make selections on screen at VxMark. After the voter has made all their selections, the ballot is printed and presented to the voter. The ballot displays a textual representation of the selections and a QR code with the selections encoded. The voter reviews the ballot and, once accepted, the voter-verified ballot is ejected into the attached ballot box. VxMark's machine marked ballots are not actually tabulated until they are scanned at a scanner.
Ballots are never modified by the system after voter verification in any way that could affect voter selections or the ability to perform an audit. The only modification to ballots is the possible use of an imprinter on VxCentralScan, which prints only an identifier along the outside margin of the ballot.
Because all voter selections are recorded on paper, voter-verified, and unmodified, the election results can always be re-tabulated or audited.
There are four code repositories relevant to the voting system:
The VotingWorks codebase is written in TypeScript and Rust, two widely used languages with well established coding conventions. We make use of automatic code linters to enforce these conventions. Details about our best practices and tooling used to enforce those best practices can be found here:
Through the above best practices, tooling used to enforce those best practices, and peer code review of every change, we guarantee that we meet the following requirements:
2.3: Voting system logic is clear, meaningful, and well-structured
2.4: Voting system structure is modular, scalable, and robust
2.5: The voting system supports system processes and data with integrity
2.6: The voting system handles errors robustly and gracefully recovers from failure
Subsequent articles will describe the particular software design of each application, but some software patterns are common across all applications and described here.
All VxSuite components require a USB drive at some point in their operation for configuration or for the import or export of results. The application is polling for attached USB drives multiple times per second. If a USB drive with a FAT32 partition is detected, the application will attempt to mount the USB drive to a known mount point. Once mounted, the USB drive will be available for use by the application.
The USB drive should be ejected before removing it. Most critical operations also make sure to sync the data to the USB drive before allowing the user to continue, however, so removing the USB drive without ejecting will normally not result in problems.
Only one USB drive can be used at a time and additional USB drives inserted after the first USB drive will be ignored.
All VxSuite components must generate documents for printing or export. VxAdmin and VxScan must generate tally and ballot count reports. All components must generate diagnostic readiness reports.
In order to do so, the applications have a headless browser (specifically Chromium) launched by the backend. Exported documents are all defined in terms of HTML (using React), so the headless browser is able to load the documents and render them as PDFs. The PDFs can then be sent to printer or saved to a USB drive.
The date and time is set on the machine in during initial configuration before the machine is sent to a customer. There is some amount of "clock drift" over time for all computers, however, so eventually the clocks may be wrong. This may cause problems with authentication due to mismatched timestamps and will cause reports to have incorrect timestamps.
All machines allow system administrators and election managers to edit the date, time, and time zone in order to address clock drift or, in rare cases, to set up machines for use in a part of the jurisdiction with different time settings.
Ballot count reports contain ballot and ballot sheet counts, not contest results.
The ballot counts are broken down by ballot type - "HMPB" (for hand marked ballots), "BMD" (for machine marked ballots), and "manual" (for manually entered results, if applicable).
If there are multi-sheet ballots, the hand marked ballot counts may have sheet counts specified. The ballot count is determined by counting the first sheet of each ballot style.
When grouping is applied in a ballot count report, each group corresponds to a row in the table. In the following example, the ballot counts are grouped by both precinct and voting method:
The following metadata columns may appear in a ballot count report table in order to specify groupings:
Filters apply to the entire report. Simple filters are shown in the title, while complex filters are listed in a box below the title.
VxCentralScan is the system's batch scanner which enable election managers to efficiently scan large batches of ballots.
VxCentralScan is configured with a signed exported from VxAdmin. The election definition includes the ballot layouts necessary for interpreting ballots and the system settings which indicate what type of ballot issues (e.g. overvotes) require adjudication. After the election package is loaded by an authenticated election manager, no further configuration is required.
User Manual Reference:
After initial configuration, VxCentralScan is in test ballot mode. The election manager can toggle between test ballot mode and official ballot mode. In official ballot mode, only official ballots can be scanned and exported CVRs will be official ballot CVRs. In test ballot mode, only test ballots can be scanned and exported CVRs will be test ballot CVRs. If allowOfficialBallotsInTestMode
is set in the , official ballots will be allowed in test mode but exported CVRs will still be test CVRs.
Switching between ballot modes clears all scanned ballot data. The election manager can switch from test ballot mode to official ballot mode at any time. When in official ballot mode after ballots have been scanned, the election manager can only switch back to test ballot mode if the scanned ballot data has been backed up.
VxCentralScan can be unconfigured by an election manager or system administrator. If ballots have been scanned in official ballot mode, an election manager can only unconfigure the machine after scanned ballot data has been backed up. System administrators can unconfigure the machine at any time.
VxCentralScan is compatible with the Ricoh fi-8170 and Ricoh fi-7600 production scanners. Once either scanner is detected, the user may begin scanning batches. The scanners are controlled via commands provided by the Debian sane
package ("Scanner Access Now Easy") which creates a uniform API for most commercial scanners. As a result, no scanner-specific drivers are required.
The start time, end time, size, and index of all batches is tracked and displayed to the election manager. If a mistake was made in organizing batches or adjudicating a ballot, election managers can delete any individual batch or delete all batches.
Both Ricoh scanners can be used with an optional imprinter - the fi-819PRB for the fi-8170 and the fi-760PRB for the fi-7600. As the ballot exits the scanner, the imprinter prints an identifier on the side margin of the ballot. The identifier is the batch's UUID suffixed with the index of the sheet in the batch e.g. 378f6a69-62d3-4184-a1a7-3a5d90083e21_0000
for the first sheet in a batch. The identifier will appear in the CVR as the BallotAuditId
. Imprinting allows later reconciling CVRs to ballots such as in ballot comparison audits.
The v4 UUID (universally unique identifier) for each batch is generated when the batch is first created. It is generated with the node package uuid
which uses the system's underlying FIPS-complaint OpenSSL implementation to generated random bytes.
Batch scanning pauses whenever a ballot is not counted for any reason. Sometimes the ballot was not interpreted or cannot be accepted, in which case the ballot must be removed:
Vertical Streaks Detected - the scanner may require cleaning
Wrong Ballot Mode - test ballots in official ballot mode or official ballots in test ballot mode
Wrong Election - the hash on the ballot does not match the currently configured election
Unreadable - the ballot image could not be successfully interpreted, possibly because it is not a valid ballot or because the image skew was too great
Overvote - at least one contest is overvoted
Undervote - at least one contest is undervoted
Blank - all contests are blank
In these cases, the affected contests will be highlighted on screen to the election manager and they will have the option to remove or tabulate the ballot.
CVRs must be exported onto a USB drive and taken to VxAdmin for aggregation and reporting. The election managers has options to either save CVRs or save a backup. When saving CVRs, ballot images are only included for ballots with write-ins and rejected ballots are not included. A backup is simply a CVR export with images for all ballots and images for rejected ballots.
After ballots are interpreted, their images are saved to disk and their interpretation is stored in VxCentralScan's data store. On export, all CVRs are pulled from the data store, converted to the CVR CDF, and written to the USB drive.
A cast vote record is a record of voter selections based on the system's interpretation of a scanned ballot. Each cast vote record corresponds to a single scanned ballot sheet. Multi-sheet ballots produce multiple cast vote records.
Cast vote records are exported from VxScan and VxCentralScan onto USB drives and then imported into VxAdmin. Each cast vote record export is created with a digital signature. VxAdmin verifies this signature when importing cast vote records to ensure that the export has not been tampered with.
In order to export cast vote records efficiently and without compromising voter privacy, every cast vote record is generated individually. The structure of a cast vote record directory is as follows:
The cast vote record export contains a directory for each cast vote record, labelled with its UUID. Each specific cast vote record directory contains:
Cast Vote Record Report - Contains information about the election and the ballot interpretation in the Common Data Format. The information in the report is used as the basis for tabulation by VxAdmin.
Images - Ballot images to be used in write-in adjudication or auditing
Interpreted Ballot Layouts - Metadata for the position of ballot features such as contests, contest options, and bubbles in the ballot image. The interpreted layout data is used to properly crop and highlight ballot images for write-in adjudication.
In addition, there is a metadata file that applies to the entire export at the root of the directory, metadata.json.
The v4 UUID (universally unique identifier) for each cast vote record is generated when the ballot information is first stored in the database in VxScan or VxCentralScan. It is generated with the node package uuid
which uses the system's underlying FIPS-complaint OpenSSL implementation to generated random bytes.
In VxScan, ballot images and layouts are always included. In VxCentralScan, ballot images and layouts are only included in the cast vote record export if the ballot has write-ins that may require adjudication. When exporting a backup from VxCentralScan, however, ballot images and layouts are included for all ballots.
Layouts are not included for machine marked ballots.
In VxScan, images of rejected ballots are always included. In VxCentralScan, images of rejected ballots are not included in the cast vote record export but are included in the backup. There are no layout files or cast vote record report for rejected ballots because they are often uninterpretable. When rejected ballots are included, they will appear in the directory with the prefix rejected-
, as in the following example:
Because VxAdmin requires a VxSuite digital signature on all imported cast vote records, it's not possible to import a cast vote record from outside of VxSuite. The goal of using CDF is to help external systems consume VxSuite cast vote records for audit or analysis.
Only one ReportingDevice
is ever listed:
Each cast vote record includes a set of ballot metadata attributes:
The CVR.BallotImage
attribute exists when there are images accompanying the cast vote record. If it exists, it always contains images for both sides of the ballot. Its attributes are as follows:
Hashes of the ballot image and ballot layout files are included in the cast vote record report so that the hash (and digital signature) of the cast vote record export will reflect any change to the image or layout files.
The CDF specification allows detailing multiple versions of the same cast vote record as "snapshots."
For hand marked paper ballots, there are both "original" and "modified" snapshots. The "original" snapshot contains mark thresholds for every single bubble on the ballot and is included purely for auditability. The "modified" snapshot contains only the marks that were detected as definite marks and counted as valid or invalid votes.
For machine marked ballots, there is only an "original" snapshot.
The snapshot is referenced in the CVR.CurrentSnapshotId
field by it's identifier, which will be the unique identifier of the cast vote record along with its type, such as
The CDF specification does not have any allowance for a ballot type such as "absentee" or "precinct" at the metadata level, so it is added as information to each snapshot:
The CVRContest
class within each snapshot contains a list of vote records by contest. The fields are used as follows:
Ballot images are .jpg
files. They are included in the cast vote record in order to be used for write-in adjudication or auditing. Ballot images are generated by the scanning hardware and then normalized to reduce skew and enforce a consistent orientation. Images from VxCentralScan are in grayscale while images for VxScan are in black and white.
Ballot layouts are JSON files which describe the discovered position of ballot content within interpreted ballots, including where each contest and contest option appears. Although the layout of ballot content within the ballot grid is specified by the election definition, the ballot content may be positioned in a slightly differently location in each scanned ballot image due to offset or skew of the ballot during scanning. As a result, the layouts are included in order to have accurate image highlights and crops for write-in adjudication.
The metadata file includes data that applies to all cast vote records in the export. There is only one per export. The relevant attributes are:
Each cast vote record is associated with a batch via its BatchId
metadata field. The CDF doesn't have a place within the cast vote record report to include batch metadata, so it is included here.
VxAdmin acts as the election setup hub at the beginning of the election and the results aggregation and reporting hub at the end of an election (and also at the end of testing).
Only a can authenticate onto an unconfigured VxAdmin and configure it with an election package from an external system. The election package must be loaded via a USB drive. The election package must be a valid .zip
. If the election package zip file, election definition file, system settings file, or metadata file are not valid, VxAdmin will surface an error to the user.
It's also possible to configure VxAdmin directly from a .json
file rather than a full election package. The system will use default system settings and include no additional translations for application strings. Uploading a .json
file directly is not recommended for general use because it means you are unable to customize system settings like ballot adjudication reasons. The option exists for development purposes. VxMark, VxScan, and VxCentralScan cannot be configured from an election definition alone and require a signed election package.
Once VxAdmin is configured, system administrators and election managers can export signed election packages. The exported election package is the same as the imported election package but is accompanied by a digital signature, which ensures that the election package was validated by a certified VxAdmin. The election packages loaded into VxMark, VxScan, and VxCentralScan must be signed and unsigned election packages will be rejected.
User Manual References: &
Smart cards are used for two-factor authentication on all machines in the system. They can only be programmed at VxAdmin with system administrator privileges.
Programming, like authentication, takes place via the laptop's built-in USB card reader. Once a card is inserted, the application is able to detect the card and read and write data by issuing APDUs (application protocol data units) which are the standard form of communication between smart card readers and smart cards.
For extensive details on the authentication scheme, see the .
User Manual Reference:
VxSuite components require USB drives to be formatted with a FAT32 partition. The majority of USB drives are sold pre-formatted as FAT32, but some come in other formats. If an improperly formatted USB drive is inserted into a VxSuite component, it will be treated as if there is no USB drive.
VxAdmin includes a utility to format USB drives to be compatible with the system. The utility is available only to system administrators. It will rewrite the partition table on the drive to include a single partition with a FAT32 formatted filesystem that takes up all available space on the drive. The reformatting clears the USB drive and is thus a destructive action. If a USB drive is already correctly formatted, the feature can be used to clear its data. VxAdmin applies a new label to the reformatted USB drive in the form of VxUSB-00000, where the 0's can be any random alphanumeric characters.
Improperly formatted USB drives can only be detected and reformatted if there is some partition already existing, so a USB drive without any partitions cannot be reformatted using VxAdmin.
CVRs must be loaded from a USB drive. CVRs are exported from VxScan or VxCentralScan and are accompanied by a digital signature. CVRs without a digital signature cannot be loaded. CVR exports with digital signatures that do not match the content of the CVR exports cannot be loaded in order to prevent tampering.
The CVRs from the scanners are saved to specific directory structure on the USB drive, but exports can be loaded from anywhere on a USB drive's filesystem. When selecting an export manually, the export's metadata.json
file must be selected by the user.
When loading a full CVR export, each CVR is loaded into VxAdmin's backend store as one cvrs
record. The CVR record includes a data blob representing the interpreted votes and a series of metadata fields: ballot style, voting method (absentee vs. precinct), batch, scanner, precinct, sheet number within a multi-sheet ballot, and flags for adjudication reasons such as overvotes, undervotes, write-ins, or fully blank ballots. The metadata fields are used later on for filtering or aggregating tallies or ballot counts into groups.
Each write-in on a CVR will map to a write-ins
record, whether it is an unmarked write-in or a proper write-in. The ballot images are also each saved as database records. The write-in record links the write-in to its respective CVR, to the ballot images, and eventually to its adjudication result. The write-in adjudication interface essentially iterates through all write-ins
records on a per contest basis.
If an error is found when loading any single CVR in a CVR export, the entire export is rejected and a message is surfaced to the user. CVRs can be rejected for a variety of reasons: authentication failed; a file is incorrectly formatted; a CVR is not for a currently valid election, precinct, ballot style, or contest; a CVR with an identical ID has been loaded with different data. In everyday use of the system, all of these errors are either rare or impossible because VxAdmin will not allow loading CVR files from elections with mismatched metadata.
VxAdmin allows loading CVR exports that have already been partially loaded. For example, imagine that VxCentralScan exports CVRs after scanning 5 ballots and then again after scanning 10 ballots. If we first load the 5 CVR export and then the 10 CVR export, the 10 CVR export will successfully load but will ignore the CVRs that have already been imported.
VxAdmin is always in one of three ballot modes:
Unlocked Ballot Mode - no CVRs have been loaded
Test Ballot Mode - test CVRs have been loaded
Official Ballot Mode - official CVRs have been loaded
On VxAdmin, the ballot mode is determined by the ballot mode of the imported CVRs. VxAdmin differs in that respect from VxMark, VxScan, and VxCentralScan, where the ballot mode is set directly by the election manager.
Once VxAdmin is in test ballot mode, only test CVRs can be subsequently loaded. Similarly once VxAdmin is in official ballot mode, only official CVRs can be subsequently loaded.
CVRs can be removed by the user before results are marked as official or can be removed by fully unconfiguring VxAdmin. All CVRs are removed at once. The CVR data in the database along with all its dependent data - write-ins, ballot images, and adjudications - are permanently and irrecoverably deleted from the data store.
When ballots with write-ins are scanned at VxScan or VxCentralScan, they have not yet been adjudicated. The polls closed report at VxScan treats all write-ins simply as a generic "Write-In" and all unmarked write-ins as undervotes.
VxAdmin allows election managers to perform on-screen write-in adjudication. Each contest is adjudicated individually. The election manager can work through the queue of write-ins for that contest. The queue is ordered by least to most recently loaded into VxAdmin. Because multiple write-ins for a single CVR are loaded together, write-ins that appear on the same ballot sheet will appear next to each other in the queue.
The user can adjudicate the write-in in one of three ways:
Official Candidate: the list of official candidates for the given contest will be displayed and the write-in can be assigned to any official candidate
Unofficial Candidates: the list of unofficial candidates defined for the given contest by the election manager will be displayed and the write-in can be assigned to any unofficial candidate
Undervote: the write-in can be deemed invalid and will be considered in tallies as an undervote
Adjudications are immediately persisted to the data store. Adjudications can be changed or deselected.
The list of unofficial write-in candidates is created by the election manager as they adjudicate. The interface has an option to add a new write-in candidate and specify their name. That candidate will then be an option for other write-ins for the same contest. If there are no longer any adjudications that reference the unofficial candidates, their name will be removed from the list.
In most cases, the adjudication reclassifies a vote for an unadjudicated write-in to a write-in candidate. But in some cases, the adjudication effectively reclassifies a mark as a non-mark or vice versa.
If a marked write-in is adjudicated as an undervote, in addition to updating the associated write-ins
record, VxAdmin will create a vote_adjudications
record representing the switch from an indication to a non-indication. If an unmarked write-in is adjudicated for a candidate, VxAdmin will create a vote_adjudications
record representing the switch from a non-indication to an indication.
VxAdmin offers a way to enter manual tallies that are then added to reports. When manual tallies are present in a results export, they are always displayed separately from scanned tallies so that users can easily recognize the impact of manual results and recognize any human errors. The only exception to this rule is the CDF election results export in which, due to the limitations of the CDF, the manual and scanned tallies are simply combined.
The ballot style, precinct, and voting method (absentee vs. precinct) must be specified before entering manual tallies. By associating manual tallies with these pieces of metadata, they can be filtered and grouped according to those dimensions when creating reports. Notably, manual tallies are not associated with a batch or a scanner like CVRs. When filtering on batch or scanner, manual tallies are excluded. When grouping by batch or scanner, manual tallies are treated as a single batch or scanner.
The manual tallies for each contest are validated according to a standard formula:
The same formula also applies to all results generated from scanned ballots. If a vote for three contest is overvoted, it is considered three overvotes. If the contest tallies entered by the user are incomplete or invalid, the user is presented with warning messages.
In most cases, the ballot count will be the same across all contests. In some jurisdictions it is possible for different contests to have different ballot counts if, for example, someone votes on a ballot without local contests. In these cases, the user can override the ballot count on an individual contest basis.
Unofficial write-in candidates can also be allocated votes in manual tallies and new unofficial write-in candidates can be added directly from the manual tallies interface.
Vote tallying is a multi-step process which takes place on the VxAdmin backend:
Every tally is specified by a set of filters and groupings. The filter defines which CVRs are excluded and included. For example, a filter might limit the CVRs to the CVRs for a specific precinct. The group determines how tallies are subdivided by CVR metadata. For example, there may be a grouping on precinct which generates tallies for each precinct separately. Groups enable multiple related tallies to be efficiently generated at once.
Filters and groupings are specified by the user directly with the tally report builder or implicitly with built-in reports.
If there are no groupings specified, there is only one set of tallies. If the precinct grouping is specified, there will be tallies for each precinct. If multiple groupings are specified, the possible resulting groupings need to be calculated. For example, if the precinct and ballot style groupings are both specified, only some combinations are possible because not all ballot styles exist in all precincts. If filters are specified, they may rule out certain groupings. For example, if the precinct grouping is specified but only Precinct A and Precinct B are included by the filter, then the Precinct C grouping will be excluded.
If batch or scanner parameters are included, then VxAdmin will not attempt to compute the possible groupings because they may be too numerous. The potential groups will be created opportunistically during tallying.
The purpose of computing the groups when possible, rather than always creating groups opportunistically, is to know which zero tallies to expect. When we group by precinct, we'd like to generate empty results for Precinct A even if no ballots for Precinct A were cast.
CVR Aggregation
The CVRs are pulled from the data store one by one and their vote data is added into an ongoing tally of ballots, contest option votes, overvotes, and undervotes. If the tallies are being grouped, the CVR will be added into the group that matches its metadata e.g. a CVR from a Precinct A ballot will be added to the Precinct A group.
At this step in the process, the data does not contain any of the adjudication details from write-in adjudication.
CVR aggregation is the most computationally expensive step in the vote tallying so the results are cached. The cache is reset anytime a CVR file is added or removed or anytime a vote is adjudicated through write-in adjudication.
Write-In Adjudication Aggregation
The write-in adjudication records are summarized from the data store and their adjudication statuses are added into an ongoing tally of write-ins for official candidates, write-ins for unofficial candidates, invalid write-ins, and unadjudicated write-ins. They are filtered and grouped according to the same parameters as the CVRs.
The write-in adjudication report is simply the results of the write-in adjudication aggregation step, without filter or group, formatted into a PDF.
The vote tallies from the CVRs are then hydrated with the aggregated write-in adjudication tallies to create vote tallies that include the results of write-in adjudication. Before hydration, the tallies represent all write-ins as generic write-ins. The counts for generic write-ins are reconciled to counts for specific official and unofficial candidates.
Manual tallies are already aggregated in the sense that each can represent more than one ballot. Since they are divided by ballot style, precinct, and voting method, however, they may need to be merged to match the groupings (or lack thereof) of the scanned vote tallies.
Manual tallies are then combined with the scanned tallies in one of two ways. For most reports, the manual tallies are paired with the scanned tallies but are not immediately added together. Reports can then display the manual and scanned tallies separately in addition to showing the total tallies. For the CDF election results report, however, the manual tallies and scanned tallies are added together because there is not a clear way to represent the difference between them in the CDF.
Reporting
Ballot counts are tallied as part of the vote tallying flow described above, but there is a separate tallying path that only calculates the ballot counts. It is used when generating ballot count reports and exports from VxAdmin. It follows the same steps as vote tallying, but is more efficient because it can use the underlying SQLite database to generate tallies rather than iterating through each CVR one-by-one.
When tallying ballot counts, VxAdmin is actually tallying sheet counts. In an election where all ballots are a single sheet, there is no distinction between ballot counts and sheet counts. In an election with multi-sheet ballots, there may be different numbers of different ballot sheets. VxAdmin tallies each sheet separately, which is displayed on tally reports and ballot count reports.
The Ballot Count is defined in VxSuite as simply the count of first sheets. For example, if there are 1,000 first sheets and 999 second sheets, the ballot count will appear in reports as 1,000.
Results on VxAdmin are considered unofficial until the election manager marks them as official. As long as results are unofficial, CVRs can be loaded, write-ins can be adjudicated, and manual tallies can be added and edited. All reports and result exports are labelled as "Unofficial."
Once the results are final and often only after a jurisdiction's specific certification process, the election manager can mark results as official. All reports and result exports will then be labelled as "Official." CVRs can no longer be loaded, write-ins can no longer be adjudicated, and manual tallies can no longer be altered.
There are two ways to exit official results modes:
The system administrator may unconfigure VxAdmin entirely, removing the election configuration in addition to all tallies and adjudications.
The election manager may remove all tallies and adjudications at once, leaving the election configuration in place.
VxAdmin can print reports via an attached printer. The supported printer is the HP LaserJet Pro 4001dn. VxAdmin can also export reports as PDFs so, in the event of a printer failure, reports will always be available.
VxAdmin interfaces with the printer through the CUPS, the open-source printing system developed by Apple and installed on our Debian system. When a printer is attached, it is registered with the CUPS server and made available to the application. VxAdmin is able to send print jobs to the printer via CUPS commands. The HP LaserJetPro does not require any additional third-party or in-house driver because it supports the IPP (Internet Printing Protocol) which CUPS then utilizes.
Using IPP, the VxAdmin is also able to poll the detailed status (toner level, jam status, etc.) of the printer for use in the diagnostics interface.
VxAdmin can only connect with one printer at a time and will always connect with with the first attached printer.
array
-
array
-
array
-
array
-
array
-
array
-
array
-
array
- IDs for
array
- IDs for
string
- ID for
string
- ID for
array
-
string
- ID for
array
- ID for
key-value pairs, IDs mapped to names
key-value pairs, IDs mapped to names
key-value pairs, IDs mapped to descriptions
key-value pairs, IDs mapped to labels
key-value pairs, IDs mapped to term descriptions
key-value pairs, IDs mapped to titles
key-value pairs, IDs mapped to names
key-value pairs, IDs mapped to their full names
key-value pairs, IDs mapped to their short names
key-value pairs, IDs mapped to names
string
, ID for
string
- ID of
string
- ID of or
string
- ID of
The Election Results Reporting CDF requires that identifiers be in , so all identifiers from the election definition are re-formatted to be compliant. For example, "Sample County" becomes "vx_sample-county".
The write-in will be individually listed if it is
The "Precinct" and "Precinct ID" columns are that are included because this example export groups results by precinct. The other fields are standard fields:
The ballot count report CSV is the same as the table presented in the . For example:
The "Precinct", "Precinct ID" and "Voting Method" columns are that are included because this example export groups results by precinct and voting method. The other fields are standard fields:
All components for VxAdmin and VxCentralScan are commercial-off-the-shelf (COTS) components. Data sheets and related documentation are available .
kiosk-browser is a web browser restricted to rendering a single full-screen application. The code can be found in the . It is a thin application which uses as its rendering engine. The browser communicates with the frontend server which serves HTML, JavaScript, and assets. The browser also communicates with the backend server which serves application data. Electron enables the browser to access certain operating system APIs - such as open file dialogs - that a lone renderer would not have access to. The browser is launched at startup, with limited privileges, and cannot be exited. Everything a standard (non-vendor) user sees or does is mediated through kiosk-browser.
The frontend is a application served from a server. All code for the application frontends are in the under apps/[app-name]/frontend
.
The application backend is a separate server which acts as the core of the entire application in that it manages all persistent data and communication with peripherals. All code for the application backends are in the under apps/[app-name]/backend
. Most code is written in TypeScript but some performance sensitive code, such as interpretation and background daemons, are written in and executed as binaries at runtime.
All application data that persists across restarts is stored or tracked in a database. Each machine has a single database that the application backend accesses to update or retrieve data such as election configuration, cast vote record data, diagnostic data, etc. Logging data is a notable exception that is stored outside of the application databases.
The voting system achieves software independence through the use of independent voter-verifiable paper records. All ballots are voter-verified paper ballots, either or .
— Core application code
— Generic Electron-based kiosk-mode browser
— Links compatible versions of vxsuite and kiosk-browser, and includes the scripts necessary to create a production machine
— Our framework for building VxSuite and managing its dependencies, across versions and environments
We take care to ensure that we're pulling authentic/correct versions of dependencies, by verifying against known hashes, checksums, or digital signatures. The details of this are covered in .
We also require peer code review of every change (), and for larger changes and features, we hold architecture discussions as a team to land on the best possible solutions given the constraints.
Our practices around testing are covered under .
The VotingWorks codebase uses database transactions to ensure that only complete and consistent (and not partial) updates are persisted. For data synced across a machine's internal drive and a connected USB drive, namely CVRs, we detect when data has fallen out of sync after a failure using the Merkle tree hash of the data () and re-sync data as needed.
Much of 2.5 is additionally covered under .
User Manual Reference:
When the user triggers a batch scan, the scanner will start a batch and begin scanning sheets one a time. The scanner will pull the paper through and transfer a scanned image to the application which will then according to the current election definition. If any issues are found with the ballot - either the ballot is incompatible or the marks trigger some adjudication reason - scanning will pause and the election manager will be prompted to remove or tabulate the ballot. When pausing for adjudication, the batch is not yet ended. Once the election manager addresses the ballot, scanning will continue until there are no more ballots in the hopper to be scanned. At that point, the batch will end.
User Manual Reference:
In other cases, the ballot requires adjudication. The adjudication reasons for VxCentralScan are set in the as centralScanAdjudicationReasons
and map to the following:
User Manual Reference:
User Manual Reference:
VxSuite does not use any data extensions beyond the NIST specification, but some fields are made required that are not required in the original NIST specification. The two specifications can be compared by comparing the with the . The additionally required fields are listed in a table below.
The CDF specification includes many metadata fields that might help a consumer make sense of the cast vote record data. For example, you may define the candidates that are referenced as contest selections. VxSuite cast vote records all include this metadata to conform to the CDF, but nearly none of it is actually utilized when imported into VxAdmin. VxAdmin already has that information from the . The only data that is utilized by VxAdmin is the GeneratedDate
and the indication of whether or not the report is a test report in ReportType
and OtherReportType
.
The list of parties in the cast vote record report is mapped directly from the list in the election definition:
GpUnit
s are created for each in the election, for the , and for the state:
The Election
class has values mapped to it directly from the :
User Manual Reference:
The core post-election (and pre-election testing) function of VxAdmin is loading, managing, and tallying (CVRs).
User Manual Reference:
The side of the ballot that the write-in appears on is rendered to the user, with options to zoom out and back in again. In the zoomed in view, the write-in area in question is highlighted. The location of the highlight is taken from the , which is included in the cast vote record and loaded into VxAdmin alongside the ballot image.
User Manual Reference:
User Manual Reference:
The final step is for the VxAdmin backend to format the vote tallies into one of and export the result onto a USB drive or, if it is a PDF document, print it.
User Manual Reference:
ElectionReport.Format
Fixed to "summary-contest"
ElectionReport.IsTest
true
or false
depending on whether VxAdmin was in test ballot mode
ElectionReport.SequenceStart
Fixed to 1
ElectionReport.SequenceEnd
Fixed to 1
ElectionReport.GeneratedDate
The generated timestamp of the report
ElectionReport.Issuer
The county name
ElectionReport.IssuerAbbreviation
The county identifier formatted as an NCName ID
ElectionReport.VendorApplicationId
"VxAdmin, version <code version>" where code version is the software version of the system e.g. "v4"
ElectionReport.Status
If the election has been marked as official in VxAdmin, "certified", otherwise "unofficial-complete"
Party.@id
The party identifier
Party.Name
The short name of the party e.g. "Libertarian"
Party.Abbreviation
The party abbreviation e.g. "L"
Election.StartDate
The date of the election
Election.EndDate
The date of the election
Election.Name
The title of the election
Election.ElectionScopeId
The state name formatted as an NCName ID
Election.Type
Either "general" or "primary"
BallotCounts.Type
Fixed to "total"
BallotCounts.GpUnitId
The county identifier formatted as an NCName ID
BallotCounts.BallotsCast
The total ballot count for the election
Candidate.@id
The candidate identifier formatted as an NCName ID
Candidate.PartyId
The candidate's party identifier formatted as an NCName ID
Candidate.BallotName
The candidate's name as it appears on the ballot in English
Contest.@id
The contest identifier formatted as an NCName ID
Contest.Name
The contest title
Contest.ElectionDistrictId
The district identifier of the contest's associated district formatted as an NCName ID
ContestSelection.@id
The option identifier formatted as an NCName ID
BallotMeasureSelection.Selection
The option label as it appears on the ballot in English
CandidateSelection.IsWriteIn
true
or false
depending on whether the candidate is a write-in candidate
VoteCounts.Count
The vote count for the specific contest selection
VoteCounts.Type
Fixed to "total"
VoteCounts.GpUnitId
The district identifier of the contest's associated district formatted as an NCName ID
OtherCounts.Overvotes
The overvote count
OtherCounts.Undervotes
The undervote count
OtherCounts.GpUnitId
The district identifier of the contest's associated district formatted as an NCName ID
GpUnit.@id
The county, state, or district identifier formatted as an NCName ID
GpUnit.Name
The county, state, or district name
GpUnit.Type
"state", "county", or for districts, "other"
GpUnit.ComposingGpUnitIds
State: only the county identifier
County: all district identifiers
Total Write-Ins
The count of all write-ins. If unmarked write-ins are being adjudicated, they will be included in this count
Not Adjudicated
The count of all write-ins that have not been adjudicated in the write-in adjudication interface yet
Official Candidate Counts
The count of all write-ins that have been adjudicated for official candidates in the election definition
Write-In Candidate Counts
The count of all write-ins that have been adjudicated for write-in candidates added at VxAdmin
Invalid
The count of all write-ins that have been marked as invalid
Contest
The title of the contest
Contest ID
The internal identifier of the contest in the election definition
Selection
The candidate name for candidate contests, the option label for yes-no contests, or "Overvotes" or "Undervotes"
Selection ID
The internal identifier of the contest option, or "overvotes" or "undervotes"
Total Votes
The vote count for the selection
BMD
The count of machine marked ballots
HMPB
The count of hand marked paper ballots. In a multi-sheet election, this is just the count of the first sheet
HMPB Sheet {N}
The count of a particular ballot sheet. The count of the second sheet of all ballots would be "HMPB Sheet 2"
Manual
The count of manually entered ballots
Total
The total ballot count
Precinct
The name of the precinct
Precinct ID
The identifier of the precinct from the election definition
Party
The short name of the party e.g. "Republican". Included by default if the ballot style is included, in a primary.
Party ID
The identifier of the party from the election definition
Ballot Style ID
The identifier of the ballot style
Voting Method
"Absentee", "Precinct", or "Provisional"
Scanner ID
The serial number of the scanner. Included by default if the batch is included.
Batch
The label of the scanned batch
Batch ID
The scanner-assigned UUID of the scanned batch
Included Precincts
The precinct identifiers separated by commas
Included Parties
The party identifiers separated by commas
Included Ballot Styles
The ballot style identifiers separated by commas
Included Voting Methods
The voting method labels ("Precinct", "Absentee", or "Provisional") separated by commas
Included Scanners
The scanner serial numbers separated by commas
Included Batches
The batch identifiers separated by commas
HP
Elitebook 840 11" G11 Laptop
B43P6UP#ABA
High
Tripp Lite
4-Port Ultra-Slim USB Hub
U360-004-SLIM
Medium
Logitech
B100 Mouse
910-006061
Medium
Monoprice
USB-A to USB-B 2.0 6ft Cable
5438
Medium
HP
LaserJet Pro 4001dn Printer
2Z600F#BGJ
Medium
Lindy
RJ45 Port Blocker
40471
Low
Ricoh
fi-8170 Small Desktop Scanner
PA03810-B055
High
Ricoh
fi-819PRB Imprinter (for fi-8170)
PA03810-D201
Medium
Ricoh
fi-7600 Large Desktop Scanner
PA03740-B505
High
Ricoh
fi-760PRB Imprinter (for fi-7600)
PA03740-D101
Medium
Anker
Solix C300X Portable Power Station
A1723
Low
Lindy
RJ45 Port Blocker
40471
Low
Processor
Intel® Core™ Ultra 5 Processor 125U
Memory (RAM)
8GB (1x8GB) DDR5 5600 SODIMM Memory
Storage (SSD)
256GB PCIe NVMe Solid State Drive
Display
14" WUXGA UWVA LED (300 nits)
Battery
56Wh Capacity
Webcam
None
Near Field Communication
None
WWAN (Cellular)
None
WLAN (Wi-Fi)
None
Fingerprint Sensor
None
Smart Card Reader
Included
Country of Origin
TAA Designated
All
Card reader
VxScan
Scanner
VotingWorks
VxScan
Printer
VotingWorks
VxMark
Printer-scanner
VotingWorks
VxMark
Accessible controller
VotingWorks
VxMark
PAT (sip and puff)
VotingWorks
VxAdmin
Printer
VxCentralScan
Scanner
Debian
12.2.0
Additional Debian Packages
Node.js
20.16.0
pnpm
8.15.5
Application Node Packages
See the relevant lock file
Rust
1.81
Rust Packages
See the relevant lock file
yarn
1.22.22
Electron
17.4.1
Chromium
98.0.4758.141
Kiosk Browser Node Packages
See the relevant lock file
Precinct
The name of the precinct
Ballot Style
The internal identifier of the ballot style
Party
The short name of the party, e.g. "Democrat". Included by default if the ballot style is included, in a primary
Voting Method
"Precinct", "Absentee", or "Provisional"
Scanner ID
The serial number of the scanner. Included by default if the batch is included
Batch
The label for the batch, or "Manual Tallies" for manually entered data
CVR
BallotStyleId, BallotStyleUnitId, BatchId, CreatingDeviceId, UniqueId
CVRContest
CVRContestSelection
CVRContestSelection
ContestSelectionId
SelectionPosition is limited to length 1
CVRSnapshot
CVRContest
Version
Fixed to "1.0.0"
ReportType
Always includes "originating-device-export". If a test report, also includes "other"
OtherReportType
If a test report, "test", otherwise undefined
GeneratedDate
The generated date in Date Time String Format
ReportGeneratingDeviceIds
The scanner serial number
@id
The scanner serial number
SerialNumber
The scanner serial number
Manufacturer
Fixed to "VotingWorks"
@id
The party identifier from the election definition
Name
The full name of the party, e.g. "Democratic Party"
Abbreviation
The abbreviation of the party, e.g. "R"
@id
Precinct: The precinct identifier from the election definition
County: Fixed to "election-county"
State: Fixed to "election-state"
Type
Precinct: "precinct"
County or State: "other"
Name
The precinct, county, or state name
@id
The election's ballot hash
Name
The title of the election
ElectionScopeId
Fixed to "election-state"
Candidate.@id
The candidate identifier from the election definition
Candidate.Name
The candidate name
Contest.@id
The contest identifier from the election definition
Contest.Name
The contest title
CandidateContest.VotesAllowed
The contest's number of seats
CandidateContest.PrimaryPartyId
The party identifier of the associated party, if a primary contest
CandidateSelection.@id
The candidate identifier from the election definition
CandidateSelection.CandidateIds
The candidate identifier from the election definition
CandidateSelection.IsWriteIn
Whether the selection represents a write-in bubble
BallotMeasureSelection.@id
The option identifier from the election definition
BallotMeasureSelection.Selection
The option label that appeared on the ballot
BallotSheetId
The index of the sheet within the ballot. For the second sheet of a multi-sheet ballot, it would be 2
BallotStyleId
The ballot style identifier from the election definition
BallotStyleUnitId
The precinct identifier from the election definition
Batch
The batch identifier
CreatingDeviceId
The scanner serial number
ElectionId
The election's ballot hash
PartyIds
The party identifier from the election definition, if for a primary ballot
UniqueId
The UUID for the ballot generated by the scanner
BatchSequenceId
When using VxCentralScan, contains the index of the sheet within the batch. 1-indexed.
BallotAuditId
When using an imprinter on VxCentralScan, contains the UUID imprinted on the ballot
Location
Location of the image file relative to the report, such as:
Hash.Type
Fixed to "sha-256"
Hash.Value
No Images: Undefined
Images & Layouts: {SHA256 hash of image}-{SHA256 hash of layout}
Images Only: SHA256 hash of image
CVRSnapshot.Status
Fixed to ["other"]
CVRSnapshot.OtherStatus
JSON blob representing the ballot type. The possible values are "absentee", "precinct", or "provisional".
CVRContest.ContestId
The contest identifier from the election definition
CVRContest.Overvotes
The number of overvotes for a contest. The number can only be 0 or the maximum number of votes in the contest
CVRContest.Undervotes
The number of undervotes for a contest
CVRContest.WriteIns
The number of write-ins for a contest
CVRContest.Status
The list of applicable contest statuses:
"not-indicated" - no votes cast in contest
"undervoted" - fewer than allowed votes cast in contest
"overvoted" - more than allowed votes cast in contest
"invalidated-rules" - applies if overvoted
CVRContestSelection.ContestSelectionId
The option identifier from the election definition
CVRContestSelection.OptionPosition
The index of the contest position within the contest
CVRContestSelection.Status
The list of applicable contest selection statuses:
"invalidated-rules" - mark is part of an overvote
"needs-adjudication" - mark corresponds to a write-in
SelectionPosition.HasIndication
"yes" or "no" depending on whether a mark in the bubble passed the mark threshold. In the original hand marked paper ballot snapshots, this may be either value. In the modified snapshot, it is always "yes" except for unmarked write-ins.
SelectionPosition.NumberVotes
Fixed to 1
SelectionPosition.IsAllocable
"yes" except:
"no" if an overvote
"unknown" if an unmarked write-in
SelectionPosition.MarkMetricValue
The mark score, a decimal between 0 and 1.00 such as 0.23. Only included in the original snapshots.
SelectionPosition.Status
"invalidated-rules" if an overvote
"needs-adjudication" if an unmarked write-in
SelectionPosition.OtherStatus
"unmarked-write-in" if an unmarked write-in
CVRWriteIn.WriteInImage
The BallotImage
data for the side of the sheet on which the write-in occurs
arePollsClosed
For VxScan exports, the flag indicates whether polls where closed at the time of export. If polls were not closed, that may indicate an incomplete export
castVoteRecordReportMetadata
The cast vote record report without any specific cast vote record data. In other words, only data about the election and its contests
castVoteRecordRootHash
A hash of all cast vote record files in the export. Including the hash here, which rolls up into the digital signature, ensures that cast vote records cannot be added or removed from the export
batchManifest
The list of batches on the originating scanner, see below
id
The UUID of the batch generated by the scanner
label
The label for the batch
batchNumber
The sequential number of the batch, e.g. 2 for the second batch
startTime
The time a batch was started in ISO 8601 format
endTime
The time a batch ended in ISO 8601 format
sheetCount
The number of sheets (i.e. cast vote records) in a batch
scannerId
The serial number of the scanner
The maximum tabulation rate for batch-fed scanners is primarily limited by the hardware scanning speed limit for each central scanner model:
Ricoh fi-8170: 80 sheets per minute
Ricoh fi-7600: 100 sheets per minute
The sheet per minute rate is reduced by the need to interpret ballot images. Ballot interpretation speed varies based on the following ballot variables:
Ballot type
Ballot marking device ballots are slower to interpret than hand marked paper ballots.
Sheet length
Shorter (such as 11") ballots are faster to interpret than longer (such as 22") ballots.
Ballot content
Ballots with more contests and contests selections are slower to interpret.
Taking these limitations into consideration, batch scanners can be expected to scan approximately 50 sheets per minute for typical elections configurations.
In addition to the sheet per minute rate of a given batch, the maximum tabulation rate is also limited by the maximum batch size for each scanner. Due to the paper weight range supported by VxSuite, VotingWorks recommends a smaller batch size than the original manufacturer recommendation:
Ricoh fi-8170: 30 sheets per batch
Ricoh fi-7600: 100 sheets per batch
Finally, the maximum tabulation rate over the course of a day is limited by the daily volume limitations of each central scanner:
Ricoh fi-8170: 10,000 sheets/day
Ricoh fi-7600: 44,000 sheets/day
N-of-M contests
Yes/No contests
Partisan Primary Elections
The voting system itself does not have a concept of an open vs. closed primary as primary ballot styles are restricted to one party, but can support both types of primaries through procedurally restricting a voter to a given ballot style.
English
Spanish
Chinese (Simplified & Traditional)
Voter-facing content in an election package can be translated to additional left-to-right languages and imported into VxSuite, but is not formally supported or tested by VotingWorks.
The following defines the minimal specifications for VxSuite paper stock. Ballot layout requirements are enumerated in Hand Marked Ballots and Machine Marked Ballots in the System Overview.
VxSuite supports a wide range of paper for hand-marked paper ballot style printing within the following supported specifications:
Width: 8.5"
Length: 11", 14", 17", 19", 22"
Weight: 105-177gsm
Coating: uncoated
Opacity: >90%
Color: white or any pastel color >70 brightness (to ensure it binarizes to white when scanned)
VxMark requires a specific type of thermal paper ballot stock as required by the VSAP BMD 150 component:
Manufacturer: Mitsubishi
Model: Thermoscript TF 1467
Width: 8"
Length: 13.25"
VxMark also expects each sheet of thermal paper stock to have a corner cut and instructional message printed on the uncoated (back) side for proper ballot orientation.
Precincts
Election
1000
Candidates
Election
1000
Contests
Election
1000
Ballot Styles
Election
1000
Candidates
Contest
100
Vote For
Contest
50
Characters
Field Name
100
The total number of CVRs that can be imported is limited by the total disk space on the laptop (~200GB). The exact number of CVRs depends ballot size, ballot selections, tabulator used (VxScan or VxCentralScan), and other factors that determine expected CVR size.
The total number of ballots scanned is limited by the total disk space on the laptop (~200GB). The exact number of ballots depends on ballot size, complexity, and selections made that influence determine the CVR and ballot image sizes.
VxCentralScan is limited by the batch scanner maximum tabulation rate: Maximum Tabulation Rate
VxMark only supports 8" x 13.25" ballots as specified in Paper Ballot Specifications.
VxMark is limited to printing of 50 contest selections before needing to reduce text size to fit a summary ballot on a single sheet. VxMark can print a maximum of 100 contest selections with text-size reduction.
The ballot box supports up to 200 ballots before needing to be cleared.
The ballot box supports up to 3000 ballots in the main compartment before needing to be cleared.
The ballot box auxiliary compartment supports up to 100 ballots before needing to be cleared. Ballots longer than 19" must be folded.
Total ballots scanned for a given VxScan configuration are limited to 10,000 ballots. This limit is set based on the disk space available on specified external disks to ensure adequate disk space in the case of needing to re-sync CVRs.
In addition to the design requirements specified in Hand Marked Ballots, ballots are constrained by the following limits.
VxSuite hand marked paper ballot interpretation supports fractional grid coordinate positions, which enable bubble positions to be placed anywhere within the timing mark grid. However, the density of bubbles on a given ballot is limited to the total number of timing mark grid intersections to ensure accurate interpretation.
For an 11" ballot, which has a 32 x 39 timing mark grid, the maximum number of ballot positions is 1248 per page or 2496 per sheet. The maximum number of ballot positions increases for longer ballots due to the longer grid.
Candidates Per Contest
The total number of candidates per contest is limited by ballot design requirements restricting contest options to one column and one page (7.3-B.1). Therefore, the maximum number of candidates in a contest is the maximum number of bubble positions in a column. For a 22" ballot, there is a maximum of 83 possible bubble positions in a column. The maximum number of candidates per contest may be lower than 83 when accounting for instructional text and contest information in the ballot design.
This document covers supported capabilities and system limits that, alongside the System Overview, constitute the VxSuite Implementation Statement.
All VxSuite components have a diagnostics interface accessible to system administrators and election managers that allows a user to monitor or test key components. Diagnostic information is shown to the user and can be exported as a PDF readiness report.
When a test is performed, the system logs the result and creates a diagnostic record which includes the outcome of the diagnostic, pass or fail. The outcome and date of the most recent diagnostic is displayed on the readiness report. One possible use case of the readiness report is to run all diagnostics before an election and then produce a readiness report that confirms all tests passed.
Retrieves the current amount of disk space used and disk space available for the application. The displayed total disk space will not be as large as the actual disk because some space is reserved for the system. The disk space usage will not always return to 0% after clearing election data because the database may not have liberated that disk space back to the operating system, but the space will be reused once more election data is loaded.
For components with internal batteries - VxAdmin and VxCentralScan - the operating system is polled for battery status. The application will report the current charge level and whether or not the battery is currently charging.
If there is an election configured on the machine, it will display the election title and hash.
For precinct equipment, the list of ballot styles will include only those appropriate for the current precinct configuration. For central equipment, the list of ballot styles will be all ballot styles in the election. The languages for each ballot style are also enumerated.
For precinct equipment - VxScan and VxMark - the currently configured precinct will be shown, if any.
For scanners - VxScan and VxCentralScan - the currently configured mark and write-in area thresholds will be shown. The thresholds reflect what was set in the system settings.
The toner level and any alerts from the printer are displayed, such as sleep mode, paper jams, or hardware malfunctions. The user may perform a test print, which will send a mock report to the printer. The user inspects the printed document and confirms whether the test print was successful or failed.
The user may perform a test scan, which requires that a blank white sheet of paper be scanned. The scanned image is broken up into small cells and each cell is checked for the percent of black pixels after binarization. If that percent is more than slightly over 0%, that cell is flagged and the entire diagnostic fails. The goal of the diagnostic is catch any defects in the scanned images, such as streaking produced by a dirty scanner.
The user may perform a test scan, which requires that a blank white sheet of paper be scanned. The scanned image is broken up into small cells and each cell is checked for the percent of black pixels after binarization. If that percent is more than slightly over 0%, that cell is flagged and the entire diagnostic fails. The goal of the diagnostic is catch any defects in the scanned images, such as streaking produced by a dirty scanner.
The test scan process is identical to that for VxCentralScan, and the images above apply.
The user may test the printer by printing a test page. After the print, the user must indicate whether the page printed successfully or not. The diagnostic available in the diagnostics interface is identical to that which election managers are encouraged to run after loading paper, and the outcome is recorded in either case.
The diagnostics page will also display details about any printer errors if the printer is in an error state.
The user may test the speaker by triggering the chime sound and confirming whether they heard the chime or not.
The user may perform a test of the printer-scanner which mocks the ballot flow during a voting session:
User loads thermal paper
Mock ballot is printed onto paper
Mock ballot is scanned and presented in the front input tray
Mock ballot is ejected into the ballot box
A successful test indicates that the printer and scanner can properly produce and handle ballots during a voting session.
The user may perform a test of the accessible controller which validates that each button on the controller is producing the expected signal. The flow guides the user through pressing each button.
The user may perform a test of the PAT input, which is simply confirms that a PAT device can be successfully calibrated as it would be during a voting session.
The user may perform a test of the front headphone input by connecting headphones and triggering a chime. The user must indicate whether they heard the chime or not, corresponding to a pass or fail respectively.
Signed Hash Validation provides end users with a way to verify that a VotingWorks machine is running authentic unmodified VotingWorks software.
The machine preps a payload consisting of the following, with 1//shv//
as a prefix and #
as a separator:
System hash
Software version
Election ID
Current timestamp
The machine signs that payload with its TPM private key and then bundles the following together, with ;
as a separator:
Payload
Payload signature
Machine cert
This combination is displayed as a QR code. Putting this all together, the QR code contains:
This QR code can be scanned at https://check.voting.works. The site parses the QR code and performs the following verification:
Verifies the machine cert using the root VotingWorks cert.
Extracts the machine's public key from the machine cert.
Uses that public key to verify the payload signature against the original payload. If this verification succeeds, we can be confident that the machine possesses the TPM private key that pairs with the public key in the machine cert.
Because the TPM private key will only sign data if the system hash is correct, per System Integrity, we can further be confident that the software on the machine is authentic and unmodified.
After completing the above verification, https://check.voting.works displays a success indicator alongside the payload components and the machine ID as extracted from the machine cert. These attributes can be matched against what's displayed on the machine.
Refer to the following code links for more details:
VxScan is the system's precinct scanner into which voters cast their ballots directly.
VxScan includes an embedded A4 thermal roll printer which is used to print polls reports. The application communicates with the printer via a custom driver developed by VotingWorks. The application is regularly polling the printer for status, which is one of the following:
Ready
Platen is attached and paper is detected
Application may print normally
No Paper
Platen is attached but no paper is detected
Printing disabled. A warning will be shown on poll worker screens.
Cover Open
Platen is not attached
Printing disabled. If the polls are open, a warning will be shown.
Error
Most likely the printhead has overheated, but other hardware errors are possible. Details available in diagnostics interface.
Printing disabled. Poll workers will not be able to operate the polls, but election managers and system administrators can still authenticate for diagnostics.
During a polls transition, a polls report is generated and gradually sent to the printer. Documents are A4 width but of indefinite length depending on the number of contests on the ballot styles at the precinct. If the printer encounters an error or runs out of paper in the middle of a print, the poll worker will be prompted to wait or to replace the thermal roll in order to continue printing. The previous document will reprint from the beginning. In most cases, poll workers do not have to install the thermal paper roll.
Election managers normally install the thermal paper rolls via a guided flow. They will be prompted to remove the platen, install the paper, and re-install the platen. Whenever an election manager installs a thermal paper roll, they are also prompted to print a test page. The main goal of the test print is to ensure that the election manager installed the roll in the correct orientation as it is one-sided and, if reversed, will not print anything.
The printer roll can be loaded at any time, including when VxScan is off. It only requires opening the access door which may be sealed.
User Manual Reference:
VxScan scans ballots with an embedded A4/Letter document scanner which produces double-sided ballot images. The application communicates with the scanner through a custom driver developed by VotingWorks. The application sends and receives events to and from the driver, managing its transitions between scanning states (waiting, accepting, etc.) and controlling when it will or will not accept ballots.
The embedded scanner includes a multi-sheet detector (MSD) which allows the application to reject cases of voters feeding in multiple ballots at a time. The multi-sheet detection must be calibrated at the beginning of an election based on the thickness of the ballot paper. The double sheet detection calibration flow is exposed in the election manager menu. The election manager can choose to disable double sheet detection, but it is enabled by default.
The scanner's cover can be opened for cleaning, during which scanning will be disabled and the application will show a warning on screen if the polls are open.
VxScan makes noises whenever a ballot is accepted or rejected. The ballot accepted sound is a pleasant chime and the ballot rejected sound is a jarring beep. Election managers can toggle sounds on or off.
VxScan is configured with a signed election package exported from VxAdmin. The election definition includes the ballot layouts necessary for interpreting ballots and the system settings which indicate what type of ballot issues (e.g. overvotes) require adjudication.
VxScan is not fully configured until the election manager selects a precinct for the device. When polls are open, VxScan will only accept ballots for the configured precinct. If ballots for other precincts are cast, they will be rejected. The election manager may configure VxScan to "All Precincts" in which case ballots for all precincts will be accepted.
If the election definition only has one precinct, the precinct will be automatically selected.
Once polls are opened and ballots are cast, the precinct can no longer be changed. If polls are opened but ballots have not yet been cast, the precinct can still be changed but it will result in the polls resetting to closed. Resetting the polls to closed forces a poll worker to re-open the polls, which reprints the polls opened report with the new, correct precinct configuration.
After initial configuration, VxScan is in test ballot mode. The election manager can toggle between test ballot mode and official ballot mode. In official ballot mode, only official ballots can be scanned and exported CVRs will be official ballot CVRs. In test ballot mode, only test ballots can be scanned and exported CVRs will be test ballot CVRs. If allowOfficialBallotsInTestMode
is set in the system settings, official ballots will be allowed in test mode but exported CVRs will still be test CVRs.
Switching between ballot modes clears all scanned ballot data and resets the polls to closed. The election manager can switch from test ballot mode to official ballot mode at any time. When in official ballot mode after ballots have been scanned, the election manager can only switch back to test ballot mode if the CVRs have been synced to the USB drive.
VxScan can be unconfigured by an election manager or system administrator. If ballots have been scanned in official ballot mode, an election manager can only unconfigure the machine after scanned ballot data has synced to a USB drive. System administrators can unconfigure the machine at any time.
After configuration, polls are initially closed. When polls closed, ballots cannot be cast.
Poll workers open the polls to allow casting ballots. Once polls are opened, polls can only return to the initial polls closed state while remaining configured in two ways. First, switching from test ballot mode to official ballot mode will reset the polls. Second, in cases where the polls are open but no ballots have been cast, the precinct can still be changed which will also reset the polls to closed. Both of these actions can be performed by election managers but not poll workers. While polls are open, ballots can be inserted by voters, scanned, and tabulated.
Poll workers close the polls when ballots should no longer be cast. After polls have been closed, the scanner will not accept ballots and the polls cannot be opened again. Polls are closed until VxScan is unconfigured or switched from one ballot mode to another, with one exception discussed below.
VxScan also allows poll workers to pause voting a.k.a. suspend the polls. While voting is paused, the scanner will not accept ballots. Voting can be resumed, after which the polls are back in the standard polls opened state. Pausing voting might be used in an early voting model between voting days or, in the case of an emergency, to pause voting while the emergency is resolved. The poll worker may chose to close polls directly from voting paused instead of resuming voting.
If the polls have been closed, the only possible way for the polls to be re-opened is if a system administrator resets the polls to paused. Only the system administrator may do this - poll workers and election managers cannot - per the allowance in VVSG 2.0 1.1.7-E. Once polls have been reset to paused by the system administrator, voting may be resumed by a poll worker. The goal of this flow is to allow voting to continue after a poll worker has prematurely closed the polls.
Scanning can begin once VxScan is fully configured and polls are open. If any user authenticates, scanning will be disabled until they remove their card. If CVRs are not synced to the USB drive, or no USB drive is inserted, scanning will be disabled. Scanning will be enabled again once a USB drive with synced CVRs is detected. If the election manager has disabled continuous export then scanning will be enabled without a synced USB drive present.
While scanning is enabled, the scanner will grab and scan paper as soon as it is detected. The scanned image will be interpreted while the ballot is still being held by the scanner. If the ballot is interpreted successfully and contains no errors, it will be ejected into the ballot box with a chime and the screen will inform the voter that their ballot has been cast.
If the ballot cannot be interpreted, it will be rejected toward the voter. Interpretation can fail if the ballot is not a valid hand marked ballot or if the ballot enters the scanner at too much of an angle, although the paper path normally prevents significant skew. If the ballot is interpretable but is invalid due to a mismatching election, precinct, or ballot mode, it will also be rejected toward the voter.
If the ballot is interpreted and valid, but contains some voter error, the scanner will hold the ballot out of view while the errors are presented to the voter on screen. The types of errors that are flagged are determined by the precinctScanAdjudicationReasons
set in the system settings. Possible options are overvotes, undervotes, and blank ballots. In the case of overvotes or undervotes, the specific list of affected contests will be listed. The voter has the option to return the ballot to themselves or to cast it despite warnings. If there's an overvote and disallowCastingOvervotes
is set in the system settings, however, the ballot can only be returned.
If the precinctScanAdjudicationReasons
includes adjudicating unmarked write-ins, which are marks in the write-in space with the associated bubble unmarked, those unmarked write-ins will be included when determining whether a contest contains an overvote. At the same time, it will be considered an undervote. For example consider a vote for one contest. If the voter only fills in an unmarked write-in, it will be considered an undervote. If the voter fills in a bubble and a separate unmarked write-in, it will be considered an overvote when warning the voter. Warning in both these cases encourages the voter to clarify their ambiguous marks. When it votes are tallied, however, unmarked write-ins are always considered undervotes at VxScan.
Once the ballot is cast, whether immediately after interpretation or after the voter confirms to cast a ballot with errors, the image and interpretation are both saved to disk and exported to the inserted USB drive as a CDF CVR. The sheet count shown on screen will increment accordingly.
The voter interface can be tuned to be more accessible to different voters:
Text size can be adjusted to four different sizes (VVSG 2.0 7.1-G)
Contrast mode can be adjusted from the default medium contrast (VVSG 2.0 7.1-C) to a high-contrast white background, high contrast black background, or low contrast (VVSG 2.0 7.1-D)
Language can be adjusted to any language supported by the election package
The election package's app strings file specifies the translations that will be used to replace all on screen voter messages when the language is switched.
After the voter casts their ballots, settings will automatically reset to the default (VVSG 2.0 7.1-A). There is also a voter option to reset settings (VVSG 2.0 7.1-B).
VxScan exports cast vote records to the inserted USB drive continuously, after every ballot is cast, in order to avoid a lengthy export at the end of the day. All cast vote records include ballot images and images of rejected ballots are also included. If there is no USB drive in VxScan, ballots cannot be cast. If the CVRs on the USB drive are not in sync with record of ballots on disk, VxScan requires a poll worker or election manager to sync the CVRs to the USB drive. This normally occurs when a USB drive is swapped out after ballots have already been scanned.
Continuous export can be disabled by an election manager if need be, for example if a USB drive turns out to be slow or no USB drive is available. If continuous export is disabled, VxScan can be used without a USB drive inserted.
CVRs can be exported directly from the election manager menu, in which case they export all at once.
On polls open, polls closed, voting paused, and voting resumed, VxScan prints a report via the thermal printer. The poll worker has the option to reprint additional report as many times as they need. The latest polls report can be cast as long as no ballots have since been cast. For example, the polls opened report can be printed as long as zero ballots have been cast. The polls closed report can be printed indefinitely, because no ballots can be cast after polls are closed. Reports will have both the timestamp of the polls transition and of the report printed, so reports printed at a later time are distinguishable.
For details about the format of the polls reports, see VxScan Polls Reports.
For polls opened and polls closed reports, the vote interpretations of all ballots are tallied together. In the case of polls opened reports, the tallies should always be zero forming a zero report. It is impossible to have any ballots scanned already at the time of polls are opened, because the scanner is disabled until polls are opened. Pursuant to VVSG 2.0 1.1.3-B, however, in the impossible event that non-zero totals are detected on the machine when the poll worker attempts to open the polls, an error will be presented to the poll worker, and logged, and they will be unable to open the polls.
Tallies for the polls closed report are created by iterating through the data store's table of cast vote records and forming totals of votes for contest options, undervotes, overvotes, and total ballots cast. The process is the same as on VxAdmin but with far fewer steps because there are no write-in adjudication results to consider and no manual tallies. All marked write-ins are grouped as a generic "Write-In" count in reports. All unmarked write-ins are considered as undervotes.
For voting paused and voting resumed reports, only a total ballot count is included as opposed to vote tallies.
VxMark is the system's ballot marking device. It allows all voters to make selections in various interaction modes, print their ballot, verify their ballot, and cast their ballot independently. VxMark is not a tabulator, and the cast ballots must be later tabulated at VxScan or VxCentralScan.
VxMark is configured with a signed election package exported from VxAdmin. The election definition defines the ballot styles that will be available to voters. The election definition also includes the translations defined for text on the ballot, while the app strings file contains the translations for other text shown on screen. The election package's audio files are played for the voter in audio-mode.
User Manual Reference:
VxMark is not fully configured until the election manager selects a precinct for the device. When polls are open, VxMark will only allow marking ballots in the ballot styles for the configured precinct. The election manager may configure VxMark to "All Precincts" in which case all ballot styles are available.
If the election definition only has one precinct, the precinct will be automatically selected.
The precinct selection can always be changed on VxMark by an election manager, even while polls are opened.
After initial configuration, VxMark is in test ballot mode. The election manager can toggle between test ballot mode and official ballot mode. In official ballot mode, the ballots printed at VxMark will be official ballots. In test ballot mode, the ballots printed at VxMark will be test ballots.
Switching between ballot modes clears the printed ballot count and resets the polls to closed. The election manager can switch from test ballot mode to official ballot mode at any time.
VxMark can be unconfigured by an election manager or system administrator.
After configuration, polls are initially closed. When polls closed, voting is not allowed on VxMark.
Poll workers open the polls to allow voting. Once polls are opened, polls can only return to the initial polls closed state while remaining configured by switching between test and official ballot mode.
Poll workers close the polls when voting should no longer be allowed. Polls are closed until VxMark is unconfigured or switched from one ballot mode to another, with one exception discussed below.
VxMark also allows poll workers to pause voting a.k.a. suspend the polls. While voting is paused, no voting sessions can be started. Voting can be resumed, after which the polls are back in the standard polls opened state. Pausing voting might be used in an early voting model between voting days or, in the case of an emergency, to pause voting while the emergency is resolved. The poll worker may chose to close polls directly from voting paused instead of resuming voting.
If the polls have been closed, the only possible way for the polls to be re-opened is if a system administrator resets the polls to paused. Only the system administrator may do this - poll workers and election managers cannot - per the allowance in VVSG 2.0 1.1.7-E. Once polls have been reset to paused by the system administrator, voting may be resumed by a poll worker. The goal of this flow is to allow voting to continue after a poll worker has prematurely closed the polls.
When polls are opened, poll workers can enable voting sessions on behalf of voters. The poll worker selects one of the available ballot styles and loads a blank piece of thermal paper into the front input tray.
The voter navigates through the contests on their ballot style sequentially, with controls to advance to the next contest or go back to the previous contest. The contest information presented to the voter includes the contest title, the contest district, the number of selections allowed, the number of selections remaining, the contest options, the candidates' parties if applicable, and the descriptions of any ballot measures (VVSG 2.0 7.3-C).
Voters may leave contests blank or undervoted but are prevented from overvoting contests. If the voter attempts to mark an overvote, they will be presented a warning and instructed to deselect another selection if they want to make the new selection (VVSG 2.0 7.3-H). Previous selections are never automatically deselected.
If the contest allows write-ins, they may input a write-in name via a virtual keyboard.
After working through the entire ballot, the voter will then review all their selections. Any undervotes will be flagged for the voter (VVSG 2.0 7.3-I) but may be ignored. If the voter does want to make any changes, they can navigate back to any contest, make changes, and return to the review stage (VVSG 2.0 7.3-F). Once the voter is satisfied with their selections, they may print their ballot.
The thermal printer prints a ballot, presents it to the voter, and prompts them to once again review their selections. This final review differs from the initial review in two ways. First, the printed ballot is being physically presented to the user which creates a voter-verified paper trail. Second, the selections being reviewed are a result of the actual interpretation of the ballot rather than simply the selections made by the voter. As a result, even when a voter is unable to verify their printed paper ballot visually they are still able to verify the ballot through another interaction mode. Once the voter is satisfied with their second round of review, they may cast their ballot which is ejected into the attached ballot box.
If the voter finds a problem in their ballot during the final review stage, they must spoil the ballot. They are prompted to get help from a poll worker who can help them spoil their ballot and load a new, blank sheet. Once the blank sheet is loaded, the voter returns to the initial review stage from which they can edit selections before reprinting, reviewing, and casting their ballot.
While the paper ballot is being presented to the voter, they are supposed to leave it in the scanner so it can be ejected into the ballot box when they cast their ballot. Many voters will naturally remove the ballot from the scanner, however, and cannot cast their ballot until they re-insert the ballot. When they do insert their ballot, it is scanned again and they are returned to the final review stage.
It's possible that a voter removes their ballot and does not re-insert it before the voting session times out. In this case, it's possible for a poll worker to initialize a session by re-inserting an already printed ballot rather than by selecting a ballot style. The already printed ballot is scanned and the voter is dropped directly on the final review screen.
If a re-inserted ballot is somehow not compatible with the machine - wrong election, wrong precinct or wrong ballot mode - the user is alerted and the ballot is rejected out the front.
If a poll worker card is inserted during a voting session the poll worker can deactivate the current session and spoil any current ballot.
From the poll worker screen a poll worker may choose "Insert Printed Ballot" in order to scan a previously printed ballot and start a new voter session at the "Review Ballot" stage of the flow diagram above.
VxMark supports voting sessions in various display formats and interaction modes.
The voting session begins in visual mode with the following default settings:
Text Size - Medium, which corresponds to between 14pt - 16pt, meeting the default text size requirement specified in VVSG 2.0 7.1-G.
Contrast - Medium, which is at least a 10:1 contrast for all informational elements, meeting the default contrast requirement specified in VVSG 2.0 7.1-C
The voter can update the Text Size to Small, Medium, Large, or Extra-Large, which map to the four discrete text sizes defined in VVSG 2.0 7.1-G. Changing the text size changes the size of all informational elements including buttons, icons, and layout (VVSG 2.0 7.1-H) because all such elements are sized relative to the base font size. The font is always sans-serif (VVSG 2.0 7.1-J).
If there's too much content on a page to fit all on screen at once, which happens most commonly in large or extra-large text size, the interface introduces a large "More" button to indicate there are more contest option to scroll through. The interface never lays out content in a way which requires horizontal scrolling.
The voter can update the Contrast from medium contrast to low contrast, high contrast with black background, or high contrast with white background corresponding to the contrast options specified in VVSG 2.0 7.1-D. The contrast changes apply to all elements on screen.
In addition to text, color and icons are used on screen to guide voters. In conformance with color conventions (VVSG 2.0 7.1-E), red indicates danger, yellow indicates warning, and green indicates success. For example, choosing to spoil one's ballot is a red button. Purple is generally used as the primary color to indicate the recommended next step. In high contrast modes, the interface is entirely black and white to support the 20:1 contrast ratios. Icons are used on buttons to provide an additional visual cue, but are always paired with text (VVSG 2.0 7.3-L).
Ending a voting session will automatically reset all display settings to defaults (VVSG 2.0 7.1-A). The voter can change or reset the display settings to defaults at any time (VVSG 2.0 7.1-B).
In visual mode, when a voter makes a selection it is indicated in three ways:
Highlighting the selection in a contrasting color
Updating the icon from an unchecked to a checked box
Decrementing the number of remaining votes allowed shown on screen
Once a voter has used all their votes in a race, the progress button at the bottom of the screen to advance to the next race is also highlighted to encourage the voter to proceed.
Voters usually choose to use the touchscreen to vote. The voter taps buttons to move between contests and through the stages of the voting flow. The voter taps list items on each contest page in order to make selections. Touch areas never overlap and are sized to meet minimums described in VVSG 2.0 7.2-I. Touch areas require that the user's touch begins and ends within the touch area to activate, meaning that dragging a finger across a touch area will not activate the touch area, in order to avoid accidental activation (VVSG 2.0 7.2-H).
When there's more content on a page than can fit on one screen, the voter can scroll through the screen in one of two ways. First, they can tap the "More" button which acts as both the visual indicator that there are more contest options or contests. Second, they can use a swiping motion on the screen to move up or down. Swiping to see more contest options or contests is VxMark's only touch screen gesture. It cannot be used horizontally to navigate between contests or pages and (VVSG 2.0 7.2-E).
VxMark has a permanently attached accessible controller that can be used to navigate the ballot instead of using the touchscreen directly. The left and right buttons are used to navigate between contests and other screens while the up and down buttons are used to navigate between contest options or contests within each screen. The controller has a help button which will navigate to a help interface that explains the function of each button on the controller when pressed.
The currently focused element on screen is highlighted in visual mode or read aloud in audio mode. When navigating through selections on a screen large enough to be scrollable, the screen will automatically scroll to keep the focused element in view.
The accessible controller is integrated with the application via a hardware daemon that is always running. The daemon is continuously listening for input events from the controller. Each event is converted into virtual keyboard presses which are then handled by the frontend rendering agent just as a web browser might handle arrow key inputs.
VxMark has a PAT (personal assistive technology) input port into which a voter can plug in their own two-switch adaptive input such as a sip-and-puff device. When a PAT input is attached during a voting session, VxMark will enter a calibration flow where the voter will map their two inputs to "move" and "select." Different voters can calibrate their sip & puff devices differently such that sip is "move" for one and "select" for another.
Once calibrated, the voter can use the two inputs to navigate through the ballot. The "move" input advance the focus on screen to the next element and the "select" input is the equivalent of a tap in touch interaction mode. Just as with the accessible controller, the currently focused element on screen is highlighted in visual mode or read aloud in audio mode.
The PAT input is integrated with the application via a hardware daemon that is always running. The daemon is continuously listening for input events from an attached device. Each event is converted into virtual keyboard presses which are then handled by the frontend rendering agent just as a web browser might handle Tab
and Enter
.
As an alternative to the visual formats, the voter can listen to their ballot in an audio format. VxMark includes a headphone port and headphones which, when attached, will automatically play audio to guide the voter's ballot navigation. The audio includes information about the election, contests, and contest options equivalent to the information displayed on screen in visual mode. The audio also includes prompts and explanations for how to navigate the ballot that are equivalent to the cues in layout and highlighting on screen in visual mode. These instructions include but are not limited to:
how to start voting
how to make selections in a contest
how to navigate the review screens
how to use the accessible controller
The voter can adjust volume up and down or speech rate up and down with buttons on the accessible controller. Volume and rate defaults and options are calibrated according to VVSG 2.0 7.1-K. The voter can also pause and resume audio with a button on the accessible controller.
Blind or visually impaired voters can use the audio track to review their printed ballot. The printed ballot is scanned and interpreted on its way out of the printer, and the resulting interpretation is the basis for the audio played in the second review state. As a result, blind or visually impaired voters are able to verify their paper ballots just as seeing voters are able to verify their paper ballots.
VxMark supports switching the language for the current voting session to any language supported in the current election package. When the language is changed, all text on the ballot, all text shown on screen, and text read in audio mode to guide the voter will switch to the new language. The language can be changed at any time during a voting session. The language selection resets automatically after each voting session or can be reset by the voter at any time..
If a language other than English is set when printing the ballot, the ballot will show all information in both the current language - for the purpose of the voter's review - and in English - for ease of adjudication and audits.
VxMark's printer and scanner are covered by the printer cover which can be opened at any time in order to clear a paper jam or clean the scanner. In order to prevent unauthorized access, pursuant to VVSG 2.0 12.1-B, VxMark produces a jarring, audible alarm if the printer cover is opened while polls are opened. The alarm should alert a poll worker that something is wrong. A poll worker then inserts their card, which silences the alarm, and follows on screen instructions to close the printer cover.
Because authentication is required for the poll worker to open the printer cover without triggering an alarm, poll workers are instructed on screen to authenticate when clearing a paper jam. When polls are closed, the alarm is not active, so cleaning and setup can take place without triggering the alarm.
Ballot interpretation begins with the front and back images of the ballot transmitted from the scanner. Once the images are available to the application, it starts by trying to interpreting the ballot as a hand marked ballot.
Before trying to make sense of the ballot, the interpreter checks the images for vertical streaks. Vertical streaks likely indicate some sort of smudge or debris in the scanner that could interfere with the ballot image. The interpreter looks for columns of black without gaps, excluding the edges of the ballot which may be black simply from the way the scanner creates images.
If a streak is detected, the interpreter exits and surfaces the error to the application which will alert the user.
The interpreter then identifies the timing mark grid. It begins by finding all shapes in the image. It then narrows the list of shapes down to ones that look like timing marks and then further narrows the list down to timing mark shapes that fall along the edges of the image in a line.
In the example above, the interpreter found all the timing marks correctly. Sometimes, it may not. In that case, it infers any missing timing marks. Once it has all the timing mark borders, it identifies the corners in order to construct a complete timing mark grid. If at any point the interpreter does not have enough information to confidently find the complete mark grid or if the detected grid is too rotated or skewed, it causes the application to reject the ballot.
Next the interpreter will search the bottom left and top right corners of the image for a QR code. Since ballots have QR codes in the bottom left corner, the location of the QR code determines the correct orientation of the ballot and the interpreter can flip the images right-side up if necessary:
The QR code includes ballot metadata - precinct, ballot style, election hash, ballot mode - but no vote information (see Hand Marked Ballots). Up until now, the two sides of a ballot have been interpreted in parallel. At this point, the QR code metadata on the front and back are compared to ensure that they match to form a valid ballot. The ballot information from the QR code indicates which ballot layout from the election definition that the ballot conforms to, including the position of all the bubbles, contests, and write-in areas.
Now the interpreter inspects the locations of all ballot bubbles to see how filled they are. Note that bubbles are not necessarily aligned with timing marks because they can be defined in fractional grid coordinates. Each bubble is compared with a bubble template to distinguish voter marks from the bubble itself and is scored accordingly. In the images below, the orange score is the "match score", or the confidence that the bubble was found correctly. The greenish score is the "mark score", or the amount that the bubble's area is filled in. An empty bubble would receive a mark score of 0% or close to it–typically less than 1%.
The bubble mark scores are later compared against the definite mark threshold in the system settings and used to determine whether the voter made an indication that should be counted. The recommended default threshold is 7%. Setting too low of a threshold may result in stray marks or ballot folds to be considered as marks. Setting too high of a threshold may result in reasonable voter marks not being detected. While ballot instructions should recommend voters fully fill in the bubbles, at a threshold of 7% the system will detect most voter marks that pass through the bubble.
After bubbles are scored, the interpreter will then determine the location of the contest options relative to the grid. The write-in areas are defined in the election definition relative to the contest option areas. The write-in areas are then scored, similarly to the bubbles. This step only occurs in jurisdictions allowing unmarked (unbubbled) write-ins because otherwise a write-in is valid based only on the bubble. Write-in areas that cross the write-in area threshold set in the system settings will be shown in the write-in adjudication flow in VxAdmin. The system's default write-in area threshold is 5%.
After all bubbles and write-in areas are scored, interpretation is complete and the images are saved to disk. The votes are inferred from the bubbles based on the mark thresholds and eventually exported to cast vote records.
If interpretation didn't work, one possibility is that the ballot is actually a machine marked ballot, so the interpreter will then attempt to interpret the ballot as a machine marked ballot. Since votes are encoded into the QR code, the interpreter only has to find the QR code. It searches the entire document for a QR code. By searching the top and bottom half separately, it can infer the orientation of the ballot because machine marked ballots have QR codes in the top right.
VxScan consists of two main components - the collapsible ballot box and the scanner itself - which are secured together during setup.
The ballot box features a telescoping handle and wheels for ease of transport. When collapsed, closing straps run around the side of the box to hold it closed. Before the ballot box is set up, the closing straps are loosened and secured to the underside of the ballot box lid.
The front of the ballot box is the ballot door, which is used to access the bin interior and any ballots within. It's opened by pulling the ballot door latch pictured above, which automatically latches whenever the door is closed. A seal can be installed to prevent the door from being opened without tamper-evidence.
The ballot door has a built-in auxiliary bin for depositing ballots in situations where the ballots cannot be scanned. It can be in one of three states: closed, partially open for depositing ballots, or fully open for removing ballots. It's opened by pressing down on the indicated handle which can be sealed with the adjacent seal point.
After closing straps are loosened, the ballot box can be expanded. An accented foot hook can be used as an aid in expanding the box. Once the box is fully expanded, the lid can be rotated up and over 270 degrees to eventually close and latch into place.
After being latched, the ballot box lid can be released by pushing the ballot box lid handle, indicated below. The handle is covered by the scanner when installed, so it cannot be opened while the scanner is in operation and sealed in place. The box lid has a ballot slot which mates with the scanner's ballot outfeed.
The scanner attaches to the ballot box by sliding into the area in the front of the lid. Flanges on the bottom of the scanner prevent it from moving up and down or side to side. To secure the scanner front to back and lock it into place, the scanner has a security bolt which mates with the security bolt hole on the top of the ballot box. After the security bolt is engaged, the scanner cannot be removed until the security bolt is unsealed and released. This is covered more fully in the next section.
The scanner is built into a customized Pelican 1485 Air case. The COTS case features:
Seal Points
Carrying Handle
Spring-Loaded Latches
Additional cuts are made into the case for the back power port, the back cable wrap, the security bolt exit, nameplate attachment, and mounting features within the case.
The case is opened by pressing the buttons on each case latch and then lifting the lid. The elements that a voter or poll worker interact with are generally accented in purple, just as with the ballot box. In the picture below you can see the smart card insert, security bolt, ballot infeed, and printer outfeed all accented in purple.
When the scanner is first installed on the ballot box, the security bolt must be disengaged. While the security bolt is disengaged, the smart card slot will be blocked. Once the scanner is installed on the ballot box, the user must push the bolt down to lock the scanner into place on the ballot box. This will also unblock the smart card slot. In this way, the scanner can not be activated until it is properly installed.
When the scanner must be removed from the ballot box, the user pushes the security bolt release to the left to disengage the security bolt. The security bolt release is accented with graphical instructions for the user. In order to access the security bolt release, the poll worker door must be opened, which also exposes the scanner's USB ports. The poll worker door can be sealed shut by passing a seal through a cutout in the door and the hole in the top of the security bolt.
Poll workers will normally not have to interface with any parts of the scanner that have not already been described, but election managers may have to open the access door to perform maintenance on the printer and scanner. The access door is held in place by the poll worker door and can only be opened when the poll worker door is unsealed and opened. The printer and printer roll holder are beneath the access door toward the screen. The user presses the green release lever to remove the printer roll holder and install a paper roll. The scanner can be opened simply by lifting the scanner cover handle, revealing the scanner glass for easy cleaning.
The various components of the scanner are arranged and wired together within the Pelican case. Custom cut holes in the Pelican case allow mounting brackets in the top and bottom tubs which are then used to attach components.
Power enters through a power module embedded in the case. The power module connects to a 24-volt power supply which supplies the scanner and printer with power. A 12-volt power supply is daisy-chained from the 24-volt power supply in order to power the single board computer.
The metal chassis of the bottom tub is grounded to exterior ground via the power module. Everything else requiring grounding is then grounded to the bottom chassis - power supply, lid chassis, access panel, and the scanner.
All USB cables ultimately connect to the single board computer. The card reader and printer connect via a custom USB cable directly to headers on the computer. The USB hub, which then connects to the USB ports and scanner, is connected directly to the computer. Finally, a USB-C cable connects the computer to the screen. The USB-C cable carries video, audio, touch input, and power. Audio is carried through the screen to the speakers via a separate cable.
The wiring digram below outlines the power and data connections within the scanner. Black lines indicate power connections, gray lines indicate ground connections, and purple lines indicate data connections, which may also carry low voltage power.
To prevent access to internals, VotingWorks installs three seals over screws holding the metal panels in place - one on the bottom left panel covering the computer, one on the bottom right panel covering the power supplies, and one on the top panel holding the display in place. These seals are installed at the factory and are intended to be permanent aside from when panels need to be opened for repairs.
The paper path is carefully designed and tested to ensure a smooth and uninterrupted ballot scan and deposit. The voter inserts paper over the infeed which mates directly with the scanner. The scanner lid handle doubles as an upper deflector, deflecting ballots which are inserted too high by the voter. As the ballot passes through the scanner, it hits the ballot redirector behind the scanner. The ballot redirector is a logarithmic curve which ensures that wherever the ballot strikes, it will be directed downward. The ballot then passes through the outfeed slot (which is only open when the scanner case is open) into the ballot box.
VxScan includes many COTS components, which mostly fall into two categories. First, many small pieces of hardware such as fasteners are purchased commercially. These are called out in the bill of materials and are not generally critical components. Second, most of the electronic components are purchased commercially. Documentation for these components can be found in the documentation repository.
Peripheral Dynamics, Inc.
PageScan 6 Scanner
PS6
High
USBFirewire
10" USB A-B Cable
RR-ADBU-10GR
Medium
L-com
0.5m USB A-B Cable
CAABLK-90RB-05M
Medium
GlobTek, Inc.
USB-C Cable
USBCW1M0USBC2SLEMBK
Medium
HID Omnikey
Embedded Smart Card Reader
R31210375-1
Medium
Aaeon
Single-Board Computer
UPN-ADLN97-A10-0864
High
ADATA
Solid-State Drive
IM2P32A8-128GCTB5
High
Tripp Lite
USB Panel Mount
U324-001-APM
Medium
CUI Devices
Power Cable
AC-C13 NA
Low
MEAN WELL
12V Power Supply
LRS-75-12
Medium
MEAN WELL
24V Power Supply
LRS-150-24
Medium
Fujitsu
A4 Thermal Printer Mechanism
FTP-68EMCL112-R
High
Fujitsu
A4 Thermal Printer Control Board
FTP-62EDSL201-R
High
Fujitsu
A4 Thermal Printer Head Cable
FTP-62EY001-R
Medium
Coolgear
USB Hub
CG-3510S4-BOARD
Medium
Pelican
Outer Case
Pelican Air 1485
Medium
Elo
Touchscreen
E976783
Medium
As listed in the COTS table above, the high criticality components are as follows:
Aaeon UP Squared Pro 7000 Computer - As the computer which orchestrates ballot interpretation and vote tallying, the single board computer is a highly critical component. VotingWorks partners with Aaeon, Inc. and their production in Taiwan to ensure the trustworthy production and quality of each single board computer.
PageScan 6 Scanner - As the source of ballot images for interpretation, the embedded scanner is a highly critical component. Any interference in image creation could affect interpretation and subsequently vote tallies. Any interference in paper handling could affect ballot accounting. VotingWorks works closely with Peripheral Dynamics, Inc. to ensure the quality of the hardware and firmware of the scanner manufactured domestically. The specific VotingWorks firmware version is tracked by Peripheral Dynamics, Inc. and installed on all scanners.
Fujitsu Thermal Printer Mechanism and Control Board - The thermal printer is a less sensitive component than the previous two because it does not send sensitive data to the application - it only receives sensitive data. It is still a single-sourced component with complex subassemblies, however, and any issues printing would prevent jurisdictions from having accurate day-end results.
Solid State Drive - The solid state drive is the storage medium for all sensitive election data.
The medium criticality components and the reasons for their classification are as follows:
USB Cables, Hub, and Mounts - USB cables connect to the computer and carry sensitive election data, so they are medium criticality. The attack vectors are difficult, however, and all cables can be produced by alternate manufacturers.
Power Cables & Power Supplies - Power supplies and cables can affect the reliability of the equipment in initially difficult to detect ways, and are thus medium criticality, but they generally cannot corrupt election data in a targeted or undetectable way.
Pelican Case - The Pelican case cannot meaningfully affect the operation of the software, but its sole supplier is Pelican. Since the equipment is designed around the particular case, the case and its supplier are considered critical.
Touchscreen - The Elo touchscreen is responsible for rendering election data and correctly handling user touches, but it generally cannot impact election data and impacts would be perceptible. It is custom component from a single manufacturer, however, and is certainly medium criticality.
Smart Card Reader - The smart card reader is responsible for communicating with smart cards to manage authentication, and is thus a critical component.
VxMark exposes various hardware interfaces for various modes of voting. The touchscreen displays the ballot and allows making selections with touch. It tilts forward and backwards for easier viewing and for storage. An accessible controller sits on the left side of the screen held in place with light magnetic force. Beneath the accessible controller is the headphone input and above the accessible controller is a recess for resting headphones. On the lower right corner, there is another input jack for a PAT (Personal Assistive Technology) device such as a sip-and-puff.
The right side of the machine is the paper path. Paper is inserted into the printer-scanner by feeding it forward on the front paper input. When the ballot is cast, it is ejected to the rear into the attached ballot box. In case of paper jams, the printer-scanner cover can lift to expose the printer-scanner. If the cover is opened while polls are opened without authentication, an alarm will be triggered through the onboard speaker.
The primary modification made by VotingWorks to the original Smartmatic hardware is adding a smart card reader for authentication. The smart card reader is positioned beneath the accessible controller:
Power is controlled by a button in the back left of the machine. When activated, an LED will illuminate the power button. Between the power button and the power port is an additional headphone input which is not used by VxMark.
VxMark is shipped and stored in large hardshell cases with custom foam cutouts. The first case, accented in blue, is for the BMD itself, its headphones, and its power supply. The second case, accented in yellow, is for the other peripherals - legs, power supply holder, privacy shield, and ballot box.
The BMD itself must be padded as shown below for transport and vibrational testing. Padding is used to protect the screen and hold it in place, to hold the accessible controller in place, to hold the printer-scanner cover in place, and to further secure the BMD within the enclosure.
The vast majority of the VxMark hardware is the VSAP (Voting Solutions for All People) Ballot Marking Device (Model 150) developed by Los Angeles County, manufactured by Smartmatic, and purchased through Los Angeles County. In addition, VotingWorks includes a few peripherals or COTS components added through modification. Documentation for these components can be found in the documentation repository or in the separately provided Los Angeles County documentation.
Smartmatic
Ballot Marking Device
VSAP-150
High
APC
Uninterruptible Power Supply
BN1500M2
Low
HID
Smart Card Reader
R31210375-1
Medium
LORELEI
Headphones
X6
Low
Williams Audio Visual
T-Coil Neckloop
NKL-001
Low
The VSAP system provided by LA County is the primary component and the highest criticality component. The entire system runs on it. The embedded computer and the printer-scanner are both responsible for the printing of ballots and the interpretation of ballots. The various interaction interfaces - touchscreen, accessible controller, and PAT input - can all affect voter selections. VotingWorks and Los Angeles County have a robust partnership that includes the sale of the hardware itself, documentation transfer, and alignment on access control and quality assurance procedures.
The only other critical component, with medium criticality, is the HID smart card reader. As it is responsible for communicating with smart cards and facilitating authentication, it is a sensitive component.
The processing capabilities of the system are enumerated in the .
Specifically, capabilities that may be bypassed or enabled by the user are configured in the system settings file within the .
Marks are determined valid and counted by the system if the amount of darker pixels in the bubble area exceeds a configurable "definite" mark threshold set in the system settings file. This mark threshold can be adjusted to have a stricter or looser interpretation of what is considered a valid mark.
The recommended and default threshold is 7%. With this threshold setting, hand marked paper ballot interpretation detects a valid mark per the mark conditions described by VVSG 1.1.6-H.
At this default threshold, marks such as a light dot, a fold through a bubble, a stray mark outside of the bubble, or a small line just on the corner of a bubble will be considered invalid. Marks such as a completely filled bubble, half filled bubble, or an X in a bubble will be considered valid.
Examples of invalid and valid marks along with the score for that mark (in green) are shown below.
The mark threshold set on a scanner can be checked by an election official at any point by viewing the readiness report for that device as described in (see also & in the user manual).
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 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.
Then on the USB, we can use a flat structure that’s much easier to reason about and iterate over:
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:
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 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:
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:
— VxSuite authentication lib, a good starting point for all things authentication
— Artifact authentication logic
— OpenSSL commands underlying various authentication and signing operations
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 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 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:
User Manual Reference:
User Manual Reference:
User Manual Reference:
User Manual Reference:
User Manual Reference:
User Manual Reference:
User Manual Reference: , ,
User Manual Reference:
User Manual Reference:
User Manual Reference:
User Manual Reference:
User Manual Reference:
Assembly instructions are covered in the user manual under . In short, the legs are unfolded and the power block holder is set on the legs with the power block inside. The BMD then fits onto the legs with grooves on its underside.
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 .
— CVR hashing logic, including the Merkle tree implementation
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 .
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 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.
— VxSuite authentication lib, a good starting point for all things authentication
— High-level authentication state management for VxAdmin and VxCentralScan
— High-level authentication state management for VxScan
— Java Card implementation
— Certificate configuration
— OpenSSL commands underlying various authentication and signing operations
— The production machine configuration wizard
— A summary of the scripts in the authentication lib, many of which are used for production configuration
— The applet that we’re installing onto our Java Cards
-
-
-
Root hash
4
-
-
4 hash
4
4d
-
4d hash
4
4d
4d6a9dad-e6d6-4a29-89bc-9ab915012b73
4d6a9dad-e6d6-4a29-89bc-9ab915012b73 hash
…
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.
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.
VxSuite can be operated securely using the following these procedures. Some of these procedures are intentionally redundant for defense-in-depth as it is always expected that some operational mistakes are made.
VxSuite should be used only with sanctioned equipment and peripherals. In particular, when connecting USB devices to any VxSuite component, use only USB devices of the type specified in this documentation, and only devices of known and reputable source.
When there are no active elections happening, VxSuite equipment should be physically secured so it is difficult to access and so that any access is evident at a later date.
VxAdmin laptop and peripherals should be kept in their case, with numbered seals through both case seal points, preventing the case from being opened without breaking one or both seals.
VxCentralScan laptop and peripherals should be kept in their case, with numbered seals through both case seal points, preventing the case from being opened without breaking one or both seals.
The small Fujitsu/Ricoh scanner that works with VxCentralScan should be kept in its supplied soft case, with a numbered seal through the appropriate zipper hole, preventing the case from being opened without breaking the seal.
The larger Fujitsu/Ricoh scanner that works with VxCentralScan should be kept in its supplied manufacturer box, with tamper evident tape along all sides and a numbered sticker seal on the main opening.
VxScan should be kept closed with a numbered seal through one of the two seal points, preventing the case from being opened without breaking the seal.
VxAdmin, VxCentralScan, VxScan, and the Fujitsu/Ricoh scanner, once sealed, should be kept in a safely locked room or storage area that only authorized election administrators have access to.
The system admin smart cards should be kept in a safe or locked drawer, separately from VxAdmin. The PIN for the system admin smart card, if recorded on paper, should also be kept locked separate from both VxAdmin and the card.
You may consider occasionally resetting the PIN on the system administrator cards. This can be done using the VxAdmin laptop.
Both VxAdmin and VxScan are required for Logic & Accuracy testing. VxCentralScan is required if it is planned to be used for counting absentee ballots. VotingWorks's recommended practices take into account that this testing is often performed in view of the public.
When retrieving all components from their locked storage location, ensure that the seal numbers match the logs.
Keep clear and strong custody of all smart cards.
Use the system admin card only long enough to un-configure VxAdmin, VxScan, and VxCentralScan from the last election, and to program VxAdmin for the new election. Then return the system admin card to its secure storage.
At the end of L&A testing, ensure that the VxScan is ready and secured for election day:
the correct USB drive is inserted into one of the two USB slots.
the system has been switched to official-ballot mode.
the VxScan is powered down.
the VxScan case is closed and sealed.
If using VxCentralScan, ensure that that:
it is switched to live mode
it is shut down and put away in its case
the case is closed and sealed.
Store all L&A'ed equipment, with recorded seal numbers, in a secure location until election day.
Ensure that seals on equipment are untouched and appropriately numbered since L&A.
After setting up VxScan on the ballot box and opening the polls, ensure that:
the ballot box is locked and/or sealed
the auxiliary ballot box is locked and/or sealed
the VxScan is attached to the ballot box using the locking mechanism
the poll-worker access door on the VxScan is sealed shut
All seal numbers should be recorded.
A poll worker should observe VxScan from afar at all times, ensuring voters are not attempting to break any seals.
If a ballot jam requires opening the poll worker door or the ballot box door, record the corresponding seal number before breaking, and ensure resealing afterwards, recording the new seal number(s).
VxCentralScan is only meant to be accessed by authorized election administrators. However, it is safe to project the screen via overhead projector, as long as the smart card PIN is entered via the keyboard, and not via the mouse.
Unsealing the poll worker door is required to extract the USB drive after polls are closed. Ensure the seal number matches the expected value. Once the polls are closed and the USB drive is removed, take care to secure the chain of custody of the USB drive. VxScan should be closed up and sealed.
Pack up all components in their respective cases and apply seals. Record those seals.
Additionally, any auditing or review of audit logs should be completed immediately after exporting those logs from the VxScan or Central system to a USB. If there is a concern about a break in the administrative chain of custody, the user should re-export the logs to a USB from the machine and can compare the original export with the re-export to determine the logs have not been tampered with while on the USB. The logs maintained on the system are unalterable and should be pulled and reviewed in one process to ensure that chain of custody is maintained.
In addition to the processes in Procedural and Operational Security, VxSuite employs the following physical security controls for each component.
VxAdmin laptops are stored and transported in a case with two tamper-evident seal points to detect any unauthorized physical access.
VxAdmin laptops have a tamper evident adhesive seal on the laptop itself to detect any unauthorized physical access to the laptop internals.
VxAdmin laptops BIOS configurations do not allow the machine to boot if the laptop itself is opened without entering a vendor-only password & returning the device to VotingWorks.
VxAdmin has no exposed ports that are not used for voting system operations. VxAdmin's printer has an ethernet port blocker installed.
VxCentralScan laptops are stored and transported in a case with two tamper-evident seal points to detect any unauthorized physical access.
VxCentralScan laptops have a tamper evident adhesive seal on the laptop itself to detect any unauthorized physical access to the laptop internals.
VxCentralScan laptops BIOS configurations do not allow the machine to boot if the laptop itself is opened without entering a vendor-only password & returning the device to VotingWorks.
VxCentralScan has no exposed ports that are not used for voting system operations. VxCentralScan's attached batch-scanner (fi-8170) has an ethernet port blocker installed.
VxScan has two external tamper-evident seal points to detect any unauthorized physical access.
VxScan has three interior adhesive tamper-evident seals (1 on top panel; 2 on bottom panel) to detect any unauthorized access to the device internals.
VxScan has a tamper-evident seal point at the intersection of the poll worker door and security bolt to ensure secure ballot box attachment and detect any unauthorized access to poll worker functions.
VxScan's ballot box has a seal point for each ballot storage area (main & auxiliary compartments) to detect any unauthorized access to cast ballots.
VxScan triggers a visual & audible alert when a USB drive is removed in an activated state to alert any unauthorized access.
VxScan has no exposed ports when the poll worker door is sealed.
VxMark transport & storage cases have tamper-evident seal points to detect any unauthorized physical access.
VxMark has an adhesive tamper-evident seal behind the touchscreen to detect any unauthorized physical access to the device internals.
VxMark ballot box has a tamper-evident seal point to detect any unauthorized access to cast ballots and the printer-scanner.
VxMark triggers a visual & audible alert when the printer-scanner and/or printer-scanner cover is opened in an activated state to alert any unauthorized access.
VxMark has no exposed ports when the ballot box is attached and sealed. An ethernet port blocker is also installed on the ethernet port accessible when the ballot box is detached.
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.
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.
Software installation involves two steps:
Generating software images via Trusted Build: Trusted Build
Imaging machines with those images: Imaging Machines
VxSuite can be audited using post-election audits, including a ballot-comparison or batch-comparison risk-limiting audit, or an image audit.
In a ballot-comparison risk-limiting audit, the goal is to compare, for a random sample of the cast ballots, the paper ballot and the corresponding cast-vote record. The specifics of sample selection and risk-limit calculation are out of scope for this document – VotingWorks Arlo or other RLA tool may be used for this purpose.
To enable a ballot-comparison risk-limiting audit, VxSuite provides:
imprinting of ballots on VxCentralScan, with a unique identifier of the form
<BATCH-ID>_<SEQUENCE-NUM>
cast-vote records including the field BallotAuditId
which matches the imprinted identifier
Then, to run a ballot-comparison risk-limiting audit with VxSuite:
scan all ballots with VxCentralScan and the imprinter module on the Ricoh scanner. See #imprinting.
store ballots by batch, recording the batch ID displayed on VxCentralScan display. Storing ballots in the order they were scanned is helpful for later retrieval, but not strictly necessary since the sequence numbers printed on the ballots can be used to recover the proper order.
export CVRs from all the VxCentralScan's
use VxAdmin to aggregate CVRs, adjudicate write-ins, and generate final tallies
aggregate tallies and CVRs at the audit jurisdiction level, most often the State
input the CVRs and tallies into a risk-limiting audit tool
the RLA tool will generate a sample of ballots to audit
retrieve the selected ballots by batch ID and sequence number
enter the interpretation of those ballots into the RLA tool
the RLA tool will either declare the audit successful or require an escalation. The RLA tool will ultimately declare the election a success or call into question how ballots were interpreted.
In a batch-comparison risk-limiting audit, the goal is to compare, for a random sample of ballot batches, the hand tally of that batch with the corresponding batch tally. The specifics of sample selection and risk-limit calculation are out of scope for this document – VotingWorks Arlo or other RLA tool may be used for this purpose.
To enable a batch-comparison risk-limiting audit, VxSuite provides:
Tallies partitioned by scanner and/or by batch in VxAdmin
Ballot counts by scanner and by batch in VxAdmin
Then, to run a batch-comparison risk-limiting audit with VxSuite:
scan ballots either with VxScan or VxCentralScan. No need to use imprinting for VxCentralScan.
store ballots by batch – where a batch in VxCentralScan is indicated by the batch ID on screen, and a batch in VxScan is the entire set of ballots scanned by a single VxScan over the course of the election. Order of the ballots need not be maintained in either case.
export CVRs from all VxCentralScans and gather the CVRs on USB drives from all VxScans
use VxAdmin to aggregate CVRs, adjudicate write-ins, and generate final tallies
produce ballot counts by scanner and by batch on VxAdmin – these are effectively contest totals by batch files in auditing language.
aggregate tallies and ballot manifests at the audit jurisdiction level, most often the State.
input tallies and ballot manifests into the RLA tool
the RLA tool will generate a sample of batches to audit
retrieve the batches and recount them by hand – usually only for one or two contests, as directed by the audit procedure
enter the hand-count batch tallies into the RLA tool
the RLA tool will either declare the audit successful or require an escalation. The RLA tool will ultimately declare the election a success or call into question how ballots were interpreted.
In an image audit, various risk-limiting audit procedures are used, but instead of physically retrieving the ballots to audit, the scanned image of the ballot is used.
VxSuite supports image audits very simply – every VxScan and VxCentralScan produces images of the scanned ballots that can be immediately associated with the interpreted CVR. Thus, the protocols defined above for ballot-comparison and batch-comparison audits can be used, with the following simplification:
imprinting is unnecessary
instead of "retrieving a ballot", simply look up the image files corresponding to the CVR ID and interpret on screen
VxSuite hardware contains numerous subcomponents, some of which are subassemblies containing hundreds of subcomponents themselves. Successfully building, deploying, and maintaining VxSuite requires that components be sourced from scores of suppliers. If any component cannot be procured, it could impact the ability to build the system at all. If any component is defective or compromised, it could degrade the system's performance. In that sense, every component in a voting system is critical. Certain components are more critical than others, however, and the following analysis defines our strategy for ranking components with high, medium, or low criticality.
The two primary criteria we consider when assessing criticality are ability to find alternative suppliers and relationship to sensitive election data. If we are unable to easily find alternative suppliers, the component is at least medium criticality. If the component processes sensitive election data, the component is at least medium criticality. If both are true or one is especially true, the component is high criticality. In addition, there are a number of other lesser criteria mentioned below.
Any component that processes election data is at least medium criticality because, if it fails or is compromised by a malicious actor, it could theoretically alter configuration, operation, tabulation, or aggregation which could compromise the interpretation of ballots, the tallying of votes, or voter privacy.
Example 1: Any scanner in the system, because it is responsible for recording ballot images, is a high criticality component. Defects in the scanner could produce unreliable ballot images. Sophisticated malicious attacks could alter firmware or intercept data transmission to produce altered images.
Example 2: USB cables transfer election data between components and are always at least medium criticality. USB cables are generally simple and substitutable, but highly sophisticated attacks involving manipulating data passing through a USB cable are possible.
Example 3: Power cables don't carry data to and from devices so, even though they are obviously critical to the operation of the device, they don't have a direct relationship to sensitive election data and are not more critical as a result.
Most components can be or already are manufactured by multiple manufacturers. Standard screws can be purchased from various suppliers. Custom plastic or sheet metal components can be molded or cut by any number of manufacturers. Even power cables, simple USB cables, or something like a computer mouse can easily be replaced by an equivalent.
Some essential VxSuite components are unique to a single supplier or a small set of suppliers, however, making them at least medium criticality. When there is a single supplier, the product cannot be built as designed without the support of that supplier. Additionally, a single supplier creates a direct possible supply chain attack vector.
Example 1: The embedded scanner in VxScan is manufactured and sold only by Peripheral Dynamics, Inc. Without being able to source the scanner from them, it would not be possible to build VxScan as it exists today, meaning the scanner has high criticality.
Example 2: The case that forms the exterior of VxScan is a Pelican 1485 Air case. The product is designed around the case and, without the case, the product could not be built as-is, so the case is at least medium criticality. Conceivably, an alternative could be found, but it would not be straightforward.
Example 3: The anodized sheet metal panels that form the interior surfaces of VxScan can be cut and anodized by hundreds of metal shops across the country, so they are not more critical as a result of their supply.
Any component can fail, but some component failures will be caught easily in quality control while others are more difficult to detect. For example, a misshapen sheet metal part will be caught in incoming parts QC or may result in the machine being not operable or not assemble-able. A slightly fraying cable, however, might perform adequately in QC but quickly deteriorate in performance. Components that are more complex, like a printer or cable, may exhibit deteriorated or even nefarious behavior that only manifests under certain conditions outside of QC.
Simple components where failure is easily detected are less critical than complex components where failure is more difficult to detect. Due to the complexity of a subassembly like an entire laptop, we're not able to effectively QC all of its many subsystems and have to rely on the supplier for their QC more than for a simple, transparent component.
A seal point or hinge may be more critical than an interior panel because, if it is defective, it could break the physical security model of a device and allow someone to access a machine without tamper-evidence.
In each article describing the hardware of a specific component, some section will specify the high and medium criticality components and provide some explanation of why it is deemed critical. All unlisted components are low criticality.
We consider the supplier of any critical component a critical supplier.
Using the criteria described above, the VotingWorks operations team narrows down the list of medium and high criticality components. For critical COTS components, we limit our suppliers to reputable and well-established vendors and manufacturers. When assessing whether a supplier is trustworthy, we consider several factors: length of time established; reputation for reliability; degree to which organization and manufacturing is based in the United States; the outcome of previous orders; responsiveness to issues; and their focus on critical business applications. For critical VotingWorks-specific manufactured components, for example any custom cables, we look for domestic-based manufacturers with ISO quality certifications (e.g. ISO 9001) and, where possible, prefer to perform site visits during onboarding.
For each critical component, VotingWorks requires a warranty on defective components and a delivery timeline for all orders. Specifically, contracts with suppliers include language matching or to the effect of:
The supplier is required to provide a warranty for all products or services that do not conform to specification.
The supplier is required to provide a delivery timeline for each order.
Suppliers for all critical components in the bill of materials are audited at the beginning and end of each hardware development cycle. If we discover disqualifying information about a supplier, VotingWorks works to identify alternative suppliers. If components are no longer available due to supply chain issues or the end of a product's lifecycle, VotingWorks works to identify alternative suppliers.
Defective components during production are handled by:
Identifying the defect
Notifying the supplier
Returning the defective component to the supplier for analysis
Requiring an explanation from the supplier as to how the defect occurred and how it will be mitigated in the future.
Operating system: Debian 12
RAM: 8GB minimum
Chip: x86-64 / amd64 CPU architecture, Intel i5 or higher
Storage: 250GB minimum
Debian 12 will need to be installed on the build machine. You can find detailed instructions for that process under Installing Debian 12 on VxBuild. Once that is complete, we can install the VotingWorks build system, vxsuite-build-system.
Throughout the rest of this document, substitute <inventory-name>
with the inventory provided by VotingWorks.
At this point, you should have three separate VMs: debian-<inventory-name>-<apt-snapshot-date>-<debian-release-name>
, online
, and offline
. You can confirm this by running:
VotingWorks' vulnerability disclosure policy is publicly available here: https://github.com/votingworks/vxsuite/security/policy
The public disclosure policy provides instructions on how to report a vulnerability, where to see known vulnerabilities, and the coordinated vulnerability disclosure process VotingWorks follows.
Given the protection mechanisms we use to prevent unauthorized software from running on VxSuite components, the simplest way to perform a vulnerability scan is to use Nessus in a host operating system, with VxSuite components running as guest virtual machines (VMs). We turn off our networking blocks on those VMs for the sake of performing a useful vulnerability scan.
On each of these components, the same vulnerabilities are found by Nessus. There are 15, and they are all in the "Info" category of vulnerability, meaning that they only allow for information gathering about the host, not for any corruption or direct attack. All of these issues are listed here, with an explanation as to why they are not vulnerabilities of concern.
ICMP Timestamp Request Remote Date Disclosure
With networking protections in place like they are on a production machine, this issue would not exist. Even if it did, it only allows an attacker to collect the current clock of the machine, which is not a problem.
Common Platform Enumeration (CPE)
This only indicates that a particular Nessus plugin is running to determine platform properties.
Device Type
With networking protections in place like they are on a production machine, this issue would not exist. Even if it did, it only allows an attacker to determine what kind of devices this is (manufacturer, CPU power, etc.). This is considered public information, so is not a threat.
Ethernet MAC Addresses
With networking protections in place like they are on a production machine, this issue would not exist. Even if it did, it only allows an attacker to determine the MAC address of an Ethernet port, which isn't a threat.
HTTP Methods Allowed (per directory)
With networking protections in place like they are on a production machine, this issue would not exist. VxSuite uses HTTP internally within each component, so with networking protections turned off, it is obvious that allowed HTTP methods can be detected. This isn't an issue on a production system.
HyperText Transfer Protocol (HTTP) Information
With networking protections in place like they are on a production machine, this issue would not exist. Even if an attacker accessed this information, it isn't confidential.
KVM / QEMU Guest Detection (uncredentialed check)
This finding isn't relevant on a production machine, which is not a VM. This is a VM only because of the vuln-scan testing setup.
Nessus SYN scanner
With networking protections in place like they are on a production machine, this issue would not exist.
Nessus Scan Information
This only indicates that a particular Nessus plugin is running to determine running services.
OS Identification
With networking protections in place like they are on a production machine, this issue would not exist. Even if it did, it only allows an attacker to determine the operating system running on a VxSuite component, which is already public information.
Service Detection
With networking protections in place like they are on a production machine, this issue would not exist. Even if it did, the services that are running on a VxSuite component are publicly documented and it is not considered a threat for an attacker to know them.
TCP/IP Timestamps Supported
With networking protections in place like they are on a production machine, this issue would not exist. Even if it did, this only allows an attacker to determine the uptime of the VxSuite component, which is not confidential information.
Traceroute Information
With networking protections in place like they are on a production machine, this issue would not exist.
Web Server No 404 Error Code Check
The web server in question is used only for internal purposes of the VxSuite component, it is never presented as a public web site. Thus, whether it returns a 404 code or not is not important.
Web Server robots.txt Information Disclosure
The web server in question is used only for internal purposes of the VxSuite component, so the robots.txt file is irrelevant.
To install Debian 12 on the build machine (VxBuild), you will need to download the latest Debian 12 amd64 installer file here: latest Debian release.
You should use a blank USB drive to create the install drive. The process described below will wipe any existing content.
To create a USB install drive with the downloaded ISO file:
Ensure that the USB drive is available to the system. By default, it tries to attach as the /dev/sda
device. An easy way to verify this is via the following command:
If the USB drive is not attached as the /dev/sda
device, that’s ok. Simply replace /dev/sda
with the device that your USB drive did attach to.
NOTE: The command below should not include any number as part of the device path. For example, if the above command returns /dev/sda1
, you must use /dev/sda
as the path in the next command.
Now that you know the correct device path, you can create the USB install drive via the following command:
NOTE: At the time of this writing, the latest stable release is 12.8. Please update to the appropriate file name if you're using a newer version.
Once the above command completes, you can safely remove the USB install drive from your system.
Before powering VxBuild on, insert the USB install drive created in the previous step.
After powering VxBuild on, begin pressing F12 until it enters the boot menu. Boot the system from the USB install drive.
You will be presented with the following screen. Select "Graphical install" and press enter.
Select your preferred language and click "Continue".
Select your location and click "Continue".
Select your keyboard layout and click "Continue".
Select your network device (may differ from screenshot) and click "Continue".
If using a wired connection, it will configure automatically.
If using a wireless connection, select a network, select WPA/WPA2 PSK, and click "Continue". Enter your wireless network password and click "Continue".
Enter "VxBuild" for the hostname and click "Continue".
Leave the domain name blank and click "Continue".
Set the root user password and click "Continue".
Enter "Vx" for the full name for the new user and click "Continue".
Enter "vx" as the username and click "Continue".
Enter a password for the "vx" user and click Continue.
Select your timezone and click "Continue".
Select "Guided - use entire disk" and click "Continue".
Select the "/dev/nvme0n1" disk and click "Continue".
Select "All files in one partition" and click "Continue".
Select "Finish partitioning and write changes to disk" and click "Continue".
Select "Yes" and click "Continue".
The base OS installation will now begin. During the installation you will be asked to answer questions related to package management.
Select "United States" and click "Continue".
Select "deb.debian.org" and click "Continue".
Leave the proxy information blank and click "Continue".
You will be asked to configure "popularity-contest". Select "No" and click "Continue".
The installation process will continue. Once it completes, you will be presented with a final screen confirming that it was successful.
Remove the USB drive and click "Continue".
The system will reboot and automatically start Debian 12.
Log in with the "vx" user and password you created during the installation process.
Several basic configuration prompts will be displayed. Select "Next".
Select your keyboard preference and click "Next".
Turn off Location Services and click "Next".
Do not configure any online accounts. Select "Skip".
Click the "Start Using Debian GNU/Linux" button. The dialog will be closed.
The "vx" user needs to be granted sudo privileges related to configuring and initializing the build environment tools.
Open a terminal window.
As the "vx" user, you will temporarily log in to the "root" account.
You will see your terminal window prompt change to root@VxBuild
. To grant the "vx" user sudo privileges, run the following command as root.
You will see your terminal window prompt change to vx@VxBuild
. You are now the "vx" user instead of the "root" user. To confirm sudo privileges, run the following command:
The command should return "root". This confirms sudo privileges have been granted correctly.
All VxSuite applications use the same framework to log user actions, application processes, and errors. The logs are persisted to disk and can be exported by election managers or system administrators.
VxSuite applications use a shared logging library to capture required or otherwise important application log events. The core metadata for each application log event maps roughly to VVSG 2.0 standards, with some additional fields:
Log Event ID - Internal identifier for each event. The full list of log event IDs with descriptions is included in the automatically generated log documentation.
Log Event Type - Describes whether the event is a status update or an action, and whether it originated with the user, application, or larger system. The full list of log event types with descriptions is included in the automatically generated log documentation.
User - If there was an authenticated user role at the time of a user action, describes the role as system_administrator
, election_manager
, poll_worker
or vendor
. If no user was authenticated, then unknown
. If event does not originate from the user but rather automatically from the application or operating system, it will be system
.
Disposition - For actions that can fail or succeed, success
or failure
. Otherwise na
for not applicable.
Source - Indicates where in the system's architecture the log came from, whether it be the frontend renderer, the frontend server, the backend server, a hardware daemon, or the operating system.
Message - The log message itself, which may not be present if the log event ID sufficiently describes the event.
Other Metadata - Other metadata can be freely included. For example, if a user adds manual tallies on VxAdmin, the metadata of those manual tallies will be included in the logging.
Time Written - Timestamp of when the log was created.
The shared logging library formats the log events as JSON and pushes them to application processes' stdout
. All stdout
output from the application processes - both the application log events and any process output - is sent to the system's logger
utility and assigned the tag votingworksapp
.
VxSuite uses rsyslog
, an advanced implementation of the Syslog protocol, to centralize and manage logs from votingworksapp
and other sources. rsyslog
has advantages over the built-in Linux syslog
implementation of the Syslog protocol, including JSON structured messages, advanced filtering, and increased performance. Using rsyslog
, different types of logs are directed to different log files in /var/log/votingworks/
:
vx-logs.log
Key logs required by VVSG 2.0 or otherwise critical for understanding the behavior of the application or device, including:
all application log events
USB device connection events
machine boot and shutdown events
all sudo
actions
password events
dm-verity
events
auth.log
All operating system authentication-related events. Note that this will not include voting system authentication events such as logging in with a smart card - these events are in vx-logs.log
syslog
All other events emitted from the application or the operating system that do not fall into the other log files.
vx-logs.log
is the most important and informative file in most cases. The other files rarely need to be referenced. All logs required by VVSG certification will exist in vx-logs.log.
Because each log file can grow very large, the application will rotate logs if necessary with the logrotate
utility on each startup. Previous log files will be compressed and suffixed with a timestamp.
Including the various log files and log rotation, below is an example of the list of log files that might appear on a device:
The default VotingWorks log format is how logs are formatted when emitted from the application and how they appear in vx-logs.log
. It is a direct mapping of the#application-log-events fields.
Logs can be exported in CDF format, in which case the vx-logs.log
file is replaced by a vx-logs.cdf.json
file with the logging attributes mapped as follows:
ElectionEventLog.GeneratedTime
ISO formatted timestamp of when logs were exported
ElectionEventLog.Device
List containing only the current device
Device.Type
The CDF DeviceType
matching the type of machine:
VxAdmin -> "ems"
VxScan -> "scan-single"
VxCentralScan -> "scan-batch"
VxMark -> "bmd"
Device.Id
The serial number of the device
Device.Version
The software version e.g. "v3.1.2"
Device.Event
List of all events
Event.Id
A VotingWorks defined identifier for the type of event, such as "save-election-package-complete"
Event.Disposition
"success", "failure", "na", or "other"
Event.OtherDisposition
If the disposition is "other", the details appear here
Event.Sequence
The index of the log in the list of logs, zero-indexed
Event.TimeStamp
ISO formatted timestamp of when the event was logged
Event.Type
Log event type
Event.Description
Log message
Event.Details
JSON including the log source and any additional logging metadata
Event.UserId
The user at the time of the log
The log export flow exposed in all VxSuite applications also allows for exporting an errors-only version of the logs. This will only export a log file for logs where the disposition is "failure" and covers all software and hardware errors that have occurred on the machine.
A full list of all logs made in the system with a description of each one can be found here. For convenience a table is provided below mapping Table 15-1 from VVSG 2.0 Requirement 15.1-D to the appropriate logs in the VotingWorks system. The details and descriptions provided in Table 15-1 have been simplified for brevity in this table. Some items have been expanded into multiple rows to more precisely specify linked logs.
Device generated error and exception messages
Critical system status messages
diagnostic and status messages upon startup
Critical system status messages
"zero totals" check
Critical system status messages
the initiation or termination of scanner and communications equipment operation
Critical system status messages
Printer errors
Critical system status messages
Detection or remediation of malware or other malicious software
Critical system status messages
Cryptographic boot validation success/failure
Non-critical status messages
Events that require election official intervention
Device shutdown and restarts
Changes to system configuration settings
Integrity checks for executables, configuration files, data, and logs
The addition and deletion of files
System readiness results
Removable media events
Backup and restore
Authentication related events
Login/logoff events
Account lockout events
Password changes
Access control related events
User account and role (or groups) management activity
Enabling or disabling networking functionality
Installing, upgrading, patching, or modifying software or firmware
Changes to configuration settings
Abnormal process exits
Successful and failed database connection attempts (if a database is uses)
Changes to cryptographic keys
Ballot definition and modification
Voting events
Opening and closing polls
Voting events
Casting a vote
Voting events
Canceling a vote during verification
Voting events
Success or failure of log and election results exportation
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.
This risk assessment reviews threats and vulnerabilities identified in:
VxSuite hardware, including VxCentralScan, VxAdmin, vxMark, and VxScan. Also included are any items and peripherals needed to operate the equipment listed above (e.g., USB drives, scanners, printers).
VxSuite software and source code
VotingWorks internal communication and operation support systems.
This assessment was conducted following the framework outlined in NIST Special Publication 800-30 - Guide for Conducting Risk assessments.
For additional information on how physical, technical, and operational controls work together to meet the requirements of VVSG 14.1-C.1-4, please refer to , specifically:
Please also refer to the , notably the documentation.
Risk Assessment [PDF]
Risk Assessment [Excel]
We'll now clone the offline VM to prepare images for specific machine types, i.e., VxAdmin, VxCentralScan, VxMark, and VxScan. We'll create one clone/VM per machine type.
In the following steps, the vxadmin VM will be referenced, but these steps can be repeated for each machine type: vxcentralscan, vxmark, and vxscan.
To clone the offline VM, run the following command on the build machine:
This command creates a byte-for-byte clone of the offline VM, along with all settings, including network functionality disabled at the VM level.
Open virt-manager if not already open:
Double-click the vxadmin VM.
Press the start button ▶️.
Once the VM has initialized, log in with username vx and password votingworks.
To ensure that the console displays correctly, select "View" > "Resize to VM".
In the VM terminal window, run the following commands:
You will be guided through several prompts.
Select the number of the machine type that you intend to build.
Type "N" when asked whether this image is for QA.
Type "y" when asked whether this is an official release image.
Set a password for the vx-vendor
user. This password will not meaningfully be used as the vendor menu on a production image is only accessible via a vendor card.
After the script finishes, the VM will reboot. You will see a white screen displaying “Card Reader Not Detected”. In the VM menu, select "Virtual Machine" > "Shut Down" > "Shut Down". Close the VM window once shutdown is complete.
To finalize an image for production use, it's necessary to sign the image with the VotingWorks Secure Boot keys so that the image can be used on a Secure-Boot-enabled machine. This process requires the use of sensitive keys and a passphrase that should only ever be known to and used by VotingWorks. Since the Trusted Build process is performed by a third-party vendor (SLI), a process to securely sign the image without compromising the keys or passphrase is needed. This document describes that process.
Once SLI has created a Trusted Build image, the image and its VM definition (an XML file) must be securely provided to VotingWorks. To transfer the image and definition, SLI will upload to a shared S3 bucket only accessible to SLI and VotingWorks. This access is controlled via IAM permission policies.
NOTE: For documentation purposes, we will use a Trusted Build image named vxadmin. Replace that with the appropriate image name, as necessary.
Once SLI has configured their S3 access and created a Trusted Build image, they will need to upload the image and its configuration file. Additionally, hashes of each file will be generated so that SLI and VotingWorks can confirm files were not modified during the upload or download phases.
On the SLI build machine:
Once the SLI upload has completed, VotingWorks will download and verify the hash values of all files. On the VotingWorks secure build machine, while SLI observes:
Once the VotingWorks download has completed and been verified, and the VM has been successfully defined, VotingWorks can proceed with its Secure Boot signing process, while SLI observes.
VotingWorks will boot the VM, attach a virtual device containing our Secure Boot signing keys, and select the option to "Lock the System Down" from the vendor menu. When prompted, VotingWorks will enter the passphrase for the Secure Boot signing keys.
When the process completes, the lock-down script displays the system hash. This hash will be provided to SLI/EAC for official verification of the image.
Now that the image has been securely signed, VotingWorks will upload the signed image for SLI to later download. While SLI observes, VotingWorks will run the following command on the VotingWorks secure build machine:
Once this step has completed, SLI can download the files at their convenience. On the SLI build machine:
The VotingWorks build process consists of three distinct build phases: , , and . The build process uses an inventory definition containing all the necessary information for a Trusted Build. All three phases are executed within a separate virtual machine (VM) managed by the tool, running on a Debian 12 operating system.
In the online phase, the build process utilizes a base Debian 12 VM with network access enabled. All necessary code repositories and build tools are securely retrieved and transferred to a USB drive for use during the offline phase.
In the offline phase, the build process utilizes a base Debian 12 VM with network access disabled. All necessary code repositories and build tools are transferred from the USB drive created during the online phase.
In the final configuration phase, the build process utilizes clones of the offline VM to prepare images for specific machine types, i.e., VxAdmin, VxCentralScan, VxMark, and VxScan.
After completing the final configuration phase, an unlocked installation image has been created. While this image is not appropriate for production use, it can be used for testing.
For a final production image, an additional step is required to securely sign the installation image for use with Secure Boot enabled systems, described under .
The VotingWorks build system, , utilizes many different tools to install, configure, and build VotingWorks applications. To ensure the integrity of the build system and tools used, all third-party tools are verified against known hashes, checksums, or digital signatures. This appendix provides a description of the mechanism used to verify each tool and any additional resources those tools may be responsible for providing during the build process.
APT
Debian's package manager, apt, is built on a secure framework described here: .
The VotingWorks build process relies on this fundamental tool to install many of the system level tools required to build our applications. As an added measure of security, all packages are pinned to specific versions to ensure a consistent build environment over time.
To further ensure that consistent versions of dependencies and transitive dependencies are used, we create our own VotingWorks-hosted apt snapshot to pull from.
pip
Python's package manager, pip, does not verify package integrity by default. However, secure installation is possible via the method described here: .
The VotingWorks build process implements the above method in the script. The required packages are listed, along with their hash, in unique requirements files based on the operating system version and architecture. You can see an example here: .
RubyGems
Ruby's package manager, gem, does not verify package integrity by default. However, the official gem repository provides SHA256 checksums for hosted packages. You can see an example here: .
The VotingWorks build process requires the version and SHA256 checksum for any installed gems. Once the gem file has been downloaded, it is checked against this checksum to verify its integrity. You can see that checksum verification in the rubygems playbook here: .
Rust
By default, Rust is installed over an active internet connection. Since that method is not available during the offline phase of the VotingWorks build process, we use a standalone, offline installer binary provided by Rust. To verify the integrity of the installer, the downloaded file is verified against the officially published checksum that can be found in each version's official manifest. You can see that checksum verification in the rust playbook here: .
Cargo
Cargo is the Rust package manager. It provides secure installs via the combination of a manifest listing packages and their versions, along with a lock file containing the checksum for each package. That method is described here: .
Node
Node is typically installed via package managers like apt, or via a direct install from a specific version. We do not install Node via apt since we explicitly version it. As a result, we download the appropriate installer from the official Node repository. That downloaded file is then checked against the officially provided checksum. You can see that checksum verification in the node playbook here: .
npm
npm is the default Node package manager. It is installed as part of the Node install previously described.
pnpm
pnpm is another package manager for the Node ecosystem. It is installed via npm as previously described to ensure its integrity.
Packages installed via pnpm use a lockfile: pnpm-lock.yaml. This lockfile includes the version and checksum to verify integrity when packages are installed during the build process. In addition, the VotingWorks build process explicitly requires the use of --frozen-lockfile
to ensure that only the packages explicitly required are installed.
Yarn
Yarn is another package manager for the Node ecosystem. It is installed via npm as previously described to ensure its integrity.
Packages installed via yarn use a lockfile: yarn.lock. This lockfile includes the version and checksum to verify integrity when packages are installed during the build process. In addition, the VotingWorks build process explicitly requires the use of --frozen-lockfile
to ensure that only the packages explicitly required are installed.
Open virt-manager if not already open:
Double-click the offline VM.
Press the start button ▶️.
Once the VM has initialized, log in with username vx and password votingworks.
To ensure that the console displays correctly, select "View" > "Resize to VM".
In the VM terminal window, validate that networking is automatically disabled in the offline VM using the following command:
This command should either time out or immediately error with a failure in name resolution.
Ensure that the USB drive created in the online phase is attached to the build machine.
To make the USB available to the offline VM, select "Virtual Machine" > "Redirect USB Device". In the dialog box that opens, select the USB drive that you attached and close the dialog.
The USB drive will usually be available as /dev/sda1
. To confirm, you can use:
If it’s accessible by a different path, that’s fine, just make a note of it and use that path in the following steps as appropriate.
Run the following commands:
Once the offline phase completes, the base VotingWorks application has been built. You can now shut down the offline VM:
By default, newly created VMs have networking enabled. To ensure the offline VM does not have access to the internet, the networking link is disabled before the VM is ever used. This is accomplished by editing the VM's configuration (found in /etc/libvirt/qemu/offline.xml) and setting the link state to down. This functionality can be seen in the vxsuite-build-system repo, in .
If an offline VM exists, the XML configuration file is updated to set the link state to down. After that, the offline VM is explicitly re-defined from this updated XML configuration file. These steps always execute, even if the network link state is already set to down.
Any VM cloned from this offline VM will also have a disabled network since those settings are inherited from the original VM.
Virt Manager uses a local bridge network to connect to VMs. If you receive an error when starting a VM that says "Error starting domain: Cannot get interface MTU on 'virbr0': No such device", run the following command in the terminal on the build machine:
You should now be able to return to the VM and start it without issue.
To install an image on a VotingWorks component, i.e., to image a machine, you need two USB drives:
A vx-iso USB drive — is our VotingWorks-specific ISO installer program.
An image USB drive — This is an empty USB drive with two partitions, a "Data" partition that can contain as many VotingWorks images as space allows and a "Keys" partition that can optionally contain the VotingWorks Secure Boot public keys, necessary if a machine hasn't had these keys installed yet.
Clone the vx-iso repo for the tooling necessary to prepare the above:
To create a vx-iso USB drive, you can follow these instructions:
If this is SLI, we have provided you with vx-iso USB drives so that you don't need to prepare them from scratch.
To create an image USB drive, you can follow these instructions:
You'll need access to the relevant images and optionally the VotingWorks Secure Boot public keys. Both of these are stored on a private S3 bucket, though they're not sensitive, and VotingWorks can prepare temporary links to grant access to them.
If this is SLI, you do not need to create data drives with the VotingWorks Secure Boot public keys. Secure Boot has already been configured on all your machines.
Errors can be logged with any LogEventId but are indicated by a Disposition of failure
. All logs contain a Source. Every error is logged when it occurs and each log can be assumed to correspond to one instance of that error occurring unless otherwise stated in the log.
VxScan violations of physical security include scanner cover open logs:
VxScan violations of physical security include scanner cover open logs:
With disposition of failure
The system will not boot if malicious software is detected.
Various other logs
Various other logs
It is not possible to change these configuration settings any attempt to do so will result in a log.
and logs will have failures if there were errors authenticating the files.
Programming smart cards to create new user accounts: . All other security changes can only be made through configuring a new election-package with those settings: . Attempts to bypass or make changes to roles or permissions outside of this will not be allowed and result in a and/or log.
It is not possible to enable wired or wireless networking. Any attempt to bypass protections in place blocking this will result in an error and a log.
VxMark: VxScan: with the event "scanStart" to indicate the voter has started feeding a ballot. If the voter has to cast from adjudication there will be an "Accept" event.
VxMark: VxScan: with event "Reject"
You will now need to perform the process with VotingWorks. Once that process is completed, the VM and corresponding image will be ready for use with Secure Boot.
At this point, you are ready to install the image. You can find those instructions in .
Once SLI has completed the download, and hashes have been validated, the compressed image and signature can be provided to EAC for official use. At any point after submission to EAC, election officials and VotingWorks can verify the image hash as described under .
Packages installed via npm are checked against the officially provided checksums for each package. You can see that checksum verification in the node playbook here: .
Once a machine has been imaged with a signed image, you can verify the system hash against the hash of what was built and signed during the Trusted Build process. To perform this verification, you have two options.
This approach allows you to verify the system hash using a tool outside the system itself. vx-verifier is a modified version of vx-iso.
Power the machine down.
Insert the vx-verifier USB drive.
Power the machine on. It should auto-boot to the vx-verifier USB drive.
Navigate to the "Verify Hash" option, attaching a keyboard if necessary.
This will calculate and output the system hash of the installed image, which can be checked against the system hash recorded during the Trusted Build process.
Once you have verified the hash, you can press "enter" to reboot and remove the vx-verifier USB drive.
This approach allows you to verify the system hash from within the system. See Signed Hash Validation.
User centered design (UCD) is a design philosophy and process that prioritizes the needs, preferences, and behaviors of end users throughout the design and development process.
VotingWorks implemented user centered design in the following ways:
Focus on Users: Worked to understand who the users are, including voters and election administrators. This involved conducting user research including: researching election laws and practices in different states, piloting the system to gather user feedback, and conducting feedback sessions with different types of users.
Iterative Design: VotingWorks created several prototypes and tested these through pilot projects in Mississippi and New Hampshire. We also conducted targeted user testing to gather feedback and refine the software and hardware based on that feedback.
Involvement of Users: From the beginning, VotingWorks incorporated user feedback sessions, pilots, and targeted testing to make sure all changes met the needs of the end user.
Multidisciplinary Collaboration: VotingWorks gathered feedback from not just election officials and voters, but also academics and other interested stakeholders.
Usability Testing: Finally, VotingWorks conducted formal usability and accessibility testing, as required by VVSG 2.0.
After you've imaged a machine, the machine will boot into a basic configuration wizard. The majority of the steps are self-explanatory, but "Step 1: Set Machine ID" and "Step 4: Create Machine Cert" require some extra clarification.
It is important that the machine ID be unique for each machine. Many machines have a physical placard on them indicating the machine ID. That is the ID that should be used here.
On VxAdmin, you'll first see a prompt to enter a jurisdiction:
SLI should use co.sli
Then, on all machines, you'll see this prompt:
Insert a USB drive that you designate for this purpose. From here on out, we'll refer to this USB drive as the VxCertifier USB drive.
After selecting the VxCertifier USB drive, a certificate signing request will be written to it. You'll then be prompted to:
Because you'll be certifying your machine at your own facility as opposed to a VotingWorks facility, you won't be able to take the USB drive to VxCertifier, our VotingWorks certification terminal. We'll need to use a remote certification process instead.
You'll need to remove the VxCertifier USB drive, find the "csr.pem" file inside the "certs/" directory on it, and share that file with VotingWorks. This file does not contain any private information so can be shared over the internet, e.g., via email. VotingWorks will prepare a certificate given this "csr.pem" file and send the certificate back to you, in the form of a "cert.pem" file. This file, too, does not contain any private information so can be shared over the internet. You'll need to copy this "cert.pem" file back onto the VxCertifier USB drive, placing it in the same "certs/" directory that we pulled the "csr.pem" from. Re-inserting the USB drive into the machine and pressing enter should allow you to proceed successfully.
On VxAdmin, you'll be prompted to program your first system administrator card as a last step. Remember to record the displayed PIN. On other machines, no steps remain. You'll reboot into the app after this.
VxSuite product quality is ensured through both focused processes on individual aspects of the system and through overall integration testing that ensure the system is working well end-to-end.
VotingWorks code has extensive unit test coverage. All libraries in the vxsuite repository have near 100% coverage, meaning that almost every single line of code runs whenever testing is run. Across all libraries, there are over 3,000 unit tests which all together include over 10,000 assertions about the software's behavior.
Each test always either passes or fails. A test fails if an error is thrown or an assertion fails. An assertion may be something like "the phrase General Election appear on screen" or "there are 56 votes for a specific candidate after loading cast vote records."
Whenever changes are made to VotingWorks code, all tests are automatically run as part of a continuous integration (CI) process through CircleCI. Code changes cannot be merged unless all tests pass. A single failing test will prevent changes from being merged. Every test run on CirceCI provides evidence that the software is operating as expected.
Test runs are public information available via CircleCI. For example, tests running in vxsuite
are tracked here.
If tests are flaky, meaning that they usually pass but occasionally don't, VotingWorks investigates and fixes the underlying issue. Flaky tests are automatically reported by the CircleCI tooling. VotingWorks reviews new flaky tests on a weekly basis.
In addition to unit tests, VotingWorks code has end-to-end integration tests for all applications. These operate in the same continuous integration framework as the unit tests, but ensure the system works at higher-level. Instead of simply running the application server or the frontend, the entire stack is simulated to ensure that the components of the system - data store, user interface, communication between processes - are working as expected. Like unit tests, these tests are run before any code change is merged.
Whenever a software issue is discovered in manual testing or in actual operation in the field, VotingWorks creates an automated regression test after fixing the issue. For example if a particular ballot image was not interpreted but should have been interpreted, that ballot image would be used to create an automated regression test to ensure that not only is the issue fixed but that future changes cannot recreate the issue.
Even tests which are not explicitly created as regression tests have an anti-regression function. The advantage of having a robust test suite is that failing tests will inform developers when new fixes or improvements break previous functionality.
While we strive to cover as much as we possibly can with automated tests, there remains significant value in rigorous manual testing of software on actual hardware. We periodically conduct such passes and perform particularly rigorous passes before freezing software for a release.
To organize this process, we make use of checklists. A base set of checklists can be viewed here: Software QA Checklists. When performing QA before a release for a particular state or election, we often tweak these base checklists to hone in on the specific circumstances, e.g., using specific adjudication settings and paper ballot lengths.
VxSuite practices safe concurrency by using programming languages that, in their design, avoid the most common dangers of concurrency.
Most of the application code, both frontend and backend, is written in TypeScript which compiles to JavaScript executing at runtime. JavaScript is a single-threaded language. While code can execute asynchronously, it cannot execute simultaneously with other code, preventing most instances of unsafe concurrency. We do sometimes make use of a mutex, and our TypeScript mutex implementation is well-tested.
Performance-critical parts of the application, like image processing for ballot interpretation, are written in Rust. Rust was designed specifically to be a memory-safe language alternative to common low-level alternatives like C. It requires that every piece of memory has a single owner responsible for the memory's lifecycle. Unsafe memory operations are prevented by the tooling itself - the compiler and linter - to ensure memory-safe programs. The language is recommended for security critical operations by the White House (2024 Report) and industry leaders. While memory safety and concurrency safety are slightly different things, the two are deeply intertwined, and a memory-safe language avoids most of the pitfalls of unsafe concurrency.
Despite those protections, unsafe concurrency is technically still possible, as it is widely acknowledged as impossible to definitively prove that a complex piece of software is free of race conditions.
In order to catch any other instances of unsafe concurrency, VotingWorks tests software operation at scale in automated tests, as discussed above. In actively developed repositories like those for VxSuite, tests are running hundreds or thousands of times before the software is actually used on a machine. At that scale, intermittent concurrency issues are surfaced in the form of flaky tests. VotingWorks reviews flaky tests weekly and uses them to identify and fix any concurrency issues.
Once software is actually imaged to hardware, VotingWorks tests the software thoroughly and at high volumes. For example on VxScan, the precinctScanEnableShoeshineMode
flag in the #system-settings allows testing the scanner with a repeated scan that is run thousands of times before a release. In addition, VotingWorks attempts to identify edge cases and break the software during testing by performing operations in unusual or rapid succession which may reveal concurrency issues.
VotingWorks uses large-scale COTS hardware wherever possible, including laptops, single-board computers, document scanners, and printers. By virtue of designing around components that are produced and field-tested at much higher scale than voting machines ever can be, we reduce the chance of a quality problem at the design phase.
For components that must be custom-made, for example the VxScan precinct scanner module and the ballot box, we have followed the industry-standard multi-phase design process to ensure that quality issues are caught early, long before we've committed to large production runs. In particular:
design prototyping – every individual hardware component is prototyped using 3D-printing and laser-cut parts in order to validate CAD designs, in particular when managing a paper path.
lab prototypes – every hardware subsystem – e.g. the VxScan module or the VxBox ballot box – goes through multiple lab prototypes of increasing fidelity. Each prototype is fully functional and is tested with thousands of ballots, through setup and teardown repeatedly. Weaknesses are cataloged and fed into the next lab prototype.
engineering prototype – once the lab prototype is fully validated, an engineering prototype is designed, tweaked for manufacturing (DFM). The tooling is then built, in order to produce an engineering prototype that is as close as possible to the final product. The tooling is adjusted in minor ways based on tests performed on the engineering prototype.
By the time a VotingWorks hardware subsystem goes to assembly-line production, it has gone through a number of iterations to ensure that the design is high quality.
VotingWorks works closely with suppliers to ensure quality components. We require suppliers to provide documentation of their QA procedures (TA2.1.1-A.2 1). VotingWorks's policy is to report all defective components to the supplier in order to jointly plan mitigations for future orders.
VotingWorks component test procedures vary on a component-by-component basis:
Custom Manufactured Parts - Metal and plastic parts manufactured specifically for VotingWorks (e.g. a metal panel in VxScan) are visually inspected and measured against dimensions specified in their engineering drawings.
Complex Electrical Subassemblies - Large electrical components, which are often critical components, are tested before being installed in any machine. For example, the embedded scanner in VxScan is tested for motor control and image quality. VotingWorks has command-line interfaces that allow controlling and testing devices outside of a full software installing, simply by connecting them to a development machine.
Smaller or common COTS components, like a simple USB cable or piece of hardware, are visually inspected and their proper operation is covered by the quality assurance done on each individual unit.
Our complete assembly work plans are available in the document repository.
Many issues only emerge when software is installed on production hardware or when the various components of the system are used together. In order to catch these higher-level system issues, VotingWorks performs full system integration testing before every release.
At VotingWorks locations, VotingWorks testing staff set up the full suite of hardware with the latest software release. The testing procedures are designed to imitate an election flow and test any common corner cases or past issues. Most testing procedures are repeated with multiple election packages and system settings to confirm that there are not issues specific to certain configurations.
When an issues is found, the issue is immediately escalated to the engineering team for a fix. A new release is created and testing procedures are restarted in full to ensure fixes do not introduce regressions.
We have comprehensive test reports:
Every unit assembled in production is manually tested against a comprehensive checklist to ensure the unit is fully operational. The quality assurance procedures are designed to imitate an election flow and check for any issues found on past units. Checklists for VxAdmin, VxMark, VxScan, and VxCentralScan are public here in the documentation repository.
Open virt-manager:
Double-click the online VM.
Press the start button ▶️.
Once the VM has initialized, log in with username vx and password votingworks.
To ensure that the console displays correctly, select "View" > "Resize to VM".
In the VM terminal window, run the following commands:
You will be prompted for the sudo password.
The online phase will take awhile to complete.
Once it finishes, you need to attach a USB drive to the build machine. This USB drive will be used to transfer all necessary tools and code to the offline VM.
To make the USB drive available to the online VM, select "Virtual Machine" > "Redirect USB Device". In the dialog box that opens, select the USB drive that you attached and close the dialog.
In the online VM terminal:
You will be prompted to select the USB drive. Once selected, all necessary code repositories and build tools will be exported to the USB drive. After the export is complete, you can shut down the online VM:
First make sure that you've prepared USB drives for imaging, following the instructions under Preparing USB Drives for Imaging. Then follow these steps:
Power off the machine.
Insert both the vx-iso and image USB drives into the system. If this is a VxMark or a VxScan, connect a keyboard as well. If there aren't enough ports available, use a USB hub as provided by VotingWorks.
Power on the machine to begin booting vx-iso.
The precinct system components (VxMark and VxScan) are to auto-boot from a bootable USB drive when connected and should auto-boot to vx-iso.
The central system components (VxAdmin and VxCentralScan) require entering f9 after powering on to boot to USB. Select the USB drive corresponding to the vx-iso drive.
Select "Write an image". You can navigate vx-iso with the keyboard. This option will be auto-selected in 10 seconds.
If the machine already has Secure Boot keys installed, it should not prompt you to install keys. If it does for some reason, you should reach out to VotingWorks for assistance. Only if you know the keys need to be installed should you opt to install them.
The images on the image USB drive will be displayed. Select the number that identifies the correct image.
Enter 27 for the final expected size of the image in GB.
Confirm your selections and wait for imaging to complete.
Once imaging completes, remove the USB drives and press "Enter" to reboot.
On reboot, you should see a prompt for a passphrase. This passphrase is used to decrypt the machine's /var partition so that it can be re-encrypted via the TPM. Enter "insecure" — this passphrase is not relevant to our security architecture. If Secure Boot is not enabled, you'll instead see a note about needing to enable Secure Boot. The machine will auto-boot you into the BIOS. On reboot after that, you should see the passphrase prompt.
The /var partition should encrypt and expand, and you should then find yourself in Basic Configuration Wizard. Proceed to that section.
On VxMark, if you find yourself on an unexpected screen after the above steps, e.g., a Secure Boot error screen or booting straight into a previously installed image, you may need to manually edit the VxMark boot order. You can follow these instructions to do so:
Power off the machine.
Insert the vx-iso USB drive.
Power on the machine and auto-boot to vx-iso.
Use "Ctrl+C" to leave the main vx-iso interface and access a terminal.
Type efibootmgr
to list out the boot entries. The output will look something like this:
Identify the boot entry for the recently installed image. Let's say in this case we want vxadmin-signed, Boot0002.
Run the following command, replacing the index, to make that entry the first in the boot order after the USB drive:
The VxSuite TDP is open-source and publicly available. The public documents required by VVSG are listed below:
Election Event Log Coding: Logging
CDF Implementation:
Ballot Definition: Ballot Definition CDF
Cast Vote Records: Cast Vote Records
Election Results Reporting: CDF ERR Export
Logging: Logging
Barcodes:
Hand Marked QR Code: Hand Marked Ballots
BMD Summary Ballot QR Code:Machine Marked Ballots
Signed Hash Validation QR Code: Signed Hash Validation
Audit Implementation: Audit Procedure
VxScan (incl ballot box)
VxAdmin
VxCentralScan
VxMark
Estimated Replacement Rate
10 years
10 years
10 years
10 years
Estimated Cost Per Replacement
$7,500*
$2,500*
$2,500*
$7,500*
*Costs quoted here reflect 2024 costs. VotingWorks expects prices to increase over time, in line with US inflation rates.
VotingWorks requires all voting equipment customers to purchase our Technology Repair, Upgrade, Support and Training (“Trust”) Services. TRUST Services will cover warranty for all products purchased. Average annual cost of TRUST Services will not exceed 5% of the equipment purchase price for years 1-10.
A sample TRUST Services Agreement is available here.
VotingWorks will continually collect data on warranty usage at the product level and compile such data into reports. VotingWorks will share this data with the EAC at least every two years.
The voting system features three electronic display screens - on VxScan, on VxMark, and an identical screen on the laptops for VxAdmin and VxCentralScan. All screens are in conformance with 8.1-A's requirements for electronic display screens.
All screens include anti-glare technology to prevent distinct virtual images from appearing on the screen (8.1-A.1.a)
All screens surpass the 10:1 minimum contrast ratio under 500 lux (8.1-A.1.b)
All screens surpass the 12" minimum diagonal display size specified in 8.1-A.2.a, which itself surpasses the 7.9" minimum diagonal display size specified in 8.1-A.3a.
All screens surpass the 1920 x 1080 minimum display resolution specified in 8.1-A.2b, which itself surpasses the 1024 x 768 minimum display resolution specified in 8.1-A.3.b.
VxScan
15.6"
1920 x 1080
1,000:1
310 nits
VxMark
15.6"
1080 x 1920
700:1
220 nits
Central Laptops
14"
1920 x 1200
1,330:1
300 nits
All audio and video devices are tested to be in accordance with the requirements of IEC/UL 62368-1 (8.1-K).
VxScan
VxMark
Report Pending
Central Laptops
The audio-visual settings for the voter experience are described in the System Overview:
This section walks through the steps to install a Trusted Build image on a VotingWorks component using .