diff --git a/other/spec-todo/.gitignore b/other/spec-todo/.gitignore new file mode 100644 index 0000000..4fa495f --- /dev/null +++ b/other/spec-todo/.gitignore @@ -0,0 +1 @@ +*.bak.txt \ No newline at end of file diff --git a/other/spec-todo/EG-v2.1-Spec-Priorities.txt b/other/spec-todo/EG-v2.1-Spec-Priorities.txt new file mode 100644 index 0000000..56ba69e --- /dev/null +++ b/other/spec-todo/EG-v2.1-Spec-Priorities.txt @@ -0,0 +1,68 @@ + + +S3.1 Parameter Requirements +S3.2 Key Generation eq. 6 - 30 +S3.3.1 Selection Encryption, eq. 31.. +S3.3.2 Selection Encryption Identifiers and Identifier Hash +S3.3.3 Generation of the Ballot Nonce and Encryption Nonces +S3.3.4 Encryption of Ballot Nonces +S3.3.5 Ballot Well-Formedness +S3.3.6 Outline for Proofs of Ballot Correctness +S3.3.7 Details for Proofs of Ballot Correctness +S3.3.8 Proof of Satisfying the Contest Selection Limit, eq. ..62 +S3.4.1 Contest Hash eq. 70 +S3.4.2 Confirmation Code eq. 71 + "Critical" + +S3.5, S3.6.1 - S3.6.5 eq. 79 - 95 + "Essential" + +S3.6.7 Decryption of Challenged Ballots, eq. 107 - 111 +S5 Hash Computation + "Critical" + +S4 Pre-Encrypted Ballots, eq. 112 - 121 + "Secondary", + "NOT a priority", + "above S3.3.9 - S3.3.10 Supplemental and contest data" + "above S3.4.3 - S3.4.4 ballot chaining" + +S3.4.3 Voting Device Information Hash, eq. 72 +S3.4.4 Ballot Chaining, eq. 73 - 78 + "Not critical may be deferred" + +S3.3.9 Supplemental Verifiable Fields +S3.3.10 Contest Data + "Optional" + +S3.6.6 Decryption of Contest Data + "Not top priority" + "Only necessary if S3.3.9 - S3.3.10 Supplemental and contest data" + +S3.7 Election Record + "Just a recapitulation" + + [This section generates no unique or specific requirements] + +% xreq S3.1.4.b.verif1 TODO: Verification 1 + + +% xreq S3.a.f.d (Ref: S2.b) Key Ceremony + + +% xreq S3.2.c (Ref: S3.a.f.c) "EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field." + +% xreq S3.2.c (Ref: S3.a.f.c) "EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field." + +% xreq S3.2.c (Ref: S3.a.f.d) "Fewer than `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys can not produce a full decryption of any tally" + +% xreq S3.a.g EGRI encrypts other (non-vote) Ballot Data to the Joint Ballot Data Encryption Public Key + + +Joint (Vote|Ballot Data) Encryption Public Keys + +Contest (Option or Additional) Data Field + + +% xreq S3.2.3.j EGRI computes H_E from H_B and the Joint (Vote|Ballot Data) Encryption Public Keys as specified in EG DS v2.1.0 eq. 30. + \ No newline at end of file diff --git a/other/spec-todo/TODO-2.1.txt b/other/spec-todo/TODO-2.1.txt new file mode 100644 index 0000000..0ba4b58 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt @@ -0,0 +1,9670 @@ + +% xxxx ---- S0 Prefix Items - Not part of EGDS + +% xdone 20250429-20250505 Added crate dependency license info to build +% xdone 20250429-20250505 New unit test (passing) test guardian::t::t1 +% xdone 20250429-20250505 New unit test (passing) test guardian::t::t2 +% xdone 20250429-20250505 Work on key ceremony +% xdone 20250429-20250505 Guardian definition +% xdone 20250429-20250505 Resource ID and format (rid, fmt, ridfmt) returns "Cow" strings. + +% xdone 20250429-20250505 +% xdone 20250429-20250505 +% xdone 20250429-20250505 +% xdone 20250429-20250505 +% xdone 20250429-20250505 +% xdone 20250429-20250505 + + +% xtodo S0 Fix references to EGDS in the source code to always include section and page number +% xtodo S0 Fix references to EGDS in the source docs to always include section and page number + +% xtodo S0 Set panic behavior to abort. See https://gitlab.torproject.org/legacy/trac/-/issues/27199 +% xtodo S0 Remove eg/src/example*.rs files, migrate to resource_producer +% xtodo S0 Migrate away from 'anyhow' in library code +% xnote S0 Per Michael 2025-02-25, "Signed ElGamal" encryption refers to the third ciphertext component which is a Schnorr proof of knowledge of the encryption randomness. + +% xtodo S0 Q|UG left pad as necessary to ensure length is correct +% xtodo S0 MAYB|serialize format wrapped in version + +% xtodo S0 crate feature for guardian secret operations. + +% xtodo S0 ensure SecretCoefficient is serialized in a fixed-length format + +% xtodo S0 rewrite build-docs script in sh + +% xtodo S0 Build on Linux32 +% xtodo S0 Test on Linux32 +% xtodo S0 Build on Linux64 +% xtodo S0 Test on Linux64 +% xtodo S0 Build on Win64 +% xtodo S0 Test on Win64 +% xtodo S0 Test library components for Wasm32 + +% xtodo S0 docs/general: style sheet for markdown, ideally match API docs + +% xtodo S0 If an overvote occurs, the overvote must be captured, encrypted, and never decrypted. + +% xtodo S0 docs/specs/serialization: data formats section +% xtodo S0 docs/specs/serialization: standards and references section +% xtodo S0 docs/specs/serialization: election manifest section +% xtodo S0 docs/specs/serialization: election record section +% xtodo S0 docs/specs/serialization: vendor data section + +% xtodo S0 docs/api: reference NIST CDF where types clearly correspond. E.g., BallotStyle https://github.com/usnistgov/ElectionResultsReporting/blob/nist-pages/index.md#17_0_2_4_78e0236_1389366224561_797289_2360 + +% xtodo S0 docs/implementation guide/Requirements for election systems vendors: complete +% xtodo S0 docs/implementation guide/Requirements for verifier app authors: complete +% xtodo S0 docs/implementation guide/roles: consider splitting into separate pages: complete +% xtodo S0 docs/implementation guide/roles/Election Administrator: complete +% xtodo S0 docs/implementation guide/roles/Election Guardians: complete +% xtodo S0 docs/implementation guide/roles/Voters: complete +% xtodo S0 docs/implementation guide/roles/Political parties and voter-interest organizations: complete +% xtodo S0 docs/implementation guide/roles/Journalists and other media: complete +% xtodo S0 docs/implementation guide/Hardware requirements/Gurardian secret key storage: complete +% xtodo S0 docs/implementation guide/Hardware requirements/Gurardian secret key operations: complete +% xtodo S0 docs/implementation guide/step-by-step/Advance preparation: complete +% xtodo S0 docs/implementation guide/step-by-step/Key ceremony: complete +% xtodo S0 docs/implementation guide/step-by-step/Tally ceremony: complete +% xtodo S0 docs/implementation guide/step-by-step/Publishing: complete +% xtodo S0 docs/implementation guide/step-by-step/Verification: complete +% xtodo S0 docs/implementation guide/step-by-step/Reporting: complete + +% xtodo S0 docs/api: use correct logo +% xtodo S0 docs/api: complete + +% xtodo S0 docs: complete, #![warn(missing_docs)] + +% xtodo S0 docs: upload docs to github pages (see compliance notes) + +% xtodo S0 security review: ensure that no file leaks info through filesize + +% xtodo S0 distinguish between PartySelection, BallotMeasureSelection, CandidateSelection +% xtodo S0 BallotDefinition doc for write-in option +% xtodo S0 would be nice to support a PartySelection type vote, rather than rely on the vendor to give us the correct selections explicitly + +% xtodo S0 a trait for types that have pub fn validate(&Self, &ElectionParameters) + +% xtodo S0 docs: more investigation into using rust modules to build documentation along with api, observe how cargo_crev does it with the include_str! macro: https://github.com/crev-dev/cargo-crev/blob/master/cargo-crev/src/doc/mod.rs +% xtodo S0 docs: more investigation into using mdbook for all project documentation https://github.com/rust-lang/rust/issues/66249 +% xtodo S0 docs: cargo-external-doc is nice but doesn't support virtual manifests https://github.com/Geal/cargo-external-doc + +% xtodo S0 VaryingParameters (n, k) This isn't really an 'index' (ordinal), it's a cardinal number. Maybe we need a general purpose ranged number type. + +% xtodo S0 exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%) +% xtodo S0 exe: common parameter: manifest file +% xtodo S0 exe: common parameter: others + +% xtodo S0 persist/file: create artifact directories if they don't exist. q: what about permissions on guardian secret directories? + +% xtodo S0 perisist: encryption or password protection for guaridan secret key files +% xtodo S0 util: read guardian secret key, print info, suppressing secrets +% xtodo S0 util: read guardian public key, print info + +% xtodo S0 persist: define standard election directory layout - look at other implementers and users +% xtodo S0 design: key file represents its kind: guardian, ..., ? + +% xtodo S0 Consider structures defined in JSON schema https://github.com/usnistgov/ElectionResultsReporting/blob/version2/NIST_V2_election_results_reporting.json + +% xtodo S0 if electionguard.exe fails to read or write a file, check the path to see if it has a leading ~. If so, print a good error message. + +% xtodo S0 test on 32-bit target such as x86 or wasm32 +% xtodo S0 test on big-endian target such as powerpc64-unknown-linux-gnu, s390x-unknown-linux-gnu, riscv64gc-unknown-linux-gnu, or loongarch64-unknown-linux-gnu + +% xtodo S0 electionguard-test script: incorporate ballots +% xtodo S0 electionguard-test script: incorporate tally + +% xxxx ====vvvv====vvvv====vvvv====vvvv====vvvv==== DONE ====vvvv====vvvv====vvvv====vvvv====vvvv==== + +% xdone S0 move types for numbers mod p and mod q back into 'eg' lib so they can be known at compile time +% xdone S0 change obtain_resource_production_result_from_cache_downcast +% xdone S0 electionguard.exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%) +% xdone S0 electionguard.exe: seed: write random seed to artifact file +% xdone S0 electionguard.exe manifest: write ElectionManifest to pretty json file +% xdone S0 electionguard.exe manifest: write ElectionManifest to canonical bytes file +% xdone S0 electionguard.exe parameters: write ElectionParameters to json file +% xdone S0 H_V, H_P, H_M, and H_B updated for 2.0 calculation +% xdone S0 Generate joint election public key +% xdone S0 Extended base hash H_E +% xdone S0 electionguard-test script: implementation in cmd started +% xdone S0 electionguard-test script: implementation in cmd exercises all (current) functionality +% xdone S0 BigUint values (mod p or q) now left-padded as necessary to avoid leaking value via serialized file size +% xdone S0 Hash values now serialized with 'H(upper hex)' format to match spec +% xdone S0 exe: Csprng now seeded with more entropy from the operating system RNG +% xdone S0 Election-varying parameters (n and k) now checked for validity +% xdone S0 Serialization of BigUints now uses base64 encoding +% xdone S0 Rename guardian private key to secret key +% xdone S0 electionguard.exe: generate guardian secret key +% xdone S0 electionguard.exe: write guardian secret key +% xdone S0 electionguard.exe: derive guardian public key from secret key +% xdone S0 electionguard.exe: write guardian public key +% xdone S0 Guardian i uses 1-based indexing +% xdone S0 compute H_E extended base hash +% xdone S0 compute joint election public key +% xdone S0 electionguard.exe: write joint election public key to json file +% xdone S0 standardize on 'validate' instead of 'verify' when checking deserialized structures +% xdone S0 instead of from_json and to_json implement from_stdioread and to_stdiowrite +% xdone S0 every struct that has Self::from_stdioread*() should prefer Self::from_stdioread_validated() and have a self.validate() +% xdone S0 electionguard.exe: write H_E extended base hash to json file +% xdone S0 convert many uses of if !() { bail!() } to ensure!() +% xdone S0 Generate data structure docs from the Reference Implementation in Rust +% xdone S0 eg: New constrained numeric type for indices. Convert n, k, and other indices to this type. +% xdone S0 build-docs script: initial implementation in cmd +% xdone S0 evaluate scripting language 'nu' https://www.nushell.sh/ +% xdone S0 electionguard-test script: begin rewrite in nu +% xdone S0 doc/LICENSE: checked +% xdone S0 doc/SECURITY.md: complete +% xdone S0 docs/general: begin writing +% xdone S0 docs/api: begin writing +% xdone S0 Remove link to internal site +% xdone S0 VaryingParameters: enum BallotChaining { Prohibited, Allowed, Required, } +% xdone S0 remove old EG 1.54 constants +% xdone S0 exe: Under artifacts dir, first level dirs are specific to secrecy requirements +% xdone S0 Ballot define data type +% xdone S0 get fixeduint stuff out of bunna branch +% xdone S0 Merge code from Anunay +% xdone S0 doc/SUPPORT.md: complete +% xdone S0 doc/README.md: complete +% xdone S0 doc/CODE_OF_CONDUCT.md: complete +% xdone S0 doc/BUILDING.md: complete +% xdone S0 Complete all planned code reorganization/renaming +% xdone S0 docs/implementation guide/References: complete +% xdone S0 a trait for fn to_canonical_json() +% xdone S0 many to_stdiowrite() methods have common code that could be factored into a common function +% xdone S0 a trait for types that have to_stdiowrite() +% xdone S0 a trait for types that have to_stdiowrite() and perhaps _pretty() and _canonical() variants + +% xdone S0 serialize bignums only all as uppercase hex + +% xxxx ====vvvv====vvvv====vvvv==== funcional requirements from text of EG 2.1 Design Specification ====vvvv====vvvv====vvvv==== + +% xnote S0 Abbreviation: 'EG' ElectionGuard, possibly implies v2.1.0 Design Specification and/or Reference Implementation +% xnote S0 Abbreviation: 'EGDS' ElectionGuard v2.1.0 Design Specification +% xnote S0 Abbreviation: 'EGRI' ElectionGuard v2.1.0 Reference Implementation +% xnote S0 Abbreviation: 'RI' - Reference Implementation +% xnote S0 In EG, Ballots are always encrypted, so 'Ballot' implies 'encrypted' +% xnote S0 A 'Preencrypted Ballot' is a complete set of ciphertexts for all options which contains no information about Voter Selections. +% xtodo S0 What is a Preencrypted Ballot called after Voter Selections have been applied? + +% xxxx 7mG3TEw43xIyPPcsStd4x9+hhlNRkCdNugt2ZqLDjqXFUs12L5G8J9DZ8o4dVEljODWqL1A9kwrP +% xxxx rJ5U6QTTc7yxZzWkHMnj9Ru9Y7T7Kwb7yYMyC+x8vom2hgomjr+vl+RiUL+dKAUNgNrX1NdlO2Td +% xxxx zMBkT05HZK6F/dtDgq4O9ErcpKa0c7vtq5n8SPH3d2CqqgTILSF+XdfVQTGflX1M3tIXZEEygGeh +% xxxx GVuL3AMp5Lb/HsI+ZMw5pcYv++ViW2DPI8Okl4pKFFx/azJIygVYIyk3kk1AwZoXH3F6SuqIn2s6 +% xxxx 8BCgdRhOK46nVaOVT77AHU4WF56ov1Noq9sGk/P+lNbDXFH92hkXYINjCdtkvUMj4h7VcBCS4c/T +% xxxx TFD2pz7IHHBOaWFGksqgcpeIK9eA/nKxJLt8/JZZa7tt5YBdpK6Nlznrr/leUDY2GAK6PJxmJMpU +% xxxx ks3sCPDguEw3dofa3h1pstq9f53ORPhIIAsPIGzNPvbPz3Z5fL+bvF1ycEq1ca9dN4FZ6ereLIv/ +% xxxx UVQRzh0AobPXgE2PkFtrIuEgmLMp2bDlInJXymo692E2PUPZo/K3r/bB7FVEIf8rCQp6KuPKiefy +% xxxx 6s+bXN3MuujTywaSplkm5lv8+I88hoHKqtKM2X3E2LDk2afjm0qU6AXQye1Vljt8k6Am+IhRCqnr +% xxxx t6Ia6SI+l+Zew0sdgMENnwqaQf5qFgtoAbswNRAc2joMQQXZ/vK61pjC5GyJ0XAahJQdIuBJaGPc +% xxxx +% xxxx +% xxxx ---- S1 Introduction + +\pagebreak + +\tableofcontents + +\pagebreak + +\section{Introduction} +This document describes the cryptographic details of the design of \EG, which can be used in conjunction with many new and existing voting systems to enable both end-to-end (E2E) verifiability and privacy-enhanced risk-limiting audits (RLAs). \EG is not a complete election system. It instead provides components that are designed to be flexible and to promote innovation by election officials and system developers. When properly used, it can promote voter confidence by empowering voters to independently verify the accuracy of election results. + +% xreqj { + "section": "S1.a", + "text": "EGRI can be used to enable end-to-end (E2E) verifiability.", + "sc": "na" } +% xreqj { + "section": "S1.a", + "text": "EGRI can be used to enable privacy-enhanced risk-limiting audits (RLAs).", + "sc": "na" } +% xreqj { + "section": "S1.a", + "text": "EGRI components can be used to empower individual voters to independently verify the accuracy of election results.", + "sc": "na" } + +\subsubsection*{End-to-End (E2E) Verifiability} +An E2E-verifiable election provides artifacts which allow voters to confirm that their votes have been accurately recorded and counted. Specifically, an election is End-to-end (E2E) verifiable if two properties are achieved. +\begin{enumerate} +\item Individual voters can verify that their votes have been accurately recorded. +\item Voters and observers can verify that all recorded votes have been accurately counted. +\end{enumerate} +An E2E-verifiable election does not + guarantee that the recorded votes have been cast by legitimate + voters: this needs to be ensured through the traditional voter + identification mechanisms that are already deployed in elections. + +An E2E-verifiable tally can be used as the primary tally in an election or as a verifiable secondary tally alongside traditional methods. \EG is compatible with in-person voting---either using an electronic ballot-marking device or an optical scanner capable of reading hand-marked or machine-marked ballots, with voting by mail, and even with Internet voting.\footnote{ +Note that there are many challenges to responsible Internet voting that are mitigated but not fully solved by E2E-verifiability. The 2015 U.S. Vote Foundation report at \url{https://www.usvotefoundation.org/E2E-VIV} details many of these issues, and the 2018 National Academies report at \url{https://nap.nationalacademies.org/catalog/25120/securing-the-vote-protecting-american-democracy} includes a section on Internet voting (pp. 101--105). These conclusions were reaffirmed by a 2022 study conducted by Berkeley's Goldman School of Public Policy available at \url{https://gspp.berkeley.edu/index.php/research-and-impact/news/recent-news/csp-working-group-findings-on-mobile-voting}. +} + +% xreqj { + "section": "S1.b", + "text": "EGRI allows individual voters to verify that their votes have been accurately recorded.", + "sc": "" } +% xreqj { + "section": "S1.b", + "text": "EGRI allows voters to verify that all recorded votes have been accurately counted.", + "sc": "" } +% xreqj { + "section": "S1.b", + "text": "EGRI allows observers to verify that all recorded votes have been accurately counted.", + "sc": "" } +% xreqj { + "section": "S1.b", + "text": "EGRI provides an E2E-verifiable tally which can be used as the primary tally in an election.", + "sc": "" } +% xreqj { + "section": "S1.b", + "text": "EGRI provides an E2E-verifiable tally which can be used as a verifiable secondary tally alongside traditional methods.", + "sc": "" } +% xreqj { + "section": "S1.b", + "text": "EGRI is compatible with in-person voting using an electronic ballot-marking device.", + "sc": "na" } +% xreqj { + "section": "S1.b", + "text": "EGRI is compatible with in-person voting using an optical scanner capable of reading hand-marked ballots.", + "sc": "na" } +% xreqj { + "section": "S1.b", + "text": "EGRI is compatible with in-person voting using an optical scanner capable of reading machine-marked ballots.", + "sc": "na" } +% xreqj { + "section": "S1.b", + "text": "EGRI is compatible with voting by mail.", + "sc": "na" } +% xreqj { + "section": "S1.b", + "text": "EGRI is compatible with Internet voting.", + "sc": "na" } + +\subsubsection*{Risk-Limiting Audits (RLAs)} +RLAs offer election administrators efficient methods to validate reported election tallies against physical ballot records. There are several varieties of RLAs, but the most efficient and practical are \emph{ballot-comparison audits}, in which electronic \emph{cast-vote records} (CVRs) are individually compared against physical ballots. + +The challenge with ballot-comparison audits is that public release of the full set of CVRs can compromise voter privacy while an audit without public disclosure of CVRs offers no basis for public confidence in the outcome. \EG can bridge this gap by enabling public disclosure of encrypted ballots that can be matched directly to physical ballots selected for auditing and can also be proven to match the reported tallies. + +Making an election E2E-verifiable, in addition to conducting an RLA, + can offer guarantees that the physical ballot records used in + the RLA are those produced by the voters. + +% xnote Abbreviation: 'EG' ElectionGuard, possibly implies v2.1.0 Design Specification and/or Reference Implementation +% xnote Abbreviation: 'EGDS' ElectionGuard v2.1.0 Design Specification +% xnote Abbreviation: 'EGRI' ElectionGuard v2.1.0 Reference Implementation +% xnote Abbreviation: 'Ballot' is encrypted, by definition in EGDS v2.1.0. + +% xreqj { + "section": "S1.c", + "text": "EGRI enables public disclosure of encrypted ballots that can be matched directly to physical ballots selected for RLA auditing.", + "sc": "" } +% xreqj { + "section": "S1.c", + "text": "EGRI can prove (or fail to prove) that publicly disclosed EG ballots match reported tallies", + "sc": "ace" } +% xreqj { + "section": "S1.c", + "text": "EGRI can prove (or fail to prove) that physical ballot records used in an RLA are those produced by the voters", + "sc": "na" } + +\subsubsection*{About this Specification} +This specification can be used by expert reviewers to evaluate the details of the \EG process and by independent parties to write \EG implementations. This document is not intended to be a fully detailed implementation specification. It does not specify serialization and data structures for the election record or mappings between the notation used here and the corresponding data types and components of a specific implementation. However, this document, together with a detailed implementation specification or a well-documented \EG implementation, can be used by independent parties to write \EG verifiers to confirm the consistency of election artifacts with announced election results. + +% xreqj { + "section": "S1.d", + "text": "EGRI provides a \"detailed implementation specification\" and/or qualifies as a \"well-documented ElectionGuard implementation\"", + "sc": "ace" } +% xreqj { + "section": "S1.d", + "text": "EGRI can be used by independent parties to write ElectionGuard verifiers to confirm the consistency of election artifacts with announced election results.", + "sc": "na" } +% xreqj { + "section": "S1.d", + "text": "EGRI can be used by independent parties to write verifiers to confirm (or refute) the consistency of election artifacts with announced election results", + "sc": "na" } + +% xxxx 7/yehJAmhBwDQhjuURFyYHgLPJYYUCOG4y5iFqeT6vJa9XH/tx57sk3941Ws7DUSt9OMimhg4CO/ +% xxxx kBkkw7JpOgmrGoJTwZLYhqaGbWSC4S76nVee6a0Fggfkd1V0ZfUhxuIa/xrZwOtbogbkdeY3P5VH +% xxxx RvZnQWyKelViFDkdosUkbv7mLYr7EOz4QA/hHSCZb63jZSgwB0B/YHOLcsZMVD+iwcvF/LyA7Rso +% xxxx vXAv4YRDn9ZhRzfwEWbLjCMwl5tUXRrO+twjEaatxzf7TgEgqu9EvmZufgb4pqbsJK/yacmT0+im +% xxxx yUup0bEnQQcDsjiqsAtNDtGi6f8TF5XA2KtjNFvl1XRNMV1xyswsvN1+OgtLocO4y0rWfjaSYskn +% xxxx a8dziVk65E/J9EafXvuARSj+xmNImZlbQ6e3BfsNx94VG7o4PmzfNu1G6naqeTlXA/Ge1EA6UXRv +% xxxx tC6rH3qVQZXQEtfjJx5t8ngr66va5hfuALhF3bWNWv5268LkKRSBoorKRClhzzrG7soJ0vurEbNE +% xxxx pDe2H0sl9lYyDvbrCGVEZfNKac/7/NzLUZ5CzV7SkAk8EdQVGvC8Q+Qd7uNmirh+xZ55fwxQcspN +% xxxx Ru84FgZftzi7roxZi5yF/LI0Uhsu/tOgcfe2FYryLYvq3ANRHClDUDw0uO3rVuQKvGimaZp24b4p +% xxxx t1d3etUocMn5iOpLX35gPViIsTZsO/FBCzwuSQoJV0osW4FXHCKCdGyRxeuMNfuYlEtReYwOapgf +% xxxx +% xxxx +% xxxx ---- S2.a Overview + +%pagebreak +\section{Overview}\label{sec:overview} + +This section gives a very brief and high-level overview of the \EG system's functionality and how it can be used during an election or in a post-election audit such as an RLA. + +To use \EG in an election, a set of \emph{guardians} is enlisted to serve as trustees who manage cryptographic keys. The members of a canvassing board can serve as guardians.\footnote{It might also be possible to use trusted hardware to perform the duties of guardians. This is explored in https://eprint.iacr.org/2024/915. It is important to note that the role of guardians is to protect confidentiality of votes. Compromised guardians---whether instantiated as humans or hardware---cannot compromise the integrity of the election tallies.} Prior to the commencement of voting or auditing, the guardians work together to form a public encryption key that will be used to encrypt individual ballots. + +After the conclusion of voting or auditing, a \emph{quorum} of guardians is necessary to produce the artifacts required to enable public verification of the tally. + + +Prior to, throughout, and after an election (or audit) using \EG, an \emph{election administrator} facilitates the \EG protocols and procedures and is responsible for populating and publishing the artifacts of an election in the form of the \emph{election record}. + +% xreqj { + "section": "S2.a", + "text": "A 'Guardian' role exists with certain duties.", + "sc": "na" } +% xreqj { + "section": "S2.a", + "text": "EGRI enables a Guardian to protect confidentiality of votes.", + "sc": "na" } +% xreqj { + "section": "S2.a", + "text": "EGRI enables members of a canvassing board to serve as Guardians.", + "sc": "na" } +% xreqj { + "section": "S2.a", + "text": "EGRI enables Guardians to manage cryptographic keys.", + "sc": "" } +% xreqj { + "section": "S2.a", + "text": "Compromised human Guardians cannot compromise the integrity of the election tallies.", + "sc": "" } +% xreqj { + "section": "S2.a", + "text": "Compromised \"hardware Guardians\" cannot compromise the integrity of the election tallies.", + "sc": "" } +% xreqj { + "section": "S2.a", + "text": "EGRI enables Guardians to work together to form a public encryption key for homomorphically-tallied vote encryption.", + "sc": "utsp", + "utsp": "eg::joint_public_key::t::t1_generate_jvepk_k" } +% xreqj { + "section": "S2.a", + "text": "EGRI enables Guardians to work together to form a public encryption key for non-homomorphically-tallied ballot data.", + "sc": "utsp", + "utsp": "eg::joint_public_key::t::t2_generate_jbdepk_k_hat" } +% xreqj { + "section": "S2.a[x]", + "text": "EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.", + "sc": "" } +% xreqj { + "section": "S2.a", + "text": "EGRI allows a quorum of guardians to produce all artifacts required to enable public verification of the tally.", + "sc": "" } +% xreqj { + "section": "S2.a", + "text": "No set of guardians fewer than a quorum are able to produce the artifacts required to enable public verification of the tally.", + "sc": "ace" } +% xreqj { + "section": "S2.a", + "text": "% xnote S3.7 Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: An 'Election Administrator' role exists", + "sc": "na" } +% xreqj { + "section": "S2.a", + "text": "Note: EG defines protocols", + "sc": "ace" } +% xreqj { + "section": "S2.a", + "text": "Note: EG defines procedures", + "sc": "ace" } +% xnote S2.a Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: % xreq2j S2.a EGRI enables the Election Administrator to facilitate defined EG protocols +% xnote S3.7 Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: % xreq2j S2.a EGRI enables the Election Administrator to facilitate defined EG procedures + +% xxxx aAA91UVGc4BXuw1qR7HGHuZXSY/0mC2N48N8sNAoU9LlNUmrHH53yTAYhKEVDBo6DDVUfXxszF2B +% xxxx ia3UX+uiOaMHj5Cq4m0ssi1IPjzUVa+1sIFZbdW/zu8EUkrzsgmw2jHDVAMzpuD0OSpME1vYeQj9 +% xxxx i7esJDK4G5ZpWUy2VM0v6z3acUbHsFr+HH8USQ5+APrZgXWMFtTsDtjof9Qq+QkvCGZnTx1QvCfL +% xxxx 0WeGS41KG4a/7ar2XwRe/m6fPDWNb7oVLAvxcI84V2WMoyy6TtOj+/RED3Kg4Nh0pvQKO+05x3fb +% xxxx /yE7uYz4PVT5DY/6SsjNULsQW1bvmWFqV0bUmPE0QcxxWspiRJJyq+DB0KpShYF4+srYESqt2ooS +% xxxx F6v+jyMTxW4mqdLzxgjKOZXDLVS9ETdgOYpDgveWQBVCbRXqTsGi7Bos8bF8BikigCqNspk2c/2I +% xxxx X1zm36UNl7eObQ3/YsKg+TY+aBMwpSqpDLahQSp4JTZJH3KpdC10PCuuOkwyUiSj4A4sgCK+JAkd +% xxxx eIdrkzBIYKCl8e63Puu0nYXioJpO3jKSgNhUfynve6DeHkxoS4F6gVjgn31sceYUYDOEmIwJRAqt +% xxxx bYnkRAsWTeu3lVu4MHjycb5WfABsc+EzGjEFNvRqFsO8PDLJSgTH+xMV3JOMzB8EwWmy4R20FlQU +% xxxx +OtzHckdYiod7+uy1gsx5e2qxFEq0HFkkXgsAyzPFhndcGudxWYIaLDgq50Lf/ObJ7CSlwYn1aRx +% xxxx 4sPQ4Er/Slg36tbahyHpie0xWqSH/aUYGZUGdeC2fyE9qDK6L37GNYJVsp39wU989UQAZf72Zlqz +% xxxx +% xxxx +% xxxx ---- S2.b Key Generation + +\subsubsection*{Key Generation} +Prior to the start of voting (for an E2E-verifiable election) or auditing (for an RLA), the election guardians participate in a process wherein they generate public keys to be used in the election. Each guardian generates its own public-secret key pairs. One set of guardian public keys will be combined to form a single public key with which votes are encrypted, another set will be combined to form a second public key to encrypt other ballot data. Each guardian also generates an additional communication key pair used to communicate privately with other guardians to exchange information about secret keys so that the election record can be produced after voting or auditing is complete---even if not all guardians are available at that time. + +The key generation ceremony begins with each guardian publishing its public keys together with proofs of knowledge of the associated secret keys. Once all public keys are published, each guardian uses each other guardian's public communication key to encrypt shares of its own secret keys. Finally, each guardian decrypts the shares it receives from other guardians and checks them for consistency. If the received shares verify, the receiving guardian announces its completion. If any guardian notices any irregularities such as a failed share verification, a failed proof verification, or an inconsistent set of keys, the guardian complains and halts the protocol. If an investigation identifies a misbehaving guardian, it is removed and the protocol restarted from scratch. It is the responsibility of the guardians together with the election administrator to conclude the key generation. + + +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to generate its own Vote Encryption (Public|Secret) Key pair prior to the start of voting or RLA.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to generate its own Ballot Data Encryption (Public|Secret) Key pair prior to the start of voting or RLA.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to generate its own Guardian Communication (Public|Secret) Key pair pair prior to the start of voting or RLA.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables Guardians to employ their Guardian Communication Key pairs to communicate privately with other guardians to exchange information about secret keys.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables Guardians to keep their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys and Key Shares secret.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables Guardians to perform operations using their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys and Key Shares without leaking or exposing them, to the extent practical within the constraints of hardware/os platform.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables potential integration of dedicated hardware tokens or devices with which Guardians could to perform operations using their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables production of an 'Election Record' even if not all guardians are available at that time with", + "sc": "" } +information exchanged by Guardians using their Guardian Communication Key pairs. +% xreqj { + "section": "S2.b", + "text": "(=~ S2.a[x]): EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "Guardians participate in a 'Key Generation Ceremony'.", + "sc": "ace" } +% xreqj { + "section": "S2.b", + "text": "At the beginning of the Key Generation Ceremony, EGRI enables each guardian to publish its public keys and proofs of knowledge of the associated secret keys.", + "sc": "ace" } +% xreqj { + "section": "S2.b", + "text": "At the beginning of the Key Generation Ceremony, EGRI enables each Guardian to receive the published keys and proofs of knowledge of the associated secret keys from the other Guardians.", + "sc": "ace" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to determine that they have received the public keys and proofs of knowledge of the associated secret keys from all other Guardians, or identify those which have not yet been received.", + "sc": "nyi" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Vote Encryption key.", + "sc": "ics" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Ballot Data Encryption key.", + "sc": "ics" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to send the encrypted shares of each of its own (two, non-Communication) secret keys to the other Guardian corresponding to the Guardian Communication Public Key to which it was encrypted.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to use its own Guardian Communication Secret Key to decrypt any shares of other guardians secret keys that it has received.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to check for consistency any shares of other guardians secret keys it has received.", + "sc": "nyi" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to query or observe which particular shares from all other Guardians it has recieved, decrypted, checked for consistency, and verified, and which are missing, inconsistent, or failed.", + "sc": "nyi" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to determine that it has received, decrypted, checked for consistency, and verified shares from all other Guardians.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "Josh 2025-03-18: \"The code should emit a success or failure indicator that a human can use to verify that the [received key shares] have the required properties.\".", + "sc": "nyi" } +% xnote S2.b +% xreqj { + "section": "S2.b", + "text": "EGRI can emit information sufficicient to enable an \"all greens on a dashboard\"-type display for a Guardian to observe the (received, decrypted, checked for consistency, and verified) state of shares from the other Guardians.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI can emit a formatted message to enable a Guardian to \"announce its completion\" once it it has received, checked for consistency, and verified shares from all other Guardians.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to notice the irregularity of a failed share verification.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to notice the irregularity of a failed proof verification.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to notice the irregularity of an inconsistent set of keys.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables a Guardian to \"complain and halt the protocol\" subsequent to noticing a failed share verification, a failed proof verification, or an inconsistent set of keys. (Is this published? A communications key message?)", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI enables, supports, or facilitates the initiation of a 'Key Ceremony Irregularity Investigation' subsequent to a Guardian complaint of irregularity.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to distinguish between an 'Actively Misbehaving Guardian' and any other type of error.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to identify an Actively Misbehaving Guardian.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to effectively troubleshoot errors other than an Actively Misbehaving Guardian.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "Sufficient data, documentation, tools, and/or other resources are available to enable the Key Ceremony Irregularity Investigation to effectively troubleshoot multiple simultaneous errors, potentially of different types.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI allows to remove a Guardian subsequent to a Key Ceremony Irregularity Investigation which has identified it as an 'Actively Misbehaving Guardian'.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "EGRI allows to restart the Key Generation Ceremony protocol from scratch subsequent to an Investigation which has identified and removed an Actively Misbehaving Guardian.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "TODO? what if its another type of error? Can you just restart whenever? Any of this published?", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "TODO? EGRI enables a Guardian to publish the ciphertext of any encrypted secret key shares that it sent.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "TODO? EGRI enables a Guardian to publish the ciphertext of any encrypted secret key shares that it received.", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "TODO? EGRI enables a Guardian to publish the plaintext corresponding to the ciphertext of any encrypted secret key shares that it sent? Only subsequent to the initiation of Key Ceremony Irregularity Investigation?", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "TODO? EGRI enables a Guardian to publish the plaintext decrypted from the ciphertext of any encrypted shares that it received? Only subsequent to the initiation of Key Ceremony Irregularity Investigation?", + "sc": "" } +% xreqj { + "section": "S2.b", + "text": "Note: The Guardians and Election Administrator together have a duty to successfully complete key generation and conclude the Key Generation Ceremony.", + "sc": "" } + +% xxxx /6UOYCeaSiowbc0+Z7uBiZRiE+dWApx05QcprMnZGFexzNkMNJdXY1ax+WVAj/nxYY1MZ5PBzhDR +% xxxx ROa8PG2wVtaLN6zgbe3l1RU8NfkMD6cHmRJmCdxpHjNWBva42Y9VTMuR++3wWONc5o+izAeM9DL0 +% xxxx t/XLazttDogYi1DN973vSXz1iOEyP3V869H2g3hLOPL/umgkFKEQaSn7DXc8LtOmSyrIx+znPz8l +% xxxx j5yHr/V22iQxT768hcUYxYaqWJrqjJ/zml3S9ercIj3DgTmlxjGJ7JSGdmhxAhga/cF3wznk4908 +% xxxx Hp5YvINlskZU305gRJ68tUm//jZOvO3kx/So1hCBmv14ps+ooNQATzkrrpeNuxXra4WOTE+gEmjP +% xxxx nigPlQ6q7wPT2MvXO+GfuwGNUa45MQfsewHaxowjEdTF44hE3JvR/MR8WZSmSvgkvFFxmVmi5/Hc +% xxxx 9l1kLwWgJWiUhZFbljrKbRi4y1qz8fa7+7neczevFbJcfGrIEvPX27aDLV+nNXoOtUYYP+JgPG8I +% xxxx uUrT1mVLsf8jkfj3Cs0ljvRt0dXXVR7L6t5LOtKJjZIueiHOo61Gt6aXA0i1OAddS/9uT/wKT98k +% xxxx 7Z551zCbrB87hOgiDXjl8xfY52GWIYUBLy0EJ8obz7BCe0qNl96yP4yPJjK8QcM6IhZNLV+AupBQ +% xxxx AD9P+AuPoxdm3hLXSLGmeBqjD6PxymWh5t9PcLQ/Z3SmNWDERrAXQg7CASEW/PNtv1yFonlqhpiW +% xxxx +% xxxx +% xxxx ---- S2.c Ballot Encryption + +\subsubsection*{Ballot Encryption} +In most uses, the election system makes a single call to the \EG API after each voter completes the process of making selections or with each ballot to be encrypted for an RLA. \EG will encrypt the selections made by the voter and return a confirmation code which the system should give to the voter.\footnote{The confirmation code is not necessary for RLA usage.} + +% xreqj { + "section": "S2.c.a[i]", + "text": "After a voter completes the process of making selections, the election system can make a single call to the EGRI API with the voter selections to encrypt and record the EG Ballot and return a confirmation code for the voter.", + "sc": "" } +% xreqj { + "section": "S2.c.a", + "text": "Other than the single call to the EGRI API described in (S2.c.a[i]), there is no other point at which an existing election system must interface with EGRI.", + "sc": "" } + +% xreqj { + "section": "S2.c.a", + "text": "An RLA system can make the single call to the EGRI API described in (S2.c.a[i]), but any confirmation code returned may be discarded.", + "sc": "" } + +This is the only point where an existing election system must interface with \EG. In most uses of \EG, voters will have an opportunity to challenge their encrypted ballots and view their decryptions to ensure that the encryptions are correct.\footnote{A ballot that has been decrypted should be regarded as a test ballot and should not be included in an election tally. After a ballot is challenged, the voter should have an opportunity to cast a fresh ballot. In an E2E-verifiable election, a decrypted ballot should never be cast. However, in an RLA, some anonymized cast ballots may ultimately be challenged and decrypted.} +In certain vote-by-mail scenarios and when \EG is used within an RLA, cast-vote records can be provided in batch without any interface between the voting equipment and \EG. + +% xreqj { + "section": "S2.c.b", + "text": "EGRI can accept can accept voter selections in batches directly. This is to support certain vote-by-mail scenarios and RLA.", + "sc": "" } +% xreqj { + "section": "S2.c.b", + "text": "EGRI records which Ballots have been decrypted ensures that they can never be \"cast\" or participate in any future Tally. Note that this only applies to homomorphically tallied fields and not, say, decrypting the Ciphertext of a non-homomorphically tallied write-in text field.", + "sc": "" } +% xreqj { + "section": "S2.c.b", + "text": "A decrypted ballot should never be 'Cast'. Josh 2025-03-19: Univeral knowledge of all challenged ballots is not required, just checking whatever local store is available is sufficient.", + "sc": "" } +% xreqj { + "section": "S2.c.b", + "text": "Implementation Note: After obtaining their confirmation code, voters should have an opportunity to 'challenge' their ballots in order to view the decrypted selections of their EG Ballot to verify that the encryptions were correct. The voter must be informed that the challenged ballot and decrypted selections will eventually be published as part of the public Election Record.", + "sc": "" } +% xreqj { + "section": "S2.c.b", + "text": "Implementation note: In an RLA, presumably the non-EG ballots were already cast. Some of the resulting EG Ballots may be anonymized, challenged, and decrypted.", + "sc": "" } + +The encrypted ballots are published along with non-interactive zero-knowledge (NIZK) proofs of their well-formedness. The proofs assert that an encrypted ballot is well-formed, which means that it is a legitimate ballot and adheres to the limits imposed on selection options and contests. For example, they prove that a selection did not receive more votes than allowed and that no more than the allowed number of votes were received across the selection options in each contest. The encryption method used herein has a homomorphic property which allows the encrypted ballots to be combined into a single aggregate ballot which consists of encryptions of the election tallies. + +% xreqj { + "section": "S2.c.c", + "text": "EGRI produces EG Ballots that contain encrypted voter selections.", + "sc": "utsp", + "utsp": "eg::ballot::t::ballotstyle1_contest1_votes_0_1" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI produces EG Ballots that may contain encrypted additional data fields for some or all Contests.", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI produces EG Ballots that contain a Ballot Nonce, encrypted to the Joint Ballot Data Encryption Public Key.", + "sc": "utsp", + "utsp": "eg::ballot::t::ballotstyle1_contest1_votes_0_1" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI records the EG Ballots for later publication as part of the Election Record.", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI produces EG Ballots that non-interactive zero-knowledge (NIZK) proofs of their well-formedness.", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "There can exist no well-formed ballot which is not legitimate, by definition.", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI produces EG Ballots with NIZK proofs proving that, for each contest option, no more than the allowed number of votes were recorded. (Option Selection Limit)", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI produces EG Ballots with NIZK proofs proving that, for each contest, no more than the total allowed number of votes were recorded. (Contest Selection Limit)", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI produces EG Ballots with Contests having additional data fields may contain NIZK proofs similar to those of voter selectable options or other properties.", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI allows to combine EG Ballots into an Aggregate Ballot.", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "EGRI allows to combine EG Ballots into a single Aggregate Ballot.", + "sc": "" } +% xreqj { + "section": "S2.c.c", + "text": "An Aggregate Ballot formed by combining EG Ballots contains, for every option in every contest, the tally for that option encrypted to the Joint Vote Public Key.", + "sc": "" } + +% xxxx pO3EGzPLTr7ozztkHTqU38BpwrTFVWDrQeJLSOvgpIO/2XInb3Ugtl23B7LmX9ozOV74QlhosopV +% xxxx YLfWoG5ukMpawKD6G6n3vMAP7lNiNl5P7Rfy+qBJ4wf4FrfAymwubtihp382lSmLHLe614uMiTdH +% xxxx SMVDwMYJ9RLIpFpMyTdJj1TzzMXznoszoOKjKRvt0NwV1+51Lldr/hR+eeoN0s64vbxUHSxPaTAz +% xxxx 7pt4zJjOpnY37lWc5rZ0vZQ1JTOBs+lyWU69AzgxeRlKPv7DFySrw1ZYNsJ5EiAXkV82wPOUe6Lx +% xxxx i4znyOxPd5EYDZbZ41bCbbCRtS6OJ+SR77PRye7CTbYbKGX1c0T+XTkoZud3DqTa3C9cd1TavM4S +% xxxx G0e6lXu4JzAhEAu3KELr/CQE0T4/Kc71eq3pnhQMZ2ZOfh0i41T3al+VR29d8gmt5YzzxtenPYxp +% xxxx 1PHcXiEqu5suSZgc+2RLNkS2TS5sdEYyUw90VH6ql8lGqpf3EY+dWQDH9cznPCxXzcNawN0YZX4a +% xxxx IuU6eIXfEYybz75pJpZXXJvNwLn5MVe6jN4mn7ue5Q+LuGZ4BRs/pjyiIiYbGr/IyT6MbkhfxCNO +% xxxx OqyJKlNFqjbzjwMaFdC6SsyE9HJe8XNISe+FNhNXk7r2vVADrRBJG/HKkfjrdZDjxhTTUyPFCRy5 +% xxxx WmEU9m5ab9ncnHIw59CwSwaXgIqrOoTnz6EgcsM7pH3ojIymo/1KC5gCJS80E1S58LsXLDiTa+8U +% xxxx +% xxxx +% xxxx ---- S2.d Verifiable Decryption + +\subsubsection*{Verifiable Decryption} +In the final step, election guardians each independently use a share of the secret decryption key +to jointly decrypt the election tallies and generate associated verification data. It is not necessary for all guardians to be available to complete this step. If some guardians are missing, a quorum of guardians is sufficient to complete decryption and generate the verification data. The number of guardians and the quorum required to decrypt are parameters fixed at the time of key generation. + +% xreqj { + "section": "S2.d", + "text": "Each guardian can compute from the previously shared secret key fragments a share of the secret decryption key", + "sc": "" } +% xreqj { + "section": "S2.d", + "text": "Guardians can independently use their share of the secret decryption key to jointly decrypt the election tallies", + "sc": "" } +% xreqj { + "section": "S2.d", + "text": "Guardians can independently use their share of the secret decryption key to generate associated verification data", + "sc": "" } +% xreqj { + "section": "S2.d", + "text": "Only a quorum of guardians is sufficient to complete decryption", + "sc": "" } +% xreqj { + "section": "S2.d", + "text": "Only a quorum of guardians is sufficient to generate the verification data", + "sc": "" } + +% xxxx gE9IyhGQYzbhCoijQV65tJptZUWdPR8DCjZWKntAO7vblsNUK6HNLL7N1QDvYPSpcW8ci23QrWtN +% xxxx OAW6lVLf89GmcFIz0DEw/2cAlsn3pbWOJfOvKLT1px8LN7tTCFjnz8fb4+mCBvCe1fv5UBENxrv5 +% xxxx cQ5bsB94Qs/dZUs6MgmzIDRspsybzy6rgUYxM+zJcTUuQWnSgS3kcRvqyAbseX9XHsLVm6WAIR8Y +% xxxx oQmjhRaNXfr+GoivUEMxw/KU8IDKtVdzkHW4SgJpZprqlxvafWONSd50wuNHd9ZVCXk13oQIjPCr +% xxxx eu7V/wmZ8UQU4ZomFIxF83SB1I+6sxrBN5x5GMn/O4e7GqueCwGvfzCnsMymEanei+ZoRggWdbx5 +% xxxx V3mKXffaV1I6RmVI4V0q1IRSWSMvL+ZG0tC++7hdhK31Ruj7wCWV9vUGbuDpQpnEyr3QUGLNp1wL +% xxxx lpxCLEJs4RnjB6gMkU038QUJhRd/lZJa4P5IwSa/qdTfdJqT3rXA8PuMZTXP5e8SibgKUJoa+52j +% xxxx s4MCRtvWs8Nf7QGa2xYG6jeGOiEbAfShQAR2+67OaDoPlqE28CJF0p9FDWdGosQLCYp4zUOn3G2m +% xxxx zugN16qspuvJ8q3a7xbvFRfoXuU+EfItR7HiCV++XK3LXGIMtNdhpA3KeNWt6WuvsqNsbEwuAE6l +% xxxx LDRReTSPCvWmDWTdM1DFLpz6ucB6fGo91YGveiJ3iLHDhtIjJbReL2JFRERsVOQfKGtABawasUBs +% xxxx +% xxxx +% xxxx ---- S2.e.a Independent Verification + +\subsubsection*{Independent Verification} +Observers can use this open specification and/or accompanying materials to write independent \emph{election verifiers} that can confirm the well-formedness of each encrypted ballot, the correct aggre\-ga\-tion of these ballots, and the accurate decryption of election tallies. + +The verification of an election consists of various verification steps that can be separated into three groupings. + +% xreqj { + "section": "S2.e.a", + "text": "An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the well-formedness of every EG Ballot.", + "sc": "" } +% xreqj { + "section": "S2.e.a", + "text": "An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the well-formedness the correct aggregation of EG Ballots.", + "sc": "" } +% xreqj { + "section": "S2.e.a", + "text": "An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the accurate decryption of election tallies.", + "sc": "" } + +% xxxx KdRd30L7vICqBgxbzZI8hhrLbiInd03szG+BuBgCXlC4UBG6hwO6N0QFI9uSjLujHoU70NSBjJUe +% xxxx drFsnvrSb4dxBbuOLG8Q1OAXkY8YAG6RaQdbk3vlwOYnLtnndU5SvFd07zWE51xDreLLcbz+YiNt +% xxxx 4VIWsMUGSGrhQIr2IgW1/Wcic+4D+cbbEC1PvaxAfn9boljsdCSn3as2bSHFkGfvk9DsgT8g1Pq6 +% xxxx NLSPxsOAUDyAzp9Gi7qq5VbNtjo4s6MrTffyRhGLIIlplLlNdwOFhGFOvohGGiID1e5M54J3FVa/ +% xxxx gpFbFMSnz7TsSgZTBcxliuDXBGpE8HCI/KIEk71Lk17s2/yT6i+TCGzOmjzds8u27SPKbPO3/GAN +% xxxx J0tS7Z6nw+WoBW3dfrqMC5EQXlGyQtqyEnV5M7YxDi2Ncs9rzDd27b2dSFI7EqYfra1Csosphh45 +% xxxx cEPYERRNnCRGKEwmIPYrIWJUr68YMqlG7ITbj68qeoSCa5E9SEaJAbLo7Mp9+dB+jtetrqS/IwkB +% xxxx nxHuqrhIe6HPNQxiQfTVn8/fXkEJNMx/3sD4+JV7xCqfAMG+AOLTZxVCxXKo2cAF1VRRxe3gbiTT +% xxxx 3MRovs0qaVFgvuFp6yjSDTw7899qQMgiCLRy0BLJUpjdW4rIelo95CHmrmT/ePggk79oiiHHsEhX +% xxxx ys4GBdBVlLa9VhSc7t9WHkiPDgf2uBn1Xjtj6Xx0u6cS2XqG5D8IEqXUJ+KKh6hVptf3zjf5TDtn +% xxxx +% xxxx +% xxxx ---- S2.e.b Independent Verification - Verification of Key Generation + +\paragraph{Verification of Key Generation.} +The first group of verification steps pertain to the key generation process. The privacy of the votes depends on the guardians performing these verification steps, and guardians must verify the key generation before any voting operations start. + +Under normal circumstances, guardians will engage in a key generation ceremony, produce the election public keys, and verify the generation without controversy; and there is no need for independent verification of this process. Still, independent verification can help to resolve conflicts should one or more parties to the key generation ceremony complain about the process. + +These steps should therefore be considered to be optional for an independent verifier. The integrity of an election record can be fully verified by independent parties without any of the key verification steps. However, a ``premium'' verifier may wish to include these steps to verify the correctness of guardian actions during the key generation ceremony. + +The key generation verification steps are highlighted in orange boxes in this document. + +% xreqj { + "section": "S2.e.b", + "text": "A verifier can be independently implemented which performs the Guardian Key verification steps S6.2.2.verif2 and S6.2.2.verif3 .", + "sc": "" } +% xreqj { + "section": "S2.e.b", + "text": "A verifier can be independently implemented that fully verifies the integrity of an Election Record without performing either of the Guardian Key verification steps S6.2.2.verif2 and S6.2.2.verif3 .", + "sc": "" } + +% xxxx IyjsNgjVnjnGxYZRcf6hE025pdJ3lwyd+fWF2Cg6p2jMEeBjSL67PFzwoZeeNbUQG9rDaVLbndoV +% xxxx +81ewpqVyZ9NjZ/Op0nNennOBrMlbBCCRmQK4fCjkvzLrN5/UlQ4OMCyWijQrX6kSvCRU9rruwvC +% xxxx be7FA72OsY73OcVYLoZAWGuhIxpCj6J7X6RrL4bYMT5CmaTwxZTnnR/mgjCaPMXI4qbsiyP96SFF +% xxxx stSAeWFmhyTJ5My3KYpGiUj2E+/QiUqKVneYmeDT2z261rUjzVDS9UPuo/UJErQ1f/t7WLIEVz1x +% xxxx yjCv6OoduyPuLrMqWudgpI7WUK05RXMVIm7Jg7Lc4QrwskhFwtAfPMNSh+6sIhjS3DLx9qmt8m8Y +% xxxx mlQkQIoxCnE8fjEopBtJ5DdRh21G+ha1Paa/OAmpQX35wqnNlX6P1n7+lXv4r6RUVHN3rWQtdUlz +% xxxx Y8vBCLFBADymeNzVT7HiXjWd7pEfdQfvwAHWxVMvrt/8IWPvyfI3LbarTBNLM5Ju5cQ2/ecQjiW8 +% xxxx GPW9MFXJ3PJwKgMy67i2iY+uQ7N4y9ZPg8gHzYkQ8YsfL6qAC/PTVb2mCVkSa+SoHys9HLt4U21U +% xxxx Z3RouzD5Vok7pnia+tViZL6YcE+Kbm+eTmOVkpkUwQQ665R6KJqq88K4MoBhQMFQ0FmdXYAkh9V5 +% xxxx OpGqq2nkhmSAdRayobuAbJdZ0ejUDruF6JG4i1S6T8FH7L/bwl7o49hLzi++u25SoyZyJODWE+Xd +% xxxx +% xxxx +% xxxx ---- S2.e.c Independent Verification - Verification of Ballot Correctness + +\paragraph{Verification of Ballot Correctness.} +As will be described in detail below, there are instances in both the E2E-verifiability application and the RLA application where it is desirable to verifiably decrypt individual ballots. In the former application, this allows voters to confirm that their selections have been correctly recorded within an encrypted ballot, and in the latter application, this allows encrypted electronic ballots that have been selected for audit to be compared with their associated paper ballots. + +The steps required to verify the correct decryption of a ballot are grouped together to allow independent verifiers to be easily constructed whose sole purpose is to allow voters or observers to directly verify correct decryption of individual ballots. + +The ballot correctness verification steps are highlighted in blue boxes. + +% xreqj { + "section": "S2.e.c", + "text": "A verifier can be independently implemented which performs the EG Ballot correctness verification steps S6.2.7.verif13, S6.2.7.verif14, S6.2.8.verif17, S6.2.8.verif18, and S6.2.8.verif19", + "sc": "" } + +% xxxx u9rKZSiE6e22NIeGW6AMK0ae03PqxyLCqiStdcsRaEYjg0wPy50Nrp6gPgzdEaPn9F9JlY/ErEPQ +% xxxx cxGGENEQ64iqinqOaustu0bDEm1t03QK2ThkzAr1aopyopfV1fyitlQN8Bdl5aXoWV4/LWAntq7n +% xxxx vPzJz69BttD8KeU0ckEE6T5adOMMP8J3gVjzXx4AW4GLezgwZXn8fXaono5utItP1yVfcuyCDkRV +% xxxx FE+TiRwMNpv04F+B0ql4O/MxqTkt32bOcFkHglr630iCF91JS9CJKWH2Q9NOQnNlQZdODVuT+KXx +% xxxx H406VegvztTM+hMSRMJbjhLzaGh9jvi3bafeG9ysPFhznFmWjUdFhWYxCm8ds/hv+tnnr2NG1sKS +% xxxx nd9/5mkW2e8T7PgFD4wS5RI6PdIYRHZvCHfEGwQBLbPmBeoO29rqwL6k+N4RXYdOKFkOfIUU0UEy +% xxxx M6c/Pb8xLfkgDwS8IDRyuwv+tIyWifabwCp4a20VtrjJVWrWrAgcm6/9wZQghvNlO1quzc5IM8Av +% xxxx OcvrN9IBm7fz/jQ7W2o0YSgTgXJOaCWOj4CiwpPrt98h0bKfp36pObjzDgDZhWKJeTVRl5KWsg4V +% xxxx 1YsuyC+HicEYXINcvor1JofayABFcgFJrxAuHP5PR9iacOs26v9iU5bV8SA6/w9H+vRPSNz9O2oE +% xxxx KfXGzJPgyIsR6BsOLZ2B90MwYzyKnx/nE5xC0FCi03G/fw3rslYLptN9+ChOnyoqGuyFIvABvmTN +% xxxx +% xxxx +% xxxx ---- S2.e.d Independent Verification - Verification of the Election Record + +\paragraph{Verification of the Election Record.} +The remaining verification steps apply to the election as a whole. These steps enable independent verification that every ballot in an election record is well-formed, that cast ballots have been correctly aggregated homorphically, and that these aggregated values have been correctly decrypted to produce election tallies. + +It is critical that a complete verification of an election record also includes verification of the correct decryption of any individual ballots that have been decrypted as part of the process. A complete election verifier must therefore incorporate an individual ballot verifier as described above. + +The election record verification steps are highlighted in green boxes. + +% xreqj { + "section": "S2.e.d", + "text": "A verifier can be independently implemented which performs the Election Record verification steps S6.2.1.verif1, S6.2.3.verif4, S6.2.4.verif5, S6.2.4.verif6, S6.2.4.verif7, S6.2.4.verif8, S6.2.5.verif9, S6.2.5.verif10, S6.2.5.verif11, S6.2.6.verif12", + "sc": "" } + + +% xxxx b8aSJ8oMI7uMDu/K+/V6S2BacsnC02F0d+Ss5/tlTYTb5h5J+hQSbWOvCnaK46sZycN+34tBS/Ad +% xxxx ijJ9C4f+rCNhiA4xlyxx2EquCOSQ4WHAm7h8n8effAJqeP7tewxKW1poWBDbm+P1lP6cpykDTU+e +% xxxx VLp8oo6sYiUMZ3W4MSZHcsbNnm3XNfjHL3fFG6Ysra5DtV0sKcV9mUyRLEZZPp8ODL7KqXZt5r5V +% xxxx g1d2tcuBl0QHMZe6ZAzJQJE8B3uOqPXMjOgXbzbqGwHxBl3r/yMILhYOnJkN9Bx8cELZ5BLgpTWV +% xxxx Q6dhFOPW1jkM68i0ElzgEz+Hv/E22AsS85pV0MecbULLIvopiMo6rH21flXAUnBRDu7P5JQ95Hxw +% xxxx WDeMV9DmMv+GExa0PuIh6JhZ4ABgzWB6XDkC+gaQPyo4i356MeOPLp1Hr6RJf/nsiVgH+NPS0TIC +% xxxx YwC/7lI7HNvTehlavszVZgf+ZtZJKzk/8aPZJMoUcsf18pCMOCp6InmE4gcySu5UdbHkGdC0nDb0 +% xxxx ec7dgFlVGS97HYS4JW4Ax2J3q/VcZTBCKVdsO2QIOLQDZh1I1ywUTlLszJV1gA/QDNHODo+YJyeA +% xxxx dEQw98N4rAJOmODgTsN1ocs9qjsapUiZTWG+mOorgmfL1XjsJrdQLnKAD7YAbdM4rgBILqrTg6r7 +% xxxx EcRTKwGKL0ykLipgmjtbt3lPd4JNds0IineWk2s6nWSn64zEuhwTHRp6HsDGuBOhoJ8z5aFK/NE8 +% xxxx +% xxxx ---- S2.f Using this Specification + +\subsubsection*{Using this Specification} +The principal purposes of this document are to specify the functionality of the \EG toolkit and to provide details necessary for independent parties to write election verifiers that consume the artifacts produced by the toolkit. + +% xnote S2.f This section generates no unique or specific requirements + + +\pagebreak + +% xxxx ulytKSMXTLX4PqPkchShixcnayHCzD+tBYoQgu/A/bUnIJpejZIrhjxN4tFF4sqgRaKw4ousSWuZ +% xxxx PxumFdQjeT8ckWKwecxV4w3Jgl+jEHPlIG4ynPIRvE7FHGcdzLbCghdFRwcUnPwSJmUa2jK5z1KF +% xxxx IBJMvnWcbGJv+syE7v4C4BH3CJFv8cZYe1AlOHiksKP1KetmQnOzjm+IocKy1+1pxy0Te4yor19o +% xxxx /ax1+nGvObJUKple+5+JYapVOtFLmDyvj1/pfngNmdp7QGr223J7KhIxdbKCGXagNUU6c5ru5Yrz +% xxxx m9inlQQRL/0IZMsDIrkfjkWYmKJ2bfiQC5uv6kcziO2Jx0CCARfHYFyXkck0wW275RSjO7X+23ej +% xxxx REwmSnYmynVy3HDbQb41juv6R3w+888RqYMWqFkn4c7D8Tq+zkuaFoMxSzl4EUIdYcTIzBHVyfcD +% xxxx 6ZOJILlg64yLCQzAN47cGfTGud1iFL3UXZJ5PIsS/aS6lZfXAsRr97use5gfVJQ70h16SE4djuuQ +% xxxx C057ZU7wxJxapJZY9rz9Fj2ddgjA3JMtuCgPtPOzeRTx8LekeLhQvPcSw7kSSFlOiLco1E2aYyea +% xxxx YRN+hNsjRjRNGJaT2gb3K6ycDOFaDyNKX6YADGh8C0a/yRi8FNWg/2uVUxRVuNcogFPQEBJj6xQw +% xxxx +UYezqczucYlW0CEbmiwDi7DvuTJ0xh7wKwyzzo8ZNtBniIMmrmEzK1vEY+gPMtX5n5yNiQJVpgr +% xxxx +% xxxx +% xxxx ---- S3.a.a Components + +\section{Components}\label{sec:EGcomponents} +This section describes the four principal components of \EG. +\begin{enumerate} +\item \emph{Parameter Requirements:} These are properties required of parameters to securely and efficiently instantiate the cryptographic components of \EG. These \emph{cryptographic parameters} are fixed ahead of every election and the same parameters can be used across multiple elections. A specific, recommended set of standard parameters is provided. In addition, there are properties required of the \emph{election parameters} that define the election contests, selectable options, and ballot styles, among other information. + +% xreqj { + "section": "S3.a.a.a", + "text": "EGRI enables selecting Fixed Parameters in advance of an election", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.a.a", + "text": "EGRI enables using the same Fixed Parameters across multiple elections", + "sc": "na" } +% xreqj { + "section": "S3.a.a.a", + "text": "EGRI supplies a standard set of Fixed Parameters", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.a.a", + "text": "EGRI enables defining Contests", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.a.a", + "text": "EGRI enables defining selectable Contest Options", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.a.a", + "text": "EGRI enables defining Ballot Styles", + "sc": "utsp", + "utsp": "" } + + +\item \emph{Key Generation:} Prior to each individual election, guardians must generate individual public-secret key pairs and exchange shares of secret keys to enable completion of an election even if some guardians become unavailable. Although it is preferred to generate new keys for each election, it is permissible to use the same keys for a small number of elections so long as the set of guardians remains the same. A complete new set of keys must be generated if even a single guardian is replaced. Secret key material that is not in use anymore must be destroyed. + +% xreqj { + "section": "S3.a.a.b", + "text": "(ref: S2.b) EGRI enables Guardians to generate individual public-secret key pairs", + "sc": "ics" } +% xreqj { + "section": "S3.a.a.b", + "text": "(ref: S2.b) EGRI enables Guardians to exchange shares of secret keys", + "sc": "ics" } +% xreqj { + "section": "S3.a.a.b", + "text": "EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)", + "sc": "" } +% xreqj { + "section": "S3.a.a.b", + "text": "EGRI requires that a complete new set of keys must be generated if even a single guardian is replaced (TODO: How?)", + "sc": "" } +% xreqj { + "section": "S3.a.a.b", + "text": "EGRI enables Guardians to destroy their secret keys.", + "sc": "nyi" } +% xreqj { + "section": "S3.a.a.b", + "text": "Josh 2025-03-18: \"There is a point where the rejection of the keys can happen\"", + "sc": "" } +% note S3.a.a.b Inherent properties of key generation ensure that: A complete new set of keys must be generated if n is changed +% note S3.a.a.b Inherent properties of key generation ensure that: A complete new set of keys must be generated if k is changed +% note S3.a.a.b Inherent properties of key generation ensure that: A complete new set of keys must be generated if any of the Fixed Parameters are changed +% note S3.a.a.b Inherent properties of key generation ensure that: A complete new set of keys must be generated if any of p, q, r, g is changed + +\item \emph{Ballot Encryption:} While encrypting the contents of a ballot is a relatively simple operation, most of the work of \EG is the process of creating externally-verifiable artifacts to prove that each encrypted ballot is well-formed (i.e., its decryption is a legitimate ballot without overvotes or improper values). + +% xreqj { + "section": "S3.a.a.c", + "text": "[No specific requirement]", + "sc": "na" } + +\item \emph{Verifiable Decryption:} At the conclusion of each election, guardians use their secret key shares to jointly produce election tallies together with verifiable artifacts that prove that the tallies are correct. +\end{enumerate} + +% xreqj { + "section": "S3.a.a.d", + "text": "(ref: S2.c.c) Guardians can use their secret key shares to jointly produce election tallies together with verifiable artifacts that prove that the tallies are correct.", + "sc": "" } + +% xxxx 9nru65LyYDuXuVKKRns8+B0zrgrgKOz8JiBjBJZ6+gord+xI7q2BQnDWIuq90g+4rm6P7KfmAOTc +% xxxx kL2VDTiUT5qSeVBIwohzcdrcd5uVF9dsgM8bdOvgCphLTteDF0IRguWjR+k+WiIZgN3JJP/su8v8 +% xxxx U6BzNLi/f9GHazY6W3sDCMz5v4ITIrBH64Aes1bdNOigGjDVTOqKzYULaXUhpqIJRyOmawKICXKz +% xxxx yvZbASFZM3VhMfzQ2k4J6EJDkmeXb2HThNvcC+o/tiSmvojnbLqYB0L0Q9rz2uh3TUXrQBECs1D+ +% xxxx lv3iaLs0O3IA6Yk1dWec59zF0QT1mIfNzwqKdW7uHR8XUK61G86ErvW7XuBHbkOtp6tXqPfkAwFy +% xxxx /CwfvDJ6pEYfuWdNR8fr/f6/NkPxZchGxAr1MUnm2CMQvTI5pk6WiOhXyUSGacmfHbaolTjESOvS +% xxxx LDVxuO8w6deNWy4wmehuyuK2itvnRVAk+CiOh7M0iCHJalH87S3jnGg+Skb1kX47MLog5ykkva4+ +% xxxx OC/RODqjve4DZ0hX4IoVRKLA1eGvlyfVBZTPAqNq1dYAipM5O7BDMCrWwMzOsvSrToFTfHshBG1B +% xxxx tkV6vQ1drcXDngdE4ESLJFFbaxYVKQcpxON4EgUDpyEU0FVDfII/GPrj5QkQUKkNhFeAQR5/oUpa +% xxxx HuRuZwl+z7hUCgYD8LPguuflThDp7quk152ANZB2xRHjYebIRs2OZrTmELUDK/HhXBaVRz42h+kj +% xxxx +% xxxx +% xxxx ---- S3.a.b Components - Notation + +\subsubsection*{Notation} +In the remainder of this specification, the following notation will be used. +\begin{itemize} + \item $\Z = \{\dots,-3,-2,-1,0,1,2,3,\dots\}$ is the set of integers. + \item $\Z_n = \{0,1,2,\dots,n-1\}$ is the ring of integers modulo $n$ for some positive integer $n$. + \item $\Z_n^*$ is the multiplicative subgroup of $\Z_n$ that consists of all invertible elements modulo $n$. When $p$ is a prime, $\Z_p^* = \{1,2,3,\dots,p-1\}$. + \item $\Z_p^r$ is the set of $r^{\mathrm{th}}$ powers in $\Z_p^*$. Formally, $\Z_p^r = \{y\in\Z_p^* \mbox{ for which there exists } x\in\Z_p^* \mbox{ such that } y=x^r \bmod p\}$. When $p$ is a prime for which $p-1=qr$, then $\Z_p^r$ is an order-$q$ cyclic subgroup of $\Z_p^*$ and for each $y\in\Z_p^*$, $y\in\Z_p^r$ if and only if $y^q \bmod p = 1$. + \item $x\equiv_n y$ is the predicate that is true if and only if $(x \bmod n) = (y \bmod n)$. + \item The function $\HMAC(\ ,\ )$ shall be used to denote the \HMAC-\SHA keyed Hash Message Authentication Code (as defined in NIST PUB FIPS 198-1\footnote{NIST (2008) The Keyed-Hash Message Authentication Code (HMAC). In: FIPS 198-1. \url{https://csrc.nist.gov/publications/detail/fips/198/1/final}}) instantiated with \SHA (as defined in NIST PUB FIPS 180-4\footnote{NIST (2015) Secure Hash Standard (SHS). In: FIPS 180-4. \url{https://csrc.nist.gov/publications/detail/fips/180/4/final}}). $\HMAC$ takes as input a key $k$ and a message $m$ of arbitrary length and returns a bit string $\HMAC(k,m)$ of length 256 bits. + \item The \EG hash function $H(\ ,\ )$ is instantiated with \HMAC and thus has two inputs, both given as byte arrays. The first input to $H$ is used to bind hash values to a specific election. The second is a byte array of arbitrary length and consists of domain separation bytes and the data being hashed. $H$ outputs a digest of 256 bits, which is interpreted as a byte array of length 32. The detailed specification for $H$ is given in Section~\ref{sec:hashing}. + \item The symbol $\oplus$ denotes bitwise XOR. + \item The symbol $\parallel$ denotes concatenation. + \item In general, the variable pairs $(\alpha,\beta)$, $(a,b)$, and $(A,B)$ will be used to denote encryptions.\footnote{Note that as described below, the encryption method used herein requires a pair of values to represent each individual encryption.} Specifically, $(\alpha,\beta)$ will be used to designate encryptions of votes (usually an encryption of a zero or a one), $(A,B)$ will be used to denote aggregations of encryptions---which may be encryptions of larger values, and $(a,b)$ will be used to denote encryption commitments used to prove properties of other encryptions. +\end{itemize} + +% xnote S3.a.b This section generates no unique or specific requirements + +% xxxx 2cP5ikU/BLdkOCz7lrL/wbaMpuWqbtpm/T5/w3yoxlds56dzFbHpiLbToduxsZ6WFbV7MhDrqBLe +% xxxx DjOqthS1Ux1iVkqgdix7+KGXCWf+PTdMjwby81LmThE6TFgbEjNThsGaY9LCYayJ43RwanPQuweJ +% xxxx +vpU44wFHq2QDuh/2MG1yAFPn93lyC7FnRZwwZWnOY6geLL+iaZHdlXop3J9lE1p+DKUNZfpGtzP +% xxxx V3Tosm5I1n/K4rtcgJ44x2QIAGFTcLP+6pF3WsfT4NL9SaMECrp676PwFBjaBsQOqAhKhLh5R2zF +% xxxx l/yEMQphT84NFRGtHBLMsNxgPCMcUTmdzOwn//JBtFFCPtZ15O6Ao2i0+C6Zlog3ckSWsQxzbIqH +% xxxx odRvppgtzWb+fz2VeqXf2uWlTbN94RJcxLjanzm1mmp8FcRkMzSrPvpiziSQ1OQ0IDaUzMAY2Eok +% xxxx nA3NxknEVYzv/JKFkkMOCk9kaq20eyOk9/jkJ2nQR0oXOWkH+8Y5NpdQr7vtsXaoPjgIBP5He5ej +% xxxx Sz5LlMfVfq9W7ZigIoKmCj/MNajG95NVWHC8RwgkEjcnhXyj/JxgaRGdwlZ4dSX3aKJ8Bdpn32Fd +% xxxx KlJY9xnLISk4+DM6umKdbnSoVUSYlWezxfUWaveMYV4SZKLbzn2T5AulUFz30r2xrn969p0D5ZJo +% xxxx NnuVtUD/tYrdTloUYVBMLB2KlaAGPD5x8fUOIultkUePDnhfMR2cIq8CmdalPyyUKY0EhxOijyHW +% xxxx +% xxxx +% xxxx ---- S3.a.c Components - Encryption of Votes + +\subsubsection*{Encryption of Votes} +Encryption of votes in \EG is performed using a public key encryption method suggested by Devillez, Pereira, and Peters,\footnote{Devillez H., Pereira, O., Peters, T. (2022) \emph{How to Verifiably Encrypt Many Bits for an Election?} in ESORICS 2022 Lecture Notes in Computer Science, vol 13555. Springer, Berlin, Heidelberg. \url{https://link.springer.com/chapter/10.1007/978-3-031-17146-8_32}} which is a variant exponential form of the ElGamal cryptosystem,\footnote{ElGamal T. (1985) \emph{A Public Key Cryptosystem and a Signature Scheme Based on Discrete Logarithms}. In: Blakley G.R., Chaum D. (eds) Advances in Cryptology. CRYPTO 1984. Lecture Notes in Computer Science, vol 196. Springer, Berlin, Heidelberg. \url{https://link.springer.com/content/pdf/10.1007/3-540-39568-7_2.pdf}} which is, in turn, a static form of the widely-used Diffie-Hellman(-Merkle) key exchange.\footnote{Diffie W., Hellman, M. (1976) \emph{New Directions in Cryptography} IEEE Transactions on Information Theory, vol 22} This encryption method is called \emph{DPP vote encryption} or simply \emph{vote encryption} in this document and rests on precisely the same security basis as Diffie-Hellman(-Merkle) key exchange---which is used to protect the confidentiality of the vast majority of Internet traffic. + +% xreqj { + "section": "S3.a.c.a", + "text": "Encryption of votes in ElectionGuard is performed using the DPP vote encryption method of Devillez, Pereira, and Peters (2022)", + "sc": "" } + +Primes $p$ and $q$ are publicly fixed such that $qr=p-1$. A generator $g$ of the order $q$ subgroup $\Z_p^r$ is also fixed. (Any $g=x^r \bmod p$ for which $x\in\Z_p^*$ suffices so long as $g\neq 1$.) + +% xreqj { + "section": "S3.a.c.b", + "text": "EGRI ensures that P is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.c.b", + "text": "EGRI ensures that Q is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.c.b", + "text": "EGRI ensures that G is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.c.b", + "text": "EGRI verifies that P is prime before any processing. Note that this may be a simple comparison with the standard parameter value.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.c.b", + "text": "EGRI verifies that Q is prime before any processing. Note that this may be a simple comparison with the standard parameter value.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.c.b", + "text": "EGRI verifies that Q is is not a divisor of r=(p-1)/q before any processing. Note that this may be a simple comparison with the standard parameter value.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S3.a.c.b", + "text": "EGRI verifies that G is a generator $g$ of the order $q$ subgroup $\\Z_p^r$ before any processing. Note that this may be a simple comparison with the standard parameter value.", + "sc": "utsp", + "utsp": "" } + +A public-secret key pair can be chosen by selecting a random $s\in\Z_q$ as a secret key and publishing $K=g^s \bmod p$ as the corresponding public key.\footnote{As will be seen below, the actual public key used to encrypt votes will be a combination of separately generated public keys. So, no entity will ever be in possession of a secret key that can be used to decrypt votes.} + +% xreqj { + "section": "S3.a.c.c", + "text": "EGRI enables a Guardian to (ref: S.2.b) generate its own Vote or Ballot Data Encryption Secret Key by selecting a random $s\\in\\Z_q$.", + "sc": "" } +% xreqj { + "section": "S3.a.c.c", + "text": "EGRI enables a Guardian to (ref: S.2.b) derive its Vote or Ballot Data Encryption Public Key as $K=g^s \\bmod p$.", + "sc": "" } +% xreqj { + "section": "S3.a.c.c", + "text": "EGRI enables a Guardian to (ref: S.2.b) publish its Vote or Ballot Data Encryption Public Key.", + "sc": "" } +% xreqj { + "section": "S3.a.c.c", + "text": "No entity is ever in possession of a secret key that can be used to decrypt votes", + "sc": "" } + +A message $m\in\Z_q$ is then encrypted by selecting a random nonce $\xi\in\Z_q$ and forming the pair $(\alpha, \beta) = (g^\xi \bmod p, K^m\cdot K^\xi \bmod p) = (g^\xi \bmod p, K^{m+\xi} \bmod p)$. An encryption $(\alpha, \beta)$ can be decrypted by the holder of the secret $s$ as $\beta/\alpha^s\bmod p = K^m \bmod p$ because + +% xxxx ---- eq. 1 + +\begin{equation} +\frac{\beta}{\alpha^s} \equiv_p \frac{K^{m+\xi}}{(g^\xi)^s} \equiv_p \frac{K^m\cdot K^\xi}{(g^\xi)^s} \equiv_p \frac{K^m\cdot (g^s)^\xi}{(g^\xi)^s} \equiv_p \frac{K^m\cdot g^{\xi s}}{g^{\xi s}} \equiv_p K^m. +\end{equation} + +% xreqj { + "section": "S3.a.c.d", + "text": "Values are encrypted to a Vote or Ballot Data Encryption Public Key by selecting a random nonce $\\xi\\in\\Z_q$ and forming the pair $(\\alpha, \\beta) = (g^\\xi \\bmod p, K^m\\cdot K^\\xi \\bmod p) = (g^\\xi \\bmod p, K^{m+\\xi} \\bmod p)$ .", + "sc": "" } + +The value of $m$ can be computed from $K^m \bmod p$ as long as the message $m$ is limited to a small, known set of options.\footnote{\label{foot:BSGS}The simplest way to compute $m$ from $K^m \bmod p$ is an exhaustive search through possible values of $m$. Alternatively, a table pairing each possible value of $K^m \bmod p$ with $m$ can be pre-computed. A final option which can accommodate a larger space of possible values for $m$ is to use Shanks's baby-step giant-step method as described in the 1971 paper \emph{Class Number, a Theory of Factorization and Genera}, Proceedings of Symposium in Pure Mathematics, Vol. 20, American Mathematical Society, Providence, 1971, pp. 415-440.} + +The value of $K^m$ and therefore $m$ can also be computed from the encryption nonce $\xi$, namely via $\beta/K^\xi \equiv_p K^m$. The encryption nonce must therefore be securely protected. While the secret key $s$ allows decryption of any ciphertext encrypted to the public key $K$, the encryption nonce only allows decryption of the specific ciphertext it was used to generate. Release of an encryption nonce can, when appropriate, serve as a fast and convenient method of verifiable decryption. + +Usually, only two possible votes are encrypted in this way by \EG. An encryption of one is used to indicate that an option is selected, and an encryption of zero is used to indicate that an option is not selected. For some voting methods such as cumulative voting, Borda count, and a variety of cardinal voting methods like score voting and STAR-voting, it can be necessary to encrypt other small, positive integers. + +% xreqj { + "section": "S3.a.c.e", + "text": "Ciphertexts encrypted to a Vote or Ballot Data Encryption Public Key can be decrypted using the corresponding Secret Key $s$ as $\\beta/\\alpha^s\\bmod p = K^m \\bmod p$ using exhaustive search, a pre-computed table, or Shanks's baby-step giant-step method.", + "sc": "" } +% xreqj { + "section": "S3.a.c.e", + "text": "The value of $K^m$ can be computed from the encryption nonce $\\xi$", + "sc": "" } +% xreqj { + "section": "S3.a.c.e", + "text": "The value of $m$ can be computed from the encryption nonce $\\xi$", + "sc": "" } +% xreqj { + "section": "S3.a.c.e", + "text": "An encryption of one is used to indicate that an option is selected", + "sc": "" } +% xreqj { + "section": "S3.a.c.e", + "text": "An encryption of zero is used to indicate that an option is not selected", + "sc": "" } + +% xxxx lgxQ97jTvVpo9KROS5cZ6BZmckiDIHa86QHdpfNZg887DZ1kZSGaGBVLNj9inGk9/JrGIHJIh021 +% xxxx k0dRB9HTZHC6Vyn6Wstx0m8ACWCn5W2BAzFbXATWqgyvXatoXP/4HBnA6p/truaHe/g9QWiTLPl5 +% xxxx imbL/XO3y9nFIU+bF33iwziK3gldPx9Wbr4YODUxGgWCdCI39TbC8JfMLe+PdmiBivCkudtYPo2+ +% xxxx BnJCYe+/gJ4c0BMHkJnhnnoqsS/ZRbaelKHio+wTAJtp6cMNZseKmx/vI4oA14ja1y9zM8PTpXKG +% xxxx Ll8tDxKQldPtfd+cYcsuZuRfUiKtAWRCcM67yQn2CduDSe625C2YA8DokwCm4Rjm+eMTSJj8fL5y +% xxxx nO3Ldj7Q0Jo72Yf220iX3/l9X8FjumAFFjGCp11gOCzLdxRM/IM8yVRCkDlEF7d8FFQCfYgr02aZ +% xxxx flxfEM+rabaOmxrpdqnHaLgCA09thm2tdHQW0A/+zdxgsXmFBtLWXjXODP8y8vaewOSbcPXEJrSW +% xxxx okcn/rlpBEKFxK308GdhEdKq8nHEDvniP+YIGvE0RxBZrIZ50uHyQZyScsfYWA33BdV3JGxY0w28 +% xxxx G8yaCJUHx3w1/MWE2gEdfajQxNqeUKLCvloy/IjVaID6GcW/r3nmpyx6hOWj5PqsjSqWEynM4zfp +% xxxx z8rHg0tA5Y5uEdAov4crSgi+tbOqodc1/nWgflQNek0OVf4jHQVkwRB1XDYoYKtN+z4XigWhqR1m +% xxxx +% xxxx +% xxxx ---- S3.a.d Components - Homomorphic Properties + +\subsubsection*{Homomorphic Properties} +A fundamental quality of the DPP vote encryption is its additively homomorphic property. If two messages $m_1$ and $m_2$ are respectively encrypted as $(A_1,B_1 ) = (g^{\xi_1} \bmod p , K^{m_1+\xi_1} \bmod p)$ and $(A_2, B_2) = (g^{\xi_2}\bmod p, K^{m_2+\xi_2} \bmod p)$, then the component-wise product + +% xxxx ---- eq. 2 + +\begin{equation} + (A,B) = ( A_1 A_2 \bmod p, B_1 B_2 \bmod p) = (g^{\xi_1+\xi_2} \bmod p, K^{(m_1+m_2) + (\xi_1+\xi_2)} \bmod p) +\end{equation} +is an encryption of the sum $m_1+m_2$ using the nonce $\xi_1 + \xi_2$.\footnote{There is an implicit assumption here that $(m_1+m_2) < q$, which is easily satisfied when $m_1$ and $m_2$ are both small. If $(\xi_1+\xi_2)\geq q$, $(\xi_1+\xi_2) \bmod q$ may be substituted without changing the equation since $g^q\equiv_p 1$.} + +This additively homomorphic property is used in two important ways in \EG. First, all of the encryptions of a single option across ballots can be multiplied to form an encryption of the sum of the individual values. Since the individual values are one (or some other integer for certain voting methods) on ballots that select that option and zero otherwise, the sum is the tally of votes for that option and the product of the individual encryptions is an encryption of the tally. + +The other use is to sum all of the selections made in a single contest on a single ballot. In the simplest case, after demonstrating that each option is an encryption of either zero or one, the product of the encryptions indicates the number of options that are encryptions of one, and this can be used to show that no more ones than permitted are among the encrypted options---i.e. that no more options were selected than permitted. When larger integers are allowed, i.e.\ an option can receive multiple votes or weighted votes, the product of the ciphertexts then encrypts the total number of votes or the sum of weights and is used in the same way to ensure only the permitted number of votes or permitted sum of weights were used. + +However, as will be described below, it is possible for a holder of a nonce $\xi$ to prove to a third party that a pair $(\alpha, \beta)$ is an encryption of $m$ without revealing the nonce $\xi$ and without access to the secret $s$. + +% xreqj { + "section": "S3.a.d", + "text": "All the encryptions of a single option across ballots can be multiplied to form an encryption of the sum of the individual values", + "sc": "" } +% xreqj { + "section": "S3.a.d", + "text": "An encryption of some integer other than `0` or `1` is used for certain voting methods", + "sc": "" } +% xreqj { + "section": "S3.a.d", + "text": "The product of the encryptions of a single contest option across ballots is an encryption of the tally (S2.c.c)", + "sc": "" } +% xreqj { + "section": "S3.a.d", + "text": "Every option can be demonstrated to be an encryption of either `0` or `1` (for typical voting methods)", + "sc": "" } +% xreqj { + "section": "S3.a.d", + "text": "Every option can be demonstrated to be an encryption of some integer other than `0` or `1` (for certain voting methods)", + "sc": "" } +% xreqj { + "section": "S3.a.d", + "text": "The product of the encrypted options of a single ballot can be used to show that no more options were selected than permitted", + "sc": "" } +% xreqj { + "section": "S3.a.d", + "text": "When an option can receive multiple or weighted votes, the product of the encrypted options of a single ballot can be used to show that only the permitted number of votes or permitted sum of weights were used", + "sc": "" } +% xreqj { + "section": "S3.a.d", + "text": "A holder of a nonce $\\xi$ can prove to a third party that a pair $(\\alpha, \\beta)$ is an encryption of $m$ without access to the secret $s$ or revealing the nonce $\\xi$", + "sc": "" } + +% xxxx mkCeCT5oailkoE2uJIBe9rulPEGtxLlWnwEPAlFcOX88v07wuHr5hHjT/UE78GXNsYjby6CRrr6c +% xxxx IvZ4oYiEADuvjLCVChiRBixGLuBFFeJqBwHzj8oHLGeRsBjqR697Z7UErKoUGf89wDJBsoySt3El +% xxxx EwM50cDudz/3/AozXfwHTlV18V40x4/JkWc2RwRd6Nx6NSAWsKoiTaHBIMKhEk1hanNbZ/ZXPVJc +% xxxx my73w5ksPr7x9RHj7rPCNaHfoJZfiMGpXqDI+ylUypTd5WOoRtcUSGtVX9OXeqOwHB/+JgcXdkQy +% xxxx zNIru1j7nFTWvQ+uMvGTqB2YC8vD3ooAqgKUhKhO2w+4DDP7WDAgsLAz3JAjYgJ7Xa2eDvddsqmw +% xxxx RslS5l/7HD7V7zrpVZX3QjlLS6JTKBV4hOWyH+Q06VhMUptPmjDRLQCKgmwZWBN3T8cAcRXxKah+ +% xxxx +58+XLrsWKLx/hgelbTWvY24M+SgZap3EutsDThqGY8bEXNY2AvQQPAaSdQjmzZwjYJ50eI7DgMR +% xxxx kDWItPt4O7Uw1z6W6w/r5YM47v1SeH0s7MIpOJ5cGZAXM1FFcD8DKnC5TVaTdaRbvIXDdDsqrIQ1 +% xxxx fETUMzHqHd7IkBpvNxwo5xujKWeC/tQUHHfvOLmOU2PZEbR8eNBii5Jmsmb9rwIF5RCR4msyS93w +% xxxx S/3PoH6DyG8UY02KCcOhWYEID9hI6NSQIm+rP44dCNS/lhlVx5GhebEHKsrNI6XFu3iCnKbMwW7n +% xxxx +% xxxx +% xxxx ---- S3.a.e Components - Non-Interactive Zero-Knowledge (NIZK) Proofs + +\subsubsection*{Non-Interactive Zero-Knowledge (NIZK) Proofs} +\EG provides numerous proofs about encryption keys, encrypted ballots, and election tallies using the following four techniques. +\begin{enumerate} +\item A Schnorr proof\footnote{Schnorr C.P. (1990) Efficient Identification and Signatures for Smart Cards. In: Brassard G. (eds) Advances in Cryptology — CRYPTO' 89 Proceedings. CRYPTO 1989. Lecture Notes in Computer Science, vol 435. Springer, New York, NY. \url{https://link.springer.com/content/pdf/10.1007\%2F0-387-34805-0_22.pdf}} allows the holder of a secret key $s$ to interactively prove possession of $s$ without revealing $s$. +\item A Chaum-Pedersen proof\footnote{Chaum D., Pedersen T.P. (1993) \emph{Wallet Databases with Observers}. In: Brickell E.F. (eds) Advances in Cryptology — CRYPTO' 92. CRYPTO 1992. Lecture Notes in Computer Science, vol 740. Springer, Berlin, Heidelberg. \url{https://link.springer.com/content/pdf/10.1007\%2F3-540-48071-4_7.pdf}} allows an encryption to be interactively proven to decrypt to a particular value without revealing the nonce used for encryption or the secret decryption key $s$. (This proof can be constructed with access to either the nonce used for encryption or the secret decryption key.) +\item The Cramer-Damgård-Schoenmakers technique\footnote{Cramer R., Damgård I., Schoenmakers B. (1994) \emph{Proofs of Partial Knowledge and Simplified Design of Witness Hiding Protocols}. In: Desmedt Y.G. (eds) Advances in Cryptology — CRYPTO' 94. CRYPTO 1994. Lecture Notes in Computer Science, vol 839. Springer, Berlin, Heidelberg. \url{https://link.springer.com/content/pdf/10.1007\%2F3-540-48658-5_19.pdf}} enables a disjunction to be interactively proven without revealing which disjunct is true. +\item The Fiat-Shamir heuristic\footnote{Fiat A., Shamir A. (1987) \emph{How To Prove Yourself: Practical Solutions to Identification and Signature Problems}. In: Odlyzko A.M. (eds) Advances in Cryptology — CRYPTO' 86. CRYPTO 1986. Lecture Notes in Computer Science, vol 263. Springer, Berlin, Heidelberg. \url{https://link.springer.com/content/pdf/10.1007\%2F3-540-47721-7_12.pdf}} allows interactive proofs to be converted into non-interactive proofs. +\end{enumerate} +Using a combination of the above techniques, it is possible for \EG to demonstrate that keys are properly chosen, that ballots are well-formed, and that decryptions match claimed values.\footnote{For all proof variants, \EG uses a compact representation that omits the commitments. Proofs consist of the challenge and response values only. When verifying proofs, the verification equations can be used to recompute the commitments and check their correctness via the challenge hash computation.} + +% xreqj { + "section": "S3.a.e", + "text": "EGRI enables the use of NIZK proofs to demonstrate that keys are properly chosen.", + "sc": "" } +% xreqj { + "section": "S3.a.e", + "text": "EGRI enables the use of NIZK proofs to demonstrate that ballots are well-formed.", + "sc": "" } +% xreqj { + "section": "S3.a.e", + "text": "EGRI enables the use of NIZK proofs to demonstrate that decryptions match claimed values.", + "sc": "" } + + +% xxxx o45qlhFPw/uikME51TBqE5XEVO+s6BKGMr8pAaO/N+3DjIBCysSvsP+Uiv5Byvc9gboD6U9xN+6m +% xxxx n41Vz+rVtUV7Ut9vfoL9uKFiAVX157DPf18nYhNPT+CNDLFRh5w3xjJ/l8tmjpAnsE6eqEKRTuT1 +% xxxx wlK/Nl4uuo6Izu87EbCp5fNC/98xkku8hU1qkSIIr7eWbupvqyoJOdisPHGUHz2Qe5FHrw1WArrT +% xxxx FtnhrPrJhAObs8AWQyqQKizCcnTO0t6pC6QfZnoeUJzmbHmrUIiM9J0YY5ym4X8IArbfYYJpQl4D +% xxxx RB9LCOXHhnxV735puriYX84WpwMME0/nXh8zgICzWY4wxAU6L1i4/4ibSuvX50dina+yDE8NiqSG +% xxxx ZZJH7KtCH82fOBviw3FUA1Og/0L4Jm+k+75ncKAdgHRYbNdHRKyKQuMkvYD8OQ6ONC5LoTE7zv6z +% xxxx 5cXy9fOnkJ7ej/H6owKg0ykxjVUulYPLKBcnklMaqYM981YkEm9Y7Oynm1S8vzbnpl6RW25lT6xG +% xxxx xxaBE6SE2Celq4nxrykSh+5555Ti3KfLb2MQkTB/Ga2ZD08FjVzqhJ7Q4KmV164a3vvcWnQ76PbS +% xxxx jDmq9kGnRvXrRtNyD69nSRGhIoRuqBB4OHxPQk9G2uDqVpOl13ktDyYdOgiO8crH++TWFgaj1FMz +% xxxx juxh5ZrioUVlGV0RhImVTWYCLuV7C3YO4gVd1Xz3u2YJFTeEBQ6i4mRi4gc//ZpfjwkbR+vUR6uh +% xxxx +% xxxx +% xxxx ---- S3.a.f Components - Threshold Encryption + +\subsubsection*{Threshold Encryption} +Threshold encryption is used for encryption of ballots and other data.\footnote{Shoup, V., Gennaro, R. (2002) \emph{Securing Threshold Cryptosystems against Chosen Ciphertext Attack}. In: J.\ Cryptology, vol 15. \url{https://link.springer.com/content/pdf/10.1007/s00145-001-0020-9.pdf}} This form of encryption makes it very easy to combine individual guardian public keys into a single public key. The threshold encryption mechanism used in \EG also offers a homomorphic property that allows individual encrypted votes to be combined to form encrypted tallies. + +% xreqj { + "section": "S3.a.f.a", + "text": "EGRI enables the use of threshold encryption for encryption of ballots and other data.", + "sc": "" } +% xreqj { + "section": "S3.a.f.a", + "text": "(ref S2.b) Combining individual Guardian (Vote|Ballot Data) Public Keys into a single Joint (Vote|Ballot Data) Public Key.", + "sc": "" } +% xreqj { + "section": "S3.a.f.a", + "text": "Threshold encryption offers a homomorphic property that allows individual encrypted votes to be combined to form encrypted tallies.", + "sc": "" } + +The guardians of an election will each generate three public-secret key pairs. One of the public keys for each guardian will then be combined (as described in the following section) into a single vote encryption public key which is used to encrypt all selections made by voters in the election. The public keys of the second key pairs will be combined in the same way to form a single encryption key for other ballot data. The third key pairs will be used by guardians to privately communicate with each other. + +% xreqj { + "section": "S3.a.f.b", + "text": "Ref S2.b: \"EGRI enables a Guardian to generate its own public-secret (Vote|Ballot Data) Encryption Key pair...\"", + "sc": "" } +% xreqj { + "section": "S3.a.f.b", + "text": "Ref S2.b: \"At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian (Vote|Ballot Data) Encryption Public keys to form the Joint (Vote|Ballot Data) Encryption Public Key.\"", + "sc": "" } +% xreqj { + "section": "S3.a.f.b", + "text": "(=~ S2.a[x]): EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.", + "sc": "" } + +At the conclusion of the election, each guardian will compute a verifiable partial decryption of each tally. These partial decryptions will then be combined to form full verifiable decryptions of the election tallies. + +% xreqj { + "section": "S3.a.f.c", + "text": "EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.", + "sc": "" } +% xreqj { + "section": "S3.a.f.c", + "text": "EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.", + "sc": "" } + +To accommodate the possibility that one or more of the guardians will not be available at the conclusion of the election to form their partial decryptions, the guardians will cryptographically share\footnote{Shamir A. (1979) \emph{How to Share a Secret}. Communications of the ACM, vol 22. \url{https://dl.acm.org/doi/10.1145/359168.359176}} their secret keys amongst each other during key generation in a manner to be detailed in Section~\ref{sec:keygen}. Each guardian can then compute a share of the secret decryption key, which it uses to form the partial decryptions. A pre-determined threshold quorum value $k$ out of the $n$ guardians will be necessary to produce a full decryption. + +% xreqj { + "section": "S3.a.f.d", + "text": "(Ref: S2.b) Key Ceremony", + "sc": "" } +% xreqj { + "section": "S3.a.f.d", + "text": "(Ref: S3.a.f.c) \"EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.\"", + "sc": "" } +% xreqj { + "section": "S3.a.f.d", + "text": "(Ref: S3.a.f.c) \"EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.\"", + "sc": "" } + +% xxxx R0XoETclIu2SjP77nlxSynx91ocdXGTtaxDBlC/AQ971ICBt17EfyduiNTZLIVNd7otPbBPioV1t +% xxxx CGKD/esix3hSc2jMFdvU/av5ZY06CNshykna+ionniX9tqYBkvG3rf5KQ/ZRj4f/2FAkLDtWJLZ2 +% xxxx wStFlv3sh9plwwHU1TmWXIn8v7vUqGRadapt2onBSk/g0DUkGW7WOXK1jobrjc8pNJ/sXADULSfH +% xxxx l1siVE0/W/TYDl9KtpzUoNJ+TbNIMdf6x4AgIhop9ArZoqg1vvBtX8iE3K/4yGhi6OqXdiJRW0U1 +% xxxx DLoJjV/VJQWDU3MFSWq0kyELL9kuTDrZ0DsXPPWyxXQsmK8i2CgqKF5KvcJTqS0Ji1yrwOjEdo6+ +% xxxx u5FIxqyzigzZ/xfhaiOFeJL/wAGp/5k1pox42Td+4EBhY1qeLjSWGrZ2C7ZHD59szBdLBhlzPv43 +% xxxx qVLXEXKw15H1DDfv8qSYf/ECXnvHHe1evpmr5q+5XZef+1NSs6ySdfE1DML32D2qyCp4U1p/NtTl +% xxxx C9tm5JSx1j0sMIqHAEJMUdt8ftg/R3V/fgxAVm2YzzBL2TQwJ/ZwUSdla9cDKv2YlT0OaXDF9Dmz +% xxxx q2s30UdnjLBVCL/zANLLSbsDIt70Ita9Q24UO+xOMAFIXrnk4UiPp8zkvDK7MRPvchHHTAT3LmeO +% xxxx x62crnSvXYVsyNHqfmKyfy4X1iL1xMC888jZySJC8nVeSWD116qHI9h0vY+FO1dBL1bAZ4i5FyOP +% xxxx +% xxxx +% xxxx ---- S3.a.g Components - Encryption of Other Data + +\subsubsection*{Encryption of Other Data} +\EG provides means to encrypt data other than votes that do not need to be homo\-morphi\-cally aggregated. Such data may include write-in information that needs to be attached to an encrypted selection and is encrypted to the public key used for other ballot data, or other auxiliary data attached to an encrypted ballot, either encrypted to this public key or to an administrative public key. The non-vote data do not need to be homomorphically encrypted and can use a more standard form of public-key encryption removing the data size restrictions imposed by the vote encryption. \EG encrypts such data with signed hashed ElGamal encryption, which employs a key derivation function (\KDF) to generate a key stream that is then XORed with the data.\footnote{Zheng Y., Seberry J. (1993) \emph{Immunizing public key cryptosystems against chosen ciphertext attacks}. In: IEEE Journal on Selected Areas in Communications, vol 11. \url{https://ieeexplore.ieee.org/document/223871}} To implement the \KDF, encryption makes use of the hash-based message authentication code \HMAC. In \EG, \HMAC is instantiated as \HMAC-\SHA with the hash function \SHA. Encryption also produces a Schnorr proof of knowledge of the random nonce used for the ElGamal encryption. + +Guardians also need confidential communication channels between each other, for example to securely communicate the cryptographic shares of a guardian's secret key. +This is the purpose of the third key pair generated by each guardian. Each guardian shares the public portion of its communication key with the other guardians. + +% xreqj { + "section": "S3.a.g", + "text": "EGRI provides a non-homomorphic encryption method known as \"Hashed ElGamal\" for encrypting data other than votes", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI provides a signature method known as \"Signed Hashed ElGamal\" for encrypting data other than votes", + "sc": "" } + +% xreqj { + "section": "S3.a.g", + "text": "EGRI enables a user to generate a Hashed ElGamal (Public|Secret) Key pair for Hashed ElGamal encryption", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI enables a user to encrypt data to a Hashed ElGamal Public Key", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "Any data encrypted to a Hashed ElGamal Public Key cannot be decrypted without the Hashed ElGamal Secret Key", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI enables using a Hashed ElGamal Secret Key to decrypt data encrypted to the corresponding Hashed ElGamal Public Key", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "Hashed ElGamal encryption does not have the data size restrictions imposed by the vote encryption", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "Hashed ElGamal encryption uses a KDF based on HMAC-SHA-2-256 to generate a key stream XORed with the data", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI enables a user to generate a Signing Hashed ElGamal (Public|Secret) Key pair for Signing data encrypted with Hashed ElGamal", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI enables using a Signing Hashed ElGamal Secret Key to sign data that has been encrypted with Hashed ElGamal", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI enables using a Signing Hashed ElGamal Public Key to verify or refute a signature made with Signed Hashed ElGamal", + "sc": "" } +% xnote S3.a.g 'Sign(ed|ing) Hashed ElGamal' means 'Signed ElGamal' +% xnote S3.a.g Michael 2025-02-25, "Signed ElGamal" refers to the third ciphertext component which is a Schnorr proof of knowledge of the encryption randomness. See: { Schnorr, C.P., Jakobsson, M. (2000). Security of Signed ElGamal Encryption. In: Okamoto, T. (eds) Advances in Cryptology — ASIACRYPT 2000. ASIACRYPT 2000. Lecture Notes in Computer Science, vol 1976. Springer, Berlin, Heidelberg. https://doi.org/10.1007/3-540-44448-3_7 } https://markus-jakobsson.com/papers/jakobsson-asiacrypt00.pdf +% xreqj { + "section": "S3.a.g", + "text": "(Ref: S2.b) \"EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key a share of each of its own (two, non-Communication) secret keys.\"", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI allows to encrypt other (non-homomorphically-tallied) Ballot data to the Joint Ballot Data Encryption Public Key", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI allows to encrypt voter selection contest write-in Ballot data to the Joint Ballot Data Encryption Public Key", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI allows to encrypt other auxiliary data attached to an encrypted Ballot to the Joint Ballot Data Encryption Public Key", + "sc": "" } +% xnote S3.a.g Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI, not even signing the Election Record +% xnote S3.a.g Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: xreq S3.a.g There is an 'Administrative Public Key'. +% xnote S3.a.g Josh 2025-03-18: The Administrative Public Key is not the same as any key an Election Administrator might use to sign the Election Record +% xreqj { + "section": "S3.a.g", + "text": "EGRI allows to encrypt voter selection contest write-in Ballot data to the Administrative Public Key.", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI allows to encrypt other auxiliary data attached to an encrypted Ballot to the Administrative Public Key.", + "sc": "" } +% xreqj { + "section": "S3.a.g", + "text": "EGRI allows to encrypt other (non-homomorphically-tallied) Ballot data to the Administrative Public Key.", + "sc": "" } +% xtodo S3.a.g TODO: Do we need separate Administrative (Public|Secret) Key pairs for encryption and signing operations? +% xtodo S3.a.g TODO: How is it configured which specific other data is to be encrypted with which of the Joint Ballot Data Encryption Public Key and/or Administrative Public Key? +% xnote S3.a.g Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: S3.a.g TODO: How is the Administrative Public Key generated? +% xnote S3.a.g Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: % xtodo S3.a.g TODO: How is the Administrative Public Key distributed? +% xnote S3.a.g Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: % xtodo S3.a.g TODO: How is the Administrative Public Key authenticated? +% xnote S3.a.g Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: % xtodo S3.a.g TODO: Who controls the Administrative Secret Key? + +% xxxx uXuEvKBrCpdhCjbYMlKC12vtESr4RWeK9MWFwm7BJ5wLYIjeKifA1kF/+MAMotOC0g+yvcjgD0Kq +% xxxx HDH018nXWhxf9X8GRxk/2DON1vpoh7/3vjuJhkfPGnut6uj5JgjEqKXkmO3UN4kBjaSXtzFNNBQp +% xxxx wSqlpV4y3kIt4Q2BV7oQUCwczktszuLQsc7oO+FYGb+g8NzECCYehLmcPZdQoddwGNLn5GalwQcq +% xxxx 6GWl3pJFH7dqKoDAPZB4ikHaVP+xp59ADkUuJdSFH1wyjCfKjI8tXVrNNszBJd9yitVX+bQy8w1G +% xxxx OQzB04xbK8fvNsIAAMVq4HcaL/znqae+15Uzr043xxKFM7/SoGFQJWFARDnsEJRsRJawp9yqhzlo +% xxxx NGrKxQPKbJRbi/inSgAXHsZVipws85UzQHcebEWv13vunoK9cDYlgFACUZfePswcnh9wu4ZNpcBa +% xxxx soKFlFJWBAz1OrXGExJxeJlr16b0o8RwgMMOJIguXitgMf7sHPH3HaFbMPDgc6tsqIdiwEOC0F1i +% xxxx ietO+KjH4Hj4MTSz2sFYK9ScpNv7EjfTubQcoaVCSV8aUuYN9CFyTfO/EhDKv+ABfFi8ws66IXW6 +% xxxx jNqiXjBoe1H0g6FVsbSp/FAbv++7ftrFDP66sKlPKhS6+FzfRrQO6pYKEnXMhFTIQDeZI3pko1hD +% xxxx Vb//Jc5Upt1UZnG2zGq3aatAOjjORTo44VNgPz5kkOcNlnmT/uXHI7lxyZwooThr9ZAwNUORjVe4 +% xxxx +% xxxx +% xxxx ---- S3.1 Parameter Requirements + +\subsection{Parameter Requirements}\label{sec:parameterreqs} +\EG uses integers to instantiate the encryption rather than elliptic curves in order to make construction of election verifiers as simple as possible without requiring special tools and dependencies.\footnote{Elliptic curves are commonly used in cryptographic applications because they are regarded as achieving the same security for lower cost. But they add complexity that ElectionGuard seeks to avoid.} The encryption scheme used to encrypt votes is defined by a prime $p$ and a prime $q$ which divides $(p-1)$. We use $r = (p-1)/q$ to denote the cofactor of $q$, and a generator $g$ of the order $q$ subgroup $\Z_p^r$ is fixed. We also require $r/2$ to be prime. The specific values for a 4096-bit prime $p$ and a 256-bit prime $q$ which divides $(p-1)$ and a generator $g$ that define all standard baseline parameters are given below. + +% xreqj { + "section": "S3.1", + "text": "EGRI uses integer-based encryption", + "sc": "" } +% xreqj { + "section": "S3.1", + "text": "EGRI does not use elliptic curves", + "sc": "" } +% xreqj { + "section": "S3.1", + "text": "The encryption scheme used to encrypt votes is defined by primes p and q.", + "sc": "" } +% xreqj { + "section": "S3.1", + "text": "ref:S3.a.c.b \"EGRI verifies that (P|Q|G) is prime before any processing...\"", + "sc": "" } + +% xxxx uH6JIxZVnM9Qv6n6BZoWy7a4f3GGTEEf+y/Cy8fNkuT6BQ3kDQR4K8F3St2VNT6kKfuXxohuCnsh +% xxxx Xi8R/tGp//xJZgIbHVhyD8WENo4kCATjuCA8byma7jkkF2q2cM/KpuPoJDBIVWTH1tOY83M+6zDf +% xxxx 3g0/ICsSScMDr5AIO4F70IDHxbGGto5t8bRVQS3oAZ9MiUnAMj7dqP4GGIf2GWJAaVR+k/7xpPnS +% xxxx HVtlHY4M8cJMDliZr/lwfJeEhok8yFtrtkFEGux55ZG5db7WJI0GMhizs3iif4CDCtx9itiFY0zv +% xxxx 28I4dT7hvWYSOQKh7HTdONPVB821cOOoUxQfWg1TX2Ukg28Smkjh67edIWWnIx28PDymqvJhzgEv +% xxxx 7iB2yyScRWtPhikC4WHy5WxmQTpspYwg9mLxw2GWP1XjHMUJikbVrV2XxJvEGoigwdrUU3kv9onN +% xxxx M6sekcuiawSR/XHUPibRK6VCVhpm9hFaVUCRS+NzWqYXOp/p2rKHPwi1nL7HK9cRZktYSuBDfSr0 +% xxxx 7Fqvn/EY6XdNJtLZ7k942Ef3h3mz7G5Ex2NZTbHEf6w2HB9HzJg/By++FNWnr3lTVv0C6hH0jTLE +% xxxx TQn2TDeVusiGKaLlkzoW09nBfmRSK0UwMjeUZC7abD8nPCIh/3+gFtPwaEcVwLOPq5A8PeS4q7O0 +% xxxx XMe+SBJE8xrRa9EZDkKnXRGJNuc4ZUZPSuFWnv3YiYELBnEHCGU71gle41ltRUpMICv7Gm8yHHN/ +% xxxx +% xxxx +% xxxx ---- S3.1.1 Standard Baseline Cryptographic Parameters + +\subsubsection{Standard Baseline Cryptographic Parameters}\label{sec:parameters} +Standard parameters for \EG begin with the largest 256-bit prime $q = 2^{256}-189$. The (big endian) hexadecimal representation of $q$ is as follows. + +% xxxx ---- eq. 3 + +\begin{equation} +q = \mathtt{0xFFFFFFFF \ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFF43} +\end{equation} +The modulus $p$ is then set to be a 4096-bit prime with the following properties. +\begin{enumerate} + \item The first 256 bits of $p$ are all ones. + \item The last 256 bits of $p$ are all ones. + \item $p-1$ is a multiple of $q$. + \item $(p-1)/2q$ is also prime. +\end{enumerate} +The middle 3584 bits of $p$ are chosen by starting with the first 3584 bits of the constant $\mathrm{ln}(2)$ (the natural logarithm of $2$).\footnote{See \url{https://oeis.org/A068426} for the integer sequence consisting of the bits of $\mathrm{ln}(2)$.} After pre-pending and appending 256 ones, $p$ is determined by finding the smallest prime larger than this value that satisfies the above properties. + +This works out to $p = 2^{4096}-2^{3840}+2^{256} (\lfloor2^{3584} \mathrm{ln}(2)\rfloor + \delta) + (2^{256} - 1)$ where the value of $\delta$ is given by +{\small +$$\delta=287975203778583638958138611533602521491887169409704874643524560756486080635197037903.\footnote{Discovering this value $\delta$ required enumerating roughly 2.49 million values satisfying the first three of the above properties to find the first one for which both $p$ and $(p-1)/2q$ are prime.} +$$ +} +The hexadecimal representation of $p$ is as follows. +{\allowdisplaybreaks +\begin{align*} + p \ = \ \mathtt{0x} + & \mathtt{FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF}\\ + & \mathtt{B17217F7\ D1CF79AB\ C9E3B398\ 03F2F6AF\ 40F34326\ 7298B62D\ 8A0D175B\ 8BAAFA2B}\\ + & \mathtt{E7B87620\ 6DEBAC98\ 559552FB\ 4AFA1B10\ ED2EAE35\ C1382144\ 27573B29\ 1169B825}\\ + & \mathtt{3E96CA16\ 224AE8C5\ 1ACBDA11\ 317C387E\ B9EA9BC3\ B136603B\ 256FA0EC\ 7657F74B}\\ + & \mathtt{72CE87B1\ 9D6548CA\ F5DFA6BD\ 38303248\ 655FA187\ 2F20E3A2\ DA2D97C5\ 0F3FD5C6}\\ + & \mathtt{07F4CA11\ FB5BFB90\ 610D30F8\ 8FE551A2\ EE569D6D\ FC1EFA15\ 7D2E23DE\ 1400B396}\\ + & \mathtt{17460775\ DB8990E5\ C943E732\ B479CD33\ CCCC4E65\ 9393514C\ 4C1A1E0B\ D1D6095D}\\ + & \mathtt{25669B33\ 3564A337\ 6A9C7F8A\ 5E148E82\ 074DB601\ 5CFE7AA3\ 0C480A54\ 17350D2C}\\ + & \mathtt{955D5179\ B1E17B9D\ AE313CDB\ 6C606CB1\ 078F735D\ 1B2DB31B\ 5F50B518\ 5064C18B}\\ + & \mathtt{4D162DB3\ B365853D\ 7598A195\ 1AE273EE\ 5570B6C6\ 8F969834\ 96D4E6D3\ 30AF889B}\\ + & \mathtt{44A02554\ 731CDC8E\ A17293D1\ 228A4EF9\ 8D6F5177\ FBCF0755\ 268A5C1F\ 9538B982}\\ + & \mathtt{61AFFD44\ 6B1CA3CF\ 5E9222B8\ 8C66D3C5\ 422183ED\ C9942109\ 0BBB16FA\ F3D949F2}\\ + & \mathtt{36E02B20\ CEE886B9\ 05C128D5\ 3D0BD2F9\ 62136319\ 6AF50302\ 0060E499\ 08391A0C}\\ + & \mathtt{57339BA2\ BEBA7D05\ 2AC5B61C\ C4E9207C\ EF2F0CE2\ D7373958\ D7622658\ 90445744}\\ + & \mathtt{FB5F2DA4\ B7510058\ 92D35689\ 0DEFE9CA\ D9B9D4B7\ 13E06162\ A2D8FDD0\ DF2FD608}\\ + & \mathtt{FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF} +\end{align*} +\pagebreak +The hexadecimal representation of the cofactor $r = (p-1)/q$ is as follows. +\begin{align*} + r \ = \ \mathtt{0x01}\ + & \mathtt{00000000\ 00000000\ 00000000\ 00000000\ 00000000\ 00000000\ 00000000\ 000000BC}\\ + & \mathtt{B17217F7\ D1CF79AB\ C9E3B398\ 03F2F6AF\ 40F34326\ 7298B62D\ 8A0D175B\ 8BAB857A}\\ + & \mathtt{E8F42816\ 5418806C\ 62B0EA36\ 355A3A73\ E0C74198\ 5BF6A0E3\ 130179BF\ 2F0B43E3}\\ + & \mathtt{3AD86292\ 3861B8C9\ F768C416\ 9519600B\ AD06093F\ 964B27E0\ 2D868312\ 31A9160D}\\ + & \mathtt{E48F4DA5\ 3D8AB5E6\ 9E386B69\ 4BEC1AE7\ 22D47579\ 249D5424\ 767C5C33\ B9151E07}\\ + & \mathtt{C5C11D10\ 6AC446D3\ 30B47DB5\ 9D352E47\ A53157DE\ 04461900\ F6FE360D\ B897DF53}\\ + & \mathtt{16D87C94\ AE71DAD0\ BE84B647\ C4BCF818\ C23A2D4E\ BB53C702\ A5C8062D\ 19F5E9B5}\\ + & \mathtt{033A94F7\ FF732F54\ 12971286\ 9D97B8C9\ 6C412921\ A9D86797\ 70F499A0\ 41C297CF}\\ + & \mathtt{F79D4C91\ 49EB6CAF\ 67B9EA3D\ C563D965\ F3AAD137\ 7FF22DE9\ C3E62068\ DD0ED615}\\ + & \mathtt{1C37B4F7\ 4634C2BD\ 09DA912F\ D599F433\ 3A8D2CC0\ 05627DCA\ 37BAD43E\ 64A39631}\\ + & \mathtt{19C0BFE3\ 4810A21E\ E7CFC421\ D53398CB\ C7A95B3B\ F585E5A0\ 4B790E2F\ E1FE9BC2}\\ + & \mathtt{64FDA810\ 9F6454A0\ 82F5EFB2\ F37EA237\ AA29DF32\ 0D6EA860\ C41A9054\ CCD24876}\\ + & \mathtt{C6253F66\ 7BFB0139\ B5531FF3\ 01899612\ 02FD2B0D\ 55A75272\ C7FD7334\ 3F7899BC}\\ + & \mathtt{A0B36A4C\ 470A64A0\ 09244C84\ E77CEBC9\ 2417D5BB\ 13BF1816\ 7D8033EB\ 6C4DD787}\\ + & \mathtt{9FD4A7F5\ 29FD4A7F\ 529FD4A7\ F529FD4A\ 7F529FD4\ A7F529FD\ 4A7F529F\ D4A7F52A} +\end{align*} +Finally, the generator $g$ is chosen to be $g = 2^r \bmod p$ and has the following hexadecimal representation. +\begin{align*} + g \ = \ \mathtt{0x} + & \mathtt{36036FED\ 214F3B50\ DC566D3A\ 312FE413\ 1FEE1C2B\ CE6D02EA\ 39B477AC\ 05F7F885}\\ + & \mathtt{F38CFE77\ A7E45ACF\ 4029114C\ 4D7A9BFE\ 058BF2F9\ 95D2479D\ 3DDA618F\ FD910D3C}\\ + & \mathtt{4236AB2C\ FDD783A5\ 016F7465\ CF59BBF4\ 5D24A22F\ 130F2D04\ FE93B2D5\ 8BB9C1D1}\\ + & \mathtt{D27FC9A1\ 7D2AF49A\ 779F3FFB\ DCA22900\ C14202EE\ 6C996160\ 34BE35CB\ CDD3E7BB}\\ + & \mathtt{7996ADFE\ 534B63CC\ A41E21FF\ 5DC778EB\ B1B86C53\ BFBE9998\ 7D7AEA07\ 56237FB4}\\ + & \mathtt{0922139F\ 90A62F2A\ A8D9AD34\ DFF799E3\ 3C857A64\ 68D001AC\ F3B681DB\ 87DC4242}\\ + & \mathtt{755E2AC5\ A5027DB8\ 1984F033\ C4D17837\ 1F273DBB\ 4FCEA1E6\ 28C23E52\ 759BC776}\\ + & \mathtt{5728035C\ EA26B44C\ 49A65666\ 889820A4\ 5C33DD37\ EA4A1D00\ CB62305C\ D541BE1E}\\ + & \mathtt{8A92685A\ 07012B1A\ 20A746C3\ 591A2DB3\ 815000D2\ AACCFE43\ DC49E828\ C1ED7387}\\ + & \mathtt{466AFD8E\ 4BF19355\ 93B2A442\ EEC271C5\ 0AD39F73\ 3797A1EA\ 11802A25\ 57916534}\\ + & \mathtt{662A6B7E\ 9A9E449A\ 24C8CFF8\ 09E79A4D\ 806EB681\ 119330E6\ C57985E3\ 9B200B48}\\ + & \mathtt{93639FDF\ DEA49F76\ AD1ACD99\ 7EBA1365\ 7541E79E\ C57437E5\ 04EDA9DD\ 01106151}\\ + & \mathtt{6C643FB3\ 0D6D58AF\ CCD28B73\ FEDA29EC\ 12B01A5E\ B86399A5\ 93A9D5F4\ 50DE39CB}\\ + & \mathtt{92962C5E\ C6925348\ DB54D128\ FD99C14B\ 457F883E\ C20112A7\ 5A6A0581\ D3D80A3B}\\ + & \mathtt{4EF09EC8\ 6F9552FF\ DA1653F1\ 33AA2534\ 983A6F31\ B0EE4697\ 935A6B1E\ A2F75B85}\\ + & \mathtt{E7EBA151\ BA486094\ D68722B0\ 54633FEC\ 51CA3F29\ B31E77E3\ 17B178B6\ B9D8AE0F} +\end{align*} +} + +Alternative parameter sets are possible and may be allowed in future versions of \EG.\footnotemark + +\footnotetext{If alternative parameters are allowed, election verifiers must confirm that $p$, $q$, $r$, and $g$ are such that both $p$ and $q$ are prime (this may be done probabilistically using the Miller-Rabin algorithm), that $p-1 = qr$ is satisfied, that $1 1, could someone evade the Option Selection Limit by both selecting and writing-in the same candidate? Similarly if multiple write-in fields are allowed, couldn't they write-in the same choice multiple times? + +% xnote S3.1.3n Josh, Michael 2025-03-18: Out of scope for EGRI: Range proofs of the various combinations of fields (selectable options, undervoted, net undervote, overvoted, net overvote, write-in N, count written-in) may be combined under specific conditions + + +% xxxx jAXC/xAzKL29sdzBS+iFiQL/j0Xxlh+xTqh2P2kZa7sl9l7IbgGjpKMyornfqXSxnc/YGeUrE+go +% xxxx 6+BQssRtY9d9uAknjfsn0Ay4KflT2bjrU6GHCaxGJGBpAcaNa/+3O3JINbXQNJG7DOfbnPocRm6E +% xxxx D9n+5zcc+z3FIQwJxAEPeNtpfdzGElo2bf6/dMEwIUF7Lx+dAAH2AL9PXYZeTdRVfDIYU7r7ClHX +% xxxx YqUeWioHMevJL1cOWJWeGQB6E/tcjSWDlh5UIryPaPIy1zv+YPHhyDDECPWFSHxQqM/N778QFPmW +% xxxx wautsgPXiWHTh1YT+GdZSpKk+skgtdCT6jJcpsZ1wtuTbKmU/h8Y7RysSM4hDcr2XzWHcPxLJbjF +% xxxx M5w6M8J1Xj4zRhdfzaG6xzhYTyzEUiMjBvDYxGRHZV5nVErh4EthMDftnreENsu4qZZBKQ5oToDE +% xxxx /s6zmx7S1/Ll/w/j8ADwZ0MkBJuGir6krd0WF61lwWeVNT4pdyoXZXGzdbZb6MImqPuR2HUXuO5Z +% xxxx j9MY6pu5qdO8oxJO23OrKknjlldaIZWWrB7uDR1nahTQudpdnmGgM3kUVF20nan/D0oLKCTgrBFO +% xxxx 6YMtoVJPUU9oKZr0GcPsFpswvAF/+drXZBYjwES04Cnlgc5Qz+DRRlsXhcInMkU5jZwBUdOuROp3 +% xxxx 8NCTseNICbUI1tW9w9CXufPQVTcCxkbDHzznn2tOXUUxNs98bNH9+AnUAqyX1um6ilbBpA3USVu3 +% xxxx +% xxxx +% xxxx ---- S3.1.3.o Election Parameters and the Election Manifest - The data in the election manifest + +The data in the election manifest is written to a file \texttt{manifest} in a canonical representation that may be implementation specific. + +% xreqj { + "section": "S3.1.3.o", + "text": "A canonical byte representation is defined for the Election Manifest.", + "sc": "utsp", + "utsp": "eg::election_manifest::t::t1" } +% xreqj { + "section": "S3.1.3.o", + "text": "EGRI allows to write the Election Manifest canonical representation to a file.", + "sc": "ics" } + +% xxxx gKZvofj/Vi7ATDKgDudat3UuKp8kyhvPJLIbDn02WYdKhhsuO3NlTGWV2s5HNHnpKjVBSOt3HyWm +% xxxx l+zIdVvW06MSprznGQ/RXd9F2FATEMzem59aQ73RcZuGyCtu8zjQc1CiWBYLSdOB9MMIi2x8QTRS +% xxxx omi72gkPX1ax7JYqbevqvKcFNn4lw6GYIn0AOvwZKLPO5jKdY/B245eTntDjRN90/UjO/3IGOAr0 +% xxxx xTrgwhxkiUioFtAxsdX+Q39bqRr7tHEK0OMN3qkcVxevAXzOzLLC9V5dOd2CRN0NO4su0e8HBX7z +% xxxx DKjHEJR//MKC6GemdJEZKJAwfLyXslD0QUJXxei1gKhJ5V/0bhNyXFliWENOy8unCRx9agg5BZmx +% xxxx Wzb9iVuojbMZe47s8r8cIfFHEnUKH7sqXCD8a7yymaN0wU+HCOSTSz6YjTO+89UGsIlcfxxHva+y +% xxxx 477S0xO7uw5Tk06MLl4zjLFJKQqKhS4fsXR8R9ab+C/K+jns+HI/q5c4rypZMGCtwVzahqz+v5wo +% xxxx ie10O9qWDSWxqd3lovbCGoYud2X6ppu88ZfK4IX/1JBpwq0KPn296ksQ1/IcVQ1dxt1r5pDyfaQA +% xxxx 2IEgD87Hcp+bjXqOeXyT1185BqcsHSABAeh0YSkcDP9OaCRtUNFbDonz2P6/16SYTiTXgnRdoQt1 +% xxxx rjslhcRpLkjgVgNGyrFBCIUSKDeWF2efmBfIiGLMTdjYjIJxtd5l36MxCR+Um72tYGkz/geMQhOt +% xxxx +% xxxx +% xxxx ---- S3.1.4.a Election Base Hash + +\subsubsection{Election Base Hash}\label{sec:basehash} +The election manifest file \texttt{manifest} is hashed\footnote{The file that constitutes the election manifest is input to $H$ as an entire file. Again the $\mathtt{0x01}$ byte at the beginning of the second argument is a domain separation byte (in hex). In what follows, all uses of the function $H$ include such domain separation bytes.} using the \EG hash function with the parameter hash $\HH_P$ to produce the \emph{election base hash} $\HH_B$ as + +% xxxx ---- eq. 5 + +\begin{equation}\label{eq:basehash} + \HH_B = H(\HH_P; \mathtt{0x01}, \mathtt{manifest}). +\end{equation} +Incorporating the byte array $\HH_B$ into subsequent hash computations binds those hashes to the election parameters, the number of guardians, the threshold quorum value, and to a specific election by including the manifest. + +% xreqj { + "section": "S3.1.4.a", + "text": "EGRI computes H_B from H_P and the Election Manifest canonical representation as specified in EG DS v2.1.0 eq. 5.", + "sc": "utsp", + "utsp": "eg::hashes::t::t1" } + +% xxxx ytpqytzp0yCLDexlOb9mXrYfkH3GNM+ZJzpFOGpUnb4sJNA2g5I1U1sGsXKOmbII2RClUUZ3KlH4 +% xxxx zhbfcRHKdY45deI5ZksAojeyqVjS+2paaZARtoKZ291fDKXtgqA1RQZhYQKLJkp5+KjHg5mWvHr0 +% xxxx jPa+sQ0MUfWqqWCuKqNS2Kcll4wASyRDu1qr4ciPh7+3eYF520NmqnY38nCzyUhEb1zSFrAuHli2 +% xxxx j4rbEAOBK7US28oyTRILju1TwKoGOrDbYpesySX3G6XMlOL6522/23+3FL+uR45gyVdmP2ky0GSP +% xxxx bW4CEQUKagS13uSR1bocvAM0+wP+epUES2yb+GAP2phBKDpatciwbzcrNRk5H9JkdJ5SohRyM4li +% xxxx q9RTsIqnQjs6eb7x3k1yXvUTmHmrQFHCiF1qo0b4onjoH0PCSknzyOKunF3yfJmsGyEzBfAhzt09 +% xxxx KfVefhpD2IZBBhWVyEu1H3WMfmeEtu6sGViDPs7MYcS/4pN3xwH1g8Raq21KTno4S/ElQWAyBLhI +% xxxx xc7+Y8j9E5wXSHLg7VXPxykO1DSHmC4H4mNiNcF5IxCWqgJwHcN7Eo7AE/bWCPO7SS4jAEAqgo6O +% xxxx nht2UTWMA1S32helQwHc90HvZ4RuRS7oiKvtTn598LNdYeiLIzu+y2n/+OfrXOV+lGEFib6HPbKk +% xxxx PiCAMLOJrtUb//SgpF4UkcZtSsFqY6InegOTQZZYrCk7JS3Xfo6AWIGw3IWhqIrZUy4v0lsnmcye +% xxxx OeedwKjVKRJhSKMjX+8GMThWsc1u32PmPeWWO1u2NVKA82WZSDeCqp7q/rkiL2oBfh3hwvYo3r1Y +% xxxx +% xxxx +% xxxx ---- S3.1.4.b.verif1 Election Base Hash - Verification 1 (Parameter validation) + +\EGverif{\veriftitleParameterValidation}{\label{verif:parameters} +\veriftextParameterValidation} + +% xreqj { + "section": "S3.1.4.b.verif1", + "text": "TODO: Verification 1", + "sc": "" } + +% xxxx mzi4NjmgXBhlYD3FAIS9XAmbK5TspxlF3MFn7gWh3AV1Lcj3fF4dge3mCWL7yj5th6nv3uMm+U+a +% xxxx NIJm8vNPOykRx096vDrq1aekD6winOcD/BNyLL4AlIfsF5Th3oSdpMOyWYSNYPw6wRMQZFf3yB25 +% xxxx 2RcKgezEBb8oQzE0Esp2/lM7+/OaBB6/RoeKzazZzJ4rLNx0hXQbL19m1ML+xPRWNyjeJCNVg+hQ +% xxxx GNI+zAS8dsRJPguQDXSBd98QC8Hl4eHRvKQHWWZGvWrVdV0ozBwf8lqIN4+ex4ckPFleUprGexfr +% xxxx P6e2fNOCHiU0x8ZotJBcFZWi/IMG7AHWP52CEveHMD2wSE9H4bD6ns/5X6pmo8wxHEWzHsoU0j92 +% xxxx gxnOARdlDrhvtJTjgoDBhNYyFuwn/WM5FWIYVpr9ON/dvMnErvxgfzZPDLre/ytV4FoYNhiiOSwa +% xxxx F/fynbP40ztsCF6oA+czS8DT2DSWKv+Xv/Z7AQqYM80vszvKO4FzMYDlEE24O06/2aYXO8BWUVCs +% xxxx 1O47sI30W8JF7kaQoRz30Tm2WBIbRCGxI01MISWCJmOKb7RyTTF2Au4vz9hfwSC6uFOEAtlMbdei +% xxxx 7U1ljAkbZvtEvZd58tJd52X3DzpyMvgHnExIgYsQPEdql3/JvSTYF9qvom3zdEyNG2LUkFpq/Pkm +% xxxx EGD53FLUdmLZzY2f4P5VKreobzXTDU+SWMFuQ6MdZm5RarZchoSmOxBGiuLCnp6AlxbvjpH6IvDB +% xxxx +% xxxx +% xxxx ---- S3.2.a Key Generation + +\subsection{Key Generation}\label{sec:keygen} +Before an election, the number of guardians $n$ is fixed together with a quorum value $k$ that describes the number of guardians necessary to decrypt tallies and produce election verification data. The values $n$ and $k$ are integers subject to the constraint that $1\leq k \leq n$. Canvassing board members can often serve the role of election guardians, and typical values for $n$ and $k$ could be 5 and 3---indicating that 3 of 5 guardians must cooperate to produce the artifacts that enable election verification. The reason for not setting the quorum value $k$ too low is that it will also be possible for $k$ guardians to jointly decrypt individual ballots. + +% xreqj { + "section": "S3.2.a", + "text": "EGRI allows to specify the number of Guardians `n` prior to a Key Ceremony.", + "sc": "uts", + "uts": "eg::guardian_share::test::test_key_sharing" } +% xreqj { + "section": "S3.2.a", + "text": "EGRI allows to specify the quorum value `k` prior to a Key Ceremony.", + "sc": "uts", + "uts": "eg::guardian_share::test::test_key_sharing" } +% xreqj { + "section": "S3.2.a", + "text": "EGRI rejects values of `n` less than `1`.", + "sc": "utsp", + "utsp": "util::index::t::test_range,util::index::t::misc" } +% xreqj { + "section": "S3.2.a", + "text": "EGRI rejects values of `n` greater than `2^31 - 1`.", + "sc": "utsp", + "utsp": "util::index::t::test_range,util::index::t::misc" } +% xreqj { + "section": "S3.2.a", + "text": "EGRI rejects values of `k` less than `1`.", + "sc": "utsp", + "utsp": "util::index::t::test_range,util::index::t::misc" } +% xreqj { + "section": "S3.2.a", + "text": "EGRI rejects values of `k` greater than `n`.", + "sc": "ics" } + +% xxxx 2yFJ63DFu1bz3o6KLsKmboP0dV2g/+OHZi0Be4F5lsJ15Ooy8PLOkfn7xoQAdqgf/laUVq36XWjs +% xxxx 37lZVu0KAKZKg+n4Nzhvm4PQ6OUdye0cAM4XeNsTb0+8z2PnIxlJ1hT0BuR3IDBIlp3EOp4i+VUT +% xxxx aMXNml3viM5RsVOBA4dnUcPBm0q4X/wKyWmtX8dGKNu2MOyqrTs4i9xkPKbb2WVqggNWkIGcuF1F +% xxxx j21GZqD6c1wMedATQLLW6ikpl7p3MUYXvc7XpSv4rNoEokSXofCpsmFHSWrhIdslTm/7rUpI9T7Y +% xxxx 1Ufr0XhKr407YbJwSQALR4nNNyQIL+7wXmPORj/fcev4YKrjKF/UT2sIdQHwaVMtEWDLrlPhWFHu +% xxxx kr5q/fr/qbZ2bumbjNMZUfdLN0nvcdceOr+3UVAg9f6c5wlTrgE+k8vXVXBYyCwaWCgtZTIjFZyB +% xxxx IUlNuM79mngruq5L7GXbPOfliCS/jraZhsxKIIdNwd7D4EGvKeLF95dZxHCr/XUQT/svXYVhCOCL +% xxxx 4nVplBhbjbPz1hjCeJjv+Dy4JkM8miOOyT0lOPwrlm8NDLQYHhrUH4VWO15fk+h/zHyeLaThpsy1 +% xxxx OUXEm6phQ6uCeclY5BvLCR4OV1fLx/+ZAi11EAkv7d5wNzS3j/YfAmUw/skEpM1vvL9R5fUZfyuS +% xxxx 6dAqC7KtLkrDjtKp/zu24KtyP0IxjEjz8lvGu3O6xEAGvh4MREaLOY4bYN2ECUOnYAgJo4h+2bv+ +% xxxx IwyrIwwjUf5iabvLq0EhGskMVeOqJTQN7+2JkEOIbfOBGAJ1zRs+EsaB707nhmc1qeJPI2Y5S4AF +% xxxx +% xxxx +% xxxx ---- S3.2.b.n3_2 Key Generation - Note 3.2 + +\EGnote{Decryption of individual ballots does not directly compromise voter privacy since links between encrypted ballots and the voters who cast them are not retained by the system. However, voters receive confirmation codes that can be associated with individual encrypted ballots, so any group that has the ability to decrypt individual ballots can also coerce voters by demanding to see their confirmation codes. Voters who decide to disclose or publish their confirmation codes in a non-anonymous way may also have their privacy impacted by unauthorized decryptions. +} + +% xnote S3.2.b.n3_2 This section generates no unique or specific requirements + +% xxxx rUj//crK9DWV6JPZqYXVNciQY4TNgOiTj3WPus+lzd6P1H/i/2b/6kFFnOkM90xI93TIQdGueQ2S +% xxxx 4uyHxwN3yi1z5GsGua7H39eYMu8b1+36B9aA6n/rPETdASvvH/IK2WKNnHtOTqgA8d1/9FOn0pSL +% xxxx PlSyBc2exeqM0PmTwXMl1tzB2XxJ9oGRqrragn3ydEY/N9V/W3dvewjBljqbKDPvm73RCxfV7+vK +% xxxx q8pPDhhbM2/OPeBzYskKPObOtGOvdVEWO/5p+oH7RNau5If/nynuWlnb9fcnBfJxZvIOsSxFzn/e +% xxxx hPyGVqTIj0DxNib5lYb4njmEmuxKO94ALx2+iHjY69uK8O1qkSZafiiGF+Krm80oZbQUXYZIXfI7 +% xxxx SpFUK/JCp79ujRW+Xil/u1V7+sHISfijuPkH4VY0Uz6BFYD+2ajJb5NEen638LPnL3nug3mIBz7j +% xxxx ig0K351bXkpTxPyYDryVoIqrNrPoTowfRIhuQalABhgHjQRxw9jRNfxWU2iY8RCQP7D98RqwZToT +% xxxx hKT/D7ZLWe0osyvsFqT3gMlsewN1/xSImzB11lKMom8P8LQpK//Gc8/Cmf56SbGcQSzitRez6m9i +% xxxx d+jlNlYgSAh1l3l3NvJIoWuMElWCy0qcQ+vYC5Yb6wEdhE61GsH4biRDG3cUOnavzy6i9D0Ue1a7 +% xxxx 1HcCsECLZddOK7OgI2OXBwszgbjJHelciHeLRD/p7bg+922p6yu7TwOf+4jzDesOmMZ7dqrxdO9J +% xxxx +% xxxx +% xxxx ---- S3.2.c Key Generation - (after Note 3.2) + +Threshold encryption is used for encryption of ballots. This form of encryption makes it very easy to combine individual guardian public keys into a single public key for encrypting votes and ballots. The encryption used herein also offers a homomorphic property that allows individual encrypted votes to be combined to form encrypted tallies. + +The guardians of an election will each generate a public-secret key pair. The public keys will then be combined (as described in the following section) into a single vote encryption public key which is used to encrypt all selections made by voters in the election. + +At the conclusion of the election, each guardian will compute a verifiable partial decryption of each tally. These partial decryptions will then be combined to form full verifiable decryptions of the election tallies. + +To accommodate the possibility that one or more of the guardians will not be available at the conclusion of the election to form their partial decryptions, the guardians will cryptographically share\footnote{Shamir A. (1979) \emph{How to Share a Secret}. Communications of the ACM, vol 22.} their secret keys amongst each other during key generation in a manner to be detailed in the next section. Each guardian will then compute a share of the secret decryption key, which it uses to form the partial decryptions. +A pre-determined quorum value $k$ out of the $n$ guardians will be necessary to produce a full decryption. It is worth noting that fewer than $k$ partial decryptions reveal nothing about the tally. + +If the same set of $n$ guardians supports multiple elections using the same threshold value $k$, the generated keys and key shares may be reused across a small number of elections, although it is preferred to generate new keys for each election. + +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S2.b) Guardians can generate individual public-secret key pairs", + "sc": "utep", + "utep": "eg::guardian_public_key::test::test_key_generation" } +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S2.b) Guardians can exchange shares of secret keys", + "sc": "ute", + "ute": "eg::guardian_share::test::test_key_sharing" } +% xtodo S3.2.c (Ref: S3.a.a.b) "EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)" +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S3.a.a.b) \"EGRI requires that a complete new set of keys must be generated if even a single guardian is replaced\"", + "sc": "na" } +% xreqj { + "section": "S3.2.c", + "text": "EGRI rejects any election data unless 1 <= k <= n < 2^31", + "sc": "ics" } +% xreqj { + "section": "S3.2.c", + "text": "Each Guardian is associated with a human readable identifier referred to as the 'Guardian Name'.", + "sc": "utep", + "utep": "eg::guardian::t::t1,eg::guardian::t::t2" } +% xreqj { + "section": "S3.2.c", + "text": "EGRI accepts Guardian labels composed of printable characters and (internal, non-contiguous) 0x20 space characters.", + "sc": "utsp", + "utsp": "eg::guardian::t::t1" } +% xreqj { + "section": "S3.2.c", + "text": "EGRI rejects Guardian labels that contain line break characters.", + "sc": "utsp", + "utsp": "eg::guardian::t::t2" } +% xreqj { + "section": "S3.2.c", + "text": "EGRI rejects Guardian labels that have leading or trailing whitespace.", + "sc": "utsp", + "utsp": "eg::guardian::t::t2" } +% xreqj { + "section": "S3.2.c", + "text": "EGRI rejects Guardian labels that contain contiguous sequences of whitespace other than a single 0x20 space.", + "sc": "utsp", + "utsp": "eg::guardian::t::t2" } +% xreqj { + "section": "S3.2.c", + "text": "EGRI rejects Guardian labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs).", + "sc": "utsp", + "utsp": "eg::guardian::t::t2" } +% xreqj { + "section": "S3.2.c", + "text": "EGRI rejects Guardian labels having no printable characters", + "sc": "utsp", + "utsp": "eg::guardian::t::t2" } +% xreqj { + "section": "S3.2.c", + "text": "EGRI rejects Guardian labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)", + "sc": "ics", + "status_note": "difficult to test because the Rust std lib rejects malformed UTF-8" +} +% xreqj { + "section": "S3.2.c", + "text": "Ballots are encrypted using threshold encryption", + "sc": "ute", + "ute": "eg::tally_ballots::t::test_tally_ballots" } +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S2.b) \"At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.\"", + "sc": "ute", + "ute": "eg::tally_ballots::t::test_tally_ballots" } +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S2.b) \"At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.\"", + "sc": "ute", + "ute": "eg::tally_ballots::t::test_tally_ballots" } +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S2.b) Every guardian can Shamir-share their secret keys during key generation", + "sc": "uts", + "uts": "eg::guardian_share::test::test_key_sharing" } +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S2.c.c) Voter Selections can be encrypted to the Joint (Vote|Ballot Data) Encryption Public Keys.", + "sc": "ute", + "ute": "eg::tally_ballots::t::test_tally_ballots" } +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S3.a.f.c) \"EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.\"", + "sc": "ute", + "ute": "eg::tally_ballots::t::test_tally_ballots" } +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S3.a.f.c) \"EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.\"", + "sc": "ute", + "ute": "eg::tally_ballots::t::test_tally_ballots" } +% xreqj { + "section": "S3.2.c", + "text": "(Ref: S3.a.f.d) \"Fewer than `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys can not produce a full decryption of any tally\"", + "sc": "na" } + +% xxxx wJb9nQ31X6mjDiDNu8XL8eKws+z9tgxsi+tbULPi2/yXyJAl09Iokvhz3caWNqea/e8Qa9z4unxN +% xxxx y7ehXzojjluCbyYOBg9zkNVMWU2hqoImfiECEu+Qz9RqcPM7fofdwndZT3DsAuGYYcVkekAG8T+N +% xxxx rM6z6QnvdomdbYSELUmeVmkPOm24DJdtF5fUqD9qxRDxDq0G3NRpWGdgmhout1ZNMyuzxai76mGV +% xxxx OwHnrRTfdXn+AZyp8LqXCYJriPk4LHV82U2PzH7nCjjWKdonte3dNkNrFFuFWRwcA1Vb7yWMH9ru +% xxxx nR+6KLsAG55ZAMrKupLa3ZBEjE6lW0iFkAFoV3eMesA4Qgu5+VBKWR0GmMblU8wV3d5jfthf13W8 +% xxxx cXO5fezezXY3SXnjEBxyxCnUZkCBpogYZboWJM3RTkveS22XldxF/ZTn5DPwlAv2zV6OtSO6+rCa +% xxxx c8QNK3FTpyqa3mzZCYyMivBeqrkz4SE6Lapq8EuurHDUprngPUrjyvZcnHBzMErxR5woE4cLLdiZ +% xxxx r50vSl7egrAy4hVU8iKTRpb9308RcgVCeBSV1NxnIhbGWUmxBB+Mr8QHMWV9s0/5SE1yj0/UGt33 +% xxxx jK30iptl2okcqt1dSBXJxNti3RTnDnYxKRwlXgZ5rCiutZTJuIfphHX8MQtvQecAjiATNpe3vTIo +% xxxx yVHnE6yK/9wpwhFWvbjSF4vnhlAWaGJs4Unp8xX8EptX6V/l8s8DIlhVhtlbNXxlGJ/o2TNBMHH2 +% xxxx +% xxxx +% xxxx ---- S3.2.1 Overview of Key Generation + +\subsubsection{Overview of Key Generation} +The $n$ guardians of an election are denoted by $G_1,G_2,\dots,G_n$. Each guardian $G_i$ generates an independent public-secret key pair by generating a random integer secret $s_i\in\Z_q$ and forming the public key $K_i = g^{s_i} \bmod p$. Each of these public keys will be published in the election record together with a non-interactive zero-knowledge Schnorr proof of knowledge of the associated secret key. + +The joint vote encryption public key will be +\begin{equation*} + K = \left(\prod_{i=1}^n K_i\right) \bmod p. +\end{equation*} +To enable robustness and allow for the possibility of missing guardians at the conclusion of an election, the \EG key generation includes a sharing of secret keys between guardians to enable decryption by any $k$ guardians. This sharing is verifiable, so that receiving guardians can confirm that the shares they receive are correct; and the process allows for decryption without explicitly reconstructing secret keys of missing guardians. + +Each guardian $G_i$ generates $k-1$ random polynomial coefficients $a_{i,j}$ such that $00$ is then computed as above via +$\HH_{j}=H(\HH_I; \mathtt{0x29}, \chi_1, \chi_2, \ldots, \chi_{m_B}, \B_{C,j})$ and the chaining field byte array + +% xxxx ---- eq. 76 pg. 43 + +\begin{equation} + \B_{C,j} = \mathtt{0x00000001}\parallel\HH_{j-1} +\end{equation} +contains the confirmation code $\HH_{j-1}$ of the previous ballot (or the initialization code $\HH_0$ when computing $\HH_1$). +The chain is closed at the end of an election by forming and publishing + +% xxxx ---- eq. 77 pg. 43 + +\begin{equation}\label{eq:hashchainclose} +\overline{\HH} = H(\HH_E; \mathtt{0x29}, \overline\B_{C}), +\end{equation} +using + +% xxxx ---- eq. 78 pg. 43 + +\begin{equation}\label{eq:hashchaincloseBC} + \overline\B_{C} = \mathtt{0x00000001}\parallel H(\HH_E; \mathtt{0x2B}, \HH_\ell, \B_{C,0}), +\end{equation} +where $\HH_\ell$ is the final confirmation code in the chain. + +% xreqj { + "section": "S3.4.4.c", + "text": "In the case of 'Simple chaining', EGRI computes the Chaining Fields and Chaining Value as specified in EG DS v2.1.0 eq. 74 - 78.", + "sc": "" } + + +% xxxx GyikHTUfnpILjM6bLUDlRJo9L4QCcdlQsXZTz5m8b0hi2mg4wROvSotCKSvMUYlgSGqDC5cNxvJC +% xxxx lLWEWz4Mh5PmTnws0tz5t7+WdQhi16AKi3kTC7nRkfeNWHgOpMOSe8R6L2V8GNw9ge0e0eLv/ZhH +% xxxx bAVgqfOE48BM/8A8J5kLdXh0aLUxGfem2cMtWH3R+10socJ/5wSWcGimMbBGrqtDbwXhf1P1MBEl +% xxxx 0gcnDPAy3V4U3hGyYs/GP0Gt8PIf639TSIaCYbbWwnCq0iZOJvoMY5mGjQM+O73oPnO/gGczWT34 +% xxxx utAmMSLJGPu79h98TIHHajqBYaSDEspRN/VVLcIvHl/+q6yVRFyOzCqsKSxaLeLJBlhjCMZtmK06 +% xxxx fbxH0i3bCrGCArxE18NNaElupTeOfftt3knxxrsiAI0TlwjX+nAdZ73l+VF1qC9x/vzYKfpcXJ/N +% xxxx 5HJm6u4Q9GKdASRklvD48Uf3yp3gbzo5WnEDkwNf6yZVus8Z6VsOKjlbdPArhUmXyxhhKavn3Brt +% xxxx rqA+SZOq2N94m+VgsuEno9YbKzzXdzafHeRiP0Dt2eMjBgHUd5rOBRRAWiX/1fBRzndeUQOuDzLs +% xxxx qApP9YRynyALv/6276uucgOu/Afgw68/K9lijPkgMdfkEnKEqgn1j9piRYqpaP9vml8/s6u0a/XN +% xxxx NzbmPYweSYTtlDgd9y0SuP9wJvDcu5CUBF9IlfObvPAFNaQVrL3WYoHZOvkNLiuyAJm3JUE3UFWI +% xxxx +% xxxx +% xxxx ---- S3.4.4.d Ballot Chaining - Ballot casting or challenging. + +\paragraph{Ballot casting or challenging.} +Once in possession of a confirmation code (\emph{and never before}), a voter is afforded an option to either cast the associated ballot or challenge it and restart the ballot preparation process. The precise mechanism for voters to make this choice may vary depending upon the instantiation, but this choice would ordinarily be made immediately after a voter is presented with the confirmation code, and the status of the ballot would be undetermined until the decision is made. It is possible, for instance, for a voter to make the decision directly on the voting device, or a voter may instead be afforded an option to deposit the ballot in a receptacle or to take it to a poll worker to be challenged. For vote-by-mail scenarios, a voter can be sent (hashes of) two complete sets of encryptions for each selectable option and can effect a ballot challenge implicitly by choosing which encryptions to return (see Section~\ref{sec:pre-encrypted}). + +% xreqj { + "section": "S3.4.4.d", + "text": "EGRI enables a method of recording the state of a Ballot as one of `VoterSelectionsEncrypted`, `Cast`, `Spoiled`, `Challenged`, or `ChallengedDecrypted`.", + "sc": "" } +% xreqj { + "section": "S3.4.4.d", + "text": "EGRI refuses to decrypt a ballot in the `Cast` state.", + "sc": "" } +% xreqj { + "section": "S3.4.4.d", + "text": "EGRI refuses to decrypt a ballot in the `Spoiled` state.", + "sc": "" } +% xreqj { + "section": "S3.4.4.d", + "text": "EGRI includes in any tally operation only Ballots in the `Cast` state.", + "sc": "" } +% xreqj { + "section": "S3.4.4.d", + "text": "EGRI enables decrypting a ballot in the `Challenged` state.", + "sc": "" } +% xreqj { + "section": "S3.4.4.d", + "text": "EGRI refuses to change the state of a Ballot from the `Cast` state.", + "sc": "" } +% xreqj { + "section": "S3.4.4.d", + "text": "EGRI refuses to change the state of a Ballot from the `Spoiled` state.", + "sc": "" } +% xreqj { + "section": "S3.4.4.d", + "text": "EGRI refuses to change the state of a Ballot from the `ChallengedDecrypted` state.", + "sc": "" } + +% xxxx Jm880Y9GCgVCCKy8zyfBinou19IDeDe2JbJ1yZNVtU21i1w4EAbIkz0aWXeD9xYd0DddT7XR0QPl +% xxxx rE079bFuh02Yyb1CZM1dNwOS3zW19eEjBcCJotk7RFaOSN4gO7MiCNrsJTmqQIOZdrRRnHTL2/+a +% xxxx BfbAui16ovDsgFTxymdtsFd5e72XZ/t3/55zlDry+K5NlHFfnR2yIQGUBZRC2cdsdDcA1WsHEiQu +% xxxx Jt+R4rIi3LHIiM52u89JMjcfw5PoQ+k2KMgmjcnvZMS0AEhVwgJreqBj7O8BtvpL7PPP3v9mhq5G +% xxxx 1JxaRGPIRozU/9eHbJ7OfOqasU32OfxbfU3umcWDCHFJbNr3XJ8ZGmeX0hbVgZ5WgMuJuHSn8RVK +% xxxx kcmJUFeR7UY5Eg4ft3nXuydw0GpRSbfbC5hPWAagZA8+YblLy9WDjYOCN65UuS+8UVc7m4MS7d9T +% xxxx 5piVqwgM+YGvAhTuWWtkK7zjprR2sz1M3O5KCj8g6RHWvgmqSICVJQ+0mSjhpvlqXGKjqjQfCAPE +% xxxx UKB62F0nWAJzkyn1r7zJqNa1Y+f4fhuAcgOnlPLKaA0aBq4XtikY7A3Tg2iNvSsYkqQQxBvGoGYC +% xxxx Jqn9vfxAozu6sPeXMhsXgn5Yz4xaIswiMnXlusQGqNH9FFAUq554NDZu4u3VaTcVRhc3FtBTdp7o +% xxxx ZxY3TgxQY5gMMw3XrHfwfYvrRnNjPZEca9xXZgolqWQaeMsSkCaf95rkCWp4fWbYkVT3fpySm5Ms +% xxxx +% xxxx +% xxxx ---- S3.4.4.e.verif8 Ballot Chaining - Verification 8 (Validation of confirmation codes) + +\EGverif{\veriftitleValidationOfTrackingCodes}{\label{verif:trackingcodes} +\veriftextValidationOfTrackingCodes} + +% xreqj { + "section": "S3.4.4.e.verif8", + "text": "TODO: Verification 8", + "sc": "" } + +% xxxx Yz5a/Hs/OVr3/+2aI/agIQnSXekFZ0zJmELE0vpFNv8HeYngBzVR0OrHi9cMIbWTD6PS8HD1D0dT +% xxxx VYyjsOfDqnEjoC9TjNS8UmF3/USuljQoOAffmdz5/8gPeboGkBR7Y+e7J2u9RfjXrQ7VovS2beB5 +% xxxx 9J/iUs9cqybk4tZ+jMnzMalaWRTwViqP4dxkEzHGWIJDLyF9dx+mvwZlgma3x9XjN0I2dDDVTIR7 +% xxxx FCcTVZHMg71DR5aj9I51exvOR8kRTNrOhidewYGbH6CoPOn3vDSowLSbg7j4Nvd8DjvFtKEAJC32 +% xxxx mp7rrfAoThAZb0LxyQU5It9jEXNxKyx6lt9WJL44HkC53KEHOsXqb8FO6rnJG79xQQPGdnW9jmoC +% xxxx wkovD2FScg1zXaoYCq6E9e2kdAKF1t77EYQiRVR6aCFnNx5/7N0tXDCMpZ6DZ3EtF/mWWpnUaNv1 +% xxxx Al7VgFeaeXMkwLv3G+cxRnTQ3s+28fmadXUUH3hJJpVk062nLKI7HSZtNhxbi4hKVD42S6vVIWiP +% xxxx gHnkn1lMI1+Qw/6p9KWt3QeTD3KwsDYxQdJJNV0vwScHF6pswSZGEuGKYRQw1yadF3P/ZcVX3w3T +% xxxx ApLSSq4Zjse8fgpnFNt9sQIcULZbbTM6bIJwIcC4sECobT8eOESFE5Mk7j0y47yxXWcDGtkNHISc +% xxxx l8HTjWlLhjfZAUl8I3J1XIJSWi7cAy/vdKdrnll4jd+Al+5I17SN+04QoWVBKNz8vK3KRKMEpAIM +% xxxx +% xxxx +% xxxx ---- S3.5.a Ballot Aggregation + +\subsection{Ballot Aggregation}\label{sec:ballotaggregation} +At the conclusion of voting, all of the ballot encryptions are published in the election record together with the proofs that the ballots are well-formed. Additionally, all of the encryptions of each option are homomorphically combined to form an encryption of the sum of the values that were individually encrypted. The encryptions $(\alpha_i, \beta_i)$ of each individual option are combined by forming the product + +% xxxx ---- eq. 79 + +\begin{equation} + (A,B)=\left(\Big(\prod_i\alpha_i\Big) \bmod p,\Big(\prod_i\beta_i\Big) \bmod p\right). +\end{equation} +This aggregate encryption $(A,B)$, which represents an encryption of the tally of that option, is published in the election record for each option. + +% xreqj { + "section": "S3.5.a", + "text": "EGRI enables publishing all Ballots (with proofs) in the Election Record at the conclusion of voting.", + "sc": "" } +% xreqj { + "section": "S3.5.a", + "text": "EGRI enables computing an Aggregate Ballot as specified in EG DS v2.1.0 eq. 79.", + "sc": "" } + +% xxxx WnyDmeXW6vuvPlOoWgpcvUxgjskd2d55vFgdqeZzcsDqN2gMEONOhcRWn+D4wwah4Ew+qZDPv2Vz +% xxxx O645D0KLUK3x5nUKUIS6L+OG032TIe1PciHZqbiEHzuJJ493+2of9qRsvureGeTth+8D44fKf93G +% xxxx 5rD4ckLQVBFm5fEL+flG/lSpZrKBlykeL8bzR0taTkL3XlEVXmARhvpiN+qSvQSdHgYILVxPLrff +% xxxx sGrDI7TDsNMXHu5ooOEYP1VYtMu/sc8kfSSHUKC1cLx0/5H++gLf0HO9ayYt5VXJerdI87CepJlc +% xxxx wTTGUh84XfXyd5lj1j8Jk7cIQIM4pzWd/bmCRM1bALn+npuI8IMIXzvPU3uoa93lH3SaxRLY0jXd +% xxxx +HgYh8KzLW7sScJzcMCPH3T0tW4R5TBjibk8oHZC4HOL98YwEXdtcCYZNTdOqbOtjrnBfh8IKR8O +% xxxx lZX+Ipvh2t13NoHhtJBvaVLycosViiIw0Q+FwLyJEppf3Op7HngDrncE+ydMIwqP3AwPDqJDb2Ho +% xxxx C0vecxNBuu6/3LCSFSf/PINIpaZ/ORUT3GyeOP7vrhBYaZwd+IDMLIx+Adu8DZ68+VV+pn9r9EY8 +% xxxx RjC8fk5KYAjXbHXbota0k7NXIJ+GnjLeGBe7xihSZ7xqSjMqAnBV91KqoKVlsvqYMKEpxhO91iVR +% xxxx PLP7y7BYMJ6IpOQEKE9FpOIlkbvWxglMg2Krt3kJ39gcm0sQHhycS5PHnQgRfrRG/idNkmKO95QP +% xxxx +% xxxx +% xxxx ---- S3.5.b.verif9 Ballot Aggregation - Verification 9 (Correctness of ballot aggregation) + +\EGverif{\veriftitleCorrectnessOfBallotAggregation}{\label{verif:aggregation} +\veriftextCorrectnessOfBallotAggregation} + +% xtodo S3.5.b.verif9 TODO: Verification 9 +% xreqj { + "section": "S3.5.b.verif9", + "text": "Every Ballot that fails to specify a Ballot Weight that is an integer inclusively between 1 and the maximum specified in the Election Manifest is considered invalid.", + "sc": "" } + +% xxxx KwUnxD6w4YFNxKdUL6uT33yDp0hr1oaoejzzSg4oHrYtnR89k4jFOoZBf5E1IBbOsX3is4vT4Fzk +% xxxx x9XbasNEvRIBI5lKshWYl3wM4RIYRbBKV+2dTGkV8Tm4Ci675+8FtuiMu6o39NDl2l24srTmrDg8 +% xxxx zpFLaoH4k73QVc4caYGRDWCBny7CO3Y47ZXlJ43kSVtf0PlaZBi+Axhq7wDT5tbKPHnvqnYUj7ur +% xxxx sVRrBHDexGvKqT0XaKCBQb/qWYwo0/Mb6Y8ggfdnxrOvjRUdLMBrlK9PT6aSTMY2y7jmI+gJEmzt +% xxxx vD5AzyHroONB5M24fkNfwPthyW8wl/1OuMMgDqKE978RNjQelu77XwwvjBBnbYio1UQ4fQ+x+tYS +% xxxx gl3s9WW0lBAL6MI1c+fSGE6T3pOzAu+H3d7ZyRESrCNmoJ7xOvYvgAFV3sx5xwOmsqLdYSHNyf71 +% xxxx Hxak5gotmWdVmc07Fhf/9ZwaFmJvwL0wlT3GfXeecPBoGgoJL9kakTmmemYcBQ8oEasCt3YpZfiF +% xxxx kJviB5p5cTFarKg0WSjpp+097gTZQ4MyOaXa0acN+ac1ptqXi2nxTEAQOP8I0OVjJC05uDtCLf6a +% xxxx 13JvERLizw9+W5bCK1eSo+bR6tLm/xWL95QQwDO0T1Yf0GafoGzb1lci0tZ6VH5Mq70/8em1d6Dg +% xxxx 84ryYdkOVsc3wlOtjRFZBJW+RvxeLPAwQCtd7TaMy9SbdzZKIAI3PqccmX2MfX/JGo/sJGdTX6Fs +% xxxx +% xxxx +% xxxx ---- S3.5.c Ballot Aggregation - Weighted ballots + +In some applications, such as shareholder or stakeholder voting, different votes may each have a different \emph{weight} or \emph{scaling factor} $W$ associated with each voter. \EG can accommodate weighted votes if each encrypted ballot in the election record is explicitly associated with an identified voter and weight. Weights must be small positive integers to allow correct decryption of tallies. The default value of a weight should be $1$. The \emph{weighted} aggregation is then computed as + +% xxxx ---- eq. 80 + +\begin{equation} + (A,B)=\left(\Big(\prod_i\alpha_i^{W_i}\Big) \bmod p,\Big(\prod_i\beta_i^{W_i}\Big) \bmod p\right), +\end{equation} +where $W_i$ is the weight associated with ballot $B_i$. Setting all weights to the default value of $1$ results in the un-weighted case. + +If weights are used, the equations in Verification~\ref{verif:aggregation} need to be adjusted correspondingly to include weights as above. + +% xreqj { + "section": "S3.5.c", + "text": "The Election Manifest specifies a maximum Ballot Weight as a small positive integer.", + "sc": "" } +% xreqj { + "section": "S3.5.c", + "text": "The condition \"If weights are used\" means the Election Manifest specifies a maximum Ballot Weight greater than `1`.", + "sc": "" } +% xreqj { + "section": "S3.5.c", + "text": "Every EGRI API receiving voter selections for encryption receives a value for the corresponding Ballot Weight and rejects the voter selections if the supplied Ballot Weight is not an integer inclusively between 1 and the maximum specified in the Election Manifest.", + "sc": "" } +% xreqj { + "section": "S3.5.c", + "text": "Every Ballot records its associated Ballot Weight.", + "sc": "" } +% xreqj { + "section": "S3.5.c", + "text": "Despite the language \"explicitly associated with an identified voter\", EGRI does not represent the concept of \"voter\". If an external system is used to track an association between Ballots and actual voters, the impact on voter privacy and coersion resistance must be considered extremely carefully.", + "sc": "" } +% xreqj { + "section": "S3.5.c", + "text": "EGRI enables computing an Aggregate Ballot from Weighted Ballots as specified in EG DS v2.1.0 eq. 80.", + "sc": "" } + +% xreqj { + "section": "S3.5.c", + "text": "When computing an Aggregate Ballot, the aggregation function must, using access to the full and complete Election Record information to that point in time, verify that every Ballot to be aggregated has never been decrypted, is not in a 'Challeged' or 'Spoiled' state, etc.", + "sc": "" } +% xreqj { + "section": "S3.5.c", + "text": "If such a (decrypted, challenged, spoiled, etc) Ballot were somehow to be supplied to the aggregation function, it MUST be excluded from the tally, a diagnostic message emitted, and the offending Ballot identifier(s) returned separately.", + "sc": "" } + +% xxxx HsyM6KszK8nMhkLhnxPZWKIeb79fDe2M6aBtIUYLfjdkgenAFDB+TLEdQEBBVwJo44bWVs+1Spqa +% xxxx PKO1BCoDI0Dq6uKl3TTlcA0GrmTtraQw5Zm21KSIPlZrPNY4r9KjGzCeDTR7K+5MK+55fws0VtuV +% xxxx n3l7/57StGC+eL0TClImZlTdn1F5rNUfTSJuP9KQinxgB7i/POABTDNL+EOVMHZxJqyFjHJskyuu +% xxxx /1TGKqOMnt/KyLFEg6RRsWCzOGB8vWGzMTFbiIbqM6/pTQ93kESRvW64eYe7nHc7DW54DnC8LQjR +% xxxx ya3IoBUcSxEMvjpFORCNQdydRmEHmLoXebkKM2jYgud5RxlfmJWMLDCQATLUxbnVW2h7TpJigIw7 +% xxxx VbX6dNyclg+liRmZON8Us+ot8AwDyXvsc2HAoPWQfmXtmf6m1A566a5Wib0iYBJTxaYsJyUk5DZX +% xxxx ynRCiU8WdYRODnuMB/sNqG1NUWyCKmmSHhvv5N2PmOyWGcne0QnI6Z0geZUPqPclRuHOScVYOz5P +% xxxx KeM5TMd6zusQVqP+Z/gBrOPrhsGabitGx1kWgMkF41MiJlMT+BVFd41CLUN8YsVDoLgme1WFj4Yp +% xxxx UXXhhf83SUvC/Vz091selUkZHkd3S1N9Tq+0Fwrm6ST5WcU0O30U95T0TwzRxVLU1cnkIw6lRvBb +% xxxx /rgfPcv0O5ZroMce8i9wRJ/Zi3i0S/UuZHf/7zphC5W4CdjrP3YD8HRH02pDU7XZKeqaRn53JpG+ +% xxxx +% xxxx +% xxxx ---- S3.6 Verifiable Decryption + +\subsection{Verifiable Decryption}\label{sec:verifiable_decrypt} + +% xxxx 31a5h7tkbzqFTWhcW3yba7+0sNn7lI9Cwvy+tkM/xnWAcGPn8iYjvc0PcGwx18zg0gzPt9TPIwPa +% xxxx UxpAO/CeEbGKZevOGzj7v5x/7vPM65e/Qi6MnKaatPkDxveYbbT+5V+xcBHb+CRW0kH50ozE8aNx +% xxxx JE7hmLXsVcumWUbdUeH4eO8UpLOU8Mf3Y9HARGJ5fIc7Ebesk9NiOxsv6CrABanEmB6MX9V1Pro7 +% xxxx SQrKlT6lXri9MYG9mhLz+82+CGOI+mXmkiO6Mnh9/e67W04Yn7eM/33b2N1Mcx/tfBmP1v5DDM/H +% xxxx av4Aiy9jmupOG2BvwyvQv84rmlJ/0gsqWOZCBbjDL68dNI9zQx8Viax3LIz08Mw2C8bnGxYvC9KX +% xxxx C1JbXjzVmZzUaUgdjIupnL5kgB+Wa1PS1nXNtKt6bGVvzj3jGCFAJXVVZiomGa55dhZHDjLjtJXR +% xxxx aOj24P+rGRsNJZj97FdXadEq709iqgRHHmAbannYZtsq/Cq7V54x8mPgFtxu/hD+JCp/IH9ad7Dj +% xxxx f7CXVsvvtNEf+AoRj2B+qQOsPF6kYHfgF06rvgQjib3Drxe5IpHCZtcRwFWyCAqMeXlIF/cvrNq2 +% xxxx yvNglEm0cnj5yOpynhIvpr2TFCzrMwp37eJxhbIw+Chx2b3NwI9rP/a97wglalEJziI9NBP6CIBg +% xxxx ENwjOFdVLurExLyvoEXP+t0Y+v59kodGC6piRKelGVhfY+kYR0c+aspu+54Yw3KueFF72Z/9lGAf +% xxxx +% xxxx +% xxxx ---- S3.6.1 Preliminary Verification Steps + +\subsubsection{Preliminary Verification Steps} +\label{sec:prel-verif-steps} + +When the election administrator has populated the election record with +the sets of aggregated ballots and ballots marked to be challenged, +and prior to starting any decryption operation, each guardian must +verify that all the ciphertexts marked for decryption are correct. +This is necessary to guarantee that the privacy of the votes is +preserved and that the decryption data that is produced contributes to +the verifiability of the election. + +In effect, Verification steps~\ref{verif:parameters}, +\ref{verif:guardiansPK}, and \ref{verif:electionPK} have been +performed by the guardians as part of the validation of the key +generation steps. +% +Guardians must now ensure that Verification +steps~\ref{verif:extendedBaseHash}, +\ref{verif:uniqueEncryptionIdentifiers}, \ref{verif:selection}, +\ref{verif:selectionlimit}, \ref{verif:trackingcodes}, and +\ref{verif:aggregation} are successful, which confirms the set of +ciphertexts that need to be decrypted and their validity.\footnote{Guardians can perform these verification steps themselves or rely on parties they trust to perform these verification steps in their stead.} + +% xnote S3.6.1 Josh 2025-03-19: The text "the election administrator populat[ing] the election record with the sets of aggregated ballots and ballots marked to be challenged" does not introduce a new requirement, this just refers to the process by which (Cast or Challenged) Ballots are added to the Election Record. +% xnote S3.6.1 Josh 2025-03-19: Though EGRS refers to "populat[ing] the election record with the sets of aggregated ballots", this simply refers to the set of Ballots in the `Cast` state, i.e., participating in a tally. There is a set of Ballots in the `Challenged` state ("marked to be challenged"), but they are not 'aggregated' and do not participate in any Tally (i.e., eq. 80 pg. 45). So really there is only ever a single "set of aggregated ballots" relevant to a specific Tally. + +% xreqj { + "section": "S3.6.1", + "text": "EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Vote Encryption Secret Key. Josh 2025-03-19: This will only ever include the set of `Challenged` Ballots and the aggregate encrypted tallies.", + "sc": "" } +% xtodo S3.6.1 TODO: Presumably this ("enables a Guardian to review the set of Ciphertexts marked for decryption") requirement applies to any use of the Guardian Ballot Data Encryption Secret Key as well? -> xreq S3.6.1 EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Ballot Data Encryption Secret Key. +% xtodo S3.6.1 Does EGRI need to determine whether write-in fields may actually be relevant to an outcome, or just always decrypt all write-in fields with every Tally? +% xtodo S3.6.1 Presumably, many Ballots will not have any write-in fields actually written-in. Does each Ballot record whether it has a write-in field written-in in a way that avoids the need to decrypt the ballot nonce during the Tally if write-in values are needed? +% xnote S3.6.1 Josh 2025-03-19: In theory, there could be a legitimate need to decrypt a provisionally-cast Ballot or other (say by court order), but such functionality is outside the scope of the RI. +% xreqj { + "section": "S3.6.1", + "text": "EGRI enables to communicate to the Guardians the set of Ciphertexts marked for decryption (i.e., challenged Ballots and aggregate encrypted tallies).", + "sc": "" } +% xreqj { + "section": "S3.6.1", + "text": "Josh 2025-03-19: Multiple Tallies may be conducted for a single election.", + "sc": "" } +% xreqj { + "section": "S3.6.1", + "text": "Whenever multiple Tallies are conducted for a single election, subsequent tallies should extend the current Election Record. This does not introduce a requirement for EGRI to implement any form of rollback protection.", + "sc": "" } +% xreqj { + "section": "S3.6.1", + "text": "EGRS enables a Guardian to verify or refute that all Ciphertexts are correct prior to decryption. Ref: S6.2.3.verif4, S6.2.4.verif5, S6.2.4.verif6, S6.2.4.verif7, S6.2.4.verif8, S6.2.5.verif9", + "sc": "" } +% xnote S3.6.1 Josh, Michael: The need for the Guardian's determination that all Ciphertexts are correct prior to decryption to be recorded in the Election Record was removed in EGRS v2.1.0. +% xreqj { + "section": "S3.6.1", + "text": "EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.", + "sc": "" } +% xreqj { + "section": "S3.6.1", + "text": "EGRS does not assist a Guardian to participate in the decryption of any Ciphertexts which does not both 1. belong to a set that has been marked for decryption and 2. been verified correct by that Guardian.", + "sc": "" } + +% xxxx rM41NZFzdEFXAFnPslpErt082YFPJSzjcaE771vygwXXuHJphZiPxbgFVkGzlhGrBIHkSUPx/QrQ +% xxxx IAC+ohBhTJKHUeLEDLdIm3gtuN0Aov/zhbVrQILPPkQToMMySgDEojCN0Cqjxv7duPM2Yl+Ths+F +% xxxx 2giWSppstkY3eGnnAraHo7SbXYgPpFW/tWKtBnBU9mpSg26LGXxD6ZbgbvUY9ov/wZREFeJJJvft +% xxxx /5S2RP+6hmiq4kR7MHHfGX2tiaGe7JUJgMn/eanxUcwJIjlZk3QI00qdR0K8A8xBsAhMCkqanJbb +% xxxx Sxndroah1jN35gkzJtXNtIrC8hza/rUHBkeXtRzZ2KnZtXEzJCGMsIyILDfKueArq8jE7o7sEwlH +% xxxx GXI3i5p5wYhgw/q/1G2j5MN105JnEUcsqf54cIkmqHX92PpnJHCVeee0k6tTAXM+dyw/se6Vf8iX +% xxxx w1JqwbGCnqm4g0ZSUzeSqs7ttfr/RL/5vpnfGo56FsKjF1cTtc6lydGbj+mulOTr1sNJF/jFo92Q +% xxxx silx5JGRuwfdPhVBV01hj/F17/EkrwBv5/YyeIeZ+NgeR8qLVjaT6fpmAfSu4CAHx8kCtrhbxfVG +% xxxx mwezejKy9/PV1iP8lA9wld7UVSBR5vMOiVaSeUFV+AvvGhN2NI/X7aIsV0RD4RM8YnTQ6kYzaNex +% xxxx TJCVyyaiTACOAhLijHg3PRM2qWbfKCiVVYGpiWCK7u1LB6wL8toHzyDABV09SNhDO7RSWngraVAp +% xxxx +% xxxx +% xxxx ---- S3.6.2 Verifiable Decryption Strategy + +\subsubsection{Verifiable Decryption Strategy} +\label{sec:verif-decrypt-strat} +To decrypt an aggregate encryption $(A,B)$, guardians work together in a protocol, where each guardian produces a partial decryption and contributes to an accumulated Chaum-Pedersen proof of correct decryption. + +As long as at least $k$ guardians are present for decryption, the partial decryptions produced by the guardians are used to compute the value + +% xxxx ---- eq. 81 + +\begin{equation} +M = A^s \bmod p, +\end{equation} +without ever computing the secret key $s$. This value is then used to obtain + +% xxxx ---- eq. 82 + +\begin{equation} +T = (B\cdot M^{-1}) \bmod p. +\end{equation} +This $T$ has the property that $T=K^t \bmod p$ where $t$ is the tally of the associated option. + +In general, this final computation of the tally $t$ from $T$ is computationally intractable as it requires computation of a discrete logarithm. However, in this application, $t$ is relatively small---usually bounded by the number of votes cast. This tally value $t$ can be determined from $T$ by exhaustive search, by precomputing a table of all possible $T$ values in the allowable range and then performing a single look-up, or by a combination in which some exponentiations are precomputed and a small search is used to find the value of $t$ (e.g., a partial table consisting of $K^{100} \bmod p$, $K^{200} \bmod p$, $K^{300} \bmod p$, \dots\ is precomputed and the value $T$ is repeatedly divided (or multiplied) by $K$ until a value is found that is in the partial table). The value $t$ is published in the election record, and verifiers should check both that $T = K^t \bmod p$ and that $B = (T\cdot M) \bmod p$.\footnote{For larger values of $t$ (more than 20 bits), Shanks' baby-step giant step method can be used as described in Footnote~\ref{foot:BSGS}.} + +% xreqj { + "section": "S3.6.2", + "text": "(Ref: S3.6.1) \"EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that have both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.\"", + "sc": "ace" } +% xreqj { + "section": "S3.6.2", + "text": "(Ref: S3.6.1) \"EGRS refusues to assist a Guardian to participate in the decryption of any Ciphertexts unless they both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.\"", + "sc": "ace" } +% xreqj { + "section": "S3.6.2", + "text": "(Ref: S3.a.f.c) \"EGRI enables a Guardian to compute a verifiable partial decryption...\"", + "sc": "ace" } +% xreqj { + "section": "S3.6.2", + "text": "(Ref: S3.a.f.c) \"EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies...\"", + "sc": "ace" } + +% xreqj { + "section": "S3.6.2", + "text": "EGRI enables a quorum of `k` distinct Guardians to compute `T = K^t`, for every Contest (Option or Additional) Data Field in the election. as specified in EG DS v2.1.0 eq. 81 - 82.", + "sc": "" } +% xreqj { + "section": "S3.6.2", + "text": "EGRI enables a quorum of `k` distinct Guardians to publish `T` in the Tallies of the Election Record, for every Contest (Option or Additional) Data Field in the election. Note: This is to handle scenarios in which the discrete log of `T` ends up needing more computation than the Guardian quorum has on hand at the time.", + "sc": "" } +% xreqj { + "section": "S3.6.2", + "text": "EGRI enables the practical determination of the tally `t` from `T = K^t`, for every Contest (Option or Additional) Data Field in the election.", + "sc": "" } +% xreqj { + "section": "S3.6.2", + "text": "EGRI enables the publication of the tally `t`, for every Contest (Option or Additional) Data Field in the election, in the Tallies of the Election Record.", + "sc": "" } + +% xxxx AJpUS1DAi571iitFgvff+1vR4/Sg4c77mTnq5zooRloWVwGCkYoDNFbtOGA6qJpn8skbBlw44iKq +% xxxx Lj98cBhT5QCNkYajeEsGIUOTGHLhPwf4psIHCN/gM0pXRSMqAWM1vBrNCH4FTEQORqwzjbjHaMUT +% xxxx sObwGnalx//oGVcm8BGVX2LUPymSuAHuaYYwcSrawVkOUahJnvcMfCW2meUcMUfODt/Ngido7CVS +% xxxx G6VqHyt6BFeHu5llWme3o77gBEuAVlQXgIIUK2H5Ak9FvKvxkKmVrKbYwytx82WHDICJOEbzieGX +% xxxx 5x6h3DgyIGfU2RHpbGAkkpaC+ZFDtywELDcq93jn9CpCsZCXAJ31QfiR1eFCIpWGeGB48aICOcPn +% xxxx BMCdehYeEHmnc//xURuy07t4G5p1x3WgiTNq/C2zsFiDxpONPN+IJcykLGoO5gNP6V1nB/HOlchg +% xxxx oGFT44QsAiBBp/Memn4Ul4KY2GyUtJEXcGtg3dhfLM4X/YyXAQVtNsV01uo96O9i1CArbC7+jRAh +% xxxx Dm6+ObzOIB703Dp2XrG6lmciubaMW0D9iYLn5Td83vOWbQ7lQMp2qNBxiZBZbMOOukajtme/enU/ +% xxxx aZL3we8BzBdlca2CPNsWSZMmYhnlB3WDLUMyTIOzb87oFcEiEa9nrBSpLqpkVnq16W7P7ZXSgE9N +% xxxx Iu6OVh23+rHFJ9Wk1RwEWImFO4uWqqiDXiwVU24fpD3FctHhvjucZrJCnBVoUrSPkJFjOn5ueaqy +% xxxx +% xxxx +% xxxx ---- S3.6.3 Partial Decryption by Available Guardians + +\subsubsection{Partial Decryption by Available Guardians}\label{sec:decrypt_missing} + +During the key generation process (as described in Section~\ref{sec:keygendetails}), each guardian $G_i$ has received from each other guardian $G_j$ a share $P_j(i)$ of $G_j$'s secret key $s_j$ ($1\leq i,j \leq n$ and $i \neq j$), has computed its own share $P_i(i)$, and with all shares $P_j(i)$ for $1 \leq j \leq n$ has computed + +% xxxx ---- eq. 83 + +\begin{equation} +z_i = P(i) = \left(\sum_{j = 1}^n P_j(i)\right) \bmod q = \left(P_1(i) + P_2(i) + \dots + P_n(i)\right) \bmod q. +\end{equation} +The value $z_i$ is $G_i$'s share of the implicit secret key $s = (s_1 + s_2 + \dots + s_n) \bmod q$.\footnote{Guardian $G_i$'s secret key $s_i$ is the constant coefficient of the polynomial $P_i(x)$ and therefore the implicit secret key $s = (s_1 + s_2 + \dots + s_n) \bmod q$ is the constant coefficient of the polynomial $P(x) = (P_1(x) + P_2(x) + \dots + P_n(x)) \bmod q$. The collection of the $z_i = P(i)$ are the shares for a $k$-out-of-$n$ sharing of the secret key $s$. It is important to note that this secret key $s$ is never actually computed; instead each guardian uses its share $z_i$ of the implicit secret key $s$ to form a share of any decryption that needs to be performed.} + +Each guardian $G_i$ available for decryption uses $z_i$ to compute the partial decryption + +% xxxx ---- eq. 84 + +\begin{equation} +M_{i} = A^{z_i} \bmod p +\end{equation} +that contributes to computing the value $M = A^s \bmod p$. + +% xreqj { + "section": "S3.6.3", + "text": "EGRI enables a quorum of `k` distinct Guardians to compute `T = K^t`, for every Contest (Option or Additional) Data Field in the election. as specified in EG DS v2.1.0 eq. 81 - 82.", + "sc": "" } +% xreqj { + "section": "S3.6.3", + "text": "EGRI enables a quorum of `k` distinct Guardians to publish `T` in the Tallies of the Election Record, for every Contest (Option or Additional) Data Field in the election. Note: This is to handle scenarios in which the discrete log of `T` ends up needing more computation than the Guardian quorum has on hand at the time.", + "sc": "" } +% xreqj { + "section": "S3.6.3", + "text": "EGRI enables the practical determination of the tally `t` from `T = K^t`, for every Contest (Option or Additional) Data Field in the election.", + "sc": "" } +% xreqj { + "section": "S3.6.3", + "text": "EGRI enables the publication of the tally `t`, for every Contest (Option or Additional) Data Field in the election, in the Tallies of the Election Record.", + "sc": "" } + +% xxxx EB8ffcy4p57Sn1sR5BJa1RskK5vgqREPRKDnFho9V5yLE3snlFKNi7qdworS58nF2gkHx4zTBUc4 +% xxxx 7jioPP+a7UIb+UHStnycL2oBwdflE65MmsbK0WfqGQ5XDc8g1N7nDhBhL0O7K662V0pmWuE4Zu0z +% xxxx KZMGto3SvG9ajBpFrCrdwJWpzuAXRXON0no4M7aQAgG3PUeIEfPPZDI78t8tdWVGcAWDkIhAm/nz +% xxxx pfO29Cn8X0mtA5FntaJ5MhgUVdxe/CweXGur0irouClTBtZM5vAB10G28074oMAx7FoxQd4HG3g3 +% xxxx +3iSaiRjR3SDOi7kUB34YUghmiVvttqayjn3C3MKewthrcHMkklnbcSwZH+Fz0eGi9byUtntHF4G +% xxxx 0JgeVKIVXTNSkVBMwZtsD/8kdL1Ejd72whUmh88ivRZCiqbnu1+P2GaAX6TbiUJmYA/66mCzUJt+ +% xxxx kMnkI7GoDGnWBCdsM0aRRceOfVVNoqtBReBobC5ImCgWaYmW7/Bdj0v0VZ3rUPo/vgbPU6aA04Z3 +% xxxx sSpnaToYhONC51Umo8dg2Uz9Ic6dYtVxet2tqYOiO6SNAwUtPklw/eMk1yTI6z7X4Pmh2YBAW3/2 +% xxxx E53cYsdoD+NwTV95sFxximuil1niawDuOhqDGIn7PMzLzeqyG0mCEyO3Q/17fvVO5l1jZFiqckpA +% xxxx Z5UXKrMw0yWAd63Id8yshRs5HfJ6xe+2pZnz3/3RicUb17KV3TWaLH7bAjvu+pBvg3exKGqXbMKZ +% xxxx +% xxxx +% xxxx ---- S3.6.4.a Combination of Partial Decryptions + +\subsubsection{Combination of Partial Decryptions}\label{sec:decrypt_combine} +The partial decryptions are combined to obtain $M$ with the help of the Lagrange coefficients that correspond to the available guardians. Let $U \subseteq \{1,2,\dots,n\}$ be the set of indices such that $\{G_i : i\in U\}$ is the set of available guardians. For decryption to succeed, a quorum of at least $k$ guardians is needed, i.e., $|U| = h \geq k$. All available guardians participate in the reconstruction even if $h>k$, i.e., there are more guardians available than strictly necessary for having a quorum. + +The Lagrange coefficients corresponding to $U$ are computed as + +% xxxx ---- eq. 85 + +\begin{equation} +w_i = \left(\prod_{\ell\in(U\setminus\{i\})} \frac{\ell}{\ell-i}\right) \bmod q. +\end{equation} +These coefficients are used to combine the $M_i$ for $i\in U$ provided by the available guardians to obtain + +% xxxx ---- eq. 86 + +\begin{equation}\label{eqn:threshold_decrypt} + M = \left(\prod_{i\in U}(M_{i})^{w_i}\right) \bmod p. +\end{equation} + +% xreqj { + "section": "S3.6.4.a", + "text": "EGRI enables the combination of partial decryptions as specified in EG DS v2.1.0 eq. 85 - 86.", + "sc": "" } + +% xxxx oHG0FXjEAqXdccsyVpbvERsjLiO7+vgigETRTM1OthOkcGoiOvXzJ7Ra0kdwn944e2UzFe9HxIri +% xxxx b9o6xdD+kuvVHhEgBWAWLicfmar3AUOyfNosbkA01uqhxADMJlwxqPnr3eEV+5gonZOPwBMdOJ7W +% xxxx sLitGwe9QdmqIeZX88GRpwURTtFiL037byVQ+YroQ+WjoCrvZNLeeWPmnawvVtodOHDgM0f9RDVD +% xxxx e9Wo3XDheQY4QxKSQh3ds3l5Ve2KrNA9oypBgosZSnpDlHLbKafFBX9Oz7Yq/qkyMKGei0NrXI7d +% xxxx AmXiNkEB3WZqjxA9spzsOQcdXxsyVVj5ngTk7GAiNpvV/QdsRGxM66OMGj0M4z1yHLvp6Ipj/vrE +% xxxx CvavOMDT9DV6y5/XTSXEjzsc+Ii3voLnXTccA3M6+41u/wlvro+2JNvPzu1qC5YoRFOU4Xzm7HKO +% xxxx XJKFAgakkyvms5+isFUFHrPKN3G57TkT8fkRAhnGqDU68UyoF/9yIh6lfkOGwcjismxh9TNN1mzz +% xxxx ekC3vjHXcEkmB+18bf3xL9bwkcRZ/wHd+9lEu4lbGiwRt1R1teo5foPQtX02NzPR+KtOKxMswOC8 +% xxxx GZw26YUuvivlSy4sXtc9um1J+UPEcrRYEGkvFdWxxw8Tmqdn7a0vq3HeD109L+IleOpW/SiK962s +% xxxx ZOWZnRCYUe0iw+DvNFV2DdDlsparLjBxkAgcJKK0Yg99ZBzlkzxfnKKIl8WQxiJUQZNPXwbkKI6U +% xxxx W5PicWQoi07hwL9eqeJR3dCtlGWBde8ZpDRMoOMEY/BXzFOoP6budbMYNIFM1yt9Elj7gP+wiN5h +% xxxx +% xxxx +% xxxx ---- S3.6.4.b.n3_6 Combination of Partial Decryptions - Note 3.6 + +\EGnote{The decryption $M = A^s \bmod p$ can be computed as shown in Equation~\eqref{eqn:threshold_decrypt} because $s = (\sum_{i\in U} w_i z_i) \bmod q = (\sum_{i\in U} w_i P(i)) \bmod q$. Likewise, a missing secret $s_j$ could be computed directly as $s_j = (\sum_{i\in U} w_i P_j(i)) \bmod q$. +However, it is preferable to not release any missing secret $s_j$ (or the secret $s$) and instead only release the partial decryptions that the secret would have produced. This prevents the secret from being used for additional decryptions without the cooperation of at least $k$ guardians.\vspace{2pt}\\ +As an example, consider an election with five guardians and a threshold of three. If two guardians are missing at the time of decryption, the remaining three can perform any required decryptions as described in the text above. If, instead, they take the shortcut of simply reconstructing and then using the two missing secrets, then any of the three could, at a later time, use these missing secrets together with its own secret to perform additional decryptions without the cooperation of any other guardian.} + +% xnote S3.6.4.b.n3_6 This section generates no unique or specific requirements + +% xxxx t1+kBba/SblyHM9NI+NRT/u5GR/ISPUUSJvnBlfn+dzb/c6ves3PYXTCyU0u1nA1QpUJ9gW6/K2W +% xxxx pDNdaUfBs9j8q5cTSX9NDQNyhoX0ruwMSe91mBqwFvAf8GbWzrnAeBAC9QKqPs70Jfw3Syk5nRPI +% xxxx Se1339TxMCFjlrh5du+Yd//EicuTJVy7y/JcA51TTa4Hjn/jKk5hSevCWw9ZW2cs9L/Ya1OJ7FYG +% xxxx CpNORz1J2EWWgB8eClnrZCECvAcALTv1dgpO8w2T/+3MeShN1TXJBgtbE6/5a7J4UWu94qTgZyiO +% xxxx dSaElV2WiVvipIB/i0ExCIeEAWm9riFuS+QfUtEDhrvIjfwhZkBXYu5n0D9v1fxvE2e3rNDA7Rge +% xxxx 4+yccMCR6gsFuAO+99Y0HCNtYlezkmByBqxXMslNrY49GoWz6fss4nqhO3nIt9N02IqJLdFyl3Ey +% xxxx ObJCzixZvgl3szxtVXOQwOFyRMTnzLdD5f66Hobm3ggFETMnjdVbFSeChYRKPTGyLxoaxOHLwQyF +% xxxx AZJEuPakiGQDhP+CV3r4T3MbjhL39nQ+VCtSDwHcum8qYib98JNJFOAK1dAawJpEhUIFGkrrMwqX +% xxxx r3rIbpsd3IG51R23RoFYMJRnn1NOEUxV6xHGzbfXv8BcnWyR1HERUVkZx9bHZOagwwrkfPSEsYdb +% xxxx UbfXskMcDW3yQObk82GGzLM+7907zhEmh12HUyjZ1Ybxs2DiIsyBWodOilcmWJCLtswg37gAZWCy +% xxxx +% xxxx +% xxxx ---- S3.6.5.a Proof of Correctness + +\subsubsection{Proof of Correctness}\label{sec:verifiable_decrypt_proof} +The available guardians work together to produce a Chaum-Pedersen proof that $M$ was computed correctly, which means that $M= A^s \bmod p$. The proof is computed as follows, for each option $\lambda$ in each contest $\Lambda$. + +% xnote S3.6.5.a This section generates no unique or specific requirements + +% xxxx ab7kaEqluqsKRL9Qjr3Qtd6JMe9lj4+GM5UzxoBEkiEb+UFXsGdxdBbNmYQqZwO2iWU/mqPBDCf9 +% xxxx 6L+Xx8SbIPWS6rEkb5GXNlzKUKCHJO1+R38sAAAZ1Ut3XY18NHmFjX1g85Pr/XsAM2s+8/up2rcx +% xxxx nSxcO65btVYgWPO6aufDGyF//wdN/81GHy4zDfeGkZNUTFawcUc//3cTxE0MQAJfit4SBiK7ukdy +% xxxx uBzJdx0hhtzMZlTC4are8YB97UvzJA2Do3ySma9A+KIYPszhPDvQ//89zrRTHZ7uC5IdtQhbRwvJ +% xxxx FA2Uq/FBC7A1nbEL8JTwqVB8shAwI69UqdAbx5Xl8B8j60DM2WEgmimRQjmXaRjo33YMGRAXw8ty +% xxxx ih41uaTXO4YpqmboJtHAgDdaCA1RejcgmBo5EZA+YGhqzDsY2VE1MrM1CWCfi60vhrYPPpyf2Zhr +% xxxx UipZyEbiA4WQUVDNTJG//340yYRGONTdK7O82+S8w5QRvtBMU3KioyyYsFnnqW+OV/JhSbj2yTBv +% xxxx JuKyCpeVfp1X2D5iNUr/7DbHx2fHMdeLavYC0s/XGQY80vIrKsDsAIqpYRvO5BKhlYWWRMWoAB8p +% xxxx dtq1vakksXgu6uer6qtAp+ERY14nR0CvCzzQUbVih7mBuEWMMtdTF95in5X4YtIdctCtFnMMv8xl +% xxxx 0vf+TSyBHadyzlaXlCq6n/grb3r623TQYFxjJgB4mfaRAHG6RETvJIj0WqndXtm7xccGsIVJnP8f +% xxxx +% xxxx +% xxxx ---- S3.6.5.b Proof of Correctness - NIZK Proof + +\paragraph{NIZK proof:} The available guardians jointly prove that they have shared knowledge of $s\in \Z_q$ such that $M = A^{s} \bmod p$ and $K = g^{s} \bmod p$. + +\noindent Each available guardian $G_i$, $i \in U$, selects a random value $u_i$ in $\Z_q$ and computes the commitment pair + +% xxxx ---- eq. 87 + +\begin{equation} +(a_i,b_i) = (g^{u_i} \bmod p,\ A^{u_i} \bmod p). +\end{equation} +Guardian $G_i$ then commits to $(a_i, b_i)$ by computing the hash value + +% xxxx ---- eq. 88 + +\begin{equation}\label{eq:commit2commitment} + d_i = H(\HH_E; \mathtt{0x30},\indc(\Lambda),\indo(\lambda), i, A, B, a_i, b_i, M_i, U) +\end{equation} +and, using the administrator as a mediator, sends it to every other guardian $G_j$, $j\in U$, $j\ne i$. Only after having received a value $d_j$ from every other participating guardian $G_j$, guardian $G_i$ sends the pair $(a_i, b_i)$ to every other guardian. Guardian $G_i$ verifies that the values $d_j$ have been correctly computed via equation~\eqref{eq:commit2commitment} for every $j\in U$. If $G_i$ does not receive $(a_j, b_j)$ from a participating guardian $G_j$ or if equation~\eqref{eq:commit2commitment} does not hold for any $j\in U$, then guardian $G_i$ halts the protocol and complains. + +If none of the guardians complains, the $a_i$ and $b_i$ obtained from each guardian are used to compute the accumulated commitments as + +% xxxx ---- eq. 89 + +\begin{equation} +a = \left(\prod_{i\in U} a_i\right) \bmod p,\ b = \left(\prod_{i\in U} b_i\right) \bmod p. +\end{equation} +This means $a = g^u \bmod p$ and $b = A^u \bmod p$, where $u = (\sum_{i \in U} u_i) \bmod q$ and $u$ is not computed explicitly. + +The ciphertext $(A,B)$, the commitments $(a,b)$, and the combined value $M$ are then hashed together with the extended base hash value $\HH_E$ to form a challenge + +% xxxx ---- eq. 90 + +\begin{equation}\label{eq:nizk_c_dec} +c = H_q(\HH_E;\mathtt{0x31},\indc(\Lambda),\indo(\lambda),A,B,a,b,M). +\end{equation} + +The challenge $c$ is adjusted by the $i$-th Lagrange coefficient to produce a challenge $c_i$ for available guardian $G_i$ ($i\in U$) as + +% xxxx ---- eq. 91 + +\begin{equation} +c_i = (c\cdot w_i) \bmod q. +\end{equation} +Next, each available guardian $G_i$ responds to the challenge $c_i$ with + +% xxxx ---- eq. 92 + +\begin{equation} +v_i = (u_i-c_i z_i) \bmod q. +\end{equation} + +An accumulated response + +% xxxx ---- eq. 93 + +\begin{equation} +v = \left(\sum_{i \in U} v_i\right) \bmod q +\end{equation} +is computed and the decrypted value $T = (B\cdot M^{-1}) \bmod p$ is published along with the proof $(c,v)$ in the election record. Note that $v = (u - c\cdot s) \bmod q$. + +% xreqj { + "section": "S3.6.5.b", + "text": "EGRI enables available Guardians to jointly prove that they have shared knowledge of $s\\in \\Z_q$ such that $M = A^{s} \\bmod p$ and $K = g^{s} \\bmod p$ as specified in EG DS v2.1.0 eq. 87 - 93.", + "sc": "" } + +% xxxx 6BE+WoxwSknD/O8hOTFLEmKUzoyJmSsuXFDXnSgg6ogXdENHI7REDMZwpd1GtsJmCyyz+eRKIx7a +% xxxx DxVkUFnvWlynS/Nj5WT8JPaAWdVwlVSGBTewtZor/8QjcPiVXlWprmOjDzrMbUpr5Taxcq/TTiLv +% xxxx WKe9p9Q2Sz6OtuUm0ZFKa7s88JAlvyB5D2CJCI8CENrlLKEvmi12ZQ6kkmrClXZbGLP4LqodXKFH +% xxxx yWOwno3aF+Ei6hhgwWjwOE+JTEIA7Bfv2BLYe8HlyrCwqbQOH200+INesrt6ymbatTSE1v9zDTbZ +% xxxx 9nnu0uSuYJ4YO5/VgMaPv2b2vjhO8jmfP6ggFvNB3dj7kd2R0YXnIRoc6Lo6izvap4/IJXHjk5rS +% xxxx gMXCEszmNRrYmTqlVgu/xbxw7WHnVmPQX5EgxaFyexdZ7IF5bCJpJnPLoVytlGxWh882ZJ7U2whq +% xxxx mGuJame5hMYP5u5zEzkq+n8Sl+R0cg/caSBQGY5MWdkU3SodvmlLu4WTSzls1+nt3vVbvteAN7W/ +% xxxx rQSOx5Ma6yHHtOu4MunkhdjfIge3bKJsIyAbudoY9JOxycJ4tzjmm1mDk+L6Equ62rAOoz+DD8kB +% xxxx GK4hGT1aONwrt8emdEMzitzvIIgOfBXGmr3yEYJ52oK7hUOnZwmfXBLq/sacO0OP+Opq4mzmaFeH +% xxxx x1PW/f8vDSIuwMrAC7QYeQ588/3/z5UHI5PyM2R4H66l67yxjKnqOMQnqObAms/mofr3ami3CO5X +% xxxx +% xxxx +% xxxx ---- S3.6.5.c.n3_7 Proof of Correctness - Note 3.7. + +\EGnote{\label{note:decproofindividuals} +Individual responses $v_i$ can be verified by recomputing the commitments from the responses, the individual challenge values $c_i$, the guardians' commitments $K_{j,m}$ to the coefficients $a_{j,m}$ of their secret sharing polynomials $P_j$ (see \ref{sec:keygendetails}), the ciphertext value $A$, and the partial decryptions $M_i$ as +\begin{align} + +% xxxx ---- eq. 94 + +a_i' & = \Biggl( \left(\prod_{j=1}^n\prod_{m=0}^{k-1}K_{j,m}^{i^m}\right)^{c_i}g^{v_i}\Biggr) \bmod p,\\ + +% xxxx ---- eq. 95 + +b_i' & = (A^{v_i} M_i^{c_i}) \bmod p, +\end{align} +and by checking that $a_i' = a_i$ and $b_i' = b_i$. Should the accumulated proof be invalid, this mechanism may help identify whether one of the guardians produced an invalid proof.} + +% xnote S3.6.5.c.n3_7 This section generates no unique or specific requirements + +% xxxx uxL1zfcA/m1Q3hxXE+Kh7xz1hHLbE/llm8tcJAa+X+LviT9P//mGU+DvfFxoqdGrDcDpOUDKFlYD +% xxxx v1XzAsH5q7i0keY1w1srTkW+phgJXXByTMY9pplWF6qxbPrpQoB5MBsGrDz1HoVkznxTZzihd8k6 +% xxxx BVufuag/oDm7jAO63MS6cnJ6YP0wSTtOplcZPGD/6UxFy+a7Q3CljtLRNOYWur2nIoZA3OlFd6H1 +% xxxx nPVI18AHhmT7Gp4hO/LXAMtuO92QibMeUmFzoPdbQGxagCFM8snrq79fkjzagXcebKehSUyWtULP +% xxxx lKDutA5ysZtYIOqMvRPiKnwTrMwU/APtvH5vRpHQqolsy3tR9CiXGUNEftqztWmHrKFPXbu3hv/W +% xxxx 8TN3oPoUgh3HiklNoP1k2eXTJq6Vcecl+9bna5sKeR7c+LkL4PMykK5hlDmpoSSqVYeSGytFEwne +% xxxx 75VW9UileRKXeP4gLnVPHIduhH7zYt3m4A5/wKjlXOatFJ9M2wHLxwsMzWD/Nl32oo3/M8zLsfq7 +% xxxx k2aQQK6CU7/rxV2sTeZJYudsE+QHmlW0zTPr+1h2TeUesp1qcKm4Q9RFjKsLj5rM0aAQecIUsqMr +% xxxx luIatPXVtO03zwIosfJsyOf5hB1VdhVdfmEspb5e483EdkDivSpmRfy9yToEXtCfmK2jGNT3S5aq +% xxxx dmmMBXR5RQEHVcYh7x1M0ZI51sIiAU+QUckNHfUW59eUwL0wvhpAeN3FGvZjPNDg45DQtoriBFlv +% xxxx +% xxxx +% xxxx ---- S3.6.5.d.verif10 Proof of Correctness - Verification 10 (Correctness of tally decryptions) + +\EGverif{\veriftitleCorrectnessOfDecryptions}{\label{verif:decryption} +\veriftextCorrectnessOfDecryptions} + +% xreqj { + "section": "S3.6.5.d.verif10", + "text": "(Ref: S6.2.5.verif10) Verification 10", + "sc": "nyi" } + + +% xxxx BduXSUKrSk5OMKFqvYly5b+So3JDKpGodRB5UTp2GzTHynYaixBo3WHjK82P4dq0a03f9Np4lU4a +% xxxx 6fSnZYnzR+UsUARgaWdUlfp3XwUDQ64DRWahJgOCemxbRh5HUzT0dFPsfj4ZfZKl6gS6VSWXPMmf +% xxxx 2FG0DXIfSaBVP4Wp57GrPfvD1nC8I/XKg+VwfXtDDzKwhIpyNPI2q6vQ13horoVcPw9oIE9gNDSP +% xxxx PFpmG6y52cD7oarlYPD38ZiXbUNRKlWBlayX4T9kYDMIS7S+0tXzhSvur7TK2vBqxOVzfru14OwD +% xxxx mDUrbi0AdD5w7oCKIMUYjQZ5qLXggO/ShVZQsRBrNH7O0+NNSRKMYhEMM835H8F0Td0MQPjvC1JF +% xxxx g5P7pGmy+jE9d1NXzoB2vN52EkprvGNx2UUi21LY7DKvdFmUnvGVsdwF+ju508sxFg1tECtlOXzR +% xxxx PoBvvH2JsizowUixI8G30HyzsmGacC04OLfSKMgNFI0cekBm8yXybglZfVlwB8bnFULvFmWdzrKz +% xxxx JYDgym+PbtWM00IdNn0Y5HwPTt07K+ee2ju0soT0cEQJxLCG+Wsnw0Rl/jHk01MIOB4ogQ9ZY37g +% xxxx ai0V6oRQ9ttUSaSENdepASY+XSIFHtcZ1KYiRPY8Xo2qXtdrIRq6HhEVG2zQDMX/Q68y9g1v6tkQ +% xxxx S9PvN2TqlYOEXUeUB5S0qPMuMJFK14Zy6uU2DIf3OHXFhCwua96ElfKOMBYsRvpOtiEr+7xpT2ht +% xxxx +% xxxx +% xxxx ---- S3.6.5.e Proof of Correctness - Tally verification + +\paragraph{Tally verification.} +The final step is to verify that the tallies contain the correct contests and options. + +% xnote S3.6.5.e This section generates no unique or specific requirements + +% xxxx McT4nlAKGA8SvX7ho4GcOPIAG/NfIqqi2/8veWa58i1Vq+aB3D8e5IRo48npuXMAA8KXi6lPtwdF +% xxxx oLg8MX6njma7J3scd5LAM5lkWXKZC9+yPLISulu/8+MCZ50BjZxjKpOKLC0VBK1a4rjE6r2OA65F +% xxxx f5H4npkDji+f/PgQi4bH6K8sOBhpxpuFwlMyCb0GmIp7ekUPsy/hbochJM1DHT33eV3Il2o3lPWl +% xxxx KuD/diCV+4CgEckHxh0kWcNI7HCiNBFcC4oOs7l27rohnWztG0GlarVe5ihxGgTo/kF60+jrwcIS +% xxxx F270MaPb13Y2LjJAnlA8ZLA+CCQmmq/vR6Tc9ftIGuyiroF1VT4vAMBhRus82H5aVuyZWzvl6Y0g +% xxxx Qf6y+2N+xg18WosxpDBuQ0Nbq4b1Gchlx9jS6mJAlx+sarEZec6pqp2c4v/8kf44q5o4NrMZZ0+C +% xxxx 9VtOMrzaxD85Tp9neZ2J8jaSqw36lpdxtiiTuYX4lvwRV6Z2/UwGnCMXlOZN8L0EM+1Pc5wdL/8M +% xxxx mVij2mySghrRMukczT/2clcfTSC7vy5EFA3TNwZGe4N+TUwE78OfVLGOFE9tlTfUjjvToJvsq35y +% xxxx TezRyqwVSQjPjqQHxKmpfxi3niaA6duKXFWYlQIqWv5ymQQXsTSAoqtvBI7vn0cwt0qZUdCjBdcZ +% xxxx UhyUKpmCHDPi+XPQIzEZnADrwTArx1BHvuYQOvr80MN19g3w9BmuEN4jRAWRbTOQC9MkyLOUyPAQ +% xxxx +% xxxx +% xxxx ---- S3.6.5.f.verif11 Proof of Correctness - Verification 11 (Validation of contents of tallies) + +\EGverif{\veriftitleValidationOfContentOfTallies}{\label{verif:tallies} +\veriftextValidationOfContentOfTallies} + +% xreqj { + "section": "S3.6.5.f.verif11", + "text": "(Ref: S6.2.5.verif11) Verification 11", + "sc": "nyi" } + +% xxxx KLyEbAZdTDbCD57FIK/4vzVx/owzW1nGYskuTgJUvZHDObV7rrVE2DzMzVKlfntXCSR9ZtrhwzjS +% xxxx wbnBBrlxgVt/o9TgDpjEbFRBKs1UpifEeljIV4fzTEuInA5xOUlLmrY29QCzDe4X9wHS2ZxKynQb +% xxxx AUOLtvKqlvX8LRXuChA/6Iq4I9CA8Jl6zK+1/yO4H2m0acAaUY/VaW8jNUFPt5+9gokunHkNI9NZ +% xxxx Poh6Gx9mgIiA/ORkBjA8N9X8JYXVT9FbO5zG/mtYDQOM2GYVPzzECSJK+v2VJnUljJXU6UFLogFT +% xxxx slR8Nra5pQV3TJKGzDysCC6JFQ/1/LnnrYQs5+nocyPZt6I2WGIffeN3kt7yNgXKgVqmN3iG+JuF +% xxxx CxQvCEck8XQ9Kv9OgXDFKudIbmBoTsJTfaNqhqdo+Sb4Y64X2ipc9d6QY++va56caL1AL2TqMXCF +% xxxx rgosb4bo5gIAj337hlbQrDfNv2gAMDHnXjCbyLzcfwbql6nJq8O1wSkNsIq46A60JVd3tlVJD8nB +% xxxx Sp41qFZUU3NHiBJHEljT9D+IimimEOepgnCK+zd2muLFAoZNoa1/qGG8RnEMWaSuI9a1VWDovY00 +% xxxx kGUgJ/2FGxDC4ptRpOhl3NDl62SFISIhm6v06XlxxDScNC2lKz7E7uqhBMthmNEgURbAErTCm9Vl +% xxxx sXsP0yQ08i6AQ/5qzs26NJFvB5eKm6PHJkfK3yfJV4leDNl58DPCu/zsSUep+JoFfoB1RH8IAA9W +% xxxx +% xxxx +% xxxx ---- S3.6.6.a Decryption of Contest Data (Optional) + +\subsubsection{Decryption of Contest Data (Optional)}\label{sec:decrypt_contest_data} +For each contest $\Lambda$, an encrypted ballot may contain a ciphertext $C_E = (C_0, C_1, C_2)$ encrypting contest data such as overvote and write-in text fields. The ciphertext has been generated as described in Section \ref{sec:encrypt_ext_data}. Such data may need to be decrypted if the tallies record a significant number of votes in write-in selections. If contest data of actual cast votes is decrypted, it will likely be necessary to use a mixnet\footnote{Chaum D. (1981) \emph{Untraceable Electronic Mail, Return Addresses, and Digital Pseudonyms} Communications of the ACM.} to keep decrypted contest data from being associated with individual voters. Details of mixnets are outside the scope of this document. The use of a mixnet may not be necessary for auditing applications. + +Decryption can be done by a quorum of guardians similarly to the decryption of tallies explained above. However, before each available guardian $G_i$, $i \in U$, computes a partial decryption, it verifies that the Schnorr proof is valid, i.e., it parses $C_2 = (c, v)$, then computes $a = (g^v\cdot C_0^c) \bmod p$ and +verifies that $c = H_q(H_I; \mathtt{0x27}, \indc(\Lambda), a, C_0, C_1)$ according to Equation~\eqref{eq:contestdatachallenge}. Only if this is the case, guardian $G_i$ proceeds to compute the partial decryption + +% xxxx ---- eq. 96 + +\begin{equation} + m_i = C_0^{\hat z_i} \bmod p +\end{equation} +using its precomputed ballot data encryption key share $\hat z_i$. The partial decryptions are combined using the Lagrange coefficients for the set $U$ of available guardians to obtain + +% xxxx ---- eq. 97 + +\begin{equation} +\beta = \left(\prod_{i\in U} m_i^{w_i}\right) \bmod p +\end{equation} + + +Again, the available guardians work together to produce and publish the following proof. + +% xreqj { + "section": "S3.6.6.a", + "text": "EGRI decrypts optional contest data as specified in EG DS v2.1.0 eq. 96 - 97.", + "sc": "" } +% xtodo S3.6.6.a "[write-in] data may need to be decrypted if the tallies record a significant number +of votes in write-in selections. If contest data of actual cast votes is decrypted, it will likely be +necessary to use a mixnet49 to keep decrypted contest data from being associated with individual +voters. Details of mixnets are outside the scope of this document" + + +% xxxx M5fDfSYBfjkVdiNrK0HKaWuR8Vw/lx57SgA5V9Eg7zmhqK23WGFjWIg9CXrk18Il3hOrTzhO6KGe +% xxxx eDf6hLHHfkeHu/fUhaHsqL/V9wehfBlFhCKpRe6/54qoj9SVCrkIK94PeqbYjtFgI/3TD+6xx00D +% xxxx Zv69mIyk4xcxnVNRX/UGOiPQ4yJYaEnpdCnXxaIvJg1FhVMnXxllwyV5btJlRoakjlgTx4qdDovD +% xxxx ES+IjP2CHNORhz4h6yDVnehLLlD0J6DdisxtbacjiO6M6NEwwnzKXb1LLiRPH4q7QQwU2H7MWUe9 +% xxxx +zuw1719ileXu3Ah0Yezgl1d72QlhezgS2jRQXA57CvJE+d98gAK+GbM6gDtjHEhzfCF6aQf11DW +% xxxx evWPAgiEfXpy+A1OlXAaMX2uDY4q8oQOzyhI2p6kyYVvCal6WhfrBtWuH/ivZW83poT7PRnOv+Yx +% xxxx kvmAm3ZUH59g9kzOMyv+50R9vI3AH/P4N9prZSZVYLah3b3Co5E9Os4eGivs3+DAR5+6ofU17KHE +% xxxx sYsqIIi9oAkuBJQbauoz6NJzd/uWZFUuihkKVQ/XV1SKci8OlkxWPme9URvRRMoPA9EeCH2OMkUv +% xxxx 8hc43bev56oRHtZLl+pjgZBy+KgyRH43O+LeKZW25DiXRGBCrfvwKIOybgjomMhJQBdqOWJZ2cwK +% xxxx 9Mqx7/Fx2WzqJNaJ4fdeau3frGvnxpcA8Br+SIX2O8SPpxUgy86zhbIUbOTOd1p/pis+OnehIeHP +% xxxx +% xxxx +% xxxx ---- S3.6.6.b Decryption of Contest Data (Optional) - NIZK proof + +\paragraph{NIZK proof:} The available guardians jointly prove that they have shared knowledge of $\hat s\in \Z_q$ such that $\beta = C_0^{\hat s} \bmod p$ and $\hat K = g^{\hat s} \bmod p$. + +\noindent This proof is exactly the same as the one in Section~\ref{sec:verifiable_decrypt_proof}, where the ciphertext $(A,B)$ is replaced by the contest data ciphertext $(C_0, C_1, C_2)$ as follows. +Guardian $G_i$ selects a random value $u_i$ in $\Z_q$ and computes the pair + +% xxxx ---- eq. 98 + +\begin{equation} +(a_i,b_i) = (g^{u_i} \bmod p,\ C_0^{u_i} \bmod p). +\end{equation} + +Guardian $G_i$ then commits to $(a_i, b_i)$ by computing the hash value + +% xxxx ---- eq. 99 + +\begin{equation}\label{eq:commit2commit_contestdata} + d_i = H(\HH_I; \mathtt{0x32}, \indc(\Lambda), i, C_0, C_1, C_2, a_i, b_i, m_i, U) +\end{equation} +and sends it to every other guardian $G_j$, $j\in U$, $j\ne i$. Only after having received a value $d_j$ from every other participating guardian $G_j$, guardian $G_i$ sends the pair $(a_i, b_i)$ to every other guardian. Guardian $G_i$ verifies that the values $d_j$ have been correctly computed via equation~\eqref{eq:commit2commit_contestdata} for every $j\in U$. If $G_i$ does not receive $(a_j, b_j)$ from a participating guardian $G_j$ or if equation~\eqref{eq:commit2commit_contestdata} does not hold for any $j\in U$, then guardian $G_i$ halts the protocol and complains. + +If none of the guardians complains, the values $a_i$ and $b_i$ are accumulated into + +% xxxx ---- eq. 100 + +\begin{equation} + a = \left(\prod_{i\in U} a_i\right) \bmod p,\ b = \left(\prod_{i\in U} b_i\right) \bmod p + \end{equation} +and the joint challenge value $c$ is obtained as + +% xxxx ---- eq. 101 + +\begin{equation}\label{eq:nizk_c_dec_cont} +c = H_q(\HH_I;\mathtt{0x33},\indc(\Lambda),C_0,C_1,C_2,a,b,\beta). +\end{equation} + +Each available guardian $G_i$ responds to its challenge $c_i = (c\cdot w_i) \bmod q$ with + +% xxxx ---- eq. 102 + +\begin{equation} +v_i = (u_i-c_i \hat{z}_i) \bmod q. +\end{equation} +An accumulated response + +% xxxx ---- eq. 103 + +\begin{equation} +v = \left(\sum_{i \in U} v_i\right) \bmod q +\end{equation} +is computed and the decryption value $\beta$ is published along with the proof $(c,v)$. + +Then decryption proceeds by computing the key $h = H(\HH_I; \mathtt{0x26}, \indc(\Lambda), C_0, \beta)$ and then the +encryption keys $k_1, k_2, \dots, k_{b_\Lambda}$ as + +% xxxx ---- eq. 104 + +\begin{equation} + k_i = \HMAC(h,\bytes(i,4)\parallel\mathtt{Label}\parallel\mathtt{0x00}\parallel\mathtt{Context}\parallel \bytes(b_\Lambda\cdot 256,4)) +\end{equation} +with $\mathtt{Label} = \bytes(\mathtt{``data\_enc\_keys"}, 13)$ and $\mathtt{Context} = \bytes(\mathtt{``contest\_data"}, 12)\parallel \bytes(\indc(\Lambda), 4)$, and the byte array $D$ representing the contest data string is decrypted by parsing $C_1$ in 32-byte blocks as + +% xxxx ---- eq. 105 + +\begin{equation} +C_1 = C_{1,1} \parallel C_{1,2} \parallel \dots \parallel C_{1,b_\Lambda} +\end{equation} +and obtaining + +% xxxx ---- eq. 106 + +\begin{equation} + D = C_{1,1}\oplus k_1 \parallel C_{1,2}\oplus k_2 \parallel \dots \parallel C_{1,b_\Lambda}\oplus k_{b_\Lambda}. +\end{equation} +The byte array $D$ can now be parsed to reveal the captured overvote, undervote, null vote data, and write-in text fields. + +% xreqj { + "section": "S3.6.6.b", + "text": "EGRI computes NIZK proof of Decryption of Contest Data as specified in EG DS v2.1.0 eq. 98 - 106.", + "sc": "" } + +% xxxx x9MvXzy5oaVPP98AIPAsk7w+I6b8vD1NYSmTiKk+2EWZaRp6aP3XRHsyPVsz2DdCDGjswu7+Wrcu +% xxxx XK7cZbQlGzsRFn8oNIuaYkya0qa83CRkSh+zmW5modRXylbbsKSg59SkNkCCpai4ixt+Hqmo5Tuf +% xxxx 33gCGOXSeUboFga8+FuM+HfZYZnc1So7tLInTu8lbScW/OSryEZxDX09RBy4QfCfdSjxfoQyQCvs +% xxxx uI/umNXuJwuUJjZ3cXpoe0VyFGDlbjuoSj7IeqdNh9jbGuQkBte3TRTiO6wlLmf1FEjjo/fq37RZ +% xxxx vnSt7vnzNZcUfcxvEW1VPjzm4L9+5kHHaUiOnElaZCgIkyJ66CK+dRKKgEIvbWGQZtdaHPZgyxQE +% xxxx i5o0l35R+Er2HhkboDxeZTjsyF86vgm7ykVIaXJvUbvMV4exvlBiflZASrjIZdiiR/pxBfLJIseP +% xxxx xWKN+3j30oGViFogPjttcnGHYs4WaOjPQC9G+6rLSeEaP1pyGzIR75nbO8h+5bCnt73lo194GJXi +% xxxx PnF1IIfCBvNzbbByUJ0pzKub1YfPWKIGPvfgZSlfzzIZajC5ZpDvuKRJspW7LLdBbd3mErDGdxR5 +% xxxx WVBzmGVZqRUMjDM5JFTRkTvtZPxXtP7DUE/HrWHJarMJSl6PN27LBadJMOWIbrOslY6ISOuGepUK +% xxxx lBS7QtkgCIIZa+V5SlntNDT+1WILa0DgG5xsn5AlgnNVx6K2Xuqge9AKirFuB2nJNKSaWMKqu1sF +% xxxx +% xxxx +% xxxx ---- S3.6.6.c.verif12 Decryption of Contest Data (Optional) - Verification 12 (Correctness of decryptions of contest data) + +\EGverif{\veriftitleCorrectnessOfDecryptionsOfContestData}{\label{verif:extdecryption} +\veriftextCorrectnessOfDecryptionsOfContestData} + +% xreqj { + "section": "S3.6.6.c.verif12", + "text": "TODO: Verification 12", + "sc": "" } + +% xxxx jd7YroDW7i4Kyx4czSoyaby3z90eFchEN5Ewre/6ud1tYakclhRUBWovzJuV4mYtK09NBFBDYEIT +% xxxx qr+JTtvT6dm6KThzdb9uN3kCwD2OsYkqE0RknCS6PwIzyLhxzEf8qiVZKgSSoXgpbsyIGBTVd4RA +% xxxx 8Urnd72/EKk8UAzJsaryk5VeGjvoQJELyZ5LcL/Hb8YOJM6ksFUZ7Ab8WgXqAwW2hdUF2SAsSFTy +% xxxx 80gxc20h10hxbUikfdQmAKL3+sGrUs/HbUxbGpAnyoDHck0A4zOLWu0bxjn96io7wjoZPAELrShj +% xxxx 0d3UsC9w3FoVJ6YEcGZxJPP/0kOrNPkc8iShYe1GNbDwasw47y6mgN1L4jt1m55/EVoZljF7cW/T +% xxxx ZcVaZgJU8B4pBQreiZlMUsNwGx+WLLagqHLFm4yLiJqZ3FCJtjP2FJxDM/5Lt5V6v/fLA5FkIQcd +% xxxx 6U7yPBMQCwmy1/dnAipybpTtzlsxZVtPZcjzalzqFt2Cqo+sPxEwGwvDiu5WW8yVko+9ctnplRBH +% xxxx CfGjVXcTbcyv7mc3HpYZcCy7kJBYLb1lU5cvt4Yr7q6NvGQfXGynGKrMA+A1t4GvnSdx1ZuU3ZNa +% xxxx dMUEKK2VnMQn5cyVXtJPL1AxnoEnLPe+7eT8E8XHlLsO1oZcgcge+w29vUGoXFspO5NfYrBjz0qd +% xxxx VU0lhdm43kwvFzok+DmxShrUCFFBWuJAQAM9YP3vICpnF8gldt5OJu+xBeS4YldMf9dvEuuXCHlx +% xxxx +% xxxx +% xxxx ---- S3.6.7.a Decryption of Challenged Ballots + +\subsubsection{Decryption of Challenged Ballots}\label{sec:decrypt_challenged} +Each and every challenged ballot must be verifiably decrypted. Guardians could use their shares of the election secret key in precisely the way they do to decrypt election tallies. However, it is more efficient and makes it easier for a verifier if the available guardians instead use their shares of the ballot data encryption key to decrypt the ballot nonce $\xi_B$, which was encrypted as described in Section~\ref{sec:encrypt-ball-nonc}. +Once the ballot nonce is available, the individual encryption nonces $\xi_{i,j}$ that were used to encrypt each selection on the challenged ballot as described in Section~\ref{sec:noncegen} (or the encryption nonce $\xi$ used to encrypt contest data as described in Section~\ref{sec:encrypt_ext_data}) can be derived and published. The ballot nonce $\xi_B$ should not be published. + +% xnote S3.6.7.a This section generates no unique or specific requirements + +% xxxx EhwVcR/JP/q/Wk+Nzq6lX/lyjQQUbD0cJTEwtgTMELV3shLoxrpjuGO3X5xfYNKbOvFxaRA4oo+x +% xxxx AKIBFjDkCA5paCeYQdyGGUPHiIpiYv5WQ5RgaufBy2FJhUTsuUiycfXScCCH2dRzH+RjRc1LRZkd +% xxxx DL3h//3MttSA8wd6rWW+kBQ4jyMcRu3VbN5EPm4Rhfs6QHfGkPmmcOBGIg27NYVyvZAhFwET3xMn +% xxxx FK9rXV9hzdKs2OCNAnLfIhOadYT5+vSKIKJdz8b4iY7tx/ZxbAVii3W4WtrjSgDrDBJjM8AQlL3y +% xxxx QbtKGpkF25nP8FDqMMzMT3ZovdGwQbXMdQqzdaTg3nqmJO6iBfLdGuY6g1IJSf94+QgVqmV1v43p +% xxxx TdvM1I9fe7fIcKf8/qXBR2LWCv7PTodFDKQ5pQTCph0dTlA1/OPxmh6i4GTvZ74j1ArYxtgK5bu+ +% xxxx 9eaJPht2434UC6q691Ss0L5gFsBKDH7EgEAYim/CkXA4UgJGL8/v26bUshDtcAZWP7mx9MovvYsR +% xxxx W95DC6oSx8Mgk7jipKODvob8MWAD1yGIBG7Peie1s9/Xi64mXMSkqxN5CQHetpq8ux8bugw2DzDI +% xxxx cilrIr0Z+q/5K/TO6fYyTYPPJIastx0Ythf7phnQed7/iFuSYiJis4133+6tGmtjfcLX8yX9X68r +% xxxx w53uaXB/5geDVEiNRPirMUzx7ZRaHX0Xn5Yy4nmUzahb0pQnE+zjhIjILoe92Y/DYle93TLyeBXN +% xxxx +% xxxx +% xxxx ---- S3.6.7.b Decryption of Challenged Ballots - Decryption of ballot nonces. + +\paragraph{Decryption of ballot nonces.} + Given the ciphertext $C_{\xi_B} = (C_{\xi_B,0}, C_{\xi_B,1}, C_{\xi_B,2})$, which is an encryption of the ballot nonce $\xi_B$, each available guardian $G_i$, $i\in U$, first verifies the validity of the Schnorr proof $C_{\xi_B,2} = (c_B,v_B)$. This is done by computing $a_B=(g^{v_B}\cdot C_{\xi_B,0}^{c_B}) \bmod p$ and verifying that $c_B = H_q(H_I; \mathtt{0x23}, a_B, C_{\xi_B,0}, C_{\xi_B,1})$ according to Equation~\eqref{eq:ballot_nonce_proof}. + +Only if the proof is verified as correct, does guardian $G_i$ use its ballot data encryption key share $\hat{z}_i$ to compute the partial decryption + +% xxxx ---- eq. 107 + +\begin{equation} +m_i = C_{\xi_B,0}^{\hat{z}_i} \bmod p. +\end{equation} +Partial decryptions from the available guardians are combined via the Lagrange coefficients to obtain + +% xxxx ---- eq. 108 + +\begin{equation} + \beta_B = \left(\prod_{i\in U} m_i^{w_i}\right) \bmod p. +\end{equation} + + +The ballot nonce can be decrypted by computing +$h = H(\HH_I; \mathtt{0x22}, C_{\xi_B,0}, \beta_B)$ via Equation~\eqref{eq:master-key-enc-of-ballot-nonce} using $\beta_B$ +and from that deriving the encryption key via Equation~\eqref{eq:derived-keys-enc-of-ballot-nonce} as $k_1 = \HMAC(h, \mathtt{0x01} \parallel \mathtt{Label} \parallel \mathtt{0x00} \parallel \mathtt{Context} \parallel \mathtt{0x0100})$, where +$\mathtt{Label} = \bytes(\mathtt{``ballot\_nonce"}, 12)$ and +$\mathtt{Context} = \bytes(\mathtt{``ballot\_nonce\_encrypt"}, 20)$. The ballot nonce can be computed as $\xi_B = C_{\xi_B,1} \oplus k_1$. + +The desired encryption nonces are derived from $\xi_B$ via Equation~\eqref{eq:noncegen} for option encryptions (yielding $\xi_{i,j}$) or via Equation~\eqref{eq:noncegen_contestdata} for contest data (yielding $\xi$). + +% xreqj { + "section": "S3.6.7.b", + "text": "EGRI decrypts challenged Ballots Ballot Nonces as specified in EG DS v2.1.0 eq. 107 - 108.", + "sc": "" } + +% xxxx 5rDA6p2sc3OzajTYqrN+8Vn6TmfrY9Ih9ju3OXa8/py0IjhAYmfys2gsHCHj54n0DXFI9+5mQr/t +% xxxx WEH/renFsq/nB/JbQ2rURl1v3I2XTmCeb0FVnUyaw2HuesOTSXDHc4OJGkLxKpHITL4+oFwIbhrP +% xxxx vV7SmuoRXff0IFzas5Ey0Wv48W7vddw25HTIMzkyH3YfwD+/LyxHxRE3Fs7IAujea5yvcfrpCyMX +% xxxx W7M1GHIw4MJ4vnQ5npw1HJo7TPDyQMQmQPRBEW0neEJ9QvmcurVHUuIeyYPmjogSyM1UtD+5ruUV +% xxxx QztMa1HPY7ksuwLlX74dLWuafWvFKofSp0sA/W+CWs0EcwqRgJ/snN8JSDy3e+rKMXoOkanLTTFH +% xxxx xR53vmpdOy9UjKnDeQjaqyRHc9v5yX6eM+baw75fHHo03tAtM/gTJ87v356/8+YBaqND/hdPMJB7 +% xxxx h63jL7FMnLn5C/GBxBdNdUVneIdFWER5O7d9B7HbCX9RLv305XM8OtCK/RKfVumtcA4swLk0oO7e +% xxxx 5XlzkQykAk31tFeYRypcvQfiTrY3nl9bHCSOiUSM9JwrgcBYSeeC/UU4xT2ga2lJicRg/njcbz+F +% xxxx DRyBUT6mz/7eQKG+DgMyaPVVV9TmE6XHAPSMgWjdvxUU0xnVhg3YvpvW3s1evWrcZnO9Ifs23BEb +% xxxx vAHKgd3AtTLHBVxOx5ySelnY9cOXzGHcur+yzafMLTdNsJrKi0wcU/L0o3MRI8EW+W1K9ntEGcMR +% xxxx +% xxxx +% xxxx ---- S3.6.7.c Decryption of Challenged Ballots - Decryption with encryption nonces. + +\paragraph{Decryption with encryption nonces.} +An encryption nonce $\xi_{i,j}\in \Z_q$ that was used to encrypt a selection $\sigma$ as $(\alpha_{i,j}, \beta_{i,j}) = (g^{\xi_{i,j}} \bmod p, K^{\sigma+\xi_{i,j}}\bmod p)$ can be used to decrypt the ciphertext $(\alpha_{i,j}, \beta_{i,j})$ by computing + +% xxxx ---- eq. 109 + +\begin{equation} +K^\sigma = (\beta_{i,j}\cdot K^{-\xi_{i,j}})\bmod p. +\end{equation} +Then, the small discrete logarithm problem is solved to obtain $\sigma$ as is done for all other decryptions of ciphertexts that were encrypted usind DPP vote encryption. + +To decrypt contest data given the contest data encryption nonce $\xi$, one computes the value + +% xxxx ---- eq. 110 + +\begin{equation} +\beta = \hat K^\xi \bmod p, +\end{equation} +then derives the secret key $h$ using Equation~\eqref{eq:k_enc_contest}, and computes the encryption keys $k_1, k_2, \dots, k_{b_{\Lambda}}$ using Equation~\eqref{eq:contestdata_enckeys}. +These keys are then used to decrypt the contest data from the contest data ciphertext $(C_0, C_1, C_2)$, where $C_1 = C_{1,1} \parallel C_{1,2} \parallel \dots \parallel C_{1,b_\Lambda}$, by computing + +% xxxx ---- eq. 111 + +\begin{equation} + D = C_{1,1}\oplus k_1 \parallel C_{1,2}\oplus k_2 \parallel \dots \parallel C_{1,b_\Lambda}\oplus k_{b_\Lambda}. +\end{equation} + +% xreqj { + "section": "S3.6.7.c", + "text": "EGRI decrypts challenged Ballots from encryption nonces as specified in EG DS v2.1.0 eq. 109 - 111.", + "sc": "" } + +% xxxx 6sADjZeAY3GSvubdRdS6ROZPmscWR2bOKL2D98ZniXyB2JUgo8TxWQc8H0+E0VRoltg08VScz43M +% xxxx Aq8T4Qgns0s87ohCVuiwxi3NkRwMVQW+7tWy37hFVf1Zga4M8q1/SPd2zByxPSHi6Yr7cdfqK1Lv +% xxxx ObH+VeT1xHNutjBISycldbLhDo6VZEIAbfEFiXrjD5du569X48/xXMpZvAUBdXr8gB4a3iJVXbpx +% xxxx cqSkeHuNvX8CTGyG3iPwAMYc0xN1AYacOqTy1Xhf2lBIEIyUywMy44sR9iEakLLaLRFsIS/ax/KG +% xxxx +jH6CoE/lEiXSV6NiGZrpbE6IukLaW6pMti1/OPy2gnqvBVHpEUKdSa7P2PZM8krX0wrXwABD5s0 +% xxxx ELAoT9BkbTcFWKz+X/EblcqUwo+pp5mu6b1fMG4JXgJX6g8yqo/veX5HmeKONOohj4+MF9/MSICI +% xxxx CWiimDRzeAK4QYZ11YZHrcB+ouJWya4Kz/tB+KPkpV9PeCb0Ver51LAj226xcRGs3GM40O5Sw6Nn +% xxxx AptN2w+ipR/Nt1BcZPkz4Lfqw8N9edzfKvnyYhw49cO1Pc5NbcqmMssIpTs+7GfnO+4pXYbp1TEr +% xxxx AEGX6SV2Rgysx1wS4Aj46PnbVk4YNb/MH5YIahOuwMUWhlXmAQD+GUiz1qXKtWuGBYFpuwuky7GI +% xxxx nA6eWwE9zAPLspRBybnmS5bwj80Ry2UqxBPdJjD1MXc4+UDZOVWe9GRBEHrkUYs2qhT1G+CE/uiC +% xxxx +% xxxx +% xxxx ---- S3.6.7.d Decryption of Challenged Ballots - Verifying decryption with nonces. + +\paragraph{Verifying decryption with nonces.} +When the ballot nonce $\xi_B$ +that was used to encrypt a ballot $B$ is decrypted, a + verifiable decryption can be produced by deriving the + encryption nonces $\xi_{i,j}$ that have been used to encrypt the selections, then + publishing these encryption nonces. When specific selections or + contests are challenged, as it is common in the context of risk + limiting audits targeting specific contests for instance, the + publication can be restricted to the desired subset of encryption + nonces, improving the privacy of the votes (this would not be + possible if the ballot nonce were published instead). This +approach can drastically reduce the amount of computation required of +both guardians and verifiers. + + +Specifically, when possessing encryption nonces $\xi_{i,j}$ and the corresponding +selections made on a ballot, a verification application can repeat the +vote encryption process as described in \S\ref{sec:ballotencryption} +as follows. +\begin{itemize} + \item Using each available encryption nonce $\xi_{i,j}$, it recomputes the selection encryptions as shown in Equation~\eqref{eqn:encryptvote} of Section~\ref{sec:selectionencryption}. + \item It derives the contest hashes as shown in + Equation~(\refeq{eq:contesthash}) of Section~\ref{sec:contesthash} + using the selection encryptions, which are either recomputed when + encryption nonces are available, or are given when the encryption + nonces are not available. + \item Finally, it recomputes the confirmation code $\HH_C$ via the contest hashes as described in + Equation~(\ref{eq:confirmationcode}) of Section~\ref{sec:confirmationcode}. If the chaining field is not the byte array consisting of only \texttt{0x00}, more information must be provided to the verifier as specified by the chaining mode, see Section~\ref{sec:ballotchaining}. +\end{itemize} +If the resulting confirmation code matches the confirmation code provided to the voter, then this is verification that the confirmation code corresponds to a ballot with votes for the indicated selections. + +Note that confirmation codes {\em do not} include any of the zero-knowledge proofs of ballot correctness. So a verifier does not need to reproduce these zero-knowledge proofs. Only the actual encryptions of the selections must be regenerated and confirmed. As such, the success of the verification process described here {\em does not} guarantee that a ballot with a given confirmation code is valid: the valid encryptions must be accompanied by valid zero-knowledge proofs of ballot correctness. + +Also, $\xi_{i,j}$ or $\xi$ values that are inconsistent with recorded encryptions {\em do not} mean that the selection encryptions or contest data encryptions encrypt incorrect selections or contest data: they may only reflect that the ballot nonce encryption was incorrect. This, alone, would not prevent a correct tallying process, should the ballot be included in the tally. + +An election verifier must confirm Verifications~\ref{verif:challengedDecryptWithNonce} and~\ref{verif:challengedballots}. + +% xreqj { + "section": "S3.6.7.d", + "text": "TODO: Decryption of Challenged Ballots - Verifying decryption with nonces.", + "sc": "" } + +% xxxx 2X65qF/D1IYu0sVHUjdxxYQ6wgtpVUDjxMbJkILTunCXqIKMxOB6xcOZBrr9f29tC05Dh4cES6NC +% xxxx YbbjgWnFzstoMVngA8AY01QOaPdZPIYk1QhYOmcCiiO51zKCZma1N+mFmu7J4f7pgOKfWwaKYqmL +% xxxx yIgFVejoMyeSWLbWu/LFuUP8wiBaNv5nzFig6MFxJjqjz2f+avp1GqwUxMBvj23N9Vjnjwfdqvgo +% xxxx b2d9qKsZR+4RPq1zeLp5w9fXQa5hRQ0VTUD/jIQw7OBlJBQTBOmO7Q1VOPH460Z4m/iWnmh/ScvC +% xxxx sDiLr7fFTB5IiaQYdNl/S6+9Gf2bAUWsEnPZP8qCrK1XGiLyU1j0RDH+EdBdj9Xkv3T5kWighmFs +% xxxx e/uzhctY0XaioE6Fr+40Sf51zwCyYxHUAYSL1j4/VrqdcaewRC07P3MvpXlvdbgUkO2sme8QM80n +% xxxx pAK7jBLebvRVTJBN6cP8CPsQi3HCq3Pps4KO4n9cu+/08tc4tLd1QxinrZaykBMq+MDUQDbAHw/d +% xxxx 2GB/6DqUPy1kfDXR4LHu2b5bk+CnxCAbDJKx9trEzsO9k44ao2VuTLftElSl4WcxvWGTkV/QGR/F +% xxxx g4jLSCcT/UASBqubLyzjqpREz5oDstp1Ioon4BsaTWos50zBBddOzTaDZ5HwoQRXaeKInFaYbhp6 +% xxxx 83YgOXYta6BKRPzR/eHzb6mFS5ehV0IkSf2wHZMsb2lwA5xg1e/uVZl25zYCB8cFK+VxvW55vpbQ +% xxxx +% xxxx +% xxxx ---- S3.6.7.e.verif13 Decryption of Challenged Ballots - Verification 13 (Correctness of decryptions for challenged ballots) + +\EGverifBallot{\veriftitleCorrectnessOfDecryptionsChallengedWithNonce}{\label{verif:challengedDecryptWithNonce} +\veriftextCorrectnessOfDecryptionsChallengedWithNonce} + +% xreqj { + "section": "S3.6.7.e.verif13", + "text": "TODO: Verification 13", + "sc": "" } + +% xxxx EnwKITdQA9v1/Cclii7FSqEH0ryx4g4/3Xs3is3PMSDH6qhH2CeW3dsFoar0Jf5nUBTcpewHXMNr +% xxxx 9PBgIGp5hi1s4vHpW4GGdCUHcYhEVm4C10mZ/rzReknEK1SwCjD5X8wBisg9X4ulj19NF+ESqG6/ +% xxxx R+apDGCpzTU5kd0wY83LrHqrHY2YGJ9WPDl2TqhXNWudKk181ihuTNEI0TkHioc14Oo+UqUkW5RP +% xxxx 96AIuS4GJOe+lVL+/Kr1aJvlYUnjElv1XCMu97anyWCiSbdryM0keBPEZ/JgPWtLGS8AMhvlqp0U +% xxxx HoHjj54be568gJANtOfNPqYlP/Xj9VpFgUGc+94MGUFAhItPi1clUeiq89NCF5Bst4SEfJYxywAS +% xxxx 8/FN7XmDD6QPngq7IuEjFY0/JDrTsm0iL6ocgVKECDcylEaELWELxpLXss/xXYWxvze+IXDT4w8v +% xxxx 4By8KaWq1j5JS93Z6PmFQVYh66NDLaRUQN73kOEmDNQcEVA9DLMNzqjvS/q3PqNOOaLAaIxWSzuf +% xxxx cmwPmTrFK1aHCyLYGsgId020jnfo46Bx5RaKUD96pRKl6V6wLG/NFL8+/yFu4oJBjFN2Gckt6WxK +% xxxx YT2JekBOWJYbIyQ/5fG6fO5gN5C+O21Rc1j0QyUZ/ihISGAm5BgJzdpj4tlKyfH3yFKhPGcu2F7b +% xxxx JcCDICiQ2fYyaj6/NcqYKpUzwrNeJKp8dGigb4Fa22m9ZYFz6U4r37P25GfxhLHf4vFBZqC9fFuA +% xxxx +% xxxx +% xxxx ---- S3.6.7.f.verif14 Decryption of Challenged Ballots - Verification 14 (Validation of well-formedness and content of challenged ballots) + +\EGverifBallot{\veriftitleValidationOfContentOfChallenged}{\label{verif:challengedballots} +\veriftextValidationOfContentOfChallenged} + +% xreqj { + "section": "S3.6.7.f.verif14", + "text": "TODO: Verification 14", + "sc": "" } + + +% xxxx qBY20ZelV2suSuqDhBxhH4yXmnSzlZjB9rHUhu9NaqWB9bBoztISa03uQMsZrh/QJHvsqLfXO/IU +% xxxx bdGwN9qONFQvpr4UUCO5MULF52rds9okMoll/GPCe/U0izwmRl/gRrMZZw+OLqhfOJCjqvJq8zSK +% xxxx I7akzy/EGSDGgtJ2rZuYaOu1ea+TEKusPagTgyTt4TPSlsicGwkv8wCC0yK4vYxFWdc8NDmrRkbb +% xxxx s3vxUQ+wZdQUo3edBmQdY4sAt8aJYWJvektHxCFJA6jkFI+tmj66kgZhLMeeswOFz0OHES3xpxZm +% xxxx 8uTSX8DxK0KKN7AQAPi8eLw9ayLZBPThTukIhGJqhV7vqayjqy2avck0CSa8ZNhaU8j9XliN+etc +% xxxx i8sq5jct3XQ8RR8iaCqVhMMfT/Q5w/kcbpA+9tHROq7UNTRw8d2+oYvxGUqEmKZAD9W7vF1snpxM +% xxxx vD8DgEo0p+jbIWIHgql4UXXP7Ce5vkI7rX3csh4d7+RA3BkBzAAIASLRFZCl1fztrI57TPf/hNsx +% xxxx kObHZcSg+l8rPU2UH9sVhLyVDI831D/YTwNKItYkXeDLPCg/CAW9bGyfgELswasoC4O5VFpBwABr +% xxxx XmkCZILtN+9LMBjPH2XNAnYwGzLt9FgIC36cfN6RuJrI58TCG8E+4yRjjwII0ra9b4o7ls8Ntwlt +% xxxx SoicHVH4RYxFfs7jyIXrTL79biWYkHM1NGjuRzfZUCZUovZJ3tSmDDHps4Je5QR9oXEp8SAJoXz+ +% xxxx +% xxxx +% xxxx ---- S3.7 The Election Record + +\subsection{The Election Record}\label{sec:electionrecord} +The record of an election should be a full accounting of all of the election artifacts. Specifically, it should contain the following. +\begin{itemize} + \item Information sufficient to uniquely identify and describe the election, such as date, location, election type, etc.\ (not otherwise included in the election manifest). + \item The election manifest file. + \item The baseline parameters: + \begin{itemize} + \item primes $p$ and $q$ and integer $r$ such that $p=qr+1$, + \item a generator $g$ of the order $q$ multiplicative subgroup $\Z_p^*$, + \item the number $n$ of election guardians, + \item the quorum threshold $k$ of guardians required to complete verification. + \end{itemize} + \item The parameter base hash $\HH_P$ computed from the parameters. + \item The base hash value $\HH_B$ computed from the above. + \item The commitments from each election guardian to each of their polynomial coefficients. + \item The proofs from each guardian of possession of each of the associated coefficients. + \item The election public keys $K$ and $\hat K$. + \item The additional public key $\kappa_i$ from each election guardian together with the proof of knowledge of the corresponding secret key. + \item The extended base hash value $\HH_E$ computed from the above. + \item Every encrypted ballot prepared in the election (whether cast or challenged): + \begin{itemize} + \item The selection encryption identifier $\id_B$, + \item the selection encryption identifier hash $\HH_I$, + \item all of the encrypted selections on each ballot, + \item the proofs that each such value is an encryption of either zero or one (or more generally, the proofs that these values satisfy the respective option selection limits), + \item the selection limit for each contest, + \item the proof that the number of selections made does not exceed the selection limit, + \item the ballot weight if given---if no weight is given, the weight is assumed to be 1, + \item the ballot style, + \item the device information for the device that encrypted the ballot, + \item the date and time of the ballot encryption, + \item the confirmation code produced for the ballot, + \item the status of the ballot (cast or challenged). + \end{itemize} + \item The decryption of each challenged ballot: + \begin{itemize} + \item The selections made on the ballot, + \item the plaintext representation of the selections, + \item proofs of each decryption or decryption nonces. + \end{itemize} + \item Tallies of each option in an election: + \begin{itemize} + \item The encrypted tally of each option, + \item full decryptions of each encrypted tally, + \item plaintext representations of each tally, + \item proofs of correct decryption of each tally. + \end{itemize} + \item Ordered lists of the ballots encrypted by each device. +\end{itemize} +The election record should also contain the encrypted contest data when such data is available. + +An election record should be digitally signed by election administrators together with the date of the signature. The entire election record and its digital signature should be published and made available for full download by any interested individuals. Tools should also be provided for easy look up of confirmation codes by voters. + +The exact organizational structure of the election record will be specified in a separate document. + + +% xreqj { + "section": "S3.7", + "text": "(Ref: S2.b) \"EGRI enables production of an 'Election Record'\"", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record is not a static fixed format. Different information is added and modified at different times.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record format must support appending new and updated information without invalidating signatures made previously.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record format must support inclusion-by-reference (eg a Ballot ID and hash value) of batches of Ballots. Rationale: For all but very small elections, it will not be practical to hold all Ballots in a single file. Neither will it be practical to place every Ballot in a separate file in a single directory.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records any information necessary and sufficient to uniquely identify and describe the election, such as date, location, election type, etc. that is not otherwise present in the Election Manifest.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records the Election Manifest", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records the Fixed Parameters `p`, `q`, `g`, and `r`.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records the Varying Parameters `n` and `k`.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records `H_P`", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records `H_B`", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Guardian `1..n`: a name or label", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Guardian `1..n`: the polynomial coefficients commitments", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Guardian `1..n`: the polynomial coefficients the proofs of knowledge", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Guardian `1..n`: the Guardian Communication Public Key", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records the Joint Vote Encryption Public Key", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records the Joint Ballot Data Encryption Public Key", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records `H_E`", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier $\\id_B$,", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier hash $\\HH_I$,", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: all of the encrypted selections on each ballot,", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the proofs that each such value is an encryption of either zero or one (or more generally, the proofs that these values satisfy the respective option selection limits),", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the effective Contest Selection Limit", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, for every Option Field, the effective Option Selection Limit", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the stated and effective selection limits applied to each contest,", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the proof that the number of selections made does not exceed the selection limit,", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight as originally submitted with the Voter Selections", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight already applied to the Ciphertexts", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Style index,", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: device information uniquely identifying the device used for encrypting of voter selections to produce the Ballot", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the date and time of the ballot encryption (as reported by the encrypting device) with no more than 1 second granularity", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the confirmation code produced for the Ballot", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `Cast` or `Challenged` state: the state of the ballot `Cast` or `Challenged`", + "sc": "nyi" } + +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `ChallengedDecrypted` state: all the information included for the Ballot in its `Challenged` state", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `ChallengedDecrypted` state: the selections made for every Contest Option", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `ChallengedDecrypted` state: the decrypted plaintext values of every Contest (Option or Additional) Data Field", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `ChallengedDecrypted` state: the proofs of correct decryption for every Contest (Option or Additional) Data Field and/or the Contest Data Field nonce.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot in the `ChallengedDecrypted` state: the date and time of the overall ballot decryption (as reported by the decrypting device)", + "sc": "nyi" } + +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: some identifier uniquely identifying the Ballot Decryption Operation", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: the set of Ballots \"marked for decryption\"", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: the hash of the ElectionRecord at the point the Ballot Decryption Operation was initiated", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the Guardian Index", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the date and time their participation was initiated", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the hash of their view of the ElectionRecord at the instant this operation was initiated", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: for every participating Guardian: device information uniquely identifying the device used for their secret key operation", + "sc": "nyi" } +% xtodo S3.7 The Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: the partial decryption of the Ballot Nonce +% xtodo S3.7 The Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: for every Contest, for every Option Field, the partial decryption +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: for every participating Guardian:", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation: for every participating Guardian:", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation:", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Ballot Decryption Operation:", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every set of Ballots in the `ChallengedDecrypted` state:", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the Guardian Index", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the date and time of the partial decryption", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: their partial decryptions", + "sc": "nyi" } + +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Tally: for every Contest: identifiers of the set of Ballots in the `Cast` state", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the encrypted tally (eq. 79 pg 44)", + "sc": "nyi" } +% xtodo S3.7The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the partial decryption from each available Guardian? +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the full verifiable decryption of the encrypted tally", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the tally exponent", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: plaintext representation of the tally", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: proof of correct decryption of the tally", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records information uniquely identifying each device used for encryption of voter selections to produce any Ballot", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records an ordered list of the Ballots encrypted by each device.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "The Election Record records the encrypted contest data, when available.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "EGRS enables the Election Record to be produced in a format suitable for digitally signing.", + "sc": "nyi" } +% xnote S3.7 Josh 2025-02-25, 2025-03-18: All Administrator Key operations are out of scope for EGRI: xreq S3.7 EGRS enables the Election Administrator to digitally sign the Election Record. +% xreqj { + "section": "S3.7", + "text": "EGRS enables the Election Record to be published in a format suitable to the needs of Verifier applications. For example, a directory of files accessible via HTTPS supporting efficient random access.", + "sc": "nyi" } +% xreqj { + "section": "S3.7", + "text": "EGRS enables the Election Record to be published in a format suitable for archiving or bulk downloading by any interested individuals. For example, the compressed .zip and/or .tar.gz formats.", + "sc": "nyi" } +% xnote S3.7 Josh 2025-02-25, 2025-03-18, 2025-03-19: All Administrator Key operations are out of scope for EGRI: EGDS pg 56 "Election Administrators" is plural, but a single Election Administrator is referred to everywhere else a procedure is described. + +\pagebreak + +% xxxx VxGNBA5Pt0/vKoLlJidUH+VQtl037T7IsgNJ9kWkbfYyiyyy7Gw35ed3QUj1FhehQ6hDLA4KkWLc +% xxxx APm5Ql5qv357QHKQR4VZGvADcqGR3TkEbr+jHorZmXdOzDM+7zMjIcXdeKuPhqN5sTs5D+JeXaWg +% xxxx 8Wnpim8bug8NFgIZsaQJ8HlmIlHx2DMY/7ANuaXpQ5zYoqjMQ3gqeE5Klvg/8XN2CSYtWkM3DF3Z +% xxxx RoGveov4qdAzOv+TgewsT9i1ttU87+rLsNh9uaGEayvBrR7Z/ZOuihfNzeKZ/9WUyJHS6Gr6Q4XF +% xxxx JDVUpBmLjt1r/6MjvmzsfnTTdWfaLYqShk66D8W1mE6HvB0W0aAps7hn8Znnq2Vh4JRooz86MSCE +% xxxx 3oPEiLQ7mj8CZr/j8nAXxLSLjhsHkOk6bueZM1LbH+Z2Etme1MWZU+QVELXriA2g3YQPD02QcdRc +% xxxx m5WFAcr6dET94oPK0O/uiXDVFAhENdxDgmdwGnUII23RZ4AB51w/Gif1QiCSel0MTN7VESULZ4xy +% xxxx +DoklKW5IqAtHpQrrLBqijBEAHjtsRiSGBZ1cjt0r6Spg40bu2ge0ymrNWnjBMu2QGabXLSdrp7S +% xxxx /Ndx3ROWBlGCeOiv8reNBSATfMo+h6LFVOf8VsUqnPPYEUwtltdMOr1gy4ZjevwHJCDzw4Hw1Alm +% xxxx Jg9KIsXD13JpkJSqe794Isw4/Nn7fgokjfN0PzPLO8HpOvEPfMd/jFANitENxU+hvE9CaDq4ILX0 +% xxxx +% xxxx +% xxxx ---- S4 Pre-Encrypted Ballots (Optional) + +\section{Pre-Encrypted Ballots (Optional)}\label{sec:pre-encrypted} + +In typical use, \EG ballots are encrypted after the voter selections are known. However, there are several common scenarios in which it is desirable to perform the encryption before the selections of a voter are known. These include Vote-by-Mail, pre-printed ballots for use in precincts with central count or minimal in-precinct equipment, and back-ups for precincts which ordinarily perform encryption on demand. Ordinary and pre-encrypted ballots can be tallied together so that it is not revealed which votes came from which mode. Support for pre-encrypted ballots should be regarded as optional and may not be included in all implementations of \EG. + +With pre-encrypted ballots, each possible selection on a ballot is individually encrypted in advance; and the selections made by the voter indicate which encryptions are used. + +\EG requires two applications to support pre-encrypted ballots: a \emph{ballot encrypting tool} to provide data to enable printing of blank ballots and a companion \emph{ballot recording tool} to receive information about selections made on pre-printed ballots and produce data compatible with the \EG election record. + +As Section~\ref{sec:identifier} shows for regular ballots, each pre-encrypted ballot also has a $256$-bit selection encryption identifier $\id_B$ that is chosen uniformly at random and a corresponding identifier hash $\HH_I = H(\HH_E; \mathtt{0x20}, \id_B)$. + +% xreqj { + "section": "S4", + "text": "EGRI Preencrypted Ballots enable the vote-by-mail scenario.", + "sc": "na" } +% xreqj { + "section": "S4", + "text": "EGRI Preencrypted Ballots enable the \"precincts with central count or minimal in-precinct equipment\" scenario.", + "sc": "na" } +% xreqj { + "section": "S4", + "text": "EGRI Preencrypted Ballots enable back-ups for precincts which ordinarily perform encryption on demand.", + "sc": "na" } +% xreqj { + "section": "S4", + "text": "EGRI enables ordinary and Preencrypted Ballots to be tallied together.", + "sc": "nyi" } +% xreqj { + "section": "S4", + "text": "When ordinary and Preencrypted Ballots are tallied together, it does not reveal which votes came from which mode.", + "sc": "nyi" } +% xreqj { + "section": "S4", + "text": "The Election Manifest contains a configuration setting indicating whether Preencrypted Ballots are enabled for that election.", + "sc": "nyi" } +% xreqj { + "section": "S4", + "text": "If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI allows to produce data necessary to print Preencrypted Ballots. Note: Referred to in the EGRS as the \"Ballot Encryption Tool\".", + "sc": "nyi" } +% xreqj { + "section": "S4", + "text": "EGRI refuses to generate any Preencrypted Ballots unless the Election Manifest indicates Preencrypted Ballots are enabled.", + "sc": "nyi" } +% xreqj { + "section": "S4", + "text": "EGRI allows to record Voter Selections made on Preencrypted Ballots, if the Election Manifest indicates Preencrypted Ballots are enabled.", + "sc": "nyi" } +% xreqj { + "section": "S4", + "text": "If the Election Manifest indicates Preencrypted Ballots are not enabled, EGRI refuses to record any Voter Selections made on Preencrypted Ballots.", + "sc": "nyi" } +% xreqj { + "section": "S4", + "text": "For each Preencrypted Ballot generated, EGRI computes the Selection Encryption Identifier `id_B` as described in (Ref: S3.3.2.a).", + "sc": "nyi" } +% xreqj { + "section": "S4", + "text": "For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32 (Ref: S3.3.2.a).", + "sc": "nyi" } + +% xxxx n42hCbTCP22segXf3a5WwiDvFRC5yAeEJM2HMWZViPDf4xu42v4IN+gck4yRtVFWjblyoAemJcNz +% xxxx pznY53vE6xa4sgVDH9jZGnOjLGeT7KjvG7SVUfxNVfJpvhSgdNzn1FDydJ/P4MO6BvpGJuRBi5VG +% xxxx BimeqAdxC8L7CCMtutFrTN0aTzVBgMa3FrtFnOP+rsWqmzJHjqR3bzwtOo/ojH/kf3EMUJ5UX9j7 +% xxxx uvY1Iq9d8RQehi2g2OKr9QawKtfaUeQAir1j4jNgZmpQGD6kcuqW0ziCPDDOzXhRVoORmK8SrcNW +% xxxx yYZMJ7c+pIhCHVUUPRjvsGbCG/Dbwvuz8kWfn/VzBXDFsAoFu3EhUoGFa0mdSoNx4GTBSsswA7/3 +% xxxx Nu4u0gAKoas786Ej610gaE0rzfkrjuK8c1c96mCnFg3LHo5/n1aRTb2hZjNg5zRVn9QipULyY3cj +% xxxx QmuM4ecOxQZSf40x7F3uNbJJsHPZHaLISPsh1vDmtJA0xVApFuF9Z7vaLQK7yghPysv280cwNstd +% xxxx i947KkxOgXwbfhCgl3pnBtoFOHiWJz3/RwIjIa7VHB6sq6MuuBrTw1zSbEVVNirHLWeRUToHrAzh +% xxxx vmQdqSVboZFPI3mzKPoLBmXEq+9qodkgYJCaaQtjTbHuASPFPQNL8XO8qmVJ+IsBTPdhb7KzqiSL +% xxxx bL9KxJtc6MipqYa+iRdF+nWoUqZkNNYOh3qmahXvK+Qb/6IgmKpQXW11XcEfGPm0Ps859dQ3a5n2 +% xxxx +% xxxx +% xxxx ---- S4.1 Format of Pre-Encrypted Ballots + +\subsection{Format of Pre-Encrypted Ballots}\label{sec:preenc_format} +Each selectable option within each contest is associated with a vector $\Psi$ of encryptions $E_j$---with one encryption for each selectable option within the contest. Selectable options in a contest have a unique order determined by their option indices in increasing order. Let $i$ be the position of the selectable option in this ordering (not necessarily identical to its option index). +In its \emph{normal} form and for a contest with $m$ selection options, this vector + +% xxxx ---- eq. 112 + +\begin{equation}\label{eq:encvector} + \Psi_{i,m}=\langle E_1,E_2,\ldots,E_m\rangle +\end{equation} +includes an encryption $E_i=(\alpha_i,\beta_i) = \Enc(1; \xi_i)$ of one in the vector position $i$ associated with the selection made and encryptions $E_j=(\alpha_j,\beta_j) = \Enc(0, \xi_j)$ of zero in every other position $1\leq j \leq m$, $j\neq i$, where $\xi_i$ and the $\xi_j$ are (pseudo-)random encryption nonces. For example, the vector $\Psi_{2,4}$ associated with selecting the second option in a contest with a total of $m=4$ selection options is $\Psi_{2,4} = \langle \Enc(0; \xi_1), \Enc(1; \xi_2), \Enc(0; \xi_3), \Enc(0; \xi_4)\rangle$. This corresponds precisely to the standard \EG encryption of a vote for the same option. There is also a \emph{null} form $\Psi_{0,m} = \langle \Enc(0; \xi_1), \Enc(0; \xi_2), \dots, \Enc(0; \xi_m)\rangle$ which is the same form except that all values are encryptions of zero. + +The principal difference between a pre-encrypted ballot and a standard \EG ballot is that while standard \EG has a single vector of encryptions for each contest, here we have a vector of encryptions for each selectable option in each contest (generally including the possibility of an undervote in which no selections are made). Another difference is that while a standard \EG contest encryption can contain multiple selections, each vector of pre-encryptions represents at most one selection. However, in a contest where a voter is allowed to make multiple selections, multiple pre-encryption vectors can be combined to form a single contest encryption vector with multiple encryptions of one that precisely matches the standard \EG format. + +% todo S4.1 pg. 57 QUESTION: Mentions a "vector of pre-encryptions" and "pre-encryption vectors". Are these the same thing as a "vector `Phi` of encryptions"? + +% xnote S4.5 pg. 63 EGRS states that verifiers must verify that "All short codes shown to voters are correctly computed from selection hashes in the election record", which implies that all "short codes shown to voters" are published in the Election Record. +% xnote S4.5 pg. 63 EGRS states that verifiers must verify that "All short codes shown to voters are correctly computed from selection hashes in the election record", which implies that all Selection Hashes are published in the Election Record. +% xnote S4.5 pg. 63 EGRS states that verifiers must verify that "selection hashes in the election [are] correctly computed from the pre-encryption vectors published in [pg. 64] the election record.", which implies that all pre-encryption vectors are published in the Election Record. + +% xtodo [S4.5 pg. 63] "For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected" + + + +% xreqj { + "section": "S4.1", + "text": "pg. 57 EGRI `Preencrypted Ballot`, for every Contest in the BallotStyle, for every Contest Option plus an additional null selection, computes a vector `Psi_{i,m}` of length `m`, where `m` is equal to the number of Contest Options and `i` is the Contest Option index. Note1: This is captial `Psi`. Note2: Although `m` is denoted here as a subscript, it is not an index into a second dimension.", + "sc": "nyi" } +% xreqj { + "section": "S4.1", + "text": "pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` for `0 <= i <= m`, where `m` is equal to the number of Contest Options, `i = 0` corresponds to the null vote, and for `1 <= i` `i` is the corresponding Option Index.", + "sc": "nyi" } +% xreqj { + "section": "S4.1", + "text": "pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `1 <= i` as a length `m` vector having an encryption of `1` at (1-based) position `i`, and encryptions of `0` at all other options. Note: This corresponds to non-null vote cases.", + "sc": "nyi" } +% xreqj { + "section": "S4.1", + "text": "pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `i = 0` as a length `m` vector of encryptions of `0`. Note: This corresponds to the null vote case.", + "sc": "nyi" } + +% xreqj { + "section": "S4.1", + "text": "pg. 57 Note: EGRI `PreencryptedBallot`, when encrypting ContestOption vectors `Psi_{j,m}`, the encryption nonce `xi_{i,j}` is derived from `H_I` and `xi_B` as specified in eq. 121 (Ref: S4.2.1).", + "sc": "nyi" } +% xreqj { + "section": "S4.1", + "text": "pg. 57 For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in [eq. 121 pg. 61].", + "sc": "nyi" } + +% xtodo S4.1 pg. 58 "in a contest where a voter is allowed to make multiple selections, multiple pre-encryption vectors can be combined to form a single contest encryption vector with multiple encryptions of one that precisely matches the standard \EG format." +% xtodo S4.1 pg. 58 QUESTION: How are they "combined"? E.g., are these multiple encryptions combined such that there is still a single vector `Psi_{i,m}` for Option `i`, or are there additional Option vectors? Note: [S4.1.4 pg. 63] says "For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected", which seems to imply they are multiplied. +% xtodo S4.1 pg. 58 QUESTION: What about when the effective Option Selection Limit is greater than `1`? Do all possible assignments within the effective (Option and Contest) Selection Limits require distinct Selection Vectors and Selection Hashes? E.g. say a Contest has two options and the Contest and Option Selection Limits are all `5`. How is the selection vector constructed which allows the voter to apply a value of `3` to Option 1 and `2` to Option 2? +% xtodo S4.1 pg. 58 QUESTION: How to these "multiple pre-encryption vectors" map to "the j-th selection vector" and "the k-th encryption" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}? + +% xxxx 9EdEGzv+kNb72YaKJRNdgBmeX2pEPgfQNREEONW65eeCKz5lHJcd8xQYTXIRjy29R5lRmYLI9Won +% xxxx FaBqfdisptALRMr2FwXFnJXKpgkmtsyC29ls0aVIeGJlvhS4aK8DBvw0AwI26F+DpnJi2p0vpu2S +% xxxx +/XwqMFdSyahwYop0RwDRrTz7gB5UobRjb7K2XvWZnsOyaBiBKIU5COzJ2Y5eCu7TL8eeDmmOUkz +% xxxx FktklIxVO++lV0MBISg0vWof7Wd01Ph6+DWATZgkfmEljE1qKvR7wx+HD5gLqgWm7FI+5Sqv2JWW +% xxxx F452bRROB2PO+58cYvXzo1llbRNuaMS8+yG7+DKp3CXth/d0wUg7J40STpvdyFDMG8uD6iE02HyV +% xxxx iAw9Di6m/em82NthGufbN/nzXib9aFgoLzy2v/JtwzRSQimeZ/1Mjy6WNez26jOz5EB86f5Z0Jrg +% xxxx i2Ecz3CdflPnnWB/2on8IqrXFwBmGSBrCmCszQQIdjjlgH7bvYr9YjLTdLQLqWYyz63MQD/rjuz5 +% xxxx H1teBxoFB27gFXc3f3PR+ypgoXKbKcEwasW0VEbfFvh1Yz0ABXS6NRekixjajYDIAhhZvLmhkh4T +% xxxx lfIcCao6UmyxZRT4Xw32UUDXJ6pBhvArqSfgBEb8HP8aKFYx7CnIPB9W4QN5RnaVypPC0Nc7sSjK +% xxxx G0lFB5rYj26VhNuF+uSdGxu5Lbc+ED9A3iT7q3XT1zC4hmDKaUf6CtzlnouuV5zA/fxF4Sd1iDv7 +% xxxx +% xxxx +% xxxx ---- S4.1.1 Selection Hash + +\subsubsection{Selection Hash}\label{sec:selectionhash_pre} +Each pre-encrypted vector of a pre-encrypted ballot is hashed using the \EG hash function $H$ (specified in detail in Section~\ref{sec:hashing}) to form a \emph{selection hash}. For all selectable options, i.e., for each option at position $i$ in the contest with $1\leq i \leq m$, the hash value $\psi_i$ of the selection vector $\Psi_{i,m} = \langle E_1,E_2,\ldots,E_m\rangle$ is computed as + +% xxxx ---- eq. 113 + +\begin{equation}\label{eq:selectionhash_pre} + \psi_i = H(\HH_I; \mathtt{0x40}, \alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m), +\end{equation} +where $E_i = (\alpha_i, \beta_i)$ is an encryption of one and $E_j = (\alpha_j, \beta_j)$ is an encryption of zero for $j \neq i$. + +% xreqj { + "section": "S4.1.1", + "text": "pg. 58 EGRI computes the Selection Hash `psi_i` for a single Option `i` according to [eq. 115 pg. 58]", + "sc": "nyi" } + +In a contest with a selection limit of $L$, an additional $L$ null vectors are hashed to obtain + +% xxxx ---- eq. 114 + +\begin{equation}\label{eq:nullhash_pre} + \psi_{m+\ell}=H(\HH_I; \mathtt{0x40}, \alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m), +\end{equation} +where all $E_i = (\alpha_i, \beta_i)$ are encyptions of zero and $1\leq \ell \leq L$. + +% xreqj { + "section": "S4.1.1", + "text": "pg. 58 \"In a contest with a selection limit of L, an additional L null vectors are hashed\" \"where all $E_i = (\\alpha_i, \\beta_i)$ are encyptions of zero and `1 <= l <= L`.\"", + "sc": "nyi" } +% xreqj { + "section": "S4.1.1", + "text": "pg. 58 EGRI computes the Selection Hash `psi_{m+l}` for an additional L null vector according to [eq. 114 pg. 58]", + "sc": "nyi" } + +% xtodo S4.1.1 pg. 58 QUESTION: S4.1 pg. 57 designates the "null form" explicitly as `Psi_{0,m}`, but (eq. 114 pg. 58) refers to them as `psi_{m+l}` where `1 <= l <= L`. How to these multiple pre-encryption vectors map to "the j-th selection vector" and "the k-th encryption" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}? + +% xxxx jTeCITq1n0zJGFogu+fkDQSDH07EdLlCSSUQIt3X50Tj/lBesQG4xhFSFtYY0IsIV+nT56IMMtLJ +% xxxx +8FqpHP6BmsN6Jt10YwMrcz4aOzwZwEk+RIZnQLgsaBTxg9CaOp2hvzBJCfQWLvW7QvvVImrgGh/ +% xxxx Lak1P1lATd2/5PexzaHP4lBoaRgxdd3QVRLRkTf7f+/PofDC7xv9eqoxWGot57+xXcnQNukXTED9 +% xxxx poyr9kQLoOK5Z9Htc7A/yh3Euh7D4CTU91Vq5WpVLX3Gcpy3+VMquTgq0ozfC7Kqy8lHoXK0+mEr +% xxxx FTkbK/HxMck6sLCdKkpm6I1w6HvEw+gOFDXmkQtJUX1OoYvFUcDaXz+JNA4T1bUKZHJLYm1kui68 +% xxxx xonYg2zyElu+7tZWu0lGFifddG9VuhybxyfmzGNJ9ycvspasRZ2WkvS76r2cmKsY0kW81hbmCgs8 +% xxxx mvk2vIrCkEi3gicQPms1t0T8BZkgTUhOl35bwlJzrfRcG3wACxHSmN6T5ASaw4opmn5DwGl4JZMJ +% xxxx Elag2awHtO5bO8BSCWHrjEYbWew+a5fEWgNSGAlSczUhZT1XkYHo5S74txvz2AKvWEKNtyepW5NG +% xxxx gVuquD1Pc1ukZM/vPA6u6+fGyeCYnLl+wHlS75eo7XcBU8pxA5ocSRDmKIoio/sMc4e5u6Q2xlcI +% xxxx XAshp4zgwUNgMk7SkbszLR/GGTc/mSZImtH7jW4z2BoSZFcIRE2B/8O/SUGViXgT1IAQ8CDhL3+L +% xxxx +% xxxx +% xxxx ---- S4.1.2 Contest Hash + +\subsubsection{Contest Hash}\label{sec:contesthash_pre} +All of the selection hashes within each contest will ultimately be hashed {\em in sorted order}\footnote{It is critical that these selection hashes be sorted in order to purge the information about which hash is associated with each selection.} to form the \emph{contest hash} of that contest. Each contest on a ballot has a unique position determined by the position of its contest index in the list of contest indices in increasing order. The contest hash for the $l$-th contest (with label $\Lambda_l$) on the ballot is computed as + +% xxxx ---- eq. 115 + +\begin{equation}\label{eq:contesthash_pre} + \chi_l = H(\HH_I; \mathtt{0x41}, \indc(\Lambda_l), \psi_{\pi(1)},\psi_{\pi(2)},\ldots,\psi_{\pi(m+L)}), +\end{equation} +where $\pi$ is a permutation that represents the sorting of the selection hashes. This means that contests are \emph{not} hashed in the order given by the contest indices, but instead $\pi(i)<\pi(j)$ implies that $\psi_{\pi(i)}<\psi_{\pi(j)}$ (when hash values are interpreted as integers in big endian byte order). +The sorting is required so that the order of the selection hashes $\psi_1,\psi_2,\ldots,\psi_m$ does not reveal the contents of the encryptions that are used to generate the hashes. + +% xreqj { + "section": "S4.1.2", + "text": "pg. 58 EGRI sorts all Selection Hashes in a contest prior to their use in the computation of the Contest Hash.", + "sc": "nyi" } +% xreqj { + "section": "S4.1.2", + "text": "pg. 58 EGRI computes the Contest Hash `chi_l` according to [eq. 115 pg. 58]", + "sc": "nyi" } + +% xxxx j5h3I8BdNrGsOuQVIXvGbuSkZTKEm3sRsAZsZVu2NrXr2+q5LGxb6ix4icXEVB24voXfKFdzM6Xe +% xxxx Epr+iTexH3KIO58lsxWzJ4U65IGvmLxQxR4dsLCt0GlgyoUTOh1XX9GzdbN0na2ZwI2anRHTz4q8 +% xxxx GWBp7kmpvejD3WdbAQZUrhTxgqnGoZLMzLPyBbECtxzHhwWGJ8pPnmsL/rYYOisJRD+VfMIHW1RW +% xxxx XhI99m/27ipxmu8hEXXI1/ZEvGEryVgMgQ8zkRJc0Mw4HwD1PSx0ruUUG8FmU4uD/eTogfnCanN8 +% xxxx xgkI3GUt/MSWgfl6HYFxKb38mbUlVJxgEPSy9yJAXzc7qpuz3r9TKJTOyrOeSaU2JT8vd8+ODRRg +% xxxx 3uyZYHeOAp4AZ1nbtCb1DJtCRuyfN+4NoThQpG5qXFdE5ylAAYN+/m5SgrbZNYLXHq1UMUk2eGQu +% xxxx mA9QfJhBi4JhlpVNttVEWxCJsTXgrZKQu7JIFpkivBkLAs4CRisldAuA4P7JWvqj7oKpwv3nHWYW +% xxxx Qp4t4dw5lNBkXCwNBX4uKF5IfKeSrO1omQ5DWinxYFFvGZtlCvTPEM4Si4i0djb2ACAh5OAc64o7 +% xxxx HcPwgVIs77agJ6hd/TB59yCSJeXtYsvZ5VY4s0WtnKSaN+ytoLstUVh8CYdYhVzRMWRd2fbMhB20 +% xxxx rFlIEY4WSfqdsOrsCVHSjp3qSvuzZGHUcw7pBym/60xibMoUC9LWuBV0p7egAfOpQk8ZmZsYzEyU +% xxxx +% xxxx +% xxxx ---- S4.1.3 Confirmation Code + +\subsubsection{Confirmation Code}\label{sec:confirmationcode_pre} +While contest hashes for pre-encrypted ballots are computed from selection hashes, which differs from the standard scenario for \EG described in Section~\ref{sec:confirmationcodes}, the computation of confirmation codes aligns with the previous case. The \emph{confirmation code} $\HH_C$ of a pre-encrypted ballot is generated as the hash of all the contest hashes on the ballot in sequential order. If there are $m_B$ contests on the ballot (in sequential order specified by their contest indices in the election manifest file), its confirmation code is computed as + +% xxxx ---- eq. 116 + +\begin{equation}\label{eq:ballothash_pre} + \HH_C=H(\HH_I; \mathtt{0x42}, \chi_1,\chi_2,\ldots, \chi_{m_B}, \B_{C}). +\end{equation} + +% xreqj { + "section": "S4.1.3", + "text": "pg. 58 EGRI computes the Confirmation Code `H_C` according to [eq. 116 pg. 58]", + "sc": "nyi" } + +% xxxx j0+H1aVE8xlhhsiazsiMIsZNNJmm1VfPvIHbekzrqtkNsoTGNWUE0PeasL0bPJi0otp+eA5C4bOC +% xxxx rpx6pxNqegvgFxi7Gp4VoKbw9KRcdiYAsp3zPCpvEtT5aCGEIoJsgs7HZPjZeLvzP1Nx33SfeWe1 +% xxxx EQWMPEtaaPTESm6WR9esLNNIJXZt3OtQm8pcsTThk2CALU5mW8udHCqwqJmZ/Bzngk1BTpWERKnZ +% xxxx Y3ikMdUGmRCuizSvz4jjITc644eI/w2LDWuHBIqXJmnZd5dKzwbp/0uaMyrLvZiySQmzu0LrjCLZ +% xxxx IhobdPf87CjMJZSx+NGXdbRoWrRJhgkQ8zB/1k3dfJ31avk3AlkMfTXmPwE1CeSr6l3Up9tHkjMq +% xxxx ujN8Cyhi+1eNM5+v/4Rq+7lukrVRE3UAUZCXRE3ymezicnCSRILtNddDjWjHoPa2+zcPqvLRgD+8 +% xxxx GtDHhxsyNg6qb3kBHAJY56RH2MOS06tT0bCfofgOi11HrL4CeF5Gs84ISZ0W9qBp8ugBEdoZfdjq +% xxxx 47Co9iTxl48ldBQeJ+aGtKRCr0hTIygAerauBi1jdDjkCklBpgey9QoIm6C+6jCtv9LLU4Xryehj +% xxxx +PNGKyGeSfOGk3dClZVf1xSqUtEQm/SaL7Tie6c5nj16Hp9qxgdUHHd4waXFbQek4FXKyGYFPuJU +% xxxx M93EwppzwNObomE7Y8My67fwLEp43ZtPE+tDhEZLptQN6JGYX2Ha4hTQ2+6fkrUPr66dFZRdel4d +% xxxx +% xxxx +% xxxx ---- S4.1.4 Ballot Chaining + +\subsubsection{Ballot Chaining}\label{sec:ballotchaining_pre} +Use of the chaining field input byte array $\B_{C}$ is the same as described in Section~\ref{sec:ballotchaining} to enable ballot chaining on the device generating pre-encrypted ballots. In particular, if simple chaining is used, the chain is initialized with + +% xxxx ---- eq. 117 + +\begin{equation}\label{eq:hashchain0_pre} +\HH_0=H(\HH_E; \mathtt{0x42}, \B_{C,0}) +\end{equation} + and the chain is closed with + +% xxxx ---- eq. 118 + +\begin{equation}\label{eq:hashchainclose_pre} +\overline{\HH} = H(\HH_E; \mathtt{0x42}, \overline\B_{C}), +\end{equation} +where $\B_{C,0}$ and $\overline\B_{C}$ are defined as in Section~\ref{sec:ballotchaining}. Note that the device information hash $\HH_{DI}$ is computed as in Section~\ref{sec:votingdevinfohash}, only with a different domain separation byte, i.e., + +% xxxx ---- eq. 119 + +\begin{equation}\label{eq:devinfohash_pre} +\HH_{DI} = H(H_E; \mathtt{0x43}, S_{\mathrm{device}}). +\end{equation} +Likewise, the hash computation for $\overline{B}_C$ has a different domain separation byte as well, i.e., + +% xxxx ---- eq. 120 + +\begin{equation}\label{eq:hashchaincloseBC_pre} +\overline\B_{C} = \mathtt{0x00000001}\parallel H(\HH_E; \mathtt{0x44}, \HH_\ell, \B_{C,0}). +\end{equation} + +A pre-encrypted ballot's hash will typically be printed directly on the ballot. +Ideally, two copies of the ballot hash will be included on each ballot with one remaining permanently with the ballot and the other in an immediately adjacent location on a removable tab. These removable ballot codes are intended to be retained by voters and can be used as an identifier to allow voters to look up their (encrypted) ballots in the election record and confirm that they include the proper \emph{short codes} as described below. + +% xreqj { + "section": "S4.1.4", + "text": "pg. 59 EGRI computes Ballot Chaining values for Preencrypted Ballots according to eq. 117-120 pg. 59.", + "sc": "nyi" } + +% xtodo S4.1.4 pg. 59 EGRS says "A pre-encrypted ballot’s hash will typically be printed directly on the ballot. Ideally, two copies of the ballot hash will be included on each ballot with one remaining permanently with the ballot and the other in an immediately adjacent location on a removable tab. These removable ballot codes are intended to be retained by voters and can be used as an identifier to allow voters to look up their (encrypted) ballots in the election record and confirm that they include the proper short codes as described below." + +% xxxx z8LboWuM08TMiQukJQTMBWvt3Z/KeRhX8cf/08UZvFUaaW4G3cmt8uNGqt8vy4fcryEeLn0aBEBb +% xxxx 0j7gBuR4R7834NH/H4fKqOJpsQZYPPyYVExqFe6L7OUwAm2Che6qXQfhMJwLnLzqezewRQEbLp14 +% xxxx A6bdM6Am2uoAeFUaJjwwlE5rH5FXZutt7rS7sOihL5XsA3h7Dz6DW4XlBcQAprBAE5XVHdzVwsn8 +% xxxx jFTeMbYKxW9UWQPtiWw1MIt2tabMz+eEmfcinInFZj1o08ZTLPZOIEh/nRXvY4WkKNWTmKoVtwNe +% xxxx FoCwTukKwb2LjCSWvRwaWbsH/hCRHUX+c2Q4XMS0z7iRZK/Zm6Tv2lUna2c+KVavzumOQ2kOp4C1 +% xxxx gJPcG0sFH5zKQQH/poiMx/C0yyYQ0l37Uvhtwksg8JfBlx8+5s9ZreEsqwgdu/5wJRmlRZJDDL3k +% xxxx 9CQzLmZtFdx76JyhY7z0QhJXXqIXxj6meKKWWP/GwtZIjmJYxXqFxvudO6gRaRVhedi/TokyhlNf +% xxxx Md/ClI9aSDAx92Jgw4MFQ+BGlUA9O25E8gb4I+i8DbEzD65uIzJZH+SXSH+ELdvJHgJNsv1Y8HdM +% xxxx vIJSuV6czhpJntaxMwNG+3wUy3c/5Y/zyKtWwqTFOc+5LqMs5qTnqNHjQDcRUFLYBbGgWIT6Ba0w +% xxxx oH/wXg0eJMlOLS9OQWwSCHifwomENi2BRq9frXe2SPZjCMyrtiABvUiMIrKlGzeHMOLAtRth4zlF +% xxxx +% xxxx +% xxxx ---- S4.1.5.a Short Codes + +\subsubsection{Short Codes}\label{sec:shortcodes_pre} +In any instantiation of pre-encrypted ballots, an additional \emph{hash trimming function} $\Omega$ must be provided. The hash trimming function takes as its input a selection hash, and its output is a \emph{short code}. As an example, $\Omega$ could produce the last byte of its input in a specified form. + For instance, a short code representation could be a pair of hex characters, a pair of letters from a 16-letter alphabet, a letter followed by a digit, or a three-digit number. The size of a short code does not need to be a single byte, but this is a convenient choice. Different vendors or jurisdictions might choose to distinguish themselves by using their own preferred short code formats, so the details of the short code format are intentionally left open. However, $\Omega$ must be completely specified in the election manifest so that a verifier can match its functionality. + +The hash trimming function $\Omega$ associates each selection on a ballot with a short code. \emph{The short codes on a ballot need not be unique.} However, it is required that the short codes within a contest be unique. If there is a collision of short codes within a contest, the ballot nonce should be changed to generate a new ballot. When a pre-encrypted ballot is presented to a voter, the short codes associated with each selection should be displayed beside the selection. If the ballot is cast by a voter, the short codes associated with selections made by the voter will be published as part of the election record. + +% xreqj { + "section": "S4.1.5.a", + "text": "pg. 59 If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI requires that the Election Manifest specify unambiguously the Hash Trimming Function `Omega` to be used for all Preencrypted Ballots generated for the election.", + "sc": "nyi" } +% xreqj { + "section": "S4.1.5.a", + "text": "pg. 59 EGRI provides one or more Hash Trimming Functions.", + "sc": "nyi" } + +% xxxx xX0HHbXqHsdqUeVCC4TH1mUvcSAWQ11Bd8jB46s65bDTVVvMAOz8Cx11RvGVBXfFva/+VX5iEOaE +% xxxx p9IkAaN46vfm6/Vk8w1vf7Rr1FSbp585RcRtFL+cXMA+HadfNIHB3jh8IVFHGNoPnSNYVNKBJxBk +% xxxx RzlOcQw3p8hazVEcvsXXDUYues5cbtlGRLv0nJm+kbbU8sLa5h+B7e+2n03TK29N01JHJ4tvO/On +% xxxx OUN0hSwCquxziuA6u5KGcf4Rw5N5dNSkm4nvuQQRO59gArbcNmCCzHoHzgKE1/1hRx25LZxzKQ46 +% xxxx fU9kLypah/DrahfPV7Bqgo+v6+/RIwOiVtw/7HcwMed1LxjhwpWZ5O/0/TXLjvkjEKkbt/CeN9KR +% xxxx VDU3VbGcc0aXkJRxTqEUUloBMLmcQEZf4298iy5aqglN8wW81gS4eKM6pKhPCRizp9ipsY8WKGbB +% xxxx 9s8FosMpAMw2fVJBP20h550igf+YFiyEiSm3kdtDfKZD892rlctEv4DjLLib1GqOsnvi+8qT6iV2 +% xxxx NEbaRa9DjiVrCEQwj7MFThD7zVmd7rt8K6FEooUlHPSbphTk7RJA3/JpJY+X7AK2QZKUNiHWZxup +% xxxx o+GefvMsL5VEKVYoAnhQwKsE9hN2STVB0PM0BsJ3LISIUpiY2CHvw93UqQNmUXPNGKXZwL43aC+p +% xxxx C2maBSoMCMxWnFhT7Wax3rPrazptCyY99yPy10j7zI+I/a9b/nk+a0sDT/nwrou1fJ6pM0H62LT/ +% xxxx +% xxxx +% xxxx ---- S4.1.5.b Short Codes - Undervotes. + +\paragraph{Undervotes.} +In addition to the short codes for each possible selection, short codes are provided to indicate undervotes.\footnote{It may not be necessary to print undervote short codes on ballots.} A short code for a null vote is generated from a vector of encryptions of zero. + +% xpage ---------------------------------------- pg. 60 ----------------------------------------- + +In a contest in which the voter may select only one option there will be a single pre-encrypted null vote and associated short code to indicate that the voter did not make a selection in that contest. In general, the number of null votes and short codes for a contest should match the selection limit of the contest. So, for example, a contest in which a voter may make three selections should have three null short codes. The short codes corresponding to the selections made by each voter will be published in the election record, so the use of null short codes allows the election record to not reveal undervotes. + +% xreqj { + "section": "S4.1.5.b", + "text": "If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides short codes to indicate undervotes", + "sc": "nyi" } + +% xxxx OaGSNNG9m1hfAj0WnaxIkkwKuzpTlJgzZd0Zd0Z3FyBYerR0TEBWvqarS2AlEKITdjGICi7D11Wa +% xxxx ilnQfT2pNOM1Ph2EOpsTYHbzSl/FFg5b3fKE5YtTZPD0AoDbZQvjAWBsLKymalyWMW961Rtu1r7b +% xxxx m7QdY3qCIUdCfyDDWYrDwUSRqnHGUzXK7aw0cfd5PIM1Qid+6fdHWJPEUzRYCUEhneIouzQD8ctm +% xxxx aQU+5Tef7BxENh5AZZ1r/r8nvNpnYHCMy79ympnS8Z6H8j6r3ing+yvAnmb8MI0OFAO5F+ORZsZg +% xxxx 0/oFZ8hp1H6oGlh0d0d0GunDYdAEbNZ97qYSx/k3OPKnjhNyq2OVWfhKkQSp2q2k2XNz0/w1sWjn +% xxxx 5OlNS2yQCh4nHS+25l2j7IQ3Wt+vnBJPAV6eB4XkSMLcDFrbc/uqAgGSc7eTgxB4x/GB6qF+MF9i +% xxxx yJgD7+hqQwuySWhLKuSv4O9gw6GXXRALVa7/Ix16zA1iSFqHfiWE6iZlSUCx15FgrjS67gt9orTU +% xxxx MTyZgTfaiiKHrsE2dU/wPGtbNXR8HqrMoyKuRB/UhLHQxwBqqLiKuMdVCSInDckNEmgRajcVMkKi +% xxxx UxXVnOR6Huw24cYlzzVWQzVXNdMb0FVtJOcGVYdrmEATkdmNeY7ZWsYdfJENkL+DRWmgUypxL3p/ +% xxxx nsw6Pie/EUaGXObzNH0J5mrsiJ9uGrDxgp6ORWBwaDMn0t7d+8oONOzXCk3OflWKf3mTvGNzonHu +% xxxx +% xxxx +% xxxx ---- S4.2 The Ballot Encrypting Tool + +\subsection{The Ballot Encrypting Tool} +The encrypting tool for pre-encrypted ballots takes as input parameters +\begin{itemize} + \item the election manifest, + \item a ballot style index, + \item the cryptographic parameters and the election vote encryption key $K$, + \item and a nonce encryption key (usually the election data encryption key $\hat K$). +\end{itemize} +The tool produces the following outputs---which can be used to construct a single pre-encrypted ballot.\footnote{Note that it will likely be desirable to construct a wrapper that can be called to produce data for a specified number of pre-encrypted ballots.} +\begin{itemize} + \item An encryption of the ballot nonce used to encrypt the ballot, encrypted using the nonce encryption key, + \item a selection hash value for each possible selection in the ballot style, + \item for each contest, additional null hash values corresponding in number to the contest selection limit, + \item a contest hash for each contest computed from the sorted list of selection hashes and null hashes for that contest, + \item and a confirmation code consisting of a hash of all of the contest hashes on the ballot. +\end{itemize} + +The encrypting tool operates as follows. + +First, it samples uniformly, at random, a $256$-bit selection encryption identifier $\id_B$ and computes the corresponding identifier hash $\HH_I = H(\HH_E; \mathtt{0x20}, \id_B)$, just like for regular ballots as described in Section~\ref{sec:identifier}. +Then, it generates a 256-bit ballot nonce $\xi_B$ for the ballot and encrypts this nonce with the nonce encryption key provided as shown in Section~\ref{sec:encrypt-ball-nonc}. + +Next, for each contest on the indicated ballot style, an encryption vector is produced for each selection within the contest (see Equation~\eqref{eq:encvector}). This encryption vector is deterministically derived from the ballot nonce $\xi_B$ and consists of an encryption of one in the position corresponding to the selection and an encryption of zero in all other positions in the contest (see Equation~\eqref{eq:selectionhash_pre}). Additionally, one or more null vectors consisting entirely of encryptions of zeros are produced (again deterministically from the ballot nonce) as in Equation~\eqref{eq:nullhash_pre}. The number of null vectors should match the selection limit $L$ of the contest. The selection hashes and null hashes (computed as described in Section~\ref{sec:selectionhash_pre}) are then sorted numerically and hashed together (in sorted order) to produce the contest hash as shown in Equation~\eqref{eq:contesthash_pre} in Section~\ref{sec:contesthash_pre}. + +Finally, the contest hashes are themselves hashed sequentially to form the ballot’s confirmation code according to Equation~\eqref{eq:ballothash_pre} in Section~\ref{sec:confirmationcode_pre}. + +% xreqj { + "section": "S4.2", + "text": "If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides a \"Ballot Encrypting Tool\" which operates as describedin S4.2", + "sc": "nyi" } + +% xxxx xmOcQP2/ugIjermaAIpF5NCc/XhwQS5K//Bqh2Kpnt/ViWVO3j6FR20+bGypa977Ewwu7ViLoakf +% xxxx dNbQbpYClpFJSgXB63Q01I7foGWe2HsYB+kNR47w5xsyumHs3QOPUFMPmxRY1uo/f0BR+yXt0cdO +% xxxx +jCD/f7ncxwQrmcBwUgwbYu5aOH/1Y4k+/tTI11E4DkNPIOAPlXBbhSggXiP9r912cBoCg0ezqZh +% xxxx BDq2oP6KkyfWbhZ/G/ErJUp21BvXM7+9pFzmwhPks4TQ6X04fMxCeTdE+SgzTFvrC+KbMLDmhKJy +% xxxx uYl1a+SVBhaSqgrtpECbhbZaF62mAUKxs3l6TOe+cKNvgDkUkGRQ+3LmCPUyQj99Ne8xA3ZdQycE +% xxxx hMbRlG1AO4ybwaOekxVRxRpAa5sSGndT3PYR6bq+46d93LDNYBVjJIUYG7SZoFyyFSGZaQBhqHyV +% xxxx JpktwiDRb8FZ9B7ZxIu5f/ckWW9Fyg9PLqBCojoPLBxB5C1YFEDc/whNulfs+JnnqeOiojcFOP3e +% xxxx dy7tBppq5ZNyqRfgpcJ93M1Gdp5N7EZPmTcgBnStJZGnjSqlhcuPPbP14uQwd3EGf2e0s7fAK2pP +% xxxx izLH2JIyJ/DkgArMmXXQlHPAKWiSVw2gRoRI38spyArTQ3xLJzh7PQEeJbJXEnekvFERRC+rGU2h +% xxxx VOf9qztWq7XtnqO7EEVqbu1TvENLkaA6DutbLviOJ89oj+n6FzECGwKgx+UUYNHb70eLTvWHfKSa +% xxxx +% xxxx +% xxxx ---- S4.2.1 Deterministic Nonce Derivation + +\subsubsection{Deterministic Nonce Derivation}\label{sec:noncegen_pre} +The process of deterministic encryption is guided by the ballot nonce $\xi_B$. Following the manifest discussed in Section~\ref{sec:manifest}, each contest has a unique index $i$ and, within each contest, each possible selection has a unique index $j$. The nonce used within the $i$-th contest and within that the $j$-th selection vector to form the $k$-th encryption is + +% xxxx ---- eq. 121 + +\begin{equation}\label{eq:noncegen_pre} + \xi_{i,j,k}=H_q(\HH_I; \mathtt{0x45},i,j,k,\xi_B). +\end{equation} +Note that the nonce $\xi_{i,j,k}$ will be used to encrypt a one whenever $j=k$ and a zero whenever $j \neq k$. Note also that some of the selection labels will represent null votes. If labels for null votes are not included within the manifest file, the sequence of indices should be extended accordingly. + +The output of the encryption tool includes the encrypted ballot nonce (computed as described in Section~\ref{sec:encrypt-ball-nonc}), the selection hash corresponding to each selection on the ballot (including nulls), the contest hashes, and the confirmation code. + +% xreqj { + "section": "S4.2.1", + "text": "If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI produces nonces deterministically as described in eq. 121", + "sc": "nyi" } + +% xxxx x7Hxj7/owx7uEGQ6UF95Uq4SMLmn2yNcQ774Ob5MpwW7IZ5flIXMgYcYLFsXDK7EuHqBZRXRhMrI +% xxxx Cs7t97hqoD6NTv90JxFVQH6ruzi6hRH32juxBwBsaGkYvy5W4WMr36EUC++nvvA+HVhV6ecD9s57 +% xxxx PED0lzuUz7rJkkZoY97DY7ca91mku4t3fT8vAx7/Oe4Nih0ee34dL/nRl3Y+xwp1N/t7Wf1SdWB/ +% xxxx M8aQVEAsV/NgOIET1SAhdbNExE9K9z0AcXdnQBkMjei9esGrasTrikYpf53BW+GG/m+HvbSH1V1f +% xxxx XKhl3khg0SnkkXlco1tFc3wF8tyKeTutvpWSxf6IYjOCXO8D+a2VAuXbKy+HYkyKzoaCPpgB1gBa +% xxxx OtgqI5mq7OkHaB85BQqIWXHAkw1O5QkE2G874ZImudEExxwent8XRkZdUWQL/sYYbOGFNVJ+5vPm +% xxxx VOoS8km8KHC2jNNlHUqSdETjZpPw7gJgsTRX+dsO2420L/x4AZAAYkAa3VQDDnuUkhfXcxUJr2xG +% xxxx fyKKkN+kjfGZJ2r5xC64qPJLuLbafHX6cSum+cIOMd0uunIyO25YPEsM/WOlwNFc5h9aGY5lP3WW +% xxxx G49CIMmjJ7TF9wGDlSXFHuLKtGN06Ho5oFZPctW6dBYuFrXj4kzJURqlgz+hXpt7pXMnoOUFrv1/ +% xxxx xumLXxn4D6WLsIPcqVguAvL/JpR0eXae+p5WFmTPoXPH5jvrwv5thrtf1p46qQANcQDp4dgWKhTg +% xxxx +% xxxx +% xxxx ---- S4.2.2 Using the Ballot Encrypting Tool + +\subsubsection{Using the Ballot Encrypting Tool} +It is the responsibility of the entity that calls the encryption tool to produce short codes from selection hashes. This must be done in a deterministic, repeatable fashion using a hash trimming function $\Omega$. As suggested above, one option is to use a human-friendly representation of the final byte (or bytes) of each selection hash. Another would be to use an ordinal integer to indicate where in the sorted order of selection hashes within each contest each selection falls. (For example, if a contest has four possible selections, the integers 1, 2, 3, 4, 5 could be placed beside each selection – including “none” to indicate the sorted position of each of the five selection hashes.) This flexibility allows vendors to distinguish themselves with different ballot presentations and for vendors and jurisdictions to choose presentations that best accommodate their voters. + +Unless the short codes in a particular instantiation are quite long, it is likely that there will be occasional collisions of short codes within a contest. It is the responsibility of the entity that calls the ballot encryption tool to ensure that no ballot is produced with a short code that is repeated within a contest. If a collision is found, the caller simply discards this ballot data and calls the ballot encryption tool again. The caller may also choose to discard ballot data if a short code is repeated anywhere within a ballot or if two short codes---within a contest or across a ballot-–-meet some definition of similarity. The caller is free to discard data and obtain a new ballot pre-encryption as often as it likes.\footnote{Note that a malicious encryption wrapper could bias the printed ballots by, for instance, only using encryptions in which a particular selection’s hash is always numerically first within a contest. However, a malicious wrapper already knows the associations between the selection hashes, the short codes, and the actual selections, so it is not clear how a malicious wrapper could benefit from creating a bias.} + +% xreqj { + "section": "S4.2.2", + "text": "[TODO: Pre-Encrypted Ballots]", + "sc": "" } + +% xxxx Pz317QD9auw2OXxFusLOFyQznC+MGX/y9xUYRF9AeIY1O5XoeDihVt5hXCX+nFXPJGUctQL7LzMz +% xxxx +rngtE/GtC1sLyiXjHC+juTCLOEdNtI4ZXbH1qRXImWZ3MhSDKZ8FCii6JvrbaPXhCzQYzTLyo4p +% xxxx 76S6hvyjdPRWHCT6Pu1LXIm+Cntql8UziTC2r6tD0cfwj8454TtMF0HWES+cOLO8tRFFbQlKwIDw +% xxxx dGJgSUJ2fKn37Oc1dHkpwxopTjtmZ4rjM4awQLFDFXFVnI4jLkAZKS0xBdoe+ziTFaIXsxHmombt +% xxxx UX1DdKfM5wvmtUxGTFhYbF9UhJlMMZURRo6JRpyoh1L62Mj2nlRKMdpo34+u4sahvIK4qL+fl/YQ +% xxxx FEjV8xBe0bDt28I+RAGAQDmzrpSLmo1IC9/UHz5jXv2yjIP4R+7STehj6trdJWSBtE4TdEfDlJSs +% xxxx lQG9njKCI+rgx4ZjvYo3vVvgfwXVIQisHymvLbt4/Fk5gDWXwsts++apDXZ7pvjE0BegxbnsZzJz +% xxxx 7dXKxWDKD4kAjfUxtTJji6deceZl/46taFrNJIwl8AA18cGFuHe0WHPSHidSkuOYh+eQD89QYaTJ +% xxxx 8eAhuznAzuDjg1HO9Ec+4IHTCUzunGEl+vFmebLhqVYYQuweQ+pTJeqUZ/q4d40vCJh3l5SlV1Zw +% xxxx WoNnAisWGcgX8hJI7S9MJlbIBwmaxcKWq6baIdWgkObxq+od8ZVgutcICxxdTpPNEw6FoyBA9Wet +% xxxx +% xxxx +% xxxx ---- S4.3 The Ballot Recording Tool + +\subsection{The Ballot Recording Tool} +The ballot recording tool receives an election manifest, an identifier for a ballot style, the decrypted ballot nonce $\xi_B$, and, for a cast ballot, all the selections made by the voter. The recording tool uses the ballot nonce $\xi_B$ to regenerate all of the encryptions on the ballot. For a cast ballot, the tool then isolates the pre-encryptions corresponding to the selections made by the voter and, using the encryption nonces derived from the ballot nonce, generates proofs of ballot-correctness as in standard \EG section~\ref{sec:proofsballotcorrectness}. + +Note that if a contest selection limit is greater than one, the recording tool homomorphically combines the selected pre-encryption vectors corresponding to the selections made to produce a single vector of encrypted selections. The selected pre-encryption vectors are combined by componentwise multiplication (modulo $p$), and the derived encryption nonces $\xi_{i,j,k}$ are added (modulo $q$) to create suitable nonces for this combined pre-encryption vector. These derived nonces will be necessary to form zero-knowledge proofs that the associated encryption vectors are well-formed. + +For each uncast (implicitly or explicitly challenged) ballot, the recording tool returns the encryption nonces that enable the encryptions to be opened and checked. Releasing the individual encryption nonces instead of the ballot nonce enables selective decryption of specific contests only as explained for the standard \EG case in Section~\ref{sec:decrypt_challenged}. + +% xreqj { + "section": "S4.3", + "text": "[TODO: Pre-Encrypted Ballots]", + "sc": "nyi" } + +% xxxx mnBOLPI02IjMrlwPS3T/ScPyJmHN02530SnGVu+DnhuzLuj0Ek6b8DpMOJu+LQbNvdgUTqu8RibA +% xxxx cRcgA3cd0ipTIv+LcPLDaSdvRXNXMXUJYP7s4nl9YnXM02kOJbYrxE/jK8owfSq6dzUW3MaJpGsI +% xxxx tu2AJ/ASheV3rwvfI8LJR6ciOrmzpaTQI1FRQxEP39bXmuDYc1mZPaGbfZWPhb5T6aD9IyTmBj2F +% xxxx yB0UA377t+8DFJHzT1/EZMCAHz/ZFu9T7VPiThBUFvn1NPwb77A+X1TOS+zh2sJPvSJV55t+4PGY +% xxxx dpyT5zW704hJkqt0u6na/ryyRp31khONvRJ3p4It8wv6hoJtiJkAx2mqt0JDjgndgNycR3J83cS8 +% xxxx gw9mVtyS0BnTuAC/nR/jjAD8Wz/6TU50zpY+teLGuv75H5XfL5NKPmNufTp9koxoj69l2iEZRTQe +% xxxx JCxDZjmQmqkeJKAQd2Ud3TLLvI4ddD7TXreqVjI9Sr62MhCh2PijHGAH5BkVHB+CO3NhzxRCCl2G +% xxxx 7ZS/I/wVbp2uc35zcbBDQZTabvIPm4KxV2wKNUncCZbOsUsduqMKAVC7YAG9JXR69uDy9ydjXTLF +% xxxx JEaOmxyhlMqXzeGci/Py+PEadwJm3MsR0NRJMXmrvNH1paysASxyv04pOzEdJZMk+YQYlmlU//3w +% xxxx vd9Y9gzYtXXX/zICkTRqzuJUOUHyLhpnwQAUYKhUo6EhHruiP9qf1sJs/Eako5cQlxGAY8wsMUgn +% xxxx +% xxxx +% xxxx ---- S4.3.1 Using the Recording Tool + +\subsubsection{Using the Recording Tool} +A wrapper for the recording tool takes one or more encrypted ballot nonces and obtains the decryption(s) by interacting with guardians, an administrator, or a local database and then calls the recording tool with each decrypted nonce and ballot style – and for a cast ballot, the voter selections. + +If the ballot is a cast ballot, the wrapper then uses the hash trimming function $\Omega$ to compute the short codes for the selections made by the voter and posts in the election record the full set of selection hashes generated from \emph{all} pre-encryption vectors (whether or not selected by the voter), the full pre-encryption vectors corresponding to the voter selections, the proofs that these selected pre-encryption vectors are well-formed, and the short codes for the selections made by the voter. + +For an uncast ballot, the wrapper computes the short codes for all possible selections and posts in the election record the full set of pre-encryption vectors, selection hashes, and short codes for each possible selection. + +% xreqj { + "section": "S4.3.1", + "text": "[TODO: Pre-Encrypted Ballots]", + "sc": "nyi" } + +% xxxx 9C/MqAxXErpZT85emeyVF56U+7rSXTkn41RheROPog+v8tNEGlDbLK2pZ/dW8SCgtx+vdcNeaBCz +% xxxx 6YmBsGd05UKOUcHWx990YVETHZrgu8IthMnHTiupG2VXT4nphSVtNkI57GFFnOaMJW9wEN3DuBvH +% xxxx 44kRjdldW+2kTNZNfIKgFuirsPJRJtqVDu8yP+pGM/a/dzRlNHu3IGv7suz30RcZJyKY5P+QszlM +% xxxx WbV6JvunBo1ByX4ncSjS7/VTtR+53xMVSpI00+39vqoN06yNIMAZTlyPOpn4uLiYF+9O83ULrqAy +% xxxx 2YtV/p44Bcdx9BMoGhiW3tw27t1XKOr7mrxxFR92SBEzHxMOr19dCL6r9rlJ8JklweP/Tvjx62dl +% xxxx J6XwIMknT8D6DhUqmG6Aw5eiphIWvlkZDIkSVwYzzhDWIno72+79QySPXu7DmHcfFFf8A83hTKYN +% xxxx r36u+UkVir7zE7Gyrpkl51jc4x5dT4T6bxvTaQsYT8tvxkFEKevPjEOdNU6JvJ3VH3szF4ZtXniM +% xxxx 5B3ZNyt2pTpEk5KtjldC5dySVGL/iOMVsbVFL8+2IVTH3+UoLsbdKBYeeHx4U0i/AmxNBJ+n5/m4 +% xxxx 2BYicq2m/TjptBRmFs5E6vqLUxXkLcf1IwxTmnACc/dD+E+qq9v9sPws8m7be79Yz2Fn4Xk7PWrV +% xxxx dFrp64qQIeJXAgrit7FBRsiK8fJKCxHqjz2sXmCQ0OkPixta6P+FEltnpQG+vut29p7UsEn4vRec +% xxxx +% xxxx +% xxxx ---- S4.4 The Election Record + +\subsection{The Election Record} +Selection vectors generated from pre-encrypted ballots are indistinguishable from those produced by standard \EG.\footnote{Pre-encrypted ballots in an election record can easily be distinguished from ordinary ballots. However, ordinary ballots and pre-encrypted ballots contribute to the same tallies; so, no information about which votes came from which mode is revealed.} However, the election record for each pre-encrypted ballot includes a significant amount of additional information. Specifically, for each cast ballot, the election record should contain +\begin{itemize} + \item the standard \EG encrypted ballot data consisting of selection vectors for each contest together with all the standard associated zero-knowledge proofs that the ballot is well-formed, + \item the selection hashes for every option on the ballot (including null options) – sorted numerically within each contest, and + \item the short codes and pre-encryption selection vectors associated with all selectable options (including null options) on the ballot made by the voter. +\end{itemize} +Note that in a contest with a selection limit of one, the selection vector will be identical to one of the pre-encryption selection vectors. However, when a contest has a selection limit greater than one, the resulting selection vector will be a product of multiple pre-encryption selection vectors. + +For each uncast ballot, the ballot nonce for that ballot is published in the encryption record. + +While the basic pre-encrypted ballots are identical to standard \EG ballots, their con\-firma\-tion codes are computed differently. Unlike standard \EG ballots, pre-encrypted ballot confirmation codes are computed before any selections are made. The confirmation codes on pre-encrypted ballots are computed from the full set of pre-encryptions. This is the same whether the pre-encrypted ballot is cast or not. + +% xreqj { + "section": "S4.4", + "text": "[TODO: Pre-Encrypted Ballots]", + "sc": "nyi" } + +% xtodo S4.4 pg. 62 refers to "every pre-encrypted ballot listed in the election record as uncast". QUESTION: Does this imply that every `PreencryptedBallot` generated is recorded in the Election Record? +% xtodo S4.4 pg. 63 states that "For each uncast ballot, the ballot nonce for that ballot is published in the encryption record." and [S4.5 pg. 64] refers to "every pre-encrypted ballot listed in the election record as uncast". QUESTION: Does this imply that there is a point during the tally process at which generated-but-not-cast `PreencryptedBallots` are no longer accepted? If there are multiple Tallys taken, then it seems really important that this only happen at the final Tally. Someone who submitted a PreencryptedBallot that was somehow delayed in the mail probably does not want their Voter Selections made public. + +% xxxx lVdyMZdyAOpd1zyYFpBuTb4STnEWYnKNPzN+2kL6sH7RIZr6IBVJuzapSrt6PXlB5rXaf6jb2k67 +% xxxx 3NAbIXDNMm1jFsvEdqtRMWDGWUlb5dEVl4v6HKvg+nYP3/zBuOfsN1wxYJ3ucXIUgjvhTdhpn3FC +% xxxx UxMqti1qyO0zKaNmbJxcMAACYi75+6RsP7v6f7znITseh8tsOyycAx0zPDwSWtKKTkDg2K4V1SYV +% xxxx bcAggXZch21NRxwcuIv6T2Fs0w2hezOgItbPFD38oxHTgBfE+4ciY/n704lJjS+6VZG4zQtz5adZ +% xxxx HZq6kxpH+dH3YhyT+fKx/11Fb7iqDQIquhMRG55egA4XQHUmvm9dXOQU8vNBVStC9UscZq0D1owF +% xxxx tvqp+3TTnjmfc/BBbUI5+2zeMDb7zHsLK6oStQU0gylIJs+omQ1S/TSRoHZazq3mcvnaBiL5K/5S +% xxxx 6KOyweWP30v9LQ8x29ZOHNSfMmokZg8obBUsmPqMMsPM8DDzDfrmRf7Yt0WQspgwxD0NYy5uNjqW +% xxxx rCyPdiK+zDZ5gxe8+1lURIZSmbR1n2prtvpGd4IOTt3YtLy12jk4zFuaiyaS0+nfA6tHjise17C2 +% xxxx hPMklm5P7o0z4mYf7hhQuX14mGcVswxKg9M1C1iGxKmpoeHJWQuBn2hYgc9P5mLcLf2Aq0/SzSkV +% xxxx 5C4Z/zln5MSNb1ShlWYV53yMyOo40V7BSaJvjBPTg61bZHt2c733yeFl8YqundCQW9mkqsWTFCFX +% xxxx +% xxxx +% xxxx ---- S4.4.1 Election Record Presentation + +\subsubsection{Election Record Presentation} +The presentation of data in the election record should be cognizant of the fact that there are two very different uses that may be made of this record. Individual voters will want to look up their own cast and uncast ballots and to easily review that they match their expectations. Election verifiers will want to verify the cryptographic artifacts associated with individual cast and uncast ballots and check their consistency as well as the consistency of the reported tallies. + +It should therefore be possible for voters to see, in as clean a presentation as is feasible, the short codes associated with selections made on their cast ballots and the short codes associated with all possible selections on uncast ballots. The presentation of uncast ballots should match, as closely as feasible, the appearance of the physical uncast ballot as this presentation would facilitate comparison between the two. + +For election verifiers, all of the cryptographic artifacts should be made available for verification. Verifiers should not only confirm the consistency of this additional data but also that this additional data is consistent with the cleaner data views made available to individual voters. + +% xreqj { + "section": "S4.4.1", + "text": "[TODO: Pre-Encrypted Ballots]", + "sc": "nyi" } + + +% xxxx E5uW8hXnsIxbmyuPN8/Xm09WMCTVQ8TkfSt1bSb5fM9r9DiS5FEXot38mduB2kwmL25sInb0DQWt +% xxxx 4f2SaAw5RHHfC8quxGjmb/W1iCZ6cteWsEl4gI7Yi6j+HZekwdRcyHY7i1tj2sDjrDe4uiojpclF +% xxxx pUZjjzKzLGgc0aRgX9MyJ9HRbjQB2fBkGmsgWqFh8aB2JqYX0thRPIiqJe1QDL/mHlDzpc2O4PDD +% xxxx EePsDyyb2dWR5o0L9nHmvDbHudj6QmyFYSGktRMkn4/+zWVQS3IwtZrzHEA0Zod7cRuFCVZRzrLE +% xxxx u0YXDquFJC/ntGg2kcD3XaCIvcDc06fODyCWqhn2rrlmDcmVaeK9iFabUGdPaj/41ChIHXlEFg99 +% xxxx hROZEgp1Mp54tLk+OucxssWIRWDQigewj74WnW32pqZSTrmP8JcufvqZBIcevWnlqNRYgeBKY/R7 +% xxxx +k11EwR6W3xUNouUQpM7+Eqchfno/9LhRMYNOkn38HfpmxZh8xkIcGtye+J9TgcUyZz2EVEshPbS +% xxxx hJNjRiCxtMhMPsyoBk2HwNLo0tzekukitmaL3j+1TgmJoEmcr+SQwnc7hKZQLVrf/TUitqIVn7wG +% xxxx gJ2cK1bzG40cZcSMSkiGbLvYSmKOJyfzsE2QVzycRTfoDe2BvYQtuewd1LYwLTnVu5ftEbz/23B2 +% xxxx tNzj8RBgCnYAwzQ8+iCrYGNbo+WEVkDnU7i6nN02pP+iXRKooCMasSpdGLXyqyjcFHJN8pHZdEL9 +% xxxx +% xxxx +% xxxx ---- S4.5.a Verification of Pre-Encrypted Ballots + +\subsection{Verification of Pre-Encrypted Ballots}\label{sec:verification_preencrypted} +Every step of verification that applies to traditional \EG ballots also applies to pre-encrypted ballots – with the exception of the process for computing confirmation codes. However, there are some additional verification steps that must be applied to pre-encrypted ballots. Specifically, the following verifications should be done for every pre-encrypted cast ballot contained in the election record. +\begin{itemize} + \item The ballot confirmation code correctly matches the hash of all contest hashes on the ballot (listed sequentially). + \item Each contest hash correctly matches the hash of all selection hashes (including null selection hashes) within that contest (sorted within each contest). + \item All short codes shown to voters are correctly computed from selection hashes in the election record which are, in turn, correctly computed from the pre-encryption vectors published in the election record. + \item For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected. +\end{itemize} + +The following verifications should be done for every pre-encrypted ballot listed in the election record as uncast. +\begin{itemize} + \item The ballot confirmation code correctly matches the hash of all contest hashes on the ballot (listed sequentially). + \item Each contest hash correctly matches the hash of all selection hashes (including null selection hashes) within that contest (sorted within each contest). + \item All short codes on the ballot are correctly computed from the selection hashes in the election record which are, in turn, correctly computed from the pre-encryption vectors published in the election record. + \item The decryptions of all pre-encryptions correspond to the plaintext values indicated in the election manifest. +\end{itemize} + +This means that an election including pre-encrypted ballots must be verified with the following verification items: + +\textbf{Verifications~\ref{verif:parameters}, \ref{verif:guardiansPK}, \ref{verif:electionPK}, and \ref{verif:extendedBaseHash}} pertain to verifying the election parameters and key generation. They remain the same, independent of whether pre-encrypted ballots are used for an election or not. As indicated in Section~\ref{sec:overview}, these verification should be considered optional for an independent verifier. + +\textbf{Verification~\ref{verif:uniqueEncryptionIdentifiers}} must include all ballots in an election, i.e., if pre-encrypted ballots are used, this step must verify uniqueness of selection encryption identifiers for the full set of ballots, including the pre-encrypted ballots. + +\textbf{Verification~\ref{verif:selection}} must be validated for all selection encryptions on all ballots, including all individual selection encryptions within the selection vectors on pre-encrypted ballots using the appropriate option selection limits. + +\textbf{Verification~\ref{verif:selectionlimit}} confirms adherence to selection limits and must be validated for all contests on all ballots, including contests on pre-encrypted ballots with the corresponding contest selection limits. In addition, if the contest selection limit is greater than $1$, the selection vectors that are published in the election record must be validated because they may be accumulated from several pre-encrypted selection vectors with corresponding short codes. Therefore, \textbf{Verification~\ref{verif:PreEncryptedSelLimitGreaterOne}} must be validated. + +% xreqj { + "section": "S4.5.a", + "text": "[TODO: Pre-Encrypted Ballots]", + "sc": "nyi" } + +% xxxx dVP7xv3oUAQxL/n+OH8Ht3gJCRpcqDT9UQKf85/cCYbfUZPuuFhtqvtpFU4ISKilkYGzD8637/Dp +% xxxx btx6+XlEtLJdESxgO4sknVXRUPOf3bqb4mLf82+fpbrKhBii9Lqtf3n3i9lw2fBEuMzcFglVb9Ua +% xxxx 4lLIzxT+rIaUuMK5AbD46hHv3cwOzsH+4CTetigPJK5Kuc7lNDNcRqXHWvNgt8XNvdbSnC75LeBv +% xxxx rRqfjkcEQt1daE2sS5L90iCqsXpPdnKJZwzg0qc1Qhmc1337w2GsesRObQdPrpMMJKYdtE8eI0gU +% xxxx i3RgKHXsl+9uV6xt+d1GGkRiJdBa39n3QDhY3qlKsy1uS9NPvUCT0P3ow9Q9dWkBJ6uNjkWjyyV7 +% xxxx 5q8wqTtuiCplAUoGyVhweNbRw+b6Ex4Unc8VkKHIqDrJ1y3Nh4KHPij0eB+pXEPrbDV8jeW32ZAL +% xxxx gtP8hYtvs6wbTnSWRrvbEi9zzOQyYy7JBUzzy3rhNb4ddt5i4LYKTKmuExcwjXCj9m6aUatkjqdC +% xxxx LELznSMK5+mJHdiPoiNLFLZ3Su2TGqQNPfZufYRBsCDmgNuQCK5N9gl/uJDzyiGAlrcCrXP1FIEi +% xxxx ooqSZ+eEzL1UJinPOablUXjZ28bz7nb14KNs2W226xjDzTUPxYMyLKcq7PurZKeJ4Dh28lbhL3AX +% xxxx BUiFWmt7JP1LGWJv4RBInpfAYYmCEv/t3bY+3pt33FmRuGWBCyZ32xK4JKdp+wM8U56Vbxbig0uG +% xxxx +% xxxx +% xxxx ---- S4.5.b.verif15 Verification of Pre-Encrypted Ballots - Verification 15 (Validation of correct accumulation of selection vectors) + +\EGverif{\veriftitlePreEncryptedSelLimitGreaterOne}{\label{verif:PreEncryptedSelLimitGreaterOne} +\veriftextPreEncryptedSelLimitGreaterOne} + +\textbf{Verification~\ref{verif:trackingcodes}} is only used for regular \EG ballots. Confirmation codes for pre-encrypted ballots must be validated with \textbf{Verification~\ref{verif:PreEncryptedValidationOfTrackingCodes}}. + +% xreqj { + "section": "S4.5.b.verif15", + "text": "TODO: Verification 15", + "sc": "nyi" } + +% xxxx 7doDbEmrgh7Ts9N43gvWAMSGX11deoNHqdNwzocUvSxUsM4g5HSLpYdQFZf/6wr/R+CT/0YlOYoB +% xxxx xovYbIJfTaTdl7+807kT+6Xh1StaLBhbxvNokLCNZbhyluktAFXF/z/4W4FdiDmN4AwIVf97qrPl +% xxxx +zG9RC4exCXCy0dChtdWBuvBePr4Yahew02hHv/sm8DGbmmfwuiM0V7HwaSWNh1V1ekgKsUnp2XU +% xxxx cqoN5cwzpRux3nkf3zFBmQstDZjO4j60fsQ4oOxLBqozVak+F3Yd+XwnOq3hrOE0qK1ChROmJ3Zh +% xxxx ULZ4EPqG1XT1pDPaurqWxs8g6muVLRSVQot/yNrbSl3lavI062wuwii6l7NIVb1ftGdQjdU3EVYE +% xxxx Iulz2oWwCIz564gDOvnBXzDQUVPOdiUaFVSzIMuqXkbJlpHUDILZCK7Kref1A4WGCWpEaebFiJhD +% xxxx XwpQDRWgrD40WdrLFcIbd6El7CYonTmxwo93MFax2HCg4vjj3fsHZBnSDKLhZuZRght0iDqP0Yc3 +% xxxx WolYNLcwgctOv0k1Q538Dt/VGyUlPkvHL/8VO2NZeMB40UEZ43Wi2w5Kh9v8+z32snN16uqSJGIh +% xxxx YsvzAS6+vihFPVDI8YjJBTTsSoxPxIwpXTvl3SllwLkEam1eTjZA1jBBGoOJ73Mr8Brr1G7fb0dV +% xxxx tNQ2jZ3jKBOAhJsj3ZNF9E4LMEcmI9l8QT+k98nwJauEBmEdBs0H+TClHu36w9ASIX+bSYnPWSFd +% xxxx +% xxxx +% xxxx ---- S4.5.c.verif16 Verification of Pre-Encrypted Ballots - Verification 16 (Validation of confirmation codes in pre-encrypted ballots) + +\EGverif{\veriftitlePreEncryptedValidationOfTrackingCodes}{\label{verif:PreEncryptedValidationOfTrackingCodes} +\veriftextPreEncryptedValidationOfTrackingCodes} + +Additionally, for all pre-encrypted ballots, an election verifier must validate the correctness of the short codes on these ballots, i.e., it must validate \textbf{Verification~\ref{verif:PreEncryptedValidationOfShortCodes}}. + +% xreqj { + "section": "S4.5.c.verif16", + "text": "TODO: Verification 16", + "sc": "" } + +% xxxx 1lNQL/tm1vj1bs0D+HydbF3W6J40WtT84/Xri/qF8dl/y4gHpUMGvdjQpOhxzZWT8t13kAAMjOtp +% xxxx ZuFq3LHEY706/nGDpbnGeKskqJprEKbkb79rXA6qsiK9wzQY78I3y6oVaYu3NjXwvH97DeVfFdsd +% xxxx JqbFujhN5XO3I2mD/6cj6YCD5/7KqmkxDIZIer8G2917R1jvGfYxJJFK4aC52FWAeQzlwbUiekxe +% xxxx TXFgRZQjIUnCwp2YH4gOsTV98BP9cmG5eLn4Po2DFwknmYSTD+zgKtZMITa0NH6cMuN8xb8DaRYM +% xxxx QBQYFVIQTOzPrg2IXcFCcTamTduaJ5NCFYJ+2hkKayFHLksB27b4LBh6GyvUucqZ9sIeRkB0krzR +% xxxx W+EbqBAiznUBQifoK2hvdIdYAZvUhy2fNqcerrgcDpLJLRINeyP9BsU09QuyMrxs7hQQVA7hyVPS +% xxxx PWfM/csbmLB5Plp/oK+OvjRKn3IlEEW9d650Z9P1XELmvNWk6UA1AleF0qm2O8dJ84CAz2xqURt9 +% xxxx oOffNW2wGQ0KoyxN7BhsOGaD1ruXeGK/um2Gem0doCWXS6rq8GgVuy29ckBLllfhBseEjystU24q +% xxxx u/YwVChY10pY8r7KdpxhIFSzy/hW0Ir8sWSMJh9pqtUjI4Gf9wsucIol1aPeKrYqOKSJydd9OSAt +% xxxx 58B4JTp06ns8d6LrHnc5yI6UTLHWfworSKbYfJNmysuTdtAcdPB40mRpkxrHld7SD1AVuWkolsg+ +% xxxx 9IBEhTC8Ug8ma1a33fWu9HSvslNR2Td40TYE1gBcrv7Fa6f4Z7cJq2j364Ann5ov5jkGHYCspWin +% xxxx +% xxxx +% xxxx ---- S4.5.d.verif17 Verification of Pre-Encrypted Ballots - Verification 17 (Validation of short codes in pre-encrypted ballots) + +\EGverifBallot{\veriftitlePreEncryptedValidationOfShortCodes}{\label{verif:PreEncryptedValidationOfShortCodes} +\veriftextPreEncryptedValidationOfShortCodes} + +\textbf{Verification~\ref{verif:aggregation}} of correct aggregation must be validated for each option in each contest including regular and pre-encrypted ballots. + +\textbf{Verifications~\ref{verif:decryption} and \ref{verif:tallies}} validate the correct decryptions of the tallies and the correctness of the tally contents and must be performed for all tallies, including those comprising accumulations of regular ballots, pre-encrypted ballots, or both. + +\textbf{Verification~\ref{verif:extdecryption}} is not validated for pre-encrypted ballots because contests on pre-encrypted ballots do not have encrypted contest data. + +Finally, uncast pre-encrypted ballots take the place of challenged ballots and their decryptions are ``opened'' by releasing the encryption nonces $\xi_{i,j,k}$, which are derived from the ballot nonce $\xi_B$ via Equation~\ref{eq:noncegen_pre} after it has been decrypted as specified in Section~\ref{sec:decrypt_challenged}. \textbf{Verification~\ref{verif:challengedDecryptWithNonce}} is replaced by \textbf{Verification~\ref{verif:PreEncryptedValidationUncast}}. And to verify well-formedness of the uncast ballot, \textbf{Verification~\ref{verif:challengedballots}} is replaced by \textbf{Verification~\ref{verif:PreEncryptedValidationOfContentUncast}}. + +% xreqj { + "section": "S4.5.d.verif17", + "text": "TODO: Verification 17", + "sc": "" } + +% xxxx rDRK3b+a6aCUEwy+JaZrURjmZONc+pbh7F2Adx0Wl6L1v0vxvj8ZHt27PR04ReZsbozQ4tva/kIj +% xxxx Rpv+ypm3N++fdCQfVkpd+uj31NDu+pqgrd7vgSSqTOP1PONL9uv7ZjnNflhUNrxIrpfekqqDsz7d +% xxxx s7cAb36KX5EtHL9hpobd6g3nOk1ey78IyNXSooQOBfK2LzX7a0rPKYyCQXgXktDSJUTopIP84PpT +% xxxx v6ZAUvsqsX54AXqRYdycRGPaGOnrUlyxub0ZjgKnH6UiuB/3/noVLn22WeP/hSvSszNS/5KDWcHY +% xxxx Ezbc5lX5JqIlitpVa+MphQ45dF81IyAA+EtfWk6qe3RRgFtvqiDPsnOeqlW2HHt0Gxs0HzE2vcta +% xxxx ndSFXog447UMNbx2SvTSP70fosJLJyExt4x7EQkTAQNCBVvGGVjs35b+hMxw9prB73fS6cmaehkq +% xxxx 1W3xrBWlKcWxRvWKKCXEosrf/VmugtfKVxE3emlvnq7KUz9aJMke7+mFDrU+Oim30/u35v4oXoT/ +% xxxx 5uoiUU+XZQeUjp1NlT1FQ7lKcMteTTB7a2XgxykvqHAagEpan+Ncs5VahCV+4QWWbgvUhgqA7I/q +% xxxx o4MOvHEFUhNMQhFoPeY5DNWQtAMQipPkRR9sVJ3r8VKkDtFDF9hnyGtYJXjlq5u0j6Uxn1SgnlUb +% xxxx 1MsKsjcZrU3S8XKiEqXmGyIN7fOGq02IVjtwxmwruDeUSJkCrKkfu7mMJ9gKjJlod+MjIBB1Ybyk +% xxxx +% xxxx +% xxxx ---- S4.5.e.verif18 Verification of Pre-Encrypted Ballots - Verification 18 (Correctness of encryptions for uncast pre-encrypted ballots) + +\EGverifBallot{\veriftitlePreEncryptedValidationUncast}{\label{verif:PreEncryptedValidationUncast} +\veriftextPreEncryptedValidationUncast} + +% xreqj { + "section": "S4.5.e.verif18", + "text": "TODO: Verification 18", + "sc": "" } + +% xxxx NplCiQY5VUFya5VeoOBdBAFQq7f4QBxSZKA8z/Vizwnt4VdrUJ9ghxCTw1a1wQU2sFKjRvabHp5k +% xxxx 54P8yt0MHbrBUZ54qsEsunxIsfsZmJtaLm7Gb9RGJMlWy0WQ/ep9HNH3UIyOpwYe1H+Rd6X8k3yZ +% xxxx MLRE9h85iEG22XFhAFx4akIHouVUU8drKIEg48a/dnawpNHsOfDsAdfXikBu024Pe4do2mLoKbOt +% xxxx anIiZWDzsnapoVpMnMXHp+w/QUdIEgUVsKo3x5z6rKWEsJeYp9RMxRhjtPEOfLafWg7q8fbazogE +% xxxx Wj3fG44XntyYRcf/gYUx3l3dW+lpLlCAKm4u8Zyuo6wQWh1QxjavZpfkk4CpA2yUV4zEEf42mCgc +% xxxx DDS8Ah1JUgcn3LwpiXzs8NFDDy+60SnD0Zf1DIEpwYDNkR6dyWiMGhM0dAN1WQOdWs6HmnqJMfZC +% xxxx mZ/8TjHW+ys5XZZRx/SKDSLwNB55ZxTSUed9H0BpIrfaDmEmB32QtQVo/ldnSOguaWfO192YsSNV +% xxxx Bd3nKHK2loeQu6/H1Ee2hsSnWygM9hxrYmMvqxqg3C+0tM282GgJ0pOuBTIshEoAKvRXVyEx+fth +% xxxx dtDHN41jU/gOhNtdAawz7vO7oYTq+5ekqOylrLvKk+5KLCVrDc3ekluG8lhfxR3M2OE4tASk7c+Y +% xxxx qcwJcYkQE8gHvQRfvGM7wJ/WMyzpE2dLVk0SVYxF64y05FbyqhgGy9ctxmqWNfRKnsHMwRT3CeRZ +% xxxx +% xxxx +% xxxx ---- S4.5.f.verif19 Verification of Pre-Encrypted Ballots - Verification 19 (Validation of content of uncast pre-encrypted ballots) + +\EGverifBallot{\veriftitlePreEncryptedValidationOfContentOfUncast}{\label{verif:PreEncryptedValidationOfContentUncast} +\veriftextPreEncryptedValidationOfContentOfUncast} + +% xreqj { + "section": "S4.5.f.verif19", + "text": "TODO: Verification 19", + "sc": "" } + + + +\pagebreak + +% xpage 68 ---------------------------------- page 68 ---------------------------------------------- + +% xxxx C/eUiBaWgFcCJQRPr8Cd4w1bFmUgHog8EzUQ+zuYMBQcK/q82sZvQtlvP0RUXlGlU4beM066P6eb +% xxxx xeMb7qID1yHuT/OfJSDS9K9I08WhCC/cprRXdVk768TTBTaxWXo5g7FX3qzh6R4YIT4W5mRiTL+r +% xxxx b23eN37NTaRCX2GeQow3s98h+bROZUhm/6Qiyqe6Ea1TFQMxfvTIIKaAPJ1szZdCRbV8R/vCD+K0 +% xxxx RO29WTIM4JuypoaDKtcOB7eyIWwfUaz711KIIuLaWaMuwzFk2ivPUtE6pIK40IiK+hqQ/trc/qCf +% xxxx w37/SrZmqP3mHnyz+os6CweByg5DYMRKX2VkIMr6xomFdkmdNfI49DgvsUdeczZTCn7Y1HUplaN+ +% xxxx k4DTPtGB4kFIRFWZ3qDbwUAmIXC5l7HnlSJly5+vjnCE0F7DNrgvgYSuxauDvRwHv1ELM+CUWTdV +% xxxx Vi5x6PeH6a5J4ml/QqMsb1qmn+UCQab8C9Deq+a8tIueLYCcvVWA7pa75lVi+7nYvGXqkWRHCR3H +% xxxx Wm7le5w+UuUt/nHcmlj9mJORvReIOQYCjOVLGB4suNBbWpb9MVMUsqYaR2E15IGuHWnp432WJjFy +% xxxx lHbrWcu/QCS8FaVxq2yukZ9QbUW9dRDxIuCTmk4VEhGOQBZTQIQOPCTwwtrpQx0mU3IB0mbG6al8 +% xxxx 6Gm/NSXZ7GmoIJ3SCdYG2fta4VOmUE0X4rDfiQE2hxGdMG3LYiCHj8+NYl2hLpYric4Neb1CINlW +% xxxx +% xxxx +% xxxx ---- S4.6 Hash-Trimming Functions + +\subsection{Hash-Trimming Functions} +To allow vendors and jurisdictions to present distinct formats to their voters, the details of the hash-trimming function that produces short codes from full-sized hashes are not explicitly provided. However, to facilitate verification, a variety of possible hash-trimming functions are pre-specified here. + +\begin{itemize} +\item \hbox to 3in{\bf Two Hex Characters\hfil} $\Omega_1(x)=$ final byte of $x$ expressed as two hexadecimal characters. +\item \hbox to 3in{\bf Four Hex Characters\hfil} $\Omega_2(x)=$ final two bytes of $x$ expressed as four hexadecimal characters. +\item \hbox to 3in{\bf Letter-Digit\hfil} $\Omega_3(x)=$ final byte of $x$ expressed as a letter followed by a digit with $\{0,1,\ldots,255\}$ mapping to $\{A0,A1,...,A9,B0,B1,...B9,...,Z0,Z1,...,Z5\}$. +\item \hbox to 3in{\bf Digit-Letter\hfil} $\Omega_4(x)=$ final byte of $x$ expressed as a digit followed by a letter with $\{0,1,\ldots,255\}$ mapping to $\{0A,0B,...,0Z,1A,1B,...1Z,...,9A,9B,...,9V\}$. +\item \hbox to 3in{\bf Number: 0-255\hfil} $\Omega_5(x)=$ final byte of $x$ expressed as a number with $\{0,1,\ldots,255\}$ mapping to $\{0,1,\ldots,255\}$ using the identity function. +\item \hbox to 3in{\bf Number: 1-256\hfil} $\Omega_6(x)=$ final byte of $x$ expressed as a number with $\{0,1,\ldots,255\}$ mapping to $\{1,2,\ldots,256\}$ by adding 1. +\item \hbox to 3in{\bf Number: 100-355\hfil} $\Omega_7(x)=$ final byte of $x$ expressed as a number with $\{0,1,\ldots,255\}$ mapping to $\{100,101,\ldots,355\}$ by adding 100. +\item \hbox to 3in{\bf Number: 101-356\hfil} $\Omega_8(x)=$ final byte of $x$ expressed as a number with $\{0,1,\ldots,255\}$ mapping to $\{101,102,\ldots,356\}$ by adding 101. +\end{itemize} + +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Two Hex Characters` hash-trimming function `Ω_1`.", + "sc": "ics" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Four Hex Characters` hash-trimming function `Ω_2`.", + "sc": "ics" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Letter-Digit` hash-trimming function `Ω_3`.", + "sc": "ics" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Digit-Letter` hash-trimming function `Ω_4`.", + "sc": "ics" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 0-255` hash-trimming function `Ω_5`.", + "sc": "ics" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 1-256` hash-trimming function `Ω_6`.", + "sc": "ics" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 100-355` hash-trimming function `Ω_7`.", + "sc": "ics" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 101-356` hash-trimming function `Ω_8`.", + "sc": "ics" } +% xreqj { + "section": "S4.6", + "text": "EGRS ElectionManifest PreencryptedBallots feature allows to specify the use of and the complete configuration for an API-user-supplied hash-trimming function.", + "sc": "ics" } + +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can produce short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can produce short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can produce short codes using the `Letter-Digit` hash-trimming function `Ω_3`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can produce short codes using the `Digit-Letter` hash-trimming function `Ω_4`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can produce short codes using the `Number: 0-255` hash-trimming function `Ω_5`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can produce short codes using the `Number: 1-256` hash-trimming function `Ω_6`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can produce short codes using the `Number: 100-355` hash-trimming function `Ω_7`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can produce short codes using the `Number: 101-356` hash-trimming function `Ω_8`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for producing short codes.", + "sc": "nyi" } + +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can consume short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can consume short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can consume short codes using the `Letter-Digit` hash-trimming function `Ω_3`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can consume short codes using the `Digit-Letter` hash-trimming function `Ω_4`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can consume short codes using the `Number: 0-255` hash-trimming function `Ω_5`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can consume short codes using the `Number: 1-256` hash-trimming function `Ω_6`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can consume short codes using the `Number: 100-355` hash-trimming function `Ω_7`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature can consume short codes using the `Number: 101-356` hash-trimming function `Ω_8`.", + "sc": "nyi" } +% xreqj { + "section": "S4.6", + "text": "EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for consuming short codes.", + "sc": "nyi" } + +\pagebreak + +% xpage ---------------------------------------- pg. 69 ----------------------------------------- + +% xxxx 9pIHJ2sPudjH3TBWqgp0bqKwGVdxzKk+aRAgEU0V+gNDapEt5eoXPQGDEHj3OF2tQeXtl+eSOpS1 +% xxxx 2g2AtxEy8Fu4xtmcyPll6jp2BJmZUCkVnYuyk4I4LYeZZ8VpMW0e1S3Xo4jX2Ga8XB175LSoAx15 +% xxxx AoIH90SPa0lTD1kj7yd2OdwyFeJ9lZR+fHiF7UrwoL+5pxbamCGlMWlCzvgszizd5mc7q+PTlA3O +% xxxx pmcWLKLU78NmjGqIS/qqI5mJocNOZnRJLUeuUfSUdjOXc458SqFCzLsjeaRQCnpuWCN660uW1IcU +% xxxx Ln0MP16A7n5e5JyjDBTwUaGJ9Vwv/NTdDsLf12AUuIOzp1jQicqnkNzWLFNxeHmPXFhLbploLpK/ +% xxxx our6U3pJdVp/YgcMZyZDcrZsrtejctnqWsOdMoRZeK4rJDElTD7oSqBkmt0ugMY7mpxHif6smETt +% xxxx q+Ff1kE5M2cQAd9fvii8VgnxmH/4eWpMzZxvb627IprSIB4LVnkOferEqL9dVZLuAa2gEC/dIT+L +% xxxx WgH89ihb8KFf5q3ZEmRsTO1y1unKXbUTXBkJn8dXyKsyx7Q5/CViAnJ2l2nuT/inbdJ4LY6ySvhl +% xxxx k2wt3NPwxYGm09z4WBHaRe87q03o4aTDpRm/VRZKMfNfoTJ71E6a+Y3T4YcvyelWVhXMcJefuf3J +% xxxx WZrI00ZiDr01S/d9UzxDz6Or2olmHCbw+gg8kxRrJLl2PBwDcYCWvfXSgQvgFWXcu3abvJyhXVB6 +% xxxx +% xxxx ---- S5 Hash Computation + +\section{Hash Computation}\label{sec:hashing} +The function $H$ that is used throughout \EG is instantiated based on the hashed message authentication code \HMAC. This section defines how to evaluate $H$. It first defines how inputs are represented as byte arrays and then how these inputs are used to compute hash values. + +% xreqj { + "section": "S5", + "text": "EGRI uses `HMAC` as the basis for the function `H`.", + "sc": "utsp", + "utsp": "" } + +% xxxx MGeHTlm7VZ1HSZk9+w4viKjEsRsq2WrFBS02YOT5cWVXRoqb7Czts1PXd7rPGT5dIfTvhpWXgg9X +% xxxx 4tnWPN9fm1zc+KYc/IhhBFe2UcH8EAl8GeVDoJBK6JtjJNn+BnBTt3tk54c/ciDSRlRAvm6xOYq9 +% xxxx G4SlDsau8CGdF0KJhJbDCOpWdNnYjvbIxbevFjYmDfNw/FTml3u+tRu/S/j0JgxGYmi7DN9RUiyn +% xxxx fSNm0vnhKYF4P7FxrjLNqY7FMXSJ7EZ2o15uXMjJcM68VBtCFblbkxF4N7ziV6P2TacnceeFaUpj +% xxxx dNFbPW2+sn4zTdDZ01276tcYzJ44dN/RVetoDhBih4eZMZg4ywaz+iTK5cpcsWuX57liSnZOX7VS +% xxxx cSzTcs6WAjhHQLlKmG4v3FFJBSIAUVHK5VsGZodi9UYTXfC1MgYweYtOQFvmPXLTjKZ/6arW7VOR +% xxxx 61AfnbWger8Ox9FGtX0EbIA8CrWPFOvetnWBl65+xB/qHvC8dqbfHd8R56gGuyQtUTqDOOniQdg7 +% xxxx QLB/d46I3HNCnv2I03ebuIz2iQFB45OrG+7Rq4JSm14KGlxmMWOOxB3cPlvtmvlKJyxtMHi4DSYd +% xxxx 77wC1twJnE9T4TXYH9++jAeJ8D7MkyqmwdY2YMASFRityPXweUSNz3YgeXMdI+ISJYwdmhUM7Ff5 +% xxxx EuDEIb0pXMoKqetPu5MYHl1927RJiP1r4HrpYATs1WhUCJ0n67uKUq5LW1E51QApN5xTqdiwCFyy +% xxxx +% xxxx +% xxxx ---- S5.1 Input Data Representation + +\subsection{Input Data Representation}\label{sec:hashinputdata} + +All inputs to the function $H$ are byte arrays. A \emph{byte} is a non-negative integer less than $2^8$, i.e., an integer in the set $\Bcal = \{0,1,\dots,255\}$. Its binary form consists of at most $8$ bits. Its hexadecimal form consists of at most two hexadecimal characters. Here, the leading 0 characters are written out such that a byte always has exactly two hexadecimal characters. Therefore, $\Bcal$ is represented as $\{\mathtt{0x00}, \mathtt{0x01}, \mathtt{0x02},\dots \mathtt{0xFE}, \mathtt{0xFF}\}$. + +A \emph{byte array} $\mathrm{B}$ of length $m$ is an array of $m$ bytes $\mathrm{b}_0, \mathrm{b}_1, \dots, \mathrm{b}_m \in \Bcal$. It is represented by the concatenation\footnote{The symbol $\parallel$ simply denotes concatenation and does not mean that this symbol is inserted as a separator into the array in any way.} of the byte values as $\mathrm{B} = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_m$. A byte array of length $m$ consists of $8m$ bits. For $0 \leq i < 8m$, the $i$-th bit of the byte array $\mathrm{B}$ is the $(i \bmod 8)$-th bit of the byte $\mathrm{b}_{\lfloor i/8 \rfloor}$.\footnote{Here, $\lfloor\cdot\rfloor$ denotes the floor function, which means that the result is obtained by rounding down. For a real number $x$, $\lfloor x \rfloor$ is the largest integer that is not larger than $x$.} The set of all byte arrays of length exactly $m$ is denoted by $\Bcal^m$ and the set of all byte arrays of any finite length is denoted by $\Bcal^*$. + +Any byte array $\mathrm{B}$ of length $m$ represents a non-negative (i.e., unsigned) integer less than $2^{8m}$ by interpreting the bytes of $\mathrm{B}$ as the digits of the representation in base $2^8$ with the most significant bytes to the left, i.e., in big endian format. For example, the byte array $\mathrm{B} = \mathtt{0x1F}\parallel \mathtt{0xFF}$ of length $2$ represents the integer $\mathtt{0x1FFF}$ in hexadecimal form, which corresponds to the integer $2^{13}-1 = 8191$. In general, let $\mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{m-1}$ be a byte array of length $m$, the integer + +% xxxx ---- eq. 122 + +\begin{equation} + \mathrm{b}_0\cdot 2^{(m-1)\cdot 8} + \mathrm{b}_1\cdot 2^{(m-2)\cdot 8} + \dots + \mathrm{b}_{m-1} +\end{equation} +is a non-negative integer less than $2^{8m}$. Vice versa, if $0\leq a < 2^{8m}$, then the byte array of length $m$ representing $a$ is given as + +% xxxx ---- eq. 123 + +\begin{equation} + \bytes(a, m) = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{m-1},\mbox{ where } \mathrm{b}_i = \lfloor a/ 2^{8(m-1-i)} \rfloor \bmod 2^8. +\end{equation} +In this document and if not specified otherwise, a byte array and big endian non-negative integers are used synonymously. However, byte arrays representing integer data types have a fixed length and must be padded with $\mathtt{0x00}$ bytes to the left. The byte array $\mathtt{0x00}\parallel\mathtt{0x1F}\parallel \mathtt{0xFF}$ represents the same integer as $\mathrm{B} = \mathtt{0x1FFF}$, but has length $3$ instead of $2$. For the integer data types used in \EG, this is laid out in detail in the following sections. + +% xreqj { + "section": "S5.1", + "text": "EGRI converts between nonegative integers and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 122 - 123.", + "sc": "utsp", + "utsp": "" } + +% xxxx C1xIWL6xdqvIKBlCC3y14Nh2ajUABXp14cVhFtNfDRvvFf1ulKoloF+p9exLTLsw4GdCa4JgRMIO +% xxxx /fL2VPBqXgMzEaHIflJ7IQO7zxGmKjk6sIZpQThg6R0VpXa/S1q++tX7qaXN0ylZJSTWekbMdD8g +% xxxx oaC9rWSjL9k4Ak80dpXV72R9/5tPdSruH0xvZ+8vSy6a/6qGf13ozlNS8wwvQ2dxxOMfvblX/qhh +% xxxx JWfh1ZH3MGR6Kq9Ss0Qwcgzj4eOaWUfY/ULFeYAUjMa/mUCWvFiZch9+DiCZzKrSrJxo6hKgSiv1 +% xxxx 4vl9deLwlRks89cNALwLa4uaFAIImt7t8vVo0zAcnI/cJcQRRQ1HFbOyX4RkxfikXXXKCBqUvjrE +% xxxx zcgdskSCCRTymC2pvPsYJkLrRGsP3PAHmAmsJphz6C76xN82jdxiZ79dlU8iSNI5ELB5mWT2Vp2R +% xxxx m+L0hHRRBskS4yIaGau8VKopoIFPVINtl3cPmFwMUT41o5lhE7jCiaC3SXzOzGD0NMNAbTSWAJJH +% xxxx S68pMYsGKBXDRYFb19bovOeBaBK5Mf/xz0B+RNCaetxJ+Er4VAPVRtr4zwxSNfBV9FScp0ZS25jp +% xxxx v1KdFmZJChPR72fwJbjrm/wHuXuEm1U6UsG4+9F7dKgLnfaP/dCNK8IzlPBpual8IaXTibhpayMx +% xxxx 6MHALWzDqxuJFQx7/e97PaQb0Hps+0Bcn6Pk0odLSy8lgRbwrF4IvdIrSlo8wpcBkY7fZL/slryv +% xxxx +% xxxx +% xxxx ---- S5.1.1 Integers Modulo the Large Prime \texorpdfstring{$p$}{p} + +\subsubsection{Integers Modulo the Large Prime \texorpdfstring{$p$}{p}} +Most inputs to $H$ like public keys, vote encryptions and commitments for NIZK proofs consist of big integers modulo the large prime $p$, i.e., integers in the set $\Z_p = \{0,1,\dots, p-1\}$. Any such element is represented in big endian format by a fixed size byte array of length exactly $l_p$, the length + +% xpage ---------------------------------------- pg. 70 ----------------------------------------- + +of the byte array representing $p$. If $p$ has 4096 bits like the prime given in the standard parameters in Section~\ref{sec:parameters}, this length is exactly 512. The conversion from an integer to a byte array works explicitly as follows. For $a \in \Z_p$, the byte array $\bytes(a, l_p)$ of length $l_p$ representing $a$ is defined as + +% xxxx ---- eq. 124 + +\begin{equation} + \bytes(a, l_p) = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{l_p-1}, +\end{equation} +where + +% xxxx ---- eq. 125 + +\begin{equation} + \mathrm{b}_{i} = \lfloor a/ 2^{8(l_p-1-i)} \rfloor \bmod 2^8 \in \Bcal \mbox{ for } i \in \{0, 1, \dots, l_p-1\}. +\end{equation} +The bytes $\mathrm{b}_i$ are the coefficients of $a$ when it is written in base $2^8$, i.e., + +% xxxx ---- eq. 126 + +\begin{equation} +a = \sum_{i=0}^{l_p-1} \mathrm{b}_{i} 2^{8(l_p-1-i)} = \mathrm{b}_0\cdot 2^{(l_p-1)\cdot 8} + \mathrm{b}_1\cdot 2^{(l_p-2)\cdot 8} + \dots + \mathrm{b}_{l_p-1}. +\end{equation} +Any byte array $\bytes(a, l_p)$ that represents an integer $a$ modulo $p$ always has length $l_p$. +% +This means that, for the standard parameters, where $l_p = 512$, we have $\bytes(a, 512) = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{511}$, where + +% xxxx ---- eq. 126.b + +\begin{equation*} + \mathrm{b}_{i} = \lfloor a/ 2^{8(511-i)} \rfloor \bmod 2^8 \in \Bcal \mbox{ for } i \in \{0, 1, \dots, 511\}. +\end{equation*} +For example, $\bytes(0, 512) = \mathtt{0x0000\dots 000000000000}$ is a byte array of 512 \texttt{0x00}-bytes, $\bytes(15, 512) = \mathtt{0x0000\dots 00000000000F}$, $\bytes(8572345, 512)= \texttt{0x0000\dots 00000082CDB9}$, and $\bytes(p-1, 512)$ is the array shown in Section~\ref{sec:parameters} for $p$, but ending with \texttt{\dots FFFFFFFFFFFFFE}. + +% xreqj { + "section": "S5.1.1", + "text": "EGRI converts between nonegative integers mod `p` and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 124 - 126.b.", + "sc": "utsp", + "utsp": "" } + +% xxxx 81iMuwo6EMRGwOBgDuTvHKIQcojTcBLMPzusRYVHZ9mrxWdOHtiSicU8Nj6yZ7qsM7Suw8pT+Z6P +% xxxx tuwN8XqD5EGMErnDELt1gV4qYLlSH18YLrUtNLj1+fLI0l3LCPF7lYEJVAAe6G5gwKI2NeDGkkPy +% xxxx kZi+ZX2QaDNw3qMOwu+y+gJRYVcDIPHYqD2w7o+2+YdCefDvq+I2Zy2wVsvjW3w5pD0/S1U9gNNM +% xxxx HKEfhiMt0e2y8cihHlTdsJJ+KNmxK1o9yMkAx7CYtKs4P/yCYajmQOUybXvKh8Gly/4fIkJ0FL09 +% xxxx 6BG5wA7mbv2greuT+hS5O1s3Fw81datZwcT+HGnjfY71d0vAxCXYLFC/ndjkpV+dBEDEAk0a8s3f +% xxxx 2uHQXlcX/8mEAGu4koOY8FfToEnStpQQiKXlfA2HBVqrQMAcTtEGFoUwzSj4niw3yzSQx59q4mGx +% xxxx sUgcRW6eMyBUaGHSpG2aoAw9aQkww24t0obOCHyFNQeSqTdUBgtbTTDk8E6XIzdnhgJtblukk28d +% xxxx 6WMrQIn2sqEBATCP5Z2zs2BlQ4DauM+PuJKySismkOrMySNtSr0TuGaWXNm52N14dh5XjHOcTlcm +% xxxx 25Yp73UTTVfqD/cvQRXD0pzp944GFn3ZEWFeil7zY2w0H7wPQrDS+IC7XW96e2B58SrZXFelGO6o +% xxxx QWXIVU5W6X3ZjYPQqm8r8CVdBRCQhEi/Gg77pu/yO19vPlCY8UW4XbwFbIkQLNSLNibmf+/qsNBp +% xxxx +% xxxx +% xxxx ---- S5.1.2 Integers Modulo the Small Prime \texorpdfstring{$q$}{q} + +\subsubsection{Integers Modulo the Small Prime \texorpdfstring{$q$}{q}}\label{sec:intmodq} +Other inputs are integers modulo the smaller prime $q$, such as response values in NIZK proofs and encryption nonces. They are integers in the set $\Z_q = \{0,1,\dots,q-1\}$ and are represented by a fixed size byte array of length exactly $l_q$ in big endian format. For the standard parameters, $l_q=32$. The conversion from integer to byte array works as above. If $a\in \Z_q$, the byte array $\bytes(a,32)$ representing $a$ is defined as + +% xxxx ---- eq. 127 + +\begin{equation} + \bytes(a,32) = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{31}, +\end{equation} +where + +% xxxx ---- eq. 128 + +\begin{equation} + \mathrm{b}_{i} = \lfloor a/ 2^{8(31-i)} \rfloor \bmod 2^8 \in \Bcal \mbox{ for } i \in \{0, 1, \dots, 31\}. +\end{equation} +Again, the bytes are coefficients of $a$ in its $2^8$-adic form, namely + +% xxxx ---- eq. 129 + +\begin{equation} + a = \sum_{i=0}^{31} \mathrm{b}_{i} 2^{8(31-i)} = \mathrm{b}_0\cdot 2^{31\cdot 8} + \mathrm{b}_1\cdot 2^{30\cdot 8} + \dots + \mathrm{b}_{31}. +\end{equation} +All byte arrays that represent integers modulo q have length 32. For example, +\begin{align*} + \bytes(0,32) & = \mathtt{0x0000000000000000000000000000000000000000000000000000000000000000},\\ + \bytes(16,32) & = \mathtt{0x0000000000000000000000000000000000000000000000000000000000000010},\\ + \bytes(q-1,32) & = \mathtt{0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42}. +\end{align*} + +% xreqj { + "section": "S5.1.2", + "text": "EGRI converts between nonegative integers mod `q` and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 127 - 129.", + "sc": "utsp", + "utsp": "" } + +% xxxx IP6KACJbGzTEOQvIprwWFwYvsafptEzaGIUm/6pfscsXqQeKXB0Ebw87VLb3cSv7p5MmsGSFZUxr +% xxxx sv4hHLKCMZ+W2CUAwrRmorU6ccoSMH955T1Lv1yDooJDSdSkNZC9d4OjfTSPdTYXR3Z0NrmFSYPc +% xxxx NqO/10pI/q3+Uv7meMty2MbWJOXCO2kEkAhCEdt1wbwSG+TotdsKuQ4SAfPLdDN2HCM7rR8YFdRb +% xxxx rlDywlQLaZrE7/jLaskOCjrBXWr8s2GXKSVPdEjIggX+3Kjfkzs4ymiiSJR+Ii9/dxtGgqIJFspu +% xxxx 2edHxMXJLp4txlcjYAU/AncOk6NivEJVPX4G78L076fxyGunnxfbQc07/xgXGV2ebfi2R75V8c5Y +% xxxx 50Ho1ANbx2R3CNFQGl70467UU6HInMNNhKRxWSom+HdVIgp+elVUoJB+NWRzKm+3zuUcXmbvVyfo +% xxxx MPjDeup/4bxK2lWiLo6VbwEIORV9ih2PtPBEpxfDBDVqnkANpRHyJVVUumN8pQduKsWiNRys+WLd +% xxxx BF85GbBrJxAHwrQdYuUFyTj4kDY9HT8fCc2vxMkx1OphhesM7iL1AnnBuvy5mC7f9xVc1Fz8LJSB +% xxxx /BRYNyDl8tNXGWFLO+qpxoRfvYJYo51xJe+ot2CRg1XT7bKN/HZMOSRjGsZPyM+7H+JgFZrqbHy1 +% xxxx 0uFkdzzXEe+xDINbLT3YlNxbOOQ73m9L+Bfrr0YLg5l/zdR0qW+ainUB5Lu9a54edX21LHObbLCB + +% xpage ---------------------------------------- pg. 71 ----------------------------------------- + +% xxxx +% xxxx +% xxxx ---- S5.1.3 Small Integers + +\subsubsection{Small Integers} +Other integers such as \emph{indices} are much smaller and are encoded as fixed length byte arrays in big endian format in the same way, but have much smaller lengths. In \EG, all such small integers are assumed to be smaller than $2^{31}$. They can therefore be encoded with $4$ bytes, i.e., with $32$ bits and the most significant bit set to $0$.\footnote{Setting the most significant bit to $0$ is done in consideration of languages and runtimes that do not have full support for unsigned integers.} + +The number of guardians $n$ and the quorum threshold value $k$ are examples of such small integers that require even less than 4 bytes for all reasonable use cases, i.e., they are represented as $\bytes(n, 4)$ and $\bytes(k, 4)$. For example, $\bytes(5,4) = \mathtt{0x00000005}$ and $\bytes(3,4) = \mathtt{0x00000003}$. + +% xreqj { + "section": "S5.1.3", + "text": "EGRI converts between nonnegative integers less than 2^31 and 4-byte fixed-size arrays using the big endian convention.", + "sc": "utsp", + "utsp": "" } + +% xxxx jKXWbK3Z1i8C2trFfnhW+qWyiVuIjnABALTPk96GyT0KolQhbiI7KtJbWqBtX2r3BDB3kYQ7ogaj +% xxxx 2fy6Eq9s24me2N9B9ihi+HNl5HInxU8wOio0aGKsFfaacKw8D+Kcoj8dzzNttnKa0nslZWFcIkmr +% xxxx /99cnNXnVrA7ovGJ5x1wFnKFoUrZ9h5jdGl+9f6RAOvA9I+HFUHwU0+wOHu1BHPKeFP3Sd3mzJ00 +% xxxx tE01rdNd74uQRToY7Uuk9FfzPpk2CRIIYtHkDAeuUhV2DxH6CiExaMrH9L+OWPgQKyE1urFnxAHT +% xxxx hXmXFiIDuyhBGsum+GO7VPo9ul/JbNguMsxz40uYkpXBAGNxhk+Hk8RRIcAZzAsu/FP9yv31iFqG +% xxxx U2xACW8xlCa/rmQVJxxN2DZssGJxl3Dxdou91UnH1ldTUJA0LD4UIq8P5RbEzOU0gFn8WEDfVQ1y +% xxxx kNGwHqmItFdTkT3RHVJYwcaeOduWjvAVASvNbKLO/pKrWYMng7cyofUXCIDeWkX90JnmWGP12BoQ +% xxxx qT4SYHzyZ4eZqaYlavyVLOquqCqwsLGPkFMOPjCXuaftmK0BK5XLNC9OHssdAaRW7zv9dj/CJOf6 +% xxxx mJBR0ip+Dh6MX7fg5WqD4xpqOYUS4XriYXiNBqYZzovKAX92cH2kPQTiIPFUEZWiRin0ORJnWChK +% xxxx yE7PC31O6UyS1YkqznaWDLzNZ81pJvtf5U4/7Y0DB3SNxe1zNNxETaznL1ZTMkcrl8e+dwyf6V/x +% xxxx +% xxxx +% xxxx ---- S5.1.4 Strings + +\subsubsection{Strings}\label{sec:stringencoding} +When an input to the function $H$ is a string $s$, it is encoded as a byte array $\bytes(s, \len(s))$ using UTF-8 encoding. Here, $\len(s)$ is the length of the UTF-8 encoding of the string $s$ in bytes. For example, the string ``ElectionGuard" is encoded as $\bytes(\mathrm{``ElectionGuard"}, 13) = \mathtt{0x456C656374696F6E4775617264}$. +Some string inputs may be of variable length that cannot be specified in advance by this document. Such inputs to the \EG hash function $H$ start with a $4$-byte encoding in big endian format of the byte length of the string encoding followed by the encoding itself.\footnote{The maximal length of strings that can be encoded for input to $H$ is therefore $2^{31}-1$ bytes.} + +% xreqj { + "section": "S5.1.4", + "text": "EGRI hashes fixed-length strings as their minimal UTF-8 encoding.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S5.1.4", + "text": "EGRI hashes varible-length strings as their minimal UTF-8 encoding prefixed by their length in bytes as a sequence of 4 bytes using big endian convention.", + "sc": "utsp", + "utsp": "" } + +% xxxx WDaaTn3ObUVqwLNryii9fyiRDS9rqOi5iy3pykcZtrQ+0e+xlI0zWOediDbC3KXwxNYBtyuJWeG+ +% xxxx 8G5IQl7zhN00p3FKfdf1DIxSVWaT9PQX91coEdY15N9D7lZyZHpdn3xIhQ1vUxCvzVSbMRPqY1Xe +% xxxx 4kvgH64JRAdWmb7LXc2gFvgoOKNhuBlfv02x/T+7ujC71TEvu4ViW7t8lDcUuv3Glm8WBlp3mgSh +% xxxx BSS7Odj4ywT3fTPBxy/e26E06/KAFZxRLax2Qcb3GA0fbksRYE2MsCSWvZbOHstBLU67nSBtpBL9 +% xxxx nV5atE8xU85Jc5OTYK1GHXH6arY8cbxuQgYKMf34txQDUIcER+95QsuEx4y7cJzUzpC9jqgYc0XW +% xxxx NNmzU9EiuU27ZS/3KZDRYS6p1NQT1fQw73cgYRa0rp5WeZPq9cxzrg6Wm57cjMMM+1rRw9sO7cRn +% xxxx GGuD9osTKOLhBR8jmh68MZSagss8iGcw06qQqmQ0MQZ9tPTncrM9QX0cBDFivzM4uRnIC3mNsRRz +% xxxx pcGmSH64Zy/J67ZWyvPDk3DB7MW1ts+ovtX2VBG0Yrm8K7atNordUMNMshbSvTnGG5S+X8uaA61r +% xxxx wezFnf5wHLQIjRRj2SfFijkVCGx5w+Ips0EAv26mCXM/Gl49VTjYDBZdIuJFk1bhjVpGcgbM/Bu7 +% xxxx XZy3r+2jwnCkYRivZhlhJfSqredg90k3GphAmDVLyL/L1MljNLzzvI0n09wKNSLkuFOSYN5hMMRe +% xxxx +% xxxx +% xxxx ---- S5.1.5 Files + +\subsubsection{Files} +When an input to the function $H$ is a file \texttt{file}, it is parsed as input entirely, meaning that all bytes of the file are parsed to $H$ as a byte array $\bytes(\mathtt{file}, \len(\mathtt{file}))$ that is a concatenation of all the bytes of the file in order. File lengths are not specified in this document and file inputs to $H$ must start with a $4$-byte encoding\footnote{Input files to $H$ are restricted to a length of $2^{31}-1$ bytes.} in big endian format of the file byte length $\len(\mathtt{file})$ followed by the file bytes, i.e., $\bytes(\len(\mathtt{file}), 4)\parallel \bytes(\mathtt{file}, \len(\mathtt{file}))$. An example of this use case in \EG is the computation of the base hash $\HH_B$ from the \texttt{manifest} file. + +% xreqj { + "section": "S5.1.5", + "text": "EGRI can read and write files as contiguous sequences of bytes.", + "sc": "nfd", + "status_note": "" } +% xreqj { + "section": "S5.1.5", + "text": "EGRI encodes byte lengths as 4-byte big-endian values.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S5.1.5", + "text": "EGRI accepts byte lengths less than `2^31`.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S5.1.5", + "text": "EGRI does not produce byte lengths greater than `2^31 - 1`.", + "sc": "utsp", + "utsp": "" } + +% xxxx MkwVJeV8J5KQQgTjlIMKitUblIJt7PmmD7fFgQylyhrwQBC454yzRFhg7ILC1hVdSeMbnmBg0J30 +% xxxx nz6SKSMkks/g1QtlsTBawLGShz80Hkzh7+rxNt3MQ78GMgdYails404x/Y5r2pTJLn7R+XlSQbj5 +% xxxx O1C0pjFesO+0GDoJFPKkN4FgihKSLcygSiLDTBiqvV4z9eyfsXsGcZW4qNfMvYVEbp0/NVg/YhUv +% xxxx FG5o6vsoJ0bXU2yDFnK8BVoBBdnB++eImksLtrn1Lmp67aRm72D0fv0iCtasrzFYBpAtJO2nj6DU +% xxxx bkbWauncHRFA7XqVGcRwH4F1r7gAfTMZ4Sq3W38SNyZf68M+TKDzkjCsNeKOzHbehHhAnJcR8XzH +% xxxx 7uTj8SbTVmHcqrZlQ3ZPhCQl0qvwwnHqFyeL0g1Bzm6WfhZ57kVVnXX1QHYf5VCK5C/T8+bk22U+ +% xxxx LcJEp77Xo8XGg2MuycjfIl/gzAcK2iI33Rd+vAu6ZLf/qoRFtB8Y/EJqogc97QnO6I2MBYNH/v8J +% xxxx EPd4p6R2xplIaFbplvPxR7lMpXRXAHNDqY+xZHvMCPvuZuC3WiuyP8NMkq79HvZq97U9BETyctx3 +% xxxx 61WzZooH5iHF/dG7W93xLYMwfZ8Qde1/Vlu9pCz3d+xbVjExS0tAkY9YxQGaxjsNcYxp9lK81J9d +% xxxx 1Bp3deVVTEGpk+sGxELD5PS04O8+2IewLYKaeeRsOBOkxRiqVzIe3DsQ7tCZUwbG/1Yn4U9SdfkW +% xxxx +% xxxx +% xxxx ---- S5.2 Hash Function + +\subsection{Hash Function} +The hash function $H$ used in \EG is \HMAC-\SHA, i.e., \HMAC\footnote{NIST (2008) The Keyed-Hash Message Authentication Code (HMAC). In: FIPS 198-1. \url{https://csrc.nist.gov/publications/detail/fips/198/1/final}} instantiated with \SHA\footnote{NIST (2015) Secure Hash Standard (SHS). In: FIPS 180-4. \url{https://csrc.nist.gov/publications/detail/fips/180/4/final}}. Therefore, $H$ takes two byte arrays as inputs. + +The first input corresponds to the key in \HMAC. The \HMACSHA specification allows arbitrarily long keys, but preprocesses the key to make it exactly 64 bytes (512 bits) long, which is the block size for the input to the \SHA hash function. If the given key is smaller, \HMACSHA pads it with \texttt{0x00} bytes at the end, if it is longer, \HMACSHA hashes it first with \SHA + +% xpage ---------------------------------------- pg. 72 ----------------------------------------- + +and then pads it to 64 bytes. In \EG, all inputs that are used as the \HMAC key, i.e., all inputs to the first argument of $H$, have a fixed length of exactly 32 bytes. + +The second input can have arbitrary length and is only restricted by the maximal input length for \SHA and \HMAC. Hence we view the function $H$ formally as follows (understanding that \HMAC implementations pad the $32$-byte keys to exactly $64$ bytes by appending $32$ $\mathtt{0x00}$ bytes): + +% xxxx ---- eq. 130 + +\begin{equation} + H: \Bcal^{32}\times \Bcal^* \rightarrow \Bcal^{32},\ (\mathrm{B}_0, \mathrm{B}_1) \mapsto \HMACSHA(\mathrm{B}_0,\mathrm{B}_1). +\end{equation} + +\EG uses \HMAC not as a keyed hash function with a secret key or a message authentication code, but instead uses it as a general purpose hash function to implement a random oracle.\footnote{Dodis Y., Ristenpart T., Steinberger J., Tessaro S. (2012) \emph{To Hash or Not to Hash Again? (In)differentiability Results for $H^2$ and \HMAC.} This paper shows that \HMAC used as a general purpose hash function with keys of a fixed length shorter than $d-1$, where $d$ is the block length in bits of the underlying hash function, is indifferentiable from a random oracle.} + +The first input is used to bind hash values to a specific election by including the \EG version identifier \texttt{ver}, the parameter base hash $\HH_P$, the election base hash $\HH_B$, the extended base hash $\HH_E$, or the selection encryption identifier hash $\HH_I$. The second input consists of domain separation tags for different use cases and the actual data that is being hashed. + +% xreqj { + "section": "S5.2", + "text": "EGRI implements the function `H` using HMAC and SHA-2-256 as specified in EG DS v2.1.0 eq. 130.", + "sc": "utsp", + "utsp": "" } + +% xxxx EfBq3+SWrqZfu/+3OV5j7w16fxbwRExybmC4MTofqUWxyn6rVbwe9UoqHAbptu8ANYXhAqRsGfGN +% xxxx AbWEe0tZBle08XshFhTn+JzK+2DZIbW8hWiX8t72rEtGDHp7tCfYYhLkh2dIZW57nauKjT5uKEyR +% xxxx kwh64KpBBpHiy0PXFTdljl8LOE+WgGmjuaxlfMfUtGIoaRRJYUEoGEilgJB1wg5hP+Ph/LnvyNrU +% xxxx IkzfCXOO/v4itZOo7s8o31eUQUojA5PRilM8cqwU330+03rgqMy8F2vfYMSWzDdy/nwcTexQOIUg +% xxxx 7h/ffNOg7Io6DFdExbvew3z6wULnIv/lCaJvMFGLfkNTYSjI2kP5rbK5qxKqx4R2ixTf8xAeklag +% xxxx 5xmZzfuvYVjpMaUtZEkh/Pyh1yZ632oNqpBbTUhPPXE9RFfIJYXawnVTPvi+ZnEgd62pBsCVqjZo +% xxxx hEvgGeS3gEiS6FjQOf3wgonJLe7ONTVEebkr+9qfHfjT2MSLgQI2PGTJStNZIHg+Uiyd3AlOVdUe +% xxxx pwP4PMsAyP3iZeau07dquVv+f4aVa6emhOsW4AO7WNP+cTZFCAN2cVPX6lM3kIhg4PfRfi2L5dw3 +% xxxx 1rBsZX+SwvmM9zSjBl82CFNfP6jj5w08pzqgGN6gIUeOwMWKsyj9yyrPQbl/dl1vJQcmvI9EElZ9 +% xxxx 8XRrD9YwHoX+A2SgcAT2c2tCFqefmaYpW8kCcfwzUHQA6OGbZ34Eq9l5vM1/9Wj/ZHGUqB6sP35a +% xxxx +% xxxx +% xxxx ---- S5.3 Hashing Multiple Inputs + +\subsection{Hashing Multiple Inputs} +\EG often requires that multiple input elements are hashed together. In fact, the second input to the function $H$ specified in the previous section always consists of multiple parts. The input byte array $B_1$ is then simply the concatenation of the byte arrays that represent the multiple input elements as specified in Section~\ref{sec:hashinputdata} in the order specified in each case. As all byte arrays that represent input elements have a fixed length, there is no need for a separator byte or character. + +The notation in this document lists the multiple input elements in order separated by commas. The first input element forming the byte array $B_0$ and the list of elements forming the second byte array $B_1$ are separated by a semicolon. + +To illustrate the notation, consider, for example, the computation of the parameter hash $\HH_P = H(\mathtt{ver}; \mathtt{0x00}, p, q, g, n, k)$, where $\mathtt{ver} = \mathtt{0x76322E312E30} \parallel \bytes(0,26)$ (see Equation~\eqref{eq:parameterhash}). This notation means that \HMACSHA is called as $\HMACSHA(\mathrm{B}_0, \mathrm{B}_1)$ with the byte arrays\footnote{The symbol $\parallel$ does not represent a separator symbol and is simply used to denote concatenation like for byte arrays as mentioned above.} +$$\mathrm{B}_0 = \mathtt{0x76322E312E30} \parallel \bytes(0,26),\ +\mathrm{B}_1 = \mathtt{0x00}\parallel \bytes(p, l_p) \parallel \bytes(q, l_q)\parallel\bytes(g,l_p)\parallel\bytes(n, 4) \parallel\bytes(k,4)$$ +as inputs. +The first argument is simply the \EG version identifier \texttt{ver}. The second argument is the byte array that is the result of concatenating the domain separator byte $\mathtt{0x00}$ that identifies the use case and the byte arrays representing the large integers $p$, $q$, and $g$, and the small integers $n$ and $k$. + +% xreqj { + "section": "S5.3", + "text": "EGRI hashes multiple inputs using an unambiguous separation as specified in the relevant section in EG DS v2.1.0", + "sc": "utsp", + "utsp": "" } + +% xxxx 9fzn1IZeyhmHsQW42NK+fWUfmZplaT2zKK1ztFcPPeBlFn6v+nPvwRVgWwnPIl2FeFI4bXRDtulv +% xxxx xzQjmLAwbUM26PV4mKA7pxF36ftBAVFJhvMBg4LtPkuRjmEUbDFmYyM8O4S8azm4gzdXdqayrHQc +% xxxx iKmDHnz1Hddr7aR8i2B9UDhYogprpKVxuFRIYECxDajGxGSXrJBkByG15RcMLkPZEcb0ZEy6r/HY +% xxxx LK/9Pk0rw1A7IgRsX8Lq2Pc13xEECSjrPX3Q/omGbetIkIwoppE0wMhMk764EV4Y4lM6XCOx9X7/ +% xxxx EHq2DmipoiI+ZzmGWM/lo+m+Jmavtjv7VMC1wB+snqjVjTQVAP0LpHR+LeYMBLMpF+hfA1RDWUbD +% xxxx JRcJ5hbwr5wCgUozeWVjojCL4tK0O6rsSO93yD1I9B1SOh9BddZa06Sl1d4I7fcFFfZ4Ma4Fgvv5 +% xxxx jSxQ039Iue1VD0iIgmmLX91s11HgMkBU+arTVZ6p2QEVKeXmBv4FqIbSow9tMkuBYf1MogtPSzfP +% xxxx KLCh/cmsv49E+fIJtq/ukesUDAKipYt/wFjWRehRfiUbDwu068BWx5fkGcVXiu/e3cEMZqqZlhe7 +% xxxx H8dtJ+fnZw/ydyfyhh8riIuDITloYLJT9SeSuL/GJU6RjRJ1dlTqNIP7Mm5mJSqssaJ/eTxBVJgI +% xxxx JrjsdvYH7WLj4jVn+J9LKzlIJD8fI4jyDrESXnDwJ9OuuyeXw7j2l1rUIw5vnOmMMcmU1m1mCyJ6 +% xxxx +% xxxx +% xxxx ---- S5.4 Hash Function Outputs and Hashing to \texorpdfstring{$\Z_q$}{Zq} + +\subsection{Hash Function Outputs and Hashing to \texorpdfstring{$\Z_q$}{Zq}}\label{sec:hashoutputs} +The output of \SHA and therefore $H$ is a 256-bit string, which can be interpreted as a byte array of $32$ bytes. As such, outputs of $H$ are directly used as inputs to $H$ again without any modifications. + +In some cases, \EG requires a hash function with outputs that lie in the exponent space, i.e., in the set $\Z_q$. In particular, these outputs must be (close to) uniformly distributed in $\Z_q$. Such a hash function $H_q$ can be defined based on the hash function $H$. To that end, an output byte array $\mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{31}$ is interpreted as a big endian base-$2^8$ representation of an integer $a$, which means that + +% xxxx ---- eq. 131 + +\begin{equation} + a = \sum_{i=0}^{31} \mathrm{b}_{i} 2^{8(31-i)} = \mathrm{b}_0\cdot 2^{31\cdot 8} + \mathrm{b}_1\cdot 2^{30\cdot 8} + \dots + \mathrm{b}_{31}. +\end{equation} +With this interpretation, hash function outputs correspond to integers in the interval $[0,2^{256}-1]$. Note that, although this byte representation is identical to the byte representation of integers modulo $q$ as described above, the integers corresponding to hash function outputs do not have to be smaller than $q= 2^{256} - 189$. However, in practice, it is highly unlikely that an integer in the interval $[2^{256}-189, 2^{256}-1]$ will occur. When picking a 256-bit integer uniformly at random, the probability that it is not smaller than $q$ is negligibly small, namely $189/2^{256} < 2^{-248}$. + +This allows the hash function $H_q$ to be defined as\footnote{This construction of $H_q$ by simply reducing the output from $H$ modulo $q$ is tailored to the specific choice of $q = 2^{256}-189$. Should \EG be used with other parameters, the group order $q$ might be much further away from $2^{256}$ and care must be taken to ensure that hashing to $\Z_q$ is done securely with a different approach.} + +% xxxx ---- eq. 132 + +\begin{equation}\label{eq:hashmodq} + H_q: \Bcal^{32}\times \Bcal^* \rightarrow \Z_q,\ (\mathrm{B}_0, \mathrm{B}_1) \mapsto H(\mathrm{B}_0,\mathrm{B}_1) \bmod q +\end{equation} +and notation for hashing multiple inputs is identical in this document. + +% xreqj { + "section": "S5.4", + "text": "EGRI implements the function `H_q` as specified in EG DS v2.1.0 eq. 131 - 132.", + "sc": "utsp", + "utsp": "" } +% xreqj { + "section": "S5.4", + "text": "Iff Fixed Parameter `q` begins with 248 or more consecutive leading `1` valued bits, such as is the case with `q` from the standard parameter set, the function `H_q` can be simplified to `H`.", + "sc": "utsp", + "utsp": "" } + + +% xxxx LwQaJEDJzEBBKR/dasb4FtWBXnQU9pWQkmapEu/jaKz8NpSMpW83zzTv+YePuNLtmGt3L/Gugf5Q +% xxxx 3kpZMg0DUMRdGdq8XO5hgMXRqO4VyrPCGAgbhvw0u6DWBZHLvFHQdvYLZRGh+qd70yxYvYj0NjI2 +% xxxx tEpxlu+t4x0gnk4ilewyPJwwR1qlwGYRN2zfX/PtginF2fFwrci4prkmTlNe6sJ/3k6xee6hC5av +% xxxx XVGslGOgaAxTJBwhQbx1QP8Ce1XO6XBEVf8yl3TTZd/lxAaYEaUiyIbjc1O24Q5sHtZdh0mbjv0p +% xxxx 6NZsJszJdvrCwMOFJ7nVuhgkokmVi+5OweNzRxlz0aKlkpZUlGxYHh1pfnMBihsKfRy4PpZXTksa +% xxxx I8FSpjqrGfdBKRKPot/YZwSQHmoeX8jnzFoAO8shcApNpHtQGvoNuQ4XWLGy6KQr2LPmq1q3T606 +% xxxx NdRN7M7KhS/wqh4qx3q+J1UqU9UHTk2ibzY6mCoG5L8woARWP9PrtGvII2ABTQqz5mvJA8wWu5ui +% xxxx uAkcMpav14Dy/RMBtJ7bHCeUyWsUB9SS0kS25cear+nlqDmo3jvspJcNe2HGBn3eGtKXplaRyVGH +% xxxx OzdEKdefXb2hLnBb2ylBuAnSW6o5G6NM8DAYSScra0W6QIE5iZIlyemzBBvBM0Iw0oJ5ugLReE5O +% xxxx 0/0iyx+zAHzgVIna/vD018HPUo0NG+dW1h6tjtEa2SVegYvcM6+FSd0l1zFtNzoEGz/jYkksGqvC +% xxxx +% xxxx +% xxxx ---- S5.5 Domain Separation + +\subsection{Domain Separation}\label{sec:domainsep} +The tables below list every use of the hash function $H$ (and $H_q$) in this specification together with the specific domain separation input for the second argument. They explicitly provide the byte arrays $\mathrm{B}_0$ and $\mathrm{B}_1$ that are passed to \HMAC as inputs to evaluate $\HMACSHA(\mathrm{B}_0,\mathrm{B}_1)$ together with the length $\len(\B_1)$ of $\B_1$ in bytes (note that always $\len(\B_0)=32$). The tables use the standard parameters such that $l_p = 512$ and $l_q = 32$. For other parameters, the values $l_p$ and $l_q$ can be different. + +% xreqj { + "section": "S5.5", + "text": "This is expected to be a fully-redundant section, but we could double-check it", + "sc": "utsp", + "utsp": "" } + +\pagebreak + +% xxxx tyLFw6CHJIjf93lBl2/snDDLnnqGaoPw25iL4av8PmpMBUi0+SFBWuGrbzyiHVOpBkwOKhhOUKc8 +% xxxx bYRzmWEfwFx7zbZD1Kfea1RA57hsdkIU7VwvHNjO82aBD5/gvUt+zVrA57AMFYiSuI3ziQLnVJiz +% xxxx 3XSA9DmqVovzPtk8LkTyns4mpeBMJqO8g1eBR64S5PF4dgjr/X7L/48NQvcnrCBmfl8kofKQxYKI +% xxxx c0cTTIAF68yWyJ5BVyllcUdo5iz6Xief+4iBfqYjvr8mNTV3KK4AKhpe5KoUb0aVAoJ2AWfafa+z +% xxxx kJcznOh//VrXtKXefJRYgM+iSTZupbI48cO6+38Sraq9fPoSE3J20cUFht7UCTM33d9sn3WS04PF +% xxxx H45Ytm0hibQxQ8uy9KzP6AmpTVd3DRwvFD2c3DP1Gnj3XNq3Ro86GRz8wMSWcROHXvyghNgd6ReY +% xxxx E8eTrL9zaLTiv1WbaDvQuv6MkD8aXkub32/RiJ14AsjWRUpiawQLSn4/Z3VHj2sQxEqNSaKCcX80 +% xxxx xbRrEsyeXjfjY+EEFu+X8z99xwe6IiSNzRvaxjeiqF91D86q0Yz3bWd3cViyhjrBHiB72/KMuxOE +% xxxx +DQFKl1CrifWkARuiOy/SxZuxxd+WWy6T1f9epZdDu6OSpPXl2VAgkQVvkt8UcUcsl3gW2vC8SJO +% xxxx 6wMfO8JvtH8tFM2qQLeEivoq/mZwq+FhMJeNW+iamxVBVj6SnDiml6TU3O/nt2gfnpJaQSMHCjDP +% xxxx +% xxxx +% xxxx ---- S5.5.1 Parameter Base and Base Hashes + +\subsubsection{Parameter Base and Base Hashes} +\begin{center} + \begin{tabularx}{\textwidth}{Xr} + \toprule + Computation of the parameter base hash $\HH_P$ & \S\ref{sec:parameterhash}\\ + $\HH_P = H(\mathtt{ver};\mathtt{0x00}, p, q, g, n, k)$ & \eqref{eq:parameterhash}\\ + $\mathrm{B}_0 = \mathtt{ver} = \mathtt{0x76322E312E30} \parallel \bytes(0,26)$\\ + $\mathrm{B}_1 = \mathtt{0x00}\parallel \bytes(p,512)\parallel\bytes(q,32)\parallel \bytes(g,512)\parallel \bytes(n,4) \parallel \bytes(k,4)$\\ + $\len(\B_1) = 1065$\\ + \midrule + Computation of the base hash $\HH_B$ & \S\ref{sec:basehash}\\ + $\HH_B = H(\HH_P;\mathtt{0x01}, \mathtt{manifest})$ & \eqref{eq:basehash}\\ + $B_0 = \HH_P$ & \\ + $B_1 = \mathtt{0x01} \parallel \bytes(\len(\mathtt{manifest}), 4) \parallel \bytes(\mathtt{manifest}, \len(\mathtt{manifest}))$\\ + $\len(B_1) = 5 + \len(\mathtt{manifest})$ & \\ + \bottomrule + \end{tabularx} +\end{center} + +% xreqj { + "section": "S5.5.1", + "text": "This is expected to be a fully-redundant section, but we could double-check it", + "sc": "ace" } + +% xxxx sQorpLLyY4B0cw2poBz8RgpkPyH6x7F7NpHVBxROTCh6LTgeJZn+xJyQ3XH/6o4yh4EnzWjCugKM +% xxxx 5d1tQAjnoOqMYWRHDG+KYqUNvi5iprUrQAKYzidhm2+MV9p7VF6HjmiXWvj2yuNbfPTe0mwnkHeH +% xxxx gkC/haUT7jSUQ5ExKCMJSrVvHmGVxdGsDyhEO5n7NTPDvJH/j2Db/gSXQDexSyEO9USL84MoBg8F +% xxxx sbzEo1mV38LgbB9nSZXkXowxaf3yzNt7f+n+23ev8xNPyBE6o3msgv1i09cZJFp27SN1jsnd1AZT +% xxxx q/w8MTW9Y9v/fBp48RQobm6p5/31U70hrk4Cu7D2lGtyKE7od2Ou+HOsnaspJJpBx/j8OiscXBLc +% xxxx Y2XiZHmNeMwSIBx6MtMxYo4G7hwarI577iyZbiJRxUGMP3cDWse9+qQTf7TsmcQ9PVlNnVvB3H61 +% xxxx y9IKU2wzlWoUnxjEwVJ6zwC2ilKAxFUHQZ9OkLFgPywlLnt/8k5fAytZTfxWlBpIVGCzq2p6ScbV +% xxxx fmIkgTB31UlZUGq1tXyrnnAlNVGQ8FQ2eZKOwoiNtLAyR3RHzPbYyiXoS685qzWgTtfIsMcpkDjD +% xxxx BDBzu3fITmv5X8ql0ghXjoCGo0h8/B+GtjTdvcXMczb7aqcUiMKAx/hRCmoe4pAYO6my9XO+zx0H +% xxxx 1WEYrTi1sDTOxwpdNrI1zymXxanBVrH1Ih3zopXr4H8j6JOr87/ZmFwhx7pcNLjbXIX8/58gvrZf +% xxxx +% xxxx +% xxxx ---- S5.5.2 Key Generation and Extended Base Hash + +\subsubsection{Key Generation and Extended Base Hash} +\begin{center} + %%%%%%%%%%%%%%%%%%%%%% + \begin{tabularx}{\textwidth}{Xr} + \toprule + NIZK proof of knowledge of polynomial coefficients in key generation & \S\ref{sec:keygendetails}\\ + $c_{i} = H_q(\HH_P; \mathtt{0x10}, ``\mathtt{pk\_vote}", i, K_{i,0}, K_{i,1}, \dots, K_{i,k-1}, \kappa_i, h_{i,0}, h_{i,1}, \dots, h_{i,k-1}, h_{i,k})$ & \eqref{eq:hash_nizk_keygen}\\ + $\mathrm{B}_0 = \HH_P$, + $\mathrm{B}_1 = \mathtt{0x10}\parallel\mathtt{706B5F766F7465}\parallel\bytes(i, 4)\parallel \bytes(K_{i,0},512)\parallel\dots\parallel\bytes(K_{i,k-1},512)\parallel$ \\ + $\bytes(\kappa_{i},512)\parallel\dots\parallel\bytes(h_{i,k},512)$ \\ + $\len(\B_1) = 12 + 2(k+1)\cdot 512$\\ + $\hat{c}_{i} = H_q(\HH_P; \mathtt{0x10}, ``\mathtt{pk\_data}", i, \hat{K}_{i,0}, \hat{K}_{i,1}, \dots, \hat{K}_{i,k-1}, \kappa_i, \hat{h}_{i,0}, \hat{h}_{i,1}, \dots, \hat{h}_{i,k-1}, \hat{h}_{i,k})$ & \eqref{eq:hash_nizk_keygen2}\\ + $\mathrm{B}_0 = \HH_P$, + $\mathrm{B}_1 = \mathtt{0x10}\parallel\mathtt{706B5F64617461}\parallel\bytes(i, 4)\parallel \bytes(\hat K_{i,0},512)\parallel\dots\parallel\bytes(\hat K_{i,k-1},512)\parallel$\\ + $\bytes(\kappa_{i},512)\parallel\dots\parallel\bytes(\hat h_{i,k},512)$ \\ + $\len(\B_1) = 12 + 2(k+1)\cdot 512$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Encryption of key shares in key generation & \S\ref{sec:keygendetails}\\ + $k_{i,\ell} = H(\HH_P; \mathtt{0x11}, i, \ell, \kappa_\ell, \alpha_{i,\ell}, \beta_{i,\ell})$ & \eqref{eq:hash_shareenc}\\ + $\mathrm{B}_0 = \HH_P$, $\mathrm{B}_1 = \mathtt{0x11}\parallel\bytes(i,4)\parallel\bytes(\ell,4)\parallel\bytes(\kappa_\ell, 512)\parallel\bytes(\alpha_{i,\ell},512)\parallel\bytes(\beta_{i,\ell},512)$ \\ + $\len(\B_1) = 1545$\\ + Challenge computation for encryption proof & \\ + $c = H_q(H_P; \mathtt{0x12}, i, \ell, \gamma_{i,\ell}, C_{i,\ell,0}, C_{i,\ell,1})$ & \eqref{eq:share_encryption_proof}\\ + $\mathrm{B}_0 = \HH_P$, $\mathrm{B}_1 = \mathtt{0x12}\parallel\bytes(i,4)\parallel\bytes(\ell,4)\parallel\bytes(\gamma_{i,\ell}, 512)\parallel\bytes(C_{i,\ell,0},512)\parallel C_{i,\ell,1}$ \\ + $\len(\B_1) = 1097$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Comparison of different views of preliminary guardian record & \S\ref{sec:keygendetails}\\ + $\HH_{G} = \HH_{G} = H(\HH_B;\mathtt{0x13}, K, \hat{K}, + K_{1,0},\dots, K_{1,k-1}, K_{2,0}, \dots, K_{n, k-1},$ & \eqref{eqn:guardrecordhash}\\ + $\hat{K}_{1,0}, \dots, \hat{K}_{1,k-1}, \hat{K}_{2,0}, \dots, \hat{K}_{n, k-1}, + \kappa_1, \kappa_2, \dots, \kappa_n)$\\ + $\mathrm{B}_0 = \HH_B$\\ + $\mathrm{B}_1 = \mathtt{0x13} \parallel \bytes(K,512) \parallel \bytes(\hat K,512) \parallel \bytes(K_{1,0}, 512) \parallel \dots \parallel$ $\bytes(\kappa_1, 512) \parallel \dots \parallel \bytes(\kappa_n, 512)$ \\ + $\len(\B_1) = 1 + (2 + 2nk + n)\cdot 512$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of the extended base hash $\HH_E$ & \S\ref{sec:extendedhash}\\ + $\HH_E = H(\HH_B; \mathtt{0x14}, K, \hat K)$ & \eqref{eq:extbasehash}\\ + $\mathrm{B}_0 = \HH_B$, + $\mathrm{B}_1 = \mathtt{0x14} \parallel \bytes(K,512) \parallel \bytes(\hat K, 512)$ \\ + $\len(\B_1) = 1025$\\ + \bottomrule + \end{tabularx} +\end{center} + +% xreqj { + "section": "S5.5.2", + "text": "This is expected to be a fully-redundant section, but we could double-check it", + "sc": "ace" } + +% xxxx WKhy56IUgTWtCWItRrJmdOV5kTPmG2hv3vFlqIXrQ+5gFDng9E/djq0sScYw4ok2hk0DvdrmrPew +% xxxx w8h09TUUQaENwB5/zyM7KI9HjdIi7DPhoJ3zCyvjLzeHFQLRUY3EzAAahplSrCJZSq4xCBwZBcQU +% xxxx XsLjHoTSowW/Y40zYHfwCecegeWCoYTLkYiqgpt28SbhNNqSE+PEV2vL071s7uehqIDy22j1PbyR +% xxxx TRkoy1heWlxwNqdmq4SgdBl2tBka8TFm0QPPff2QxoFdt6Z0W7n1by14qZEOQR9Uzq2l+nVfZII4 +% xxxx oWA9FmUyEnZhXJkj2SVJ5AOdHLySdi0bj1yGexLlua3YKbtFMWvndJMvi6XlhPhY2s1LM4LnvHUw +% xxxx SPKj07PSGH5YHmzrfx8BniCSJE4wDzHkm33K+osKq3Dc9uCsYPANmkozonfwk3R+JHpRCMtLkZM6 +% xxxx OYVC1+cr/2Pg/6f6GNYrGaOKQXzvRy2v/ZSnlNZBdCid7VDjwhrSJzuspvTEem4JUJDXtPWPjC7d +% xxxx bcXMgBS+K86rNW0O0q1w49pyDuf6TrdyM/C/ByOYVW7f16dJQgvMxqvlIyj6o1RWSClWF5mMKowN +% xxxx oTYGcdebwMDw+bAGcjDFAlxsIElBaZ9JKjLp3RdvOYRYnKD6nuY/9vKk+hpGqjbeblSkHnClQBIR +% xxxx LhzJaI6Yv8cf84itN5BUl1GCcrAt2rPh6XWEVUpFGArWoXTAyEo0j3S3h2zYe5Ju3aTxbtKft8cm +% xxxx +% xxxx +% xxxx ---- S5.5.3 Ballot Encryption and Confirmation Codes + +\subsubsection{Ballot Encryption and Confirmation Codes} +\begin{center} + %%%%%%%%%%%%%%%%%%%%%% + \begin{tabularx}{\textwidth}{Xr} + \toprule + Computation of the selection encryption identifier hash & \$\ref{sec:identifier}\\ + $\HH_I = H(H_E; \mathtt{0x20}, \id_B)$ & \eqref{eq:identifier}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x20} \parallel \bytes(\id_B, 32)$\\ + $\len(\B_1) = 33$ \\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of encryption nonces & \$\ref{sec:noncegen}\\ + $\xi_{i,j} = H_q(\HH_I; \mathtt{0x21},i,j,\xi_B)$ & \eqref{eq:noncegen}\\ + $\mathrm{B}_0 = \HH_I$\\ + $\mathrm{B}_1 = \mathtt{0x21} \parallel \bytes(i, 4) \parallel \bytes(j,4) \parallel \bytes(\xi_B, 32)$\\ + $\len(\B_1) = 41$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Key derivation for ballot nonce encryption & \S~\ref{sec:encrypt-ball-nonc} \\ + $h = H(\HH_I; \mathtt{0x22}, \alpha_B, \beta_B)$ & (\ref{eq:master-key-enc-of-ballot-nonce})\\ + $\mathrm{B}_0 = \HH_I$, & \\ + $\mathrm{B}_1 = \mathtt{0x22} \parallel \bytes(\alpha_B,512)\parallel\bytes(\beta_B,512)$ & \\ + $\len(\B_1) = 1025$ & \\ + Challenge for ballot nonce encryption proof & \\ + $c = H_q(H_I; \mathtt{0x23}, a_B, C_{\xi_B,0}, C_{\xi_B,1})$ & \eqref{eq:ballot_nonce_proof}\\ + $\mathrm{B}_0 = \HH_I$, & \\ + $\mathrm{B}_1 = \mathtt{0x23} \parallel \bytes(a_B,512)\parallel\bytes(C_{\xi_B,0},512)\parallel C_{\xi_B,1}$ & \\ + $\len(\B_1) = 1057$ & \\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Challenge computation for 0/1-range proofs for option $\lambda$ in contest $\Lambda$ & \S\ref{sec:proofsballotcorrectness}\\ + $c = H_q(\HH_I;\mathtt{0x24},\indc(\Lambda),\indo(\lambda),\alpha,\beta,a_0,b_0,a_1,b_1)$ & \eqref{eq:encproof0_challenge}, \eqref{eq:encproof1_challenge}\\ + $\mathrm{B}_0 = \HH_I$\\ + %\multicolumn{2}{l}{ + $\mathrm{B}_1 = \mathtt{0x24} \parallel \bytes(\indc(\Lambda), 4) \parallel \bytes(\indo(\lambda), 4) \parallel\bytes(\alpha,512)\parallel\bytes(\beta,512)\parallel\bytes(a_0,512)\parallel\bytes(b_0,512)\parallel$ $\bytes(a_1,512)\parallel\bytes(b_1,512)$ & %} + \\ + $\len(\B_1) = 3081$\\ + Challenge computation for $R$-range proofs for contest $\Lambda$ and option $\lambda$ & \\ + $c = H_q(\HH_I;\mathtt{0x24},\indc(\Lambda),\indo(\lambda),\alpha,\beta,a_0,b_0,a_1,b_1,\dots,a_R,b_R)$ & \eqref{eq:rangechallenge} \\ + $\mathrm{B}_0 = \HH_I$\\ + $\mathrm{B}_1 = \mathtt{0x24} \parallel \bytes(\indc(\Lambda), 4) \parallel \bytes(\indo(\lambda), 4) \parallel\bytes(\alpha,512)\parallel\bytes(\beta,512)\parallel\bytes(a_0,512)\parallel\bytes(b_0,512)\parallel$ $\bytes(a_1,512)\parallel\bytes(b_1,512)\parallel\dots\parallel\bytes(a_R,512)\parallel\bytes(b_R,512)$ & \\ + $\len(\B_1) = 9+(2R+4)\cdot 512$\\ + \midrule + Challenge computation for selection limit proofs in contest $\Lambda$& \S\ref{sec:selectionlimit}\\ + $c = H_q(\HH_I;\mathtt{0x24},\indc(\Lambda),\bar\alpha,\bar\beta,a_0,b_0,a_1,b_1,\dots,a_L,b_L)$ & \eqref{eq:nizk_c_selection_limit}\\ + $\mathrm{B}_0 = \HH_I$\\ + %\multicolumn{2}{l}{ + $\mathrm{B}_1 = \mathtt{0x24} \parallel \bytes(\indc(\Lambda),4)\parallel\bytes(\bar\alpha,512)\parallel\bytes(\bar\beta,512)\parallel\bytes(a_0,512)\parallel\bytes(b_0,512)\parallel\dots\parallel$\\ + $\bytes(a_L,512)\parallel\bytes(b_L,512)$ \\%}\\ + $\len(\B_1) = 5+(2L+4)\cdot 512$\\ + \midrule +\end{tabularx} +%%%%%%%%%%%%%%%%%%%%%% +\begin{tabularx}{\textwidth}{Xr} + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of encryption nonces for contest data for contest $\Lambda$& \S\ref{sec:encrypt_ext_data}\\ + $\xi = H_q(\HH_I; \mathtt{0x25}, \indc(\Lambda), \xi_B)$ & \eqref{eq:noncegen_contestdata}\\ + $\B_0 = \HH_I$\\ + \multicolumn{2}{l}{ + $\B_1 = \mathtt{0x25}\parallel \bytes(\indc(\Lambda), 4)\parallel \bytes(\xi_B,32)$}\\ + $\len(\B_1) = 37$\\ + %%%%%%%%%%%%%%%%%%%%%% + Key derivation for contest data encryption &\\ + $h = H(\HH_I; \mathtt{0x26}, \indc(\Lambda), \alpha, \beta)$ & \eqref{eq:k_enc_contest}\\ + $\mathrm{B}_0 = \HH_I$, & \\ + $\mathrm{B}_1 = \mathtt{0x26}\parallel \bytes(\indc(\Lambda),4) \parallel\bytes(\alpha,512)\parallel\bytes(\beta,512)$ \\ + $\len(\B_1) = 1029$\\ + %%%%%%%%%%%%%%%%%%%%%% + Challenge computation for contest data encryption proof\\ + $c = H_q(H_I; \mathtt{0x27}, \indc(\Lambda), a, C_0, C_1)$ & \eqref{eq:contestdatachallenge}\\ + $\mathrm{B}_0 = \HH_I$, & \\ + \multicolumn{2}{l}{$\mathrm{B}_1 = \mathtt{0x27}\parallel \bytes(\indc(\Lambda),4) \parallel\bytes(a,512)\parallel\bytes(C_0,512)\parallel C_1$} \\ + $\len(\B_1) = 1029 + b_\Lambda\cdot 32$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of contest hashes & \S\ref{sec:contesthash}\\ + $\chi_l = H(\HH_I; \mathtt{0x28}, l, \alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_{m_l}, \beta_{m_l}, C_0, C_1, C_2)$ & \eqref{eq:contesthash}\\ + $\mathrm{B}_0 = \HH_I$\\ + $\mathrm{B}_1 = \mathtt{0x28} \parallel \bytes(l, 4) \parallel \bytes(\alpha_1,512)\parallel\ldots\parallel\bytes(\beta_{m_l},512)\parallel\bytes(C_0,512) \parallel C_1 \parallel\bytes(C_2, 64)$ \\ + $\len(\B_1) = 69 + (2m_l+1)\cdot 512 + b_\Lambda\cdot 32$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of confirmation codes & \S\ref{sec:confirmationcode}\\ + $\HH_C = H(\HH_I; \mathtt{0x29}, \chi_1, \chi_2, \ldots, \chi_{m_B}, \B_{C})$ & \eqref{eq:confirmationcode}\\ + $\mathrm{B}_0 = \HH_I$\\ + $\mathrm{B}_1 = \mathtt{0x29} \parallel \chi_1\parallel \chi_2\parallel \ldots\parallel \chi_{m_B}\parallel \B_{C}$\\ + $\len(\B_1) = 37 + m_B\cdot 32$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Voting device information hash & \S\ref{sec:votingdevinfohash}\\ + $\HH_{DI} = H(H_E; \mathtt{0x2A}, S_{\mathrm{device}})$ & \eqref{eq:devinfohash}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x2A} \parallel \bytes(\len(S_{\mathrm{device}}),4) \parallel \bytes(S_{\mathrm{device}}, \len(S_{\mathrm{device}}))$ \\ + $\len(\B_1) = 5 + \len(S_{\mathrm{device}})$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Hash chain initialization for ballot chaining & \S\ref{sec:ballotchaining}\\ + $\HH_0 = H(\HH_E;\mathtt{0x29},\B_{C,0})$ & \eqref{eq:hashchain0}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\B_1 = \mathtt{0x29}\parallel \B_{C,0}$\\ + $\len(\B_1) = 37$ \\ + Hash chain closing for ballot chaining & \\ + $\overline{\HH} = H(\HH_E; \mathtt{0x29}, \overline\B_{C})$ & \eqref{eq:hashchainclose}\\ + \multicolumn{2}{l}{ + $\B_1 = \mathtt{0x29}\parallel\overline\B_{C} = \mathtt{0x29} \parallel \mathtt{0x00000001}\parallel H(\HH_E; \mathtt{0x2B}, \HH_\ell, \B_{C,0})$}\\ + $\len(\B_1) = 37$ & \\ + Computation of $\overline\B_{C}$ & \\ + $\overline\B_{C} = \mathtt{0x00000001}\parallel H(\HH_E; \mathtt{0x2B}, \HH_\ell, \B_{C,0})$ & \eqref{eq:hashchaincloseBC}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\B_1 = \mathtt{0x2B}\parallel \HH_\ell \parallel \B_{C,0}$\\ + $\len(\B_1) = 69$\\ + \bottomrule + \end{tabularx} + %%%%%%%%%%%%%%%%%%%%%% +\end{center} + +% xreqj { + "section": "S5.5.3", + "text": "This is expected to be a fully-redundant section, but we could double-check it", + "sc": "ace" } + +\pagebreak + +% xxxx zvxI1fayhUbw4teHUPQLTLK0qE9tkBcK8Q1i0hUliiWm2vSybctBPsbucCd92uUXYQtlYsgV7ngk +% xxxx 0HfLJyXhyHMa6KxjruCP5SumJsMmklvTgjSskxmTpBX15CvVCG3msy9ox3kxv/6KYaRxCyHzz/wm +% xxxx ChRF5L1ZE+2rzzFy2ccUx5Q6/nUvFGakzyzh11RaJz8kK5GN6GVxzzXK9egx6ltb2HlGbzDEMCYc +% xxxx QLUSJvqnXTaQxRu1RswELXgOLsf4aPtvuqf36WLhCao1OF8EYfB8Ti2/D7qYzr1N447S4RrNuAFg +% xxxx Dlp5MUMXCmH3dy7S6K50jzg1EfVOxJjsGNHXCYGzFHIgF6TzYqlY893AbJH2sUWZgGQxm3HjGBCT +% xxxx xKfdAZsnmazEY0U3DZLhpaENHe50vgVSKSbgvT8ICOnmOdulnLxUOVTQOnnrQVy9hFQICAMD93IE +% xxxx 9wTv9sL/62hPBXFSN1FWJLYcj3xNzLnrs6yMAtNwH6a/x5rkyAqzFMkdr63DGrsJBMUk3G+muU+4 +% xxxx tdvG0+55DAgob9TJxaB1sIbUWiYb7h0g1ZgLgOLfI1LiItn4gad/5LXjqUqFjOmKBMzsvDyYTcLY +% xxxx hHCwe3f5azu0kZwbxoyMH2lZsLswaHbYBy3DND1K4N0DRlnFKXAYBx68whtEfhhlG+dgV6LkK2ez +% xxxx pl3jCPqLQ8rBFL+ZUp02xnzurtI55RyigZ9EKgt/BZMiV3Lb5pDh+8ITgnoD5AbOZUR8G0qsSu5S +% xxxx +% xxxx +% xxxx ---- S5.5.4 Verifiable Decryption + +\subsubsection{Verifiable Decryption} +\begin{center} +\begin{tabularx}{\textwidth}{Xr} + \toprule + Guardian commitments for tally decryption proof for option $\lambda$ in contest $\Lambda$ & \S\ref{sec:verifiable_decrypt_proof}\\ + $d_i = H(\HH_E; \mathtt{0x30}, \indc(\Lambda), \indo(\lambda), i, A, B, a_i, b_i, M_i, U)$ & \eqref{eq:commit2commitment}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x30} \parallel \bytes(\indc(\Lambda), 4) \parallel \bytes(\indo(\lambda), 4) \parallel\bytes(i, 4)\parallel \bytes(A,512)\parallel\bytes(B,512)\parallel\bytes(a_i,512)\parallel\bytes(b_i,512)\parallel\bytes(M_i,512)\parallel\bytes(\#U, 4)\parallel \bytes(j_1, 4)\parallel \dots \parallel\bytes(j_{\#U},4)$\\ + $\len(\B_1) = 2577 + 4\cdot \#U$\\ + %%%%%%%%%%%%%%%%%%%%%% + Challenge computation for proofs of correct tally decryption for option $\lambda$ in contest $\Lambda$& \\ + $c = H_q(\HH_E;\mathtt{0x31},\indc(\Lambda),\indo(\lambda),A,B,a,b,M)$ & \eqref{eq:nizk_c_dec}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x31} \parallel \bytes(\indc(\Lambda), 4) \parallel \bytes(\indo(\lambda), 4) \parallel\bytes(A,512)\parallel\bytes(B,512)\parallel\bytes(a,512)\parallel\bytes(b,512)\parallel\bytes(M,512)$\\ + $\len(\B_1) = 2569$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Guardian commitments for decryption proofs of contest data for contest $\Lambda$ & \S\ref{sec:decrypt_contest_data}\\ + $d_i = H(\HH_I; \mathtt{0x32}, \indc(\Lambda), i, C_0,C_1,C_2, a_i, b_i, m_i, U)$ & \eqref{eq:commit2commit_contestdata}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x32} \parallel \bytes(\indc(\Lambda), 4)\parallel\bytes(i,4) \parallel\bytes(C_0,512)\parallel C_1\parallel\bytes(C_2,64)\parallel\bytes(a_i,512)\parallel\bytes(b_i,512)\parallel\bytes(m_i,512) \parallel\bytes(\#U, 4)\parallel \bytes(j_1, 4)\parallel \dots \parallel\bytes(j_{\#U},4)$\\ + $\len(\B_1) = 2125 + 32\cdot b_\Lambda + 4\cdot \#U$\\ + %%%%%%%%%%%%%%%%%%%%%% + Challenge computation for proofs of correct decryption of contest data & \\ + $c = H_q(\HH_I;\mathtt{0x33},\indc(\Lambda),C_0,C_1,C_2,a,b,\beta)$ & \eqref{eq:nizk_c_dec_cont}\\ + $\mathrm{B}_0 = \HH_I$\\ + \multicolumn{2}{l}{$\mathrm{B}_1 = \mathtt{0x33} \parallel \bytes(\indc(\Lambda), 4) \parallel\bytes(C_0,512)\parallel C_1\parallel\bytes(C_2,64)\parallel\bytes(a,512)\parallel\bytes(b,512)\parallel\bytes(\beta,512)$}\\ + $\len(\B_1) = 2117 + 32\cdot b_\Lambda$ & \\ + %%%%%%%%%%%%%%%%%%%%%% + \bottomrule +\end{tabularx} +\end{center} + +% xreqj { + "section": "S5.5.4", + "text": "This is expected to be a fully-redundant section, but we could double-check it", + "sc": "ace" } + +% xxxx JS437qVRBAjmtsL8qo/shGPLr8cIx4+5s/WYXlv+CNEW9XopeZIaxT2gTVHBjJl9CIqNkL81/uVV +% xxxx 6NMjKg9J0q1SDIEUIzWwene0rybaomzwGoA7oHi+qXsoSdTiJq/vNfCsDJ+BwNFl0OemPSN+ilnq +% xxxx i8+hPqvltHT/2UTgtf4phZXKrSDNrDBPJincb7hH1VYEDVsZ4TTwG0Zunqq27GRRQxBFiY9ekfN7 +% xxxx GBFxPyQJEbWWSsVMpzraLIE8SPnr32blazFtCX+X/B/8lXP6q7DFYyu0YquZn0ukdShaQijMLP6P +% xxxx o8RZyVaamvjxUWsEVnvVcm2GRlO7OKBFLPfR87tGomP107TFWRxT5sW7LcQjq0SoRGiC64Qg/Rl/ +% xxxx 7agpC8sNnsEZqXb67F34uGxx36RmKlBwPvvji2P27yblzYujH1HPr6W/ghFoXPD/eWDTqN4YOpWR +% xxxx juJEJDke1W7Yh+RSla3QG2ATfBfSltuXv0fJTRiommKodLTgb8JO05DM1kDZdc1VSi+6yYN2/wMY +% xxxx 1tOvtEWjXnjs/ytAvxQh5LkORA2HN34GlzuFTVQEJ0J2ZMHKBlmvtAVkw+4sjJ4OlqARE8DdwKUT +% xxxx qD/XYvEU46eQ/xzPwm1wi1ic4itsZh9QeMcKv8kb+iG9/kUIEYhyDk0Mrd2Y3mmBvxzMp/4FAgBZ +% xxxx rsAZjubPN0dpqIwEtFxh9ZPptrtHiaITeySs6my6MRhFAgjN0ExgEpwz1SGwDXNFU9UmAAHMx0/p +% xxxx +% xxxx +% xxxx ---- S5.5.5 Pre-Encrypted Ballots + +\subsubsection{Pre-Encrypted Ballots} +\begin{center} + %%%%%%%%%%%%%%%%%%%%%% + \begin{tabularx}{\textwidth}{Xr} + \toprule + Computation of selection hashes for pre-encrypted ballots & \S\ref{sec:selectionhash_pre}\\ + $\psi_i = H(\HH_I; \mathtt{0x40}, \alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m)$ & \eqref{eq:selectionhash_pre}\\ + $\psi_{m+\ell}=H(\HH_I; \mathtt{0x40}, \alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m)$ & \eqref{eq:nullhash_pre}\\ + $\mathrm{B}_0 = \HH_I$\\ + \multicolumn{2}{l}{$\mathrm{B}_1 = \mathtt{0x40} \parallel\bytes(\alpha_1,512)\parallel\bytes(\beta_1,512)\parallel\bytes(\alpha_2,512)\parallel\ldots\parallel\bytes(\alpha_m,512)\parallel\bytes(\beta_m,512)$}\\ + $\len(\B_1) = 1 + 2m\cdot 512$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of contest hashes for pre-encrypted ballots for contest $\Lambda_l$ & \S\ref{sec:contesthash_pre}\\ + $\chi_l = H(\HH_I; \mathtt{0x41}, \indc(\Lambda_l), \psi_{\pi(1)},\psi_{\pi(2)},\ldots,\psi_{\pi(m+L)})$ & \eqref{eq:contesthash_pre}\\ + $\mathrm{B}_0 = \HH_I$\\ + $\mathrm{B}_1 = \mathtt{0x41} \parallel \bytes(\indc(\Lambda_l), 4) \parallel \psi_{\pi(1)}\parallel\psi_{\pi(2)}\parallel\ldots\parallel\psi_{\pi(m+L)}$\\ + $\len(\B_1) = 5 + (m+L)\cdot 32$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of ballot hashes for pre-encrypted ballots & \S\ref{sec:confirmationcode_pre}\\ + $\HH_C=H(\HH_I; \mathtt{0x42}, \chi_1,\chi_2,\ldots, \chi_{m_B}, \B_C)$ & \eqref{eq:ballothash_pre}\\ + $\mathrm{B}_0 = \HH_I$\\ + $\mathrm{B}_1 = \mathtt{0x42} \parallel \chi_{1}\parallel\chi_{2}\parallel\ldots\parallel\chi_{m_B}\parallel \B_C$\\ + $\len(\B_1) = 37 + m_B\cdot 32$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Hash chain initialization for ballot chaining & \S\ref{sec:ballotchaining_pre}\\ + $\HH_0 = H(\HH_E;\mathtt{0x42},\B_{C,0})$ & \eqref{eq:hashchain0_pre}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\B_1 = \mathtt{0x42}\parallel \B_{C,0}$\\ + $\len(\B_1) = 37$ \\ + Hash chain closing for ballot chaining & \\ + $\overline{\HH} = H(\HH_E; \mathtt{0x42}, \overline\B_{C})$ & \eqref{eq:hashchainclose_pre}\\ + \multicolumn{2}{l}{ + $\B_1 = \mathtt{0x42}\parallel\overline\B_{C} = \mathtt{0x42} \parallel \mathtt{0x00000001}\parallel H(\HH_E; \mathtt{0x44}, \HH_\ell, \B_{C,0})$}\\ + $\len(\B_1) = 37$ & \\ + Device information hash & \\ + $\HH_{DI} = H(H_E; \mathtt{0x43}, S_{\mathrm{device}})$ & \eqref{eq:devinfohash_pre} \\ + $\mathrm{B}_0 = \HH_E$\\ + $\B_1 = \mathtt{0x43}\parallel \bytes(\len(S_{\mathrm{device}}), 4) \parallel \bytes(S_{\mathrm{device}}, \len(S_{\mathrm{device}}))$\\ + $\len(\B_1) = 5 + \len(S_{\mathrm{device}})$ \\ + Computation of $\overline\B_{C}$ & \\ + $\overline\B_{C} = \mathtt{0x00000001}\parallel H(\HH_E; \mathtt{0x44}, \HH_\ell, \B_{C,0})$ & \eqref{eq:hashchaincloseBC_pre}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\B_1 = \mathtt{0x44}\parallel \mathtt{0x4C4F434B} \parallel \HH_\ell \parallel \B_{C,0}$\\ + $\len(\B_1) = 69$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of encryption nonces & \$\ref{sec:noncegen_pre}\\ + $\xi_{i,j,k}=H_q(\HH_I; \mathtt{0x45},i,j,k,\xi_B)$ & \eqref{eq:noncegen_pre}\\ + $\mathrm{B}_0 = \HH_I$\\ + $\mathrm{B}_1 = \mathtt{0x45} \parallel \bytes(i, 4) \parallel \bytes(j, 4) \parallel \bytes(k, 4) \parallel \bytes(\xi_B, 32)$ \\ + $\len(\B_1) = 45 $\\ + \bottomrule + \end{tabularx} + \end{center} + +% xreqj { + "section": "S5.5.5", + "text": "This is expected to be a fully-redundant section, but we could double-check it", + "sc": "ace" } + + +\pagebreak + +% xxxx 7dzGG4YyA4jinrd5svxoyK6h7fJqr39Ph/4aHY+tvB0Ad3dWDtm+w1ykO4dlfSn6k85Zb1EGMq9i +% xxxx pet75HMPj+yrMmvOPFYkW5mNHTelIs1a1I1tvlZt56SmrlkLQqWF/NeRU3S/rGcd+DDtKp4stiv2 +% xxxx USIUGbcd1oHh2e6RO4App6tEag/ZITJH0wPVkUp0Pwlty7oPGy6gRUYWDkCn8ZXkdHdWas+j2xNe +% xxxx B+PT1RGbla5i8NNC8J7iOudhIHTMx1KRd4+8rcl8hIkgwltxn3kslZ6x4Cs+0F6CDmLYzlbKkjq5 +% xxxx YIFyvwn+bldcQhZsOxjT/LmPNBA0YCe1swL9o4kUuh42aoxPwVGNO7STq5s9/dXEg91gqwrATj3K +% xxxx JVMZqnBFDyYZjgrr0bfqHQR1cjnm7042W/BnwsVLQuOdFYpW4N5IgKK3QhAIY/jQ6q23/3JPMmdg +% xxxx WWLQzwTSx54GjGbOY28d7aI5bVQroOuEbmkMp1QrerN7BVV2ZHltMDrs+TDcgEMEcS2NKTe06eZX +% xxxx OoKk5LTiHjFQMhdjQ0wwNobfe33SJG1r3Slio6BQQHXH9zgzWHEWMyPGobxclqRC8QoQ/TTcD3Gu +% xxxx GptX0+w16mm9apqrJhiFUZFOZNqPm+wNI4Hs2nabX3i95HhJ0hRtzWLm8furaFvlkUEpl2iFxVSY +% xxxx EnJGm+C8vSIaED7XfRB1/qukTXCKSqfckfxH2hqTcf9zj6LcYGuiK8pV4tB6MHP5hUsTEKYLBWcx +% xxxx +% xxxx +% xxxx ---- S6.a Verifier Construction + +\section{Verifier Construction} +While it is desirable for anyone who may construct an \EG verifier to have as complete an understanding as possible of the \EG design, this section isolates the items which must be verified and maps the variables used in the specification equations herein to the labels provided in the artifacts produced in an election. + +% xnote S6.a This section generates no unique or specific requirements + +% xxxx Yxb6AFW0Glb7MybPZth1X75McproBR+6gKFqnZf6T+gi6M7vV/0rN8+ot40T1Dz+Rv/7AyJW7WPY +% xxxx mK6BLjieVSdyYHA27csYnGb64ONFqwoDgatXieNPCM3SkqrpH3JlnXbId/Cp+CtV8yxJG0klk4z7 +% xxxx Fnh6aeXqONDapb8XVnX1i+SJzwEQOxM2MkvqIkCjSwr+gqOmDcRTJBPmpq4IVTXiCuyk9Jw02cP3 +% xxxx P8qBU3jys0s99O3PqzzvcYichKvvh1Mf2zxL+me8fPJSgLgntF1o9d0MAQnuEa40TzF8XGEXt9VF +% xxxx qGOFMGXmSSJWi4/nv2IubypWw7KwYc/Y/aVl1Dp/MDdwWONJAPX1GAZREn+3cTz0u1zTMEgC6v5U +% xxxx +lIH2MLA7kqHy2CqQsmI3vjy4ihj/6ygS5BuDNibtYoZ+dTEEuXamJRmj6EiexwEqGrDSeCLfzHE +% xxxx Acst2JG9S+vzgyTNGrj2Ra0I2FEclr7duij6WzvgRmshqR1yYj+3CsftRNMJ+PLtfSHT+DKpPo54 +% xxxx l85za9UyjjLoTKJ41eTL8mXFYb0JRv+AgSCRl9NOmhsR/97OYjwa9DQZ+tC+lQI2NOdfAYWGATN6 +% xxxx 45GkkHN8NxP1m+zA3+cJ5a3ug5SbDdLWvjoys1zh9xOR7KasrL1Nfz8I8OsXxukK0Eidpfbb6IaE +% xxxx EilhQ4/8L5hSstMPy/CJNHy4uUePbMQziPWQty4wZ8OnE2Ogly855taOVp7pmZKfEsSXN6jxASXa +% xxxx +% xxxx +% xxxx +% xxxx +% xxxx ---- S6.1.a Implementation Details + +\subsection{Implementation Details} +There are four operations which must be performed---all on very large integer values: modular addition, modular multiplication, modular exponentiation, and hash computations, i.e., evaluations of the hash function $H$. These operations can be performed using a programming language that provides native support, by importing tools to perform these large integer operations, or by implementing these operations from scratch. + +% xnote S6.1.a This section generates no unique or specific requirements + +% xxxx BSaPoecYF558ZxXciaXeYmlESjzl5vwnYi9L0iUrxxzo7W48I+3LViIy+uopzzBqsPay7ZOUN+JC +% xxxx bp6WcNQyb9wtSSZPiuK0rUHNCqC6A5QXEgAaVIReMxhFaLMCMt0I+lu9ij9iS85KtrjN1x/filul +% xxxx 1AiLD97p6rHb9XiG08lO56QOWk9GEC9cSehh8JYC4z0sbyRNERBJ0RLaVk8kKEpxIPFf4wz10YYX +% xxxx gg/0I5JeSTJGxASetvfI1uXJ79ZTj4g0cBZd6Ygh/PlQEh3/0Q7f8hP9N5oXrm5FwONzfRTmIZd8 +% xxxx ieKl+aqwg4YJoSWRwXKvbhT1Pv5cFVi+dekzzMLmf2nYYChYuMzpC+qiORPI1wuShEBvpyJ1d58G +% xxxx V8A6A++eoKx7F1LQDu9OzG8MO1oG6xUpdbUipf/L0C2B1yd0+csohni3x1cHzG00b45wA68yyO4t +% xxxx n11n6VVpjk/H6s90Ly6lUmm7oFt2J+Lho2ZWdXvhqzCxQp/BoqgBGyHzfgCRDbA67W6/jCOJhVMy +% xxxx 3HUzCfpSs93ShfAxf+J9kHeoq7C0XlTYhvdvNUl3Wuyig0TYdMgG3XaMTg3AvVcaZ31mDbAB1TCW +% xxxx P+e2WxZwSIY6MUW7yRsrl+sHQ1Xpg59du2STTXqqoKsqfjtOFlvlR1YoOtBDpGasC7fhf5uQbQPk +% xxxx LsgKEzuka3Cmbrg5gRVSN+KR6sTFu3Dxl2wvdXnIa3tP7/3aGIkAucqyjouRZ6jUyR9SYCalJzdA +% xxxx +% xxxx +% xxxx ---- S6.1.b Implementation Details - Modular Addition + +\subsubsection*{Modular Addition} +To compute $(a+b) \bmod n$, one can compute $((a \bmod n)+(b \bmod n)) \bmod n$. However, this is rarely beneficial. If it is known that $a,b\in \Z_n$, then one can choose to avoid the division normally inherent in the modular reduction and just use $(a+b) \bmod n = a+b$ (if $a+b 1, could someone evade the Option Selection Limit by both selecting and writing-in the same candidate? Similarly if multiple write-in fields are allowed, couldn't they write-in the same choice multiple times?" +S3.2.c,todo,"(Ref: S3.a.a.b) ""EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)""" +S3.2.2.a,todo,"eq8 pg22 refers to ""the secret key for guardian $G_i$"" ""the public key for guardian $G_i$"" Are these keys ever really used as such in v2.1.0 ?" +S3.2.2.o,todo,"EGRI supports a Guardian `l` in complaining to the Election Administrator and all other Guardians. TODO: In practice this likely implies providing a set of data TBD." +S3.5.b.verif9,todo,"TODO: Verification 9" +S3.6.1,todo,"TODO: Presumably this (""enables a Guardian to review the set of Ciphertexts marked for decryption"") requirement applies to any use of the Guardian Ballot Data Encryption Secret Key as well? -> xreq S3.6.1 EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Ballot Data Encryption Secret Key." +S3.6.1,todo,"Does EGRI need to determine whether write-in fields may actually be relevant to an outcome, or just always decrypt all write-in fields with every Tally?" +S3.6.1,todo,"Presumably, many Ballots will not have any write-in fields actually written-in. Does each Ballot record whether it has a write-in field written-in in a way that avoids the need to decrypt the ballot nonce during the Tally if write-in values are needed?" +S3.6.6.a,todo,"""[write-in] data may need to be decrypted if the tallies record a significant number" +S3.7,todo,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: the partial decryption of the Ballot Nonce" +S3.7,todo,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: for every Contest, for every Option Field, the partial decryption" +S3.7The,todo,"Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the partial decryption from each available Guardian?" +S4.1,todo,"pg. 58 ""in a contest where a voter is allowed to make multiple selections, multiple pre-encryption vectors can be combined to form a single contest encryption vector with multiple encryptions of one that precisely matches the standard \EG format.""" +S4.1,todo,"pg. 58 QUESTION: How are they ""combined""? E.g., are these multiple encryptions combined such that there is still a single vector `Psi_{i,m}` for Option `i`, or are there additional Option vectors? Note: [S4.1.4 pg. 63] says ""For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected"", which seems to imply they are multiplied." +S4.1,todo,"pg. 58 QUESTION: What about when the effective Option Selection Limit is greater than `1`? Do all possible assignments within the effective (Option and Contest) Selection Limits require distinct Selection Vectors and Selection Hashes? E.g. say a Contest has two options and the Contest and Option Selection Limits are all `5`. How is the selection vector constructed which allows the voter to apply a value of `3` to Option 1 and `2` to Option 2?" +S4.1,todo,"pg. 58 QUESTION: How to these ""multiple pre-encryption vectors"" map to ""the j-th selection vector"" and ""the k-th encryption"" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?" +S4.1.1,todo,"pg. 58 QUESTION: S4.1 pg. 57 designates the ""null form"" explicitly as `Psi_{0,m}`, but (eq. 114 pg. 58) refers to them as `psi_{m+l}` where `1 <= l <= L`. How to these multiple pre-encryption vectors map to ""the j-th selection vector"" and ""the k-th encryption"" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?" +S4.1.4,todo,"pg. 59 EGRS says ""A pre-encrypted ballot’s hash will typically be printed directly on the ballot. Ideally, two copies of the ballot hash will be included on each ballot with one remaining permanently with the ballot and the other in an immediately adjacent location on a removable tab. These removable ballot codes are intended to be retained by voters and can be used as an identifier to allow voters to look up their (encrypted) ballots in the election record and confirm that they include the proper short codes as described below.""" +S4.4,todo,"pg. 62 refers to ""every pre-encrypted ballot listed in the election record as uncast"". QUESTION: Does this imply that every `PreencryptedBallot` generated is recorded in the Election Record?" +S4.4,todo,"pg. 63 states that ""For each uncast ballot, the ballot nonce for that ballot is published in the encryption record."" and [S4.5 pg. 64] refers to ""every pre-encrypted ballot listed in the election record as uncast"". QUESTION: Does this imply that there is a point during the tally process at which generated-but-not-cast `PreencryptedBallots` are no longer accepted? If there are multiple Tallys taken, then it seems really important that this only happen at the final Tally. Someone who submitted a PreencryptedBallot that was somehow delayed in the mail probably does not want their Voter Selections made public." +S7,todo,"TODO: Consider any special features which may be needed for RLAs" diff --git a/other/spec-todo/TODO-2.1.txt.db.pages.csv b/other/spec-todo/TODO-2.1.txt.db.pages.csv new file mode 100644 index 0000000..8ce00bd --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.pages.csv @@ -0,0 +1,6 @@ +line_n,page_n +6803,60 +7342,69 +7425,70 +7525,71 +7628,72 diff --git a/other/spec-todo/TODO-2.1.txt.db.sections.csv b/other/spec-todo/TODO-2.1.txt.db.sections.csv new file mode 100644 index 0000000..d689ef7 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.sections.csv @@ -0,0 +1,216 @@ +line_n,section,title +2,S0,"Prefix Items - Not part of EGDS" +201,S1,Introduction +336,S2.a,Overview +424,S2.b,"Key Generation" +615,S2.c,"Ballot Encryption" +723,S2.d,"Verifiable Decryption" +762,S2.e.a,"Independent Verification" +794,S2.e.b,"Independent Verification - Verification of Key Generation" +826,S2.e.c,"Independent Verification - Verification of Ballot Correctness" +852,S2.e.d,"Independent Verification - Verification of the Election Record" +878,S2.f,"Using this Specification" +900,S3.a.a,Components +996,S3.a.b,"Components - Notation" +1027,S3.a.c,"Components - Encryption of Votes" +1146,S3.a.d,"Components - Homomorphic Properties" +1209,S3.a.e,"Components - Non-Interactive Zero-Knowledge (NIZK) Proofs" +1247,S3.a.f,"Components - Threshold Encryption" +1318,S3.a.g,"Components - Encryption of Other Data" +1423,S3.1,"Parameter Requirements" +1457,S3.1.1,"Standard Baseline Cryptographic Parameters" +1578,S3.1.1.b.n3_1,"Standard Baseline Cryptographic Parameters - Note 3.1" +1604,S3.1.2,"Parameter Base Hash" +1644,S3.1.3.a,"Election Parameters and the Election Manifest" +1720,S3.1.3.b,"Election Parameters and the Election Manifest - Labels" +1887,S3.1.3.c,"Election Parameters and the Election Manifest - Indices" +1909,S3.1.3.d,"Election Parameters and the Election Manifest - Contests and the contest index" +1932,S3.1.3.e,"Election Parameters and the Election Manifest - Selectable options and the option index" +1959,S3.1.3.f,"Election Parameters and the Election Manifest - Ballot styles and the ballot style index" +2015,S3.1.3.g,"Election Parameters and the Election Manifest - Selections, option selection limits, and contest selection limits" +2081,S3.1.3.h,"Election Parameters and the Election Manifest - Accompanying data fields" +2172,S3.1.3.i,"Election Parameters and the Election Manifest - Undervotes" +2191,S3.1.3.j,"Election Parameters and the Election Manifest - Counting undervoted contests" +2240,S3.1.3.k,"Election Parameters and the Election Manifest - Undervote difference count" +2275,S3.1.3.l,"Election Parameters and the Election Manifest - Overvotes" +2337,S3.1.3.m,"Election Parameters and the Election Manifest - Null votes" +2380,S3.1.3.n,"Election Parameters and the Election Manifest - Write-ins total" +2473,S3.1.3.o,"Election Parameters and the Election Manifest - The data in the election manifest" +2499,S3.1.4.a,"Election Base Hash" +2530,S3.1.4.b.verif1,"Election Base Hash - Verification 1 (Parameter validation)" +2552,S3.2.a,"Key Generation" +2600,S3.2.b.n3_2,"Key Generation - Note 3.2" +2619,S3.2.c,"Key Generation - (after Note 3.2)" +2744,S3.2.1,"Overview of Key Generation" +2879,S3.2.2,"Details of Key Generation" +2895,S3.2.2.a,"Details of Key Generation - Guardian coefficients and key pair for vote encryption" +2946,S3.2.2.b,"Details of Key Generation - Guardian coefficients and key pair for encrypting other ballot data" +2980,S3.2.2.c,"Details of Key Generation - Additional key pair for sharing secret data between guardians" +3022,S3.2.2.d,"Details of Key Generation - NIZK proof" +3063,S3.2.2.e,"Details of Key Generation - NIZK proof" +3106,S3.2.2.f.verif2,"Details of Key Generation - Verification 2 (Guardian public-key validation)" +3128,S3.2.2.g,"Details of Key Generation - text after Verification 2" +3158,S3.2.2.h.n3_3,"Details of Key Generation - Note 3.3" +3176,S3.2.2.i,"Details of Key Generation - Share encryption" +3263,S3.2.2.j,"Details of Key Generation - Share decryption" +3296,S3.2.2.k,"Details of Key Generation - Secret key share computation" +3329,S3.2.2.l,"Details of Key Generation - Vote encryption public key computation" +3362,S3.2.2.m,"Details of Key Generation - Ballot data encryption public key computation" +3396,S3.2.2.n.verif3,"Details of Key Generation - Verification 3 (Election public-key validation)" +3418,S3.2.2.o,"Details of Key Generation - Share verification and the guardian record" +3535,S3.2.3.a,"Extended Base Hash" +3565,S3.2.3.b.verif4,"Verification 4 Extended base hash validation" +3587,S3.3,"Ballot Encryption" +3632,S3.3.1,"Selection Encryption" +3679,S3.3.2.a,"(new in v2.1.0) Selection Encryption Identifiers and Identifier Hash" +3713,S3.3.2.b.verif5,"(new in v2.1.0) Selection Encryption Identifiers and Identifier Hash - Verification 5 (Uniqueness of selection encryption identifiers)" +3735,S3.3.3,"(was 3.3.2 in v2.0.0) Generation of the Ballot Nonce and Encryption Nonces" +3769,S3.3.4,"(new in v2.1.0) Encryption of Ballot Nonces" +3872,S3.3.5.a,"(was 3.3.3 in v2.0.0) Ballot Well-Formedness" +3890,S3.3.5.b,"(was 3.3.3 in v2.0.0) Ballot Well-Formedness - Contest and option selection limits" +3912,S3.3.5.c,"(was 3.3.3 in v2.0.0) Ballot Well-Formedness - Undervotes" +3947,S3.3.5.d,"(was 3.3.3 in v2.0.0) Ballot Well-Formedness - Overvotes" +3985,S3.3.5.e,"(was 3.3.3 in v2.0.0) Ballot Well-Formedness - Ballot well-formedness" +4020,S3.3.5.f,"(was 3.3.3 in v2.0.0) Ballot Well-Formedness - Cardinal voting methods" +4068,S3.3.6.a,"(was 3.3.4 in v2.0.0) Outline for Proofs of Ballot Correctness" +4087,S3.3.6.b,"(was 3.3.4 in v2.0.0) NIZK proof that $(\alpha,\beta)$ is an encryption of zero." +4107,S3.3.6.c,"(was 3.3.4 in v2.0.0) NIZK proof that $(\alpha,\beta)$ is an encryption of one." +4128,S3.3.6.d,"(was 3.3.4 in v2.0.0) Sketch of NIZK proof that $(\alpha,\beta)$ is an encryption of zero or one." +4150,S3.3.6.e,"(was 3.3.4 in v2.0.0) Sketch of NIZK proof that $(\alpha,\beta)$ is an encryption of an integer $\ell$ such that $0\leq \ell \leq R$." +4170,S3.3.7.a,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness" +4190,S3.3.7.b,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - Unselected option" +4210,S3.3.7.c,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - Unselected option - NIZK proof" +4294,S3.3.7.d,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - Selected option" +4326,S3.3.7.e,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - Selected option - NIZK (range) proof" +4402,S3.3.7.f,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - General case of a range proof" +4420,S3.3.7.g,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - General case of a range proof - NIZK proof" +4481,S3.3.7.h.n3_4,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - Note 3.4" +4512,S3.3.7.i.n3_5,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - Note 3.5" +4530,S3.3.7.j.verif6,"(was 3.3.5 in v2.0.0) Details for Proofs of Ballot Correctness - Verification 6 (Well-formedness of selection encryptions)" +4552,S3.3.8.a,"Proof of Satisfying the Contest Selection Limit" +4571,S3.3.8.b,"Proof of satisfying the contest selection limit - NIZK proof" +4604,S3.3.8.c.verif7,"Proof of satisfying the contest selection limit - Verification 7 (Adherence to vote limits)" +4626,S3.3.9.a,"Supplemental Verifiable Fields (Optional)" +4647,S3.3.9.b,"Supplemental Verifiable Fields (Optional) - Undervote." +4678,S3.3.9.c,"Supplemental Verifiable Fields (Optional) - Overvote." +4710,S3.3.9.d,"Supplemental Verifiable Fields (Optional) - Null vote." +4736,S3.3.9.e,"Supplemental Verifiable Fields (Optional) - Contest write-in." +4761,S3.3.10.a,"Contest Data (Optional)" +4781,S3.3.10.b,"Contest Data (Optional) - Fixed-length encoding." +4801,S3.3.10.c,"Contest Data (Optional) - Encryption." +4874,S3.4,"Confirmation Codes" +4895,S3.4.1,"Contest Hash" +4936,S3.4.2,"Confirmation Code" +4965,S3.4.3,"Voting Device Information Hash" +4994,S3.4.4.a,"Ballot Chaining" +5015,S3.4.4.b,"Ballot Chaining - No chaining." +5042,S3.4.4.c,"pg. 43 Ballot Chaining - Simple chaining." +5105,S3.4.4.d,"Ballot Chaining - Ballot casting or challenging." +5155,S3.4.4.e.verif8,"Ballot Chaining - Verification 8 (Validation of confirmation codes)" +5177,S3.5.a,"Ballot Aggregation" +5210,S3.5.b.verif9,"Ballot Aggregation - Verification 9 (Correctness of ballot aggregation)" +5233,S3.5.c,"Ballot Aggregation - Weighted ballots" +5292,S3.6,"Verifiable Decryption" +5308,S3.6.1,"Preliminary Verification Steps" +5382,S3.6.2,"Verifiable Decryption Strategy" +5452,S3.6.3,"Partial Decryption by Available Guardians" +5503,S3.6.4.a,"Combination of Partial Decryptions" +5541,S3.6.4.b.n3_6,"Combination of Partial Decryptions - Note 3.6" +5561,S3.6.5.a,"Proof of Correctness" +5580,S3.6.5.b,"Proof of Correctness - NIZK Proof" +5658,S3.6.5.c.n3_7,"Proof of Correctness - Note 3.7." +5688,S3.6.5.d.verif10,"Proof of Correctness - Verification 10 (Correctness of tally decryptions)" +5711,S3.6.5.e,"Proof of Correctness - Tally verification" +5730,S3.6.5.f.verif11,"Proof of Correctness - Verification 11 (Validation of contents of tallies)" +5752,S3.6.6.a,"Decryption of Contest Data (Optional)" +5798,S3.6.6.b,"Decryption of Contest Data (Optional) - NIZK proof" +5892,S3.6.6.c.verif12,"Decryption of Contest Data (Optional) - Verification 12 (Correctness of decryptions of contest data)" +5914,S3.6.7.a,"Decryption of Challenged Ballots" +5934,S3.6.7.b,"Decryption of Challenged Ballots - Decryption of ballot nonces." +5980,S3.6.7.c,"Decryption of Challenged Ballots - Decryption with encryption nonces." +6025,S3.6.7.d,"Decryption of Challenged Ballots - Verifying decryption with nonces." +6081,S3.6.7.e.verif13,"Decryption of Challenged Ballots - Verification 13 (Correctness of decryptions for challenged ballots)" +6103,S3.6.7.f.verif14,"Decryption of Challenged Ballots - Verification 14 (Validation of well-formedness and content of challenged ballots)" +6126,S3.7,"The Election Record" +6463,S4,"Pre-Encrypted Ballots (Optional)" +6536,S4.1,"Format of Pre-Encrypted Ballots" +6604,S4.1.1,"Selection Hash" +6653,S4.1.2,"Contest Hash" +6687,S4.1.3,"Confirmation Code" +6715,S4.1.4,"Ballot Chaining" +6769,S4.1.5.a,"Short Codes" +6798,S4.1.5.b,"Short Codes - Undervotes." +6824,S4.2,"The Ballot Encrypting Tool" +6869,S4.2.1,"Deterministic Nonce Derivation" +6900,S4.2.2,"Using the Ballot Encrypting Tool" +6924,S4.3,"The Ballot Recording Tool" +6950,S4.3.1,"Using the Recording Tool" +6976,S4.4,"The Election Record" +7011,S4.4.1,"Election Record Presentation" +7038,S4.5.a,"Verification of Pre-Encrypted Ballots" +7084,S4.5.b.verif15,"Verification of Pre-Encrypted Ballots - Verification 15 (Validation of correct accumulation of selection vectors)" +7108,S4.5.c.verif16,"Verification of Pre-Encrypted Ballots - Verification 16 (Validation of confirmation codes in pre-encrypted ballots)" +7133,S4.5.d.verif17,"Verification of Pre-Encrypted Ballots - Verification 17 (Validation of short codes in pre-encrypted ballots)" +7163,S4.5.e.verif18,"Verification of Pre-Encrypted Ballots - Verification 18 (Correctness of encryptions for uncast pre-encrypted ballots)" +7185,S4.5.f.verif19,"Verification of Pre-Encrypted Ballots - Verification 19 (Validation of content of uncast pre-encrypted ballots)" +7213,S4.6,"Hash-Trimming Functions" +7355,S5,"Hash Computation" +7378,S5.1,"Input Data Representation" +7420,S5.1.1,"Integers Modulo the Large Prime \texorpdfstring{$p$}{p}" +7477,S5.1.2,"Integers Modulo the Small Prime \texorpdfstring{$q$}{q}" +7529,S5.1.3,"Small Integers" +7554,S5.1.4,Strings +7583,S5.1.5,Files +7621,S5.2,"Hash Function" +7662,S5.3,"Hashing Multiple Inputs" +7693,S5.4,"Hash Function Outputs and Hashing to \texorpdfstring{$\Z_q$}{Zq}" +7740,S5.5,"Domain Separation" +7765,S5.5.1,"Parameter Base and Base Hashes" +7803,S5.5.2,"Key Generation and Extended Base Hash" +7869,S5.5.3,"Ballot Encryption and Confirmation Codes" +8008,S5.5.4,"Verifiable Decryption" +8060,S5.5.5,"Pre-Encrypted Ballots" +8140,S6.a,"Verifier Construction" +8161,S6.1.a,"Implementation Details" +8180,S6.1.b,"Implementation Details - Modular Addition" +8203,S6.1.c,"Implementation Details - Modular Multiplication" +8227,S6.1.d,"Implementation Details - Modular Exponentiation" +8253,S6.2,"Validation Steps" +8272,S6.2.1.verif1,"Parameter Verification" +8321,S6.2.2.a.verif2,"Verification 2 Key Ceremony Verification" +8372,S6.2.2.b.verif3,"Verification 3 Election Public Key Validation" +8411,S6.2.3.verif4,"Verification 4 Extended Base Hash Verification" +8451,S6.2.4.a.verif5,"Verification 5 Unique Encryption Identifiers" +8492,S6.2.4.b.verif6,"Verification 6 Correctness of Selection Encryptions" +8538,S6.2.4.c.verif7,"Verification 7 Adherence to Vote Limits" +8584,S6.2.4.d.verif8,"Verification 8 Validation Of Confirmation Codes" +8634,S6.2.5.a.verif9,"Tally Verification" +8674,S6.2.5.b.verif10,"Tally Verification" +8721,S6.2.5.c.verif11,"Tally Verification" +8745,S6.2.6.a.verif12,"Verification of Correct Contest Data Decryption" +8791,S6.2.7.a.verif13,"Verification 13 Correctness of decryptions for challenged ballot" +8835,S6.2.7.b.verif14,"Verification 14 Validation of well-formedness and content of challenged ballots" +8871,S6.2.8.a.verif15,"Verification 15 Validation of correct accumulation of selection vectors" +8916,S6.2.8.b.verif16,"Verification 16 Validation of confirmation codes in pre-encrypted ballots" +8966,S6.2.8.c.verif17,"Verification 17 Validation of short codes in pre-encrypted ballots" +9006,S6.2.8.d.verif18,"Verification 18 Correctness of encryptions for uncast pre-encrypted ballots" +9053,S6.2.8.e.verif19,"Verification 19 Validation of content of uncast pre-encrypted ballot" +9078,S7,"Applications to End-to-End Verifiability and Risk-Limiting Audits" +9109,S8,Acknowledgments +9129,S9.a,"Appendix: Other Parameters" +9146,S9.b,"Appendix: Reduced Parameters - Using a 3072-Bit Prime" +9223,S9.c,"Appendix: Reduced Parameters Toy Parameters for Testing Purposes Only}" +9308,S9.d,"Appendix: Glossary of Typical Variable Usage" +9411,S9.e.a,"Appendix: List of Changes Over Version 2.0.0" +9428,S9.e.3.1,"Appendix: List of Changes Over Version 2.0.0 - Section 3.1" +9450,S9.e.3.2,"Appendix: List of Changes Over Version 2.0.0 - Section 3.2" +9475,S9.e.3.3,"Appendix: List of Changes Over Version 2.0.0 - Section 3.3" +9502,S9.e.3.4,"Appendix: List of Changes Over Version 2.0.0 - Section 3.4" +9524,S9.e.3.5,"Appendix: List of Changes Over Version 2.0.0 - Section 3.5" +9543,S9.e.3.6,"Appendix: List of Changes Over Version 2.0.0 - Section 3.6" +9567,S9.e.3.7,"Appendix: List of Changes Over Version 2.0.0 - Section 3.7" +9586,S9.e.4,"Appendix: List of Changes Over Version 2.0.0 - Section 4" +9605,S9.e.4.1,"Appendix: List of Changes Over Version 2.0.0 - Section 4.1" +9625,S9.e.4.5,"Appendix: List of Changes Over Version 2.0.0 - Section 4.5" +9644,S9.e.5.4,"Appendix: List of Changes Over Version 2.0.0 - Section 5.4" +9663,S9.e.5.5,"Appendix: List of Changes Over Version 2.0.0 - Section 5.5" diff --git a/other/spec-todo/TODO-2.1.txt.db.totals.2025-04-22.csv b/other/spec-todo/TODO-2.1.txt.db.totals.2025-04-22.csv new file mode 100644 index 0000000..cc9658e --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.totals.2025-04-22.csv @@ -0,0 +1,4 @@ +date,nsya,na,nfd,ace,nyi,ics,ute,utep,uts,utsp,its,itsp,total +2025-04-01,323,4,16,47,160,14,10,1,5,45,0,0,625 +2025-04-15,324,4,16,47,156,14,10,1,5,49,0,0,626 +2025-04-22,323,4,16,47,145,17,10,1,6,74,0,0,645 diff --git a/other/spec-todo/TODO-2.1.txt.db.totals.2025-04-29.csv b/other/spec-todo/TODO-2.1.txt.db.totals.2025-04-29.csv new file mode 100644 index 0000000..adfa279 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.totals.2025-04-29.csv @@ -0,0 +1,5 @@ +date,nsya,na,nfd,ace,nyi,ics,ute,utep,uts,utsp,its,itsp,total +2025-04-01,323,4,16,47,160,14,10,1,5,45,0,0,625 +2025-04-15,324,4,16,47,156,14,10,1,5,49,0,0,626 +2025-04-22,323,4,16,47,145,17,10,1,6,74,0,0,643 +2025-04-29,274,24,16,55,164,30,10,1,6,91,0,0,673 diff --git a/other/spec-todo/TODO-2.1.txt.db.totals.2025-05-06.csv b/other/spec-todo/TODO-2.1.txt.db.totals.2025-05-06.csv new file mode 100644 index 0000000..3ad61d1 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.totals.2025-05-06.csv @@ -0,0 +1,6 @@ +date,nsya,na,nfd,ace,nyi,ics,ute,utep,uts,utsp,its,itsp,total +2025-04-01,323,4,16,47,160,14,10,1,5,45,0,0,625 +2025-04-15,324,4,16,47,156,14,10,1,5,49,0,0,626 +2025-04-22,323,4,16,47,145,17,10,1,6,74,0,0,643 +2025-04-29,274,24,16,55,164,30,10,1,6,91,0,0,671 +2025-05-06,271,24,16,55,164,31,10,4,6,100,0,0,683 diff --git a/other/spec-todo/TODO-2.1.txt.db.totals.csv b/other/spec-todo/TODO-2.1.txt.db.totals.csv new file mode 100644 index 0000000..3ad61d1 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.totals.csv @@ -0,0 +1,6 @@ +date,nsya,na,nfd,ace,nyi,ics,ute,utep,uts,utsp,its,itsp,total +2025-04-01,323,4,16,47,160,14,10,1,5,45,0,0,625 +2025-04-15,324,4,16,47,156,14,10,1,5,49,0,0,626 +2025-04-22,323,4,16,47,145,17,10,1,6,74,0,0,643 +2025-04-29,274,24,16,55,164,30,10,1,6,91,0,0,671 +2025-05-06,271,24,16,55,164,31,10,4,6,100,0,0,683 diff --git a/other/spec-todo/TODO-2.1.txt.db.totals.latest.csv b/other/spec-todo/TODO-2.1.txt.db.totals.latest.csv new file mode 100644 index 0000000..3ad61d1 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.totals.latest.csv @@ -0,0 +1,6 @@ +date,nsya,na,nfd,ace,nyi,ics,ute,utep,uts,utsp,its,itsp,total +2025-04-01,323,4,16,47,160,14,10,1,5,45,0,0,625 +2025-04-15,324,4,16,47,156,14,10,1,5,49,0,0,626 +2025-04-22,323,4,16,47,145,17,10,1,6,74,0,0,643 +2025-04-29,274,24,16,55,164,30,10,1,6,91,0,0,671 +2025-05-06,271,24,16,55,164,31,10,4,6,100,0,0,683 diff --git a/other/spec-todo/TODO-2.1.txt.db.xdones.csv b/other/spec-todo/TODO-2.1.txt.db.xdones.csv new file mode 100644 index 0000000..b3a09b2 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.xdones.csv @@ -0,0 +1,59 @@ +section,xtext +S0,"move types for numbers mod p and mod q back into 'eg' lib so they can be known at compile time" +S0,"change obtain_resource_production_result_from_cache_downcast" +S0,"electionguard.exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%)" +S0,"electionguard.exe: seed: write random seed to artifact file" +S0,"electionguard.exe manifest: write ElectionManifest to pretty json file" +S0,"electionguard.exe manifest: write ElectionManifest to canonical bytes file" +S0,"electionguard.exe parameters: write ElectionParameters to json file" +S0,"H_V, H_P, H_M, and H_B updated for 2.0 calculation" +S0,"Generate joint election public key" +S0,"Extended base hash H_E" +S0,"electionguard-test script: implementation in cmd started" +S0,"electionguard-test script: implementation in cmd exercises all (current) functionality" +S0,"BigUint values (mod p or q) now left-padded as necessary to avoid leaking value via serialized file size" +S0,"Hash values now serialized with 'H(upper hex)' format to match spec" +S0,"exe: Csprng now seeded with more entropy from the operating system RNG" +S0,"Election-varying parameters (n and k) now checked for validity" +S0,"Serialization of BigUints now uses base64 encoding" +S0,"Rename guardian private key to secret key" +S0,"electionguard.exe: generate guardian secret key" +S0,"electionguard.exe: write guardian secret key" +S0,"electionguard.exe: derive guardian public key from secret key" +S0,"electionguard.exe: write guardian public key" +S0,"Guardian i uses 1-based indexing" +S0,"compute H_E extended base hash" +S0,"compute joint election public key" +S0,"electionguard.exe: write joint election public key to json file" +S0,"standardize on 'validate' instead of 'verify' when checking deserialized structures" +S0,"instead of from_json and to_json implement from_stdioread and to_stdiowrite" +S0,"every struct that has Self::from_stdioread*() should prefer Self::from_stdioread_validated() and have a self.validate()" +S0,"electionguard.exe: write H_E extended base hash to json file" +S0,"convert many uses of if !() { bail!() } to ensure!()" +S0,"Generate data structure docs from the Reference Implementation in Rust" +S0,"eg: New constrained numeric type for indices. Convert n, k, and other indices to this type." +S0,"build-docs script: initial implementation in cmd" +S0,"evaluate scripting language 'nu' https://www.nushell.sh/" +S0,"electionguard-test script: begin rewrite in nu" +S0,"doc/LICENSE: checked" +S0,"doc/SECURITY.md: complete" +S0,"docs/general: begin writing" +S0,"docs/api: begin writing" +S0,"Remove link to internal site" +S0,"VaryingParameters: enum BallotChaining { Prohibited, Allowed, Required, }" +S0,"remove old EG 1.54 constants" +S0,"exe: Under artifacts dir, first level dirs are specific to secrecy requirements" +S0,"Ballot define data type" +S0,"get fixeduint stuff out of bunna branch" +S0,"Merge code from Anunay" +S0,"doc/SUPPORT.md: complete" +S0,"doc/README.md: complete" +S0,"doc/CODE_OF_CONDUCT.md: complete" +S0,"doc/BUILDING.md: complete" +S0,"Complete all planned code reorganization/renaming" +S0,"docs/implementation guide/References: complete" +S0,"a trait for fn to_canonical_json()" +S0,"many to_stdiowrite() methods have common code that could be factored into a common function" +S0,"a trait for types that have to_stdiowrite()" +S0,"a trait for types that have to_stdiowrite() and perhaps _pretty() and _canonical() variants" +S0,"serialize bignums only all as uppercase hex" diff --git a/other/spec-todo/TODO-2.1.txt.db.xnotes.csv b/other/spec-todo/TODO-2.1.txt.db.xnotes.csv new file mode 100644 index 0000000..d2ab331 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.xnotes.csv @@ -0,0 +1,67 @@ +section,xtext +S0,"Per Michael 2025-02-25, ""Signed ElGamal"" encryption refers to the third ciphertext component which is a Schnorr proof of knowledge of the encryption randomness." +S0,"Abbreviation: 'EG' ElectionGuard, possibly implies v2.1.0 Design Specification and/or Reference Implementation" +S0,"Abbreviation: 'EGDS' ElectionGuard v2.1.0 Design Specification" +S0,"Abbreviation: 'EGRI' ElectionGuard v2.1.0 Reference Implementation" +S0,"Abbreviation: 'RI' - Reference Implementation" +S0,"In EG, Ballots are always encrypted, so 'Ballot' implies 'encrypted'" +S0,"A 'Preencrypted Ballot' is a complete set of ciphertexts for all options which contains no information about Voter Selections." +S2.a,"Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: % xreq2j S2.a EGRI enables the Election Administrator to facilitate defined EG protocols" +S3.7,"Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: % xreq2j S2.a EGRI enables the Election Administrator to facilitate defined EG procedures" +S2.f,"This section generates no unique or specific requirements" +S3.a.b,"This section generates no unique or specific requirements" +S3.a.g,"'Sign(ed|ing) Hashed ElGamal' means 'Signed ElGamal'" +S3.a.g,"Michael 2025-02-25, ""Signed ElGamal"" refers to the third ciphertext component which is a Schnorr proof of knowledge of the encryption randomness. See: { Schnorr, C.P., Jakobsson, M. (2000). Security of Signed ElGamal Encryption. In: Okamoto, T. (eds) Advances in Cryptology — ASIACRYPT 2000. ASIACRYPT 2000. Lecture Notes in Computer Science, vol 1976. Springer, Berlin, Heidelberg. https://doi.org/10.1007/3-540-44448-3_7 } https://markus-jakobsson.com/papers/jakobsson-asiacrypt00.pdf" +S3.a.g,"Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI, not even signing the Election Record" +S3.a.g,"Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: xreq S3.a.g There is an 'Administrative Public Key'." +S3.a.g,"Josh 2025-03-18: The Administrative Public Key is not the same as any key an Election Administrator might use to sign the Election Record" +S3.a.g,"Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: S3.a.g TODO: How is the Administrative Public Key generated?" +S3.a.g,"Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: % xtodo S3.a.g TODO: How is the Administrative Public Key distributed?" +S3.a.g,"Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: % xtodo S3.a.g TODO: How is the Administrative Public Key authenticated?" +S3.a.g,"Josh 2025-03-18: All Administrator Key operations are out of scope for EGRI: % xtodo S3.a.g TODO: Who controls the Administrative Secret Key?" +S3.1.3.b,"Ref: S3.1.3.a ""The Election Manifest for each contest, specifies a label unique across all contests in the election""" +S3.1.3.b,"Ref: S3.1.3.a ""The Election Manifest for each Selectable Option in each contest, specifies a label unique across all Selectable Options in that contest""" +S3.1.3.i,"This section generates no unique or specific requirements" +S3.1.3n,"Josh, Michael 2025-03-18: Out of scope for EGRI: Range proofs of the various combinations of fields (selectable options, undervoted, net undervote, overvoted, net overvote, write-in N, count written-in) may be combined under specific conditions" +S3.2.b.n3_2,"This section generates no unique or specific requirements" +S3.2.2.g,"This section generates no unique or specific requirements" +S3.2.2.h.n3_3,"This section generates no unique or specific requirements" +S3.2.2.j,"Josh 2025-03-18: The 'decrypted key share' is not believed to be necessary to retain." +S3.2.2.o,"Josh 2025-03-18: Out of scope for EGRI: % xreq2j S3.2.2.o The Election Administrator has software." +S3.3.1.j,"Note: Though unlikely with expected values, the addition of \sigma+\xi must not wrap." +S3.3.5.a,"This section generates no unique or specific requirements" +S3.3.5.c,"This section generates no unique or specific requirements" +S3.3.6.a,"This section generates no unique or specific requirements" +S3.3.6.b,"This section generates no unique or specific requirements" +S3.3.6.c,"This section generates no unique or specific requirements" +S3.3.6.d,"This section generates no unique or specific requirements" +S3.3.6.e,"This section generates no unique or specific requirements" +S3.3.7.a,"This section generates no unique or specific requirements" +S3.3.7.b,"This section generates no unique or specific requirements" +S3.3.7.f,"This section generates no unique or specific requirements" +S3.3.8.a,"This section generates no unique or specific requirements" +S3.3.9.a,"This section generates no unique or specific requirements" +S3.3.10.a,"This section generates no unique or specific requirements" +S3.3.10.b,"This section generates no unique or specific requirements" +S3.4,"This section generates no unique or specific requirements" +S3.4.4.a,"This section generates no unique or specific requirements" +S3.6.1,"Josh 2025-03-19: The text ""the election administrator populat[ing] the election record with the sets of aggregated ballots and ballots marked to be challenged"" does not introduce a new requirement, this just refers to the process by which (Cast or Challenged) Ballots are added to the Election Record." +S3.6.1,"Josh 2025-03-19: Though EGRS refers to ""populat[ing] the election record with the sets of aggregated ballots"", this simply refers to the set of Ballots in the `Cast` state, i.e., participating in a tally. There is a set of Ballots in the `Challenged` state (""marked to be challenged""), but they are not 'aggregated' and do not participate in any Tally (i.e., eq. 80 pg. 45). So really there is only ever a single ""set of aggregated ballots"" relevant to a specific Tally." +S3.6.1,"Josh 2025-03-19: In theory, there could be a legitimate need to decrypt a provisionally-cast Ballot or other (say by court order), but such functionality is outside the scope of the RI." +S3.6.1,"Josh, Michael: The need for the Guardian's determination that all Ciphertexts are correct prior to decryption to be recorded in the Election Record was removed in EGRS v2.1.0." +S3.6.4.b.n3_6,"This section generates no unique or specific requirements" +S3.6.5.a,"This section generates no unique or specific requirements" +S3.6.5.c.n3_7,"This section generates no unique or specific requirements" +S3.6.5.e,"This section generates no unique or specific requirements" +S3.6.7.a,"This section generates no unique or specific requirements" +S3.7,"Josh 2025-02-25, 2025-03-18: All Administrator Key operations are out of scope for EGRI: xreq S3.7 EGRS enables the Election Administrator to digitally sign the Election Record." +S3.7,"Josh 2025-02-25, 2025-03-18, 2025-03-19: All Administrator Key operations are out of scope for EGRI: EGDS pg 56 ""Election Administrators"" is plural, but a single Election Administrator is referred to everywhere else a procedure is described." +S4.5,"pg. 63 EGRS states that verifiers must verify that ""All short codes shown to voters are correctly computed from selection hashes in the election record"", which implies that all ""short codes shown to voters"" are published in the Election Record." +S4.5,"pg. 63 EGRS states that verifiers must verify that ""All short codes shown to voters are correctly computed from selection hashes in the election record"", which implies that all Selection Hashes are published in the Election Record." +S4.5,"pg. 63 EGRS states that verifiers must verify that ""selection hashes in the election [are] correctly computed from the pre-encryption vectors published in [pg. 64] the election record."", which implies that all pre-encryption vectors are published in the Election Record." +S6.a,"This section generates no unique or specific requirements" +S6.1.a,"This section generates no unique or specific requirements" +S6.2,"This section generates no unique or specific requirements" +S7,"DEFERRED xreq S7 EGRI supports operation in an ""RLA Mode""." +S7,"DEFERRED xreq S7 EGRI in RLA Mode allows to encrypt the Ballot Nonce of each ballot with a Simple Administrative Key rather than the “heavyweight” Ballot Data Encryption Public Key." +S7,"DEFERRED xreq S7 EGRI in RLA Mode the Simple Administrative Key may be symmetric." diff --git a/other/spec-todo/TODO-2.1.txt.db.xreq_statuses.csv b/other/spec-todo/TODO-2.1.txt.db.xreq_statuses.csv new file mode 100644 index 0000000..afa403f --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.xreq_statuses.csv @@ -0,0 +1,12 @@ +status_code,ordinal,xtext +na,1,"Not directly applicable to implementation code" +nfd,2,"Needs further discussion" +ace,3,"Applicable to implementation code, but more directly covered elsewhere" +nyi,4,"Not yet implemented" +ics,50,"Implementation code exists covering a portion of code paths considered substantial" +ute,55,"At least one unit test exercising a relevant code path exists" +utep,60,"At least one unit test exercising a relevant code path is passing" +uts,65,"Unit tests exercising a portion of code paths considered sufficient exist" +utsp,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +its,80,"An integration test exercising a portion of code paths considered sufficient exists" +itsp,100,"An integration test exercising a portion of code paths considered sufficient is passing" diff --git a/other/spec-todo/TODO-2.1.txt.db.xreqs.csv b/other/spec-todo/TODO-2.1.txt.db.xreqs.csv new file mode 100644 index 0000000..ca761de --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.xreqs.csv @@ -0,0 +1,684 @@ +section,xtext,status_code,status_note +S1.a,"EGRI can be used to enable end-to-end (E2E) verifiability.",na, +S1.a,"EGRI can be used to enable privacy-enhanced risk-limiting audits (RLAs).",na, +S1.a,"EGRI components can be used to empower individual voters to independently verify the accuracy of election results.",na, +S1.b,"EGRI allows individual voters to verify that their votes have been accurately recorded.","", +S1.b,"EGRI allows voters to verify that all recorded votes have been accurately counted.","", +S1.b,"EGRI allows observers to verify that all recorded votes have been accurately counted.","", +S1.b,"EGRI provides an E2E-verifiable tally which can be used as the primary tally in an election.","", +S1.b,"EGRI provides an E2E-verifiable tally which can be used as a verifiable secondary tally alongside traditional methods.","", +S1.b,"EGRI is compatible with in-person voting using an electronic ballot-marking device.",na, +S1.b,"EGRI is compatible with in-person voting using an optical scanner capable of reading hand-marked ballots.",na, +S1.b,"EGRI is compatible with in-person voting using an optical scanner capable of reading machine-marked ballots.",na, +S1.b,"EGRI is compatible with voting by mail.",na, +S1.b,"EGRI is compatible with Internet voting.",na, +S1.c,"EGRI enables public disclosure of encrypted ballots that can be matched directly to physical ballots selected for RLA auditing.","", +S1.c,"EGRI can prove (or fail to prove) that publicly disclosed EG ballots match reported tallies",ace, +S1.c,"EGRI can prove (or fail to prove) that physical ballot records used in an RLA are those produced by the voters",na, +S1.d,"EGRI provides a ""detailed implementation specification"" and/or qualifies as a ""well-documented ElectionGuard implementation""",ace, +S1.d,"EGRI can be used by independent parties to write ElectionGuard verifiers to confirm the consistency of election artifacts with announced election results.",na, +S1.d,"EGRI can be used by independent parties to write verifiers to confirm (or refute) the consistency of election artifacts with announced election results",na, +S2.a,"A 'Guardian' role exists with certain duties.",na, +S2.a,"EGRI enables a Guardian to protect confidentiality of votes.",na, +S2.a,"EGRI enables members of a canvassing board to serve as Guardians.",na, +S2.a,"EGRI enables Guardians to manage cryptographic keys.","", +S2.a,"Compromised human Guardians cannot compromise the integrity of the election tallies.","", +S2.a,"Compromised ""hardware Guardians"" cannot compromise the integrity of the election tallies.","", +S2.a,"EGRI enables Guardians to work together to form a public encryption key for homomorphically-tallied vote encryption.",utsp, +S2.a,"EGRI enables Guardians to work together to form a public encryption key for non-homomorphically-tallied ballot data.",utsp, +S2.a[x],"EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.","", +S2.a,"EGRI allows a quorum of guardians to produce all artifacts required to enable public verification of the tally.","", +S2.a,"No set of guardians fewer than a quorum are able to produce the artifacts required to enable public verification of the tally.",ace, +S2.a,"% xnote S3.7 Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: An 'Election Administrator' role exists",na, +S2.a,"Note: EG defines protocols",ace, +S2.a,"Note: EG defines procedures",ace, +S2.b,"EGRI enables a Guardian to generate its own Vote Encryption (Public|Secret) Key pair prior to the start of voting or RLA.","", +S2.b,"EGRI enables a Guardian to generate its own Ballot Data Encryption (Public|Secret) Key pair prior to the start of voting or RLA.","", +S2.b,"EGRI enables a Guardian to generate its own Guardian Communication (Public|Secret) Key pair pair prior to the start of voting or RLA.","", +S2.b,"EGRI enables Guardians to employ their Guardian Communication Key pairs to communicate privately with other guardians to exchange information about secret keys.","", +S2.b,"EGRI enables Guardians to keep their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys and Key Shares secret.","", +S2.b,"EGRI enables Guardians to perform operations using their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys and Key Shares without leaking or exposing them, to the extent practical within the constraints of hardware/os platform.","", +S2.b,"EGRI enables potential integration of dedicated hardware tokens or devices with which Guardians could to perform operations using their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys.","", +S2.b,"EGRI enables production of an 'Election Record' even if not all guardians are available at that time with","", +S2.b,"(=~ S2.a[x]): EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.","", +S2.b,"Guardians participate in a 'Key Generation Ceremony'.",ace, +S2.b,"At the beginning of the Key Generation Ceremony, EGRI enables each guardian to publish its public keys and proofs of knowledge of the associated secret keys.",ace, +S2.b,"At the beginning of the Key Generation Ceremony, EGRI enables each Guardian to receive the published keys and proofs of knowledge of the associated secret keys from the other Guardians.",ace, +S2.b,"EGRI enables a Guardian to determine that they have received the public keys and proofs of knowledge of the associated secret keys from all other Guardians, or identify those which have not yet been received.",nyi, +S2.b,"EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Vote Encryption key.",ics, +S2.b,"EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Ballot Data Encryption key.",ics, +S2.b,"EGRI enables a Guardian to send the encrypted shares of each of its own (two, non-Communication) secret keys to the other Guardian corresponding to the Guardian Communication Public Key to which it was encrypted.","", +S2.b,"EGRI enables a Guardian to use its own Guardian Communication Secret Key to decrypt any shares of other guardians secret keys that it has received.","", +S2.b,"EGRI enables a Guardian to check for consistency any shares of other guardians secret keys it has received.",nyi, +S2.b,"EGRI enables a Guardian to query or observe which particular shares from all other Guardians it has recieved, decrypted, checked for consistency, and verified, and which are missing, inconsistent, or failed.",nyi, +S2.b,"EGRI enables a Guardian to determine that it has received, decrypted, checked for consistency, and verified shares from all other Guardians.","", +S2.b,"Josh 2025-03-18: ""The code should emit a success or failure indicator that a human can use to verify that the [received key shares] have the required properties."".",nyi, +S2.b,"EGRI can emit information sufficicient to enable an ""all greens on a dashboard""-type display for a Guardian to observe the (received, decrypted, checked for consistency, and verified) state of shares from the other Guardians.","", +S2.b,"EGRI can emit a formatted message to enable a Guardian to ""announce its completion"" once it it has received, checked for consistency, and verified shares from all other Guardians.","", +S2.b,"At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.","", +S2.b,"At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.","", +S2.b,"EGRI enables a Guardian to notice the irregularity of a failed share verification.","", +S2.b,"EGRI enables a Guardian to notice the irregularity of a failed proof verification.","", +S2.b,"EGRI enables a Guardian to notice the irregularity of an inconsistent set of keys.","", +S2.b,"EGRI enables a Guardian to ""complain and halt the protocol"" subsequent to noticing a failed share verification, a failed proof verification, or an inconsistent set of keys. (Is this published? A communications key message?)","", +S2.b,"EGRI enables, supports, or facilitates the initiation of a 'Key Ceremony Irregularity Investigation' subsequent to a Guardian complaint of irregularity.","", +S2.b,"Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to distinguish between an 'Actively Misbehaving Guardian' and any other type of error.","", +S2.b,"Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to identify an Actively Misbehaving Guardian.","", +S2.b,"Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to effectively troubleshoot errors other than an Actively Misbehaving Guardian.","", +S2.b,"Sufficient data, documentation, tools, and/or other resources are available to enable the Key Ceremony Irregularity Investigation to effectively troubleshoot multiple simultaneous errors, potentially of different types.","", +S2.b,"EGRI allows to remove a Guardian subsequent to a Key Ceremony Irregularity Investigation which has identified it as an 'Actively Misbehaving Guardian'.","", +S2.b,"EGRI allows to restart the Key Generation Ceremony protocol from scratch subsequent to an Investigation which has identified and removed an Actively Misbehaving Guardian.","", +S2.b,"TODO? what if its another type of error? Can you just restart whenever? Any of this published?","", +S2.b,"TODO? EGRI enables a Guardian to publish the ciphertext of any encrypted secret key shares that it sent.","", +S2.b,"TODO? EGRI enables a Guardian to publish the ciphertext of any encrypted secret key shares that it received.","", +S2.b,"TODO? EGRI enables a Guardian to publish the plaintext corresponding to the ciphertext of any encrypted secret key shares that it sent? Only subsequent to the initiation of Key Ceremony Irregularity Investigation?","", +S2.b,"TODO? EGRI enables a Guardian to publish the plaintext decrypted from the ciphertext of any encrypted shares that it received? Only subsequent to the initiation of Key Ceremony Irregularity Investigation?","", +S2.b,"Note: The Guardians and Election Administrator together have a duty to successfully complete key generation and conclude the Key Generation Ceremony.","", +S2.c.a[i],"After a voter completes the process of making selections, the election system can make a single call to the EGRI API with the voter selections to encrypt and record the EG Ballot and return a confirmation code for the voter.","", +S2.c.a,"Other than the single call to the EGRI API described in (S2.c.a[i]), there is no other point at which an existing election system must interface with EGRI.","", +S2.c.a,"An RLA system can make the single call to the EGRI API described in (S2.c.a[i]), but any confirmation code returned may be discarded.","", +S2.c.b,"EGRI can accept can accept voter selections in batches directly. This is to support certain vote-by-mail scenarios and RLA.","", +S2.c.b,"EGRI records which Ballots have been decrypted ensures that they can never be ""cast"" or participate in any future Tally. Note that this only applies to homomorphically tallied fields and not, say, decrypting the Ciphertext of a non-homomorphically tallied write-in text field.","", +S2.c.b,"A decrypted ballot should never be 'Cast'. Josh 2025-03-19: Univeral knowledge of all challenged ballots is not required, just checking whatever local store is available is sufficient.","", +S2.c.b,"Implementation Note: After obtaining their confirmation code, voters should have an opportunity to 'challenge' their ballots in order to view the decrypted selections of their EG Ballot to verify that the encryptions were correct. The voter must be informed that the challenged ballot and decrypted selections will eventually be published as part of the public Election Record.","", +S2.c.b,"Implementation note: In an RLA, presumably the non-EG ballots were already cast. Some of the resulting EG Ballots may be anonymized, challenged, and decrypted.","", +S2.c.c,"EGRI produces EG Ballots that contain encrypted voter selections.",utsp, +S2.c.c,"EGRI produces EG Ballots that may contain encrypted additional data fields for some or all Contests.","", +S2.c.c,"EGRI produces EG Ballots that contain a Ballot Nonce, encrypted to the Joint Ballot Data Encryption Public Key.",utsp, +S2.c.c,"EGRI records the EG Ballots for later publication as part of the Election Record.","", +S2.c.c,"EGRI produces EG Ballots that non-interactive zero-knowledge (NIZK) proofs of their well-formedness.","", +S2.c.c,"There can exist no well-formed ballot which is not legitimate, by definition.","", +S2.c.c,"EGRI produces EG Ballots with NIZK proofs proving that, for each contest option, no more than the allowed number of votes were recorded. (Option Selection Limit)","", +S2.c.c,"EGRI produces EG Ballots with NIZK proofs proving that, for each contest, no more than the total allowed number of votes were recorded. (Contest Selection Limit)","", +S2.c.c,"EGRI produces EG Ballots with Contests having additional data fields may contain NIZK proofs similar to those of voter selectable options or other properties.","", +S2.c.c,"EGRI allows to combine EG Ballots into an Aggregate Ballot.","", +S2.c.c,"EGRI allows to combine EG Ballots into a single Aggregate Ballot.","", +S2.c.c,"An Aggregate Ballot formed by combining EG Ballots contains, for every option in every contest, the tally for that option encrypted to the Joint Vote Public Key.","", +S2.d,"Each guardian can compute from the previously shared secret key fragments a share of the secret decryption key","", +S2.d,"Guardians can independently use their share of the secret decryption key to jointly decrypt the election tallies","", +S2.d,"Guardians can independently use their share of the secret decryption key to generate associated verification data","", +S2.d,"Only a quorum of guardians is sufficient to complete decryption","", +S2.d,"Only a quorum of guardians is sufficient to generate the verification data","", +S2.e.a,"An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the well-formedness of every EG Ballot.","", +S2.e.a,"An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the well-formedness the correct aggregation of EG Ballots.","", +S2.e.a,"An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the accurate decryption of election tallies.","", +S2.e.b,"A verifier can be independently implemented which performs the Guardian Key verification steps S6.2.2.verif2 and S6.2.2.verif3 .","", +S2.e.b,"A verifier can be independently implemented that fully verifies the integrity of an Election Record without performing either of the Guardian Key verification steps S6.2.2.verif2 and S6.2.2.verif3 .","", +S2.e.c,"A verifier can be independently implemented which performs the EG Ballot correctness verification steps S6.2.7.verif13, S6.2.7.verif14, S6.2.8.verif17, S6.2.8.verif18, and S6.2.8.verif19","", +S2.e.d,"A verifier can be independently implemented which performs the Election Record verification steps S6.2.1.verif1, S6.2.3.verif4, S6.2.4.verif5, S6.2.4.verif6, S6.2.4.verif7, S6.2.4.verif8, S6.2.5.verif9, S6.2.5.verif10, S6.2.5.verif11, S6.2.6.verif12","", +S3.a.a.a,"EGRI enables selecting Fixed Parameters in advance of an election",utsp, +S3.a.a.a,"EGRI enables using the same Fixed Parameters across multiple elections",na, +S3.a.a.a,"EGRI supplies a standard set of Fixed Parameters",utsp, +S3.a.a.a,"EGRI enables defining Contests",utsp, +S3.a.a.a,"EGRI enables defining selectable Contest Options",utsp, +S3.a.a.a,"EGRI enables defining Ballot Styles",utsp, +S3.a.a.b,"(ref: S2.b) EGRI enables Guardians to generate individual public-secret key pairs",ics, +S3.a.a.b,"(ref: S2.b) EGRI enables Guardians to exchange shares of secret keys",ics, +S3.a.a.b,"EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)","", +S3.a.a.b,"EGRI requires that a complete new set of keys must be generated if even a single guardian is replaced (TODO: How?)","", +S3.a.a.b,"EGRI enables Guardians to destroy their secret keys.",nyi, +S3.a.a.b,"Josh 2025-03-18: ""There is a point where the rejection of the keys can happen""","", +S3.a.a.c,"[No specific requirement]",na, +S3.a.a.d,"(ref: S2.c.c) Guardians can use their secret key shares to jointly produce election tallies together with verifiable artifacts that prove that the tallies are correct.","", +S3.a.c.a,"Encryption of votes in ElectionGuard is performed using the DPP vote encryption method of Devillez, Pereira, and Peters (2022)","", +S3.a.c.b,"EGRI ensures that P is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.",utsp, +S3.a.c.b,"EGRI ensures that Q is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.",utsp, +S3.a.c.b,"EGRI ensures that G is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.",utsp, +S3.a.c.b,"EGRI verifies that P is prime before any processing. Note that this may be a simple comparison with the standard parameter value.",utsp, +S3.a.c.b,"EGRI verifies that Q is prime before any processing. Note that this may be a simple comparison with the standard parameter value.",utsp, +S3.a.c.b,"EGRI verifies that Q is is not a divisor of r=(p-1)/q before any processing. Note that this may be a simple comparison with the standard parameter value.",utsp, +S3.a.c.b,"EGRI verifies that G is a generator $g$ of the order $q$ subgroup $\Z_p^r$ before any processing. Note that this may be a simple comparison with the standard parameter value.",utsp, +S3.a.c.c,"EGRI enables a Guardian to (ref: S.2.b) generate its own Vote or Ballot Data Encryption Secret Key by selecting a random $s\in\Z_q$.","", +S3.a.c.c,"EGRI enables a Guardian to (ref: S.2.b) derive its Vote or Ballot Data Encryption Public Key as $K=g^s \bmod p$.","", +S3.a.c.c,"EGRI enables a Guardian to (ref: S.2.b) publish its Vote or Ballot Data Encryption Public Key.","", +S3.a.c.c,"No entity is ever in possession of a secret key that can be used to decrypt votes","", +S3.a.c.d,"Values are encrypted to a Vote or Ballot Data Encryption Public Key by selecting a random nonce $\xi\in\Z_q$ and forming the pair $(\alpha, \beta) = (g^\xi \bmod p, K^m\cdot K^\xi \bmod p) = (g^\xi \bmod p, K^{m+\xi} \bmod p)$ .","", +S3.a.c.e,"Ciphertexts encrypted to a Vote or Ballot Data Encryption Public Key can be decrypted using the corresponding Secret Key $s$ as $\beta/\alpha^s\bmod p = K^m \bmod p$ using exhaustive search, a pre-computed table, or Shanks's baby-step giant-step method.","", +S3.a.c.e,"The value of $K^m$ can be computed from the encryption nonce $\xi$","", +S3.a.c.e,"The value of $m$ can be computed from the encryption nonce $\xi$","", +S3.a.c.e,"An encryption of one is used to indicate that an option is selected","", +S3.a.c.e,"An encryption of zero is used to indicate that an option is not selected","", +S3.a.d,"All the encryptions of a single option across ballots can be multiplied to form an encryption of the sum of the individual values","", +S3.a.d,"An encryption of some integer other than `0` or `1` is used for certain voting methods","", +S3.a.d,"The product of the encryptions of a single contest option across ballots is an encryption of the tally (S2.c.c)","", +S3.a.d,"Every option can be demonstrated to be an encryption of either `0` or `1` (for typical voting methods)","", +S3.a.d,"Every option can be demonstrated to be an encryption of some integer other than `0` or `1` (for certain voting methods)","", +S3.a.d,"The product of the encrypted options of a single ballot can be used to show that no more options were selected than permitted","", +S3.a.d,"When an option can receive multiple or weighted votes, the product of the encrypted options of a single ballot can be used to show that only the permitted number of votes or permitted sum of weights were used","", +S3.a.d,"A holder of a nonce $\xi$ can prove to a third party that a pair $(\alpha, \beta)$ is an encryption of $m$ without access to the secret $s$ or revealing the nonce $\xi$","", +S3.a.e,"EGRI enables the use of NIZK proofs to demonstrate that keys are properly chosen.","", +S3.a.e,"EGRI enables the use of NIZK proofs to demonstrate that ballots are well-formed.","", +S3.a.e,"EGRI enables the use of NIZK proofs to demonstrate that decryptions match claimed values.","", +S3.a.f.a,"EGRI enables the use of threshold encryption for encryption of ballots and other data.","", +S3.a.f.a,"(ref S2.b) Combining individual Guardian (Vote|Ballot Data) Public Keys into a single Joint (Vote|Ballot Data) Public Key.","", +S3.a.f.a,"Threshold encryption offers a homomorphic property that allows individual encrypted votes to be combined to form encrypted tallies.","", +S3.a.f.b,"Ref S2.b: ""EGRI enables a Guardian to generate its own public-secret (Vote|Ballot Data) Encryption Key pair...""","", +S3.a.f.b,"Ref S2.b: ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian (Vote|Ballot Data) Encryption Public keys to form the Joint (Vote|Ballot Data) Encryption Public Key.""","", +S3.a.f.b,"(=~ S2.a[x]): EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.","", +S3.a.f.c,"EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.","", +S3.a.f.c,"EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.","", +S3.a.f.d,"(Ref: S2.b) Key Ceremony","", +S3.a.f.d,"(Ref: S3.a.f.c) ""EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.""","", +S3.a.f.d,"(Ref: S3.a.f.c) ""EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.""","", +S3.a.g,"EGRI provides a non-homomorphic encryption method known as ""Hashed ElGamal"" for encrypting data other than votes","", +S3.a.g,"EGRI provides a signature method known as ""Signed Hashed ElGamal"" for encrypting data other than votes","", +S3.a.g,"EGRI enables a user to generate a Hashed ElGamal (Public|Secret) Key pair for Hashed ElGamal encryption","", +S3.a.g,"EGRI enables a user to encrypt data to a Hashed ElGamal Public Key","", +S3.a.g,"Any data encrypted to a Hashed ElGamal Public Key cannot be decrypted without the Hashed ElGamal Secret Key","", +S3.a.g,"EGRI enables using a Hashed ElGamal Secret Key to decrypt data encrypted to the corresponding Hashed ElGamal Public Key","", +S3.a.g,"Hashed ElGamal encryption does not have the data size restrictions imposed by the vote encryption","", +S3.a.g,"Hashed ElGamal encryption uses a KDF based on HMAC-SHA-2-256 to generate a key stream XORed with the data","", +S3.a.g,"EGRI enables a user to generate a Signing Hashed ElGamal (Public|Secret) Key pair for Signing data encrypted with Hashed ElGamal","", +S3.a.g,"EGRI enables using a Signing Hashed ElGamal Secret Key to sign data that has been encrypted with Hashed ElGamal","", +S3.a.g,"EGRI enables using a Signing Hashed ElGamal Public Key to verify or refute a signature made with Signed Hashed ElGamal","", +S3.a.g,"(Ref: S2.b) ""EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key a share of each of its own (two, non-Communication) secret keys.""","", +S3.a.g,"EGRI allows to encrypt other (non-homomorphically-tallied) Ballot data to the Joint Ballot Data Encryption Public Key","", +S3.a.g,"EGRI allows to encrypt voter selection contest write-in Ballot data to the Joint Ballot Data Encryption Public Key","", +S3.a.g,"EGRI allows to encrypt other auxiliary data attached to an encrypted Ballot to the Joint Ballot Data Encryption Public Key","", +S3.a.g,"EGRI allows to encrypt voter selection contest write-in Ballot data to the Administrative Public Key.","", +S3.a.g,"EGRI allows to encrypt other auxiliary data attached to an encrypted Ballot to the Administrative Public Key.","", +S3.a.g,"EGRI allows to encrypt other (non-homomorphically-tallied) Ballot data to the Administrative Public Key.","", +S3.1,"EGRI uses integer-based encryption","", +S3.1,"EGRI does not use elliptic curves","", +S3.1,"The encryption scheme used to encrypt votes is defined by primes p and q.","", +S3.1,"ref:S3.a.c.b ""EGRI verifies that (P|Q|G) is prime before any processing...""","", +S3.1.1.a,"The value of p matches the specific value given in EG DS v2.1.0 S3.1.1","", +S3.1.1.a,"The value of q matches the specific value given in EG DS v2.1.0 S3.1.1","", +S3.1.1.a,"The value of r matches the specific value given in EG DS v2.1.0 S3.1.1","", +S3.1.1.a,"The value of g matches the specific value given in EG DS v2.1.0 S3.1.1","", +S3.1.1.b.n3_1,"Nonstandard parameters are outside the scope of these requirements and thus not permitted.","", +S3.1.1.b.n3_1,"EGDS must reject any data bearing non-standard parameters.","", +S3.1.2,"Varying Parameter n is the number of Guardians","", +S3.1.2,"Varying Parameter k is the number of Guardians required to form a quorum for decryption.","", +S3.1.2,"EGRI computes H_P from Fixed Parameters p, q, and g, and Varying Parameters n and k as specified in EG DS v2.1.0 eq. 4","", +S3.1.3.a,"EGRI accepts an Election Manifest",utsp, +S3.1.3.a,"The Election Manifest is a single file",utsp, +S3.1.3.a,"The Election Manifest describes the modalities of the election and specifies a wide range of data about it",utsp, +S3.1.3.a,"The Election Manifest contains data that makes it unique for each election",utsp, +S3.1.3.a,"EGRI rejects any Election Manifest that does not contain data that makes it unique for each election",nfd,"Ref: S3.1.3.a: ""The manifest file must contain data that makes it unique for each election."" QUESTION: How could this possibly be implemented?" +S3.1.3.a,"The Election Manifest lists all contests in an election",utsp, +S3.1.3.a,"The Election Manifest for each Contest, specifies the Selectable Options (e.g., candiates)",utsp, +S3.1.3.a,"The Election Manifest for each Contest, specifies a label unique across all contests in the election",utsp, +S3.1.3.a,"EGRI rejects any Election Manifest containing duplicate labels for Contests",utsp, +S3.1.3.a,"The Election Manifest for each ContestOption in each Contest, specifies a label unique across all Selectable Options in that contest",utsp, +S3.1.3.a,"EGRI rejects any Election Manifest containing duplicate labels for ContestOptions in any Contest",utsp, +S3.1.3.b,"EGRI accepts ElectionManifest labels composed of printable characters and (internal, non-contiguous) 0x20 space characters",utsp, +S3.1.3.b,"EGRI rejects ElectionManifest labels that contain line break characters",utsp, +S3.1.3.b,"EGRI rejects ElectionManifest labels that have leading or trailing whitespace",utsp, +S3.1.3.b,"EGRI rejects ElectionManifest labels that contain contiguous sequences of whitespace other than a single 0x20 space",utsp, +S3.1.3.b,"EGRI rejects ElectionManifest labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)",utsp, +S3.1.3.b,"EGRI rejects ElectionManifest labels having no printable characters",utsp, +S3.1.3.b,"EGRI rejects ElectionManifest labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics,"difficult to test because the Rust std lib rejects malformed UTF-8" +S3.1.3.b,"EGRI accepts Contest labels composed of printable characters and (internal, non-contiguous) 0x20 space characters",utsp, +S3.1.3.b,"EGRI rejects Contest labels that contain line break characters",utsp, +S3.1.3.b,"EGRI rejects Contest labels that have leading or trailing whitespace",utsp, +S3.1.3.b,"EGRI rejects Contest labels that contain contiguous sequences of whitespace other than a single 0x20 space",utsp, +S3.1.3.b,"EGRI rejects Contest labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)",utsp, +S3.1.3.b,"EGRI rejects Contest labels having no printable characters",utsp, +S3.1.3.b,"EGRI rejects Contest labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics, +S3.1.3.b,"EGRI accepts ContestOption labels composed of printable characters and (internal, non-contiguous) 0x20 space characters",utsp, +S3.1.3.b,"EGRI rejects ContestOption labels that contain line break characters",utsp, +S3.1.3.b,"EGRI rejects ContestOption labels that have leading or trailing whitespace",utsp, +S3.1.3.b,"EGRI rejects ContestOption labels that contain contiguous sequences of whitespace other than a single 0x20 space",utsp, +S3.1.3.b,"EGRI rejects ContestOption labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)",utsp, +S3.1.3.b,"EGRI rejects ContestOption labels having no printable characters",utsp, +S3.1.3.b,"EGRI rejects ContestOption labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics, +S3.1.3.b,"EGRI accepts BallotStyle labels composed of printable characters and (internal, non-contiguous) 0x20 space characters",utsp, +S3.1.3.b,"EGRI rejects BallotStyle labels that contain line break characters",utsp, +S3.1.3.b,"EGRI rejects BallotStyle labels that have leading or trailing whitespace",utsp, +S3.1.3.b,"EGRI rejects BallotStyle labels that contain contiguous sequences of whitespace other than a single 0x20 space",utsp, +S3.1.3.b,"EGRI rejects BallotStyle labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)",utsp, +S3.1.3.b,"EGRI rejects BallotStyle labels having no printable characters",utsp, +S3.1.3.b,"EGRI rejects BallotStyle labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics, +S3.1.3.c,"Index values are integers in the range 1 <= i < 2^31",utsp, +S3.1.3.d,"Every Contest in the Election Manifest is uniquely identified by a Contest Index value assigned starting at 1 and continuing in the order defined in the Election Manifest.",utsp, +S3.1.3.e,"Every Selectable Option in every Contest is identified uniquely (within the Contest) by an Option Index value assigned starting at 1 (anew for each Contest) and continuing in the order defined in the in the Election Manifest.",utsp, +S3.1.3.e,"If additional data fields are present for a Contest, they are assigned Indices continuing after the Selectable Options.",ics, +S3.1.3.f,"The Election Manifest defines a single ordered list of all Ballot Styles in the election.",utsp, +S3.1.3.f,"Every Ballot Style in the Election Manifest is uniquely identified by a Ballot Style Index value assigned starting at 1 and continuing in the order defined in the Election Manifest.",utsp, +S3.1.3.f,"The first Ballot Style within the election manifest has index value 1.",utsp, +S3.1.3.f,"Every Ballot Style defines a label unique across all Ballot Styles in the manifest",nyi, +S3.1.3.f,"Every Ballot Style defines a single list of Contest Indices.",utsp, +S3.1.3.f,"Order of occurence is not significant within a Ballot Style Contest Index list.",ics, +S3.1.3.f,"EGRI rejects any Election Manifest having a Ballot Style with a Contest Index list containing a Contest Index that does not refer to a contest in the Election Manifest.",uts, +S3.1.3.f,"EGRI rejects any Election Manifest listing the same Contest Index more than once within the same Ballot Style. (It is common for a Contest Index to be listed in multiple Ballot Styles.)",sci, +S3.1.3.g,"The Election Manifest, for each Contest, may specify a nonnegative integer Contest Selection Limit.",utsp, +S3.1.3.g,"For any Contest for which a Contest Selection Limit is not specfied, an effective value of 1 is used as the default.",ics, +S3.1.3.g,"The effective Contest Selection Limit limits the maximum total value (i.e., number of votes or voter selections) that can be assigned over all Selectable Options in that contest.",ics, +S3.1.3.g,"EGRI may warn if any effective Contest Selection Limit is zero",nyi, +S3.1.3.g,"The Election Manifest, for each Selectable Option in each Contest, may specify a nonnegative integer Option Selection Limit.",utsp, +S3.1.3.g,"For any Selectable Option for which a Option Selection Limit is not specfied, an effective value of 1 is used as the default.",utsp, +S3.1.3.g,"EGRI may warn if any effective Option Selection Limit is zero",nyi, +S3.1.3.g,"The effective Option Selection Limit limits the maximum value (i.e., number of votes) that can be assigned to that particular Selectable Option.",ics, +S3.1.3.g,"The effective Selection Limit limits the maximum value that can be assigned to that Selectable Option",ics, +S3.1.3.g,"If an effective (specified or defaulted) Option Selection Limit exceeds the effective (specified or defaulted) Contest Selection Limit, EGRI applies the smaller value. EGRI may emit a diagnostic message in this case.",ics, +S3.1.3.g,"If the sum of all effective (specified or defaulted) Option Selection Limits for a Contest exceeds that Contest's effective (specified or defaulted) Contest Selection Limit, EGRI applies the smaller value. This is an ordinary case for which EGRI should not emit a specific diagnostic message.",ics, +S3.1.3.h,"Election Manifest offers optional data fields for accompanying data.",nfd,"" +S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied at the top level.",nfd,"" +S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Contests. Note: These are different from Additional Contest Data Fields which may be recorded after the Selectable Option Fields on a Ballot.",nfd,"" +S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Selectable Options.",nfd,"" +S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Ballot Styles.",nfd,"" +S3.1.3.h,"Election Manifest Accompanying Data Fields may go beyond short labels.",nfd,"" +S3.1.3.h,"Election Manifest Accompanying Data Fields may be non-unique.",nfd,"" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election.",nfd,"" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the jurisdiction.",nfd,"" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election device manufacturers.",nfd,"" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about software versions.",nfd,"" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election date.",nfd,"" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election location.",nfd,"" +S3.1.3.h,"EGRI allows an Election Manifest to specify many other things, including things not listed here.",nfd,"" +S3.1.3.j,"'Undervoted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limit",nyi, +S3.1.3.j,"Election Manifest allows to specify whether to record Undervoted Contest Conditions (unless the Contest specifies otherwise).",utsp, +S3.1.3.j,"Election Manifest allows to specify, for each Contest, whether to record the Undervoted Contest Condition for that Contest.",utsp, +S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification (None currently). The value of this additional Contest Data Field is `1` to record the Undervoted Contest Condition and `0` otherwise.",nyi, +S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.",nyi, +S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.",nyi, +S3.1.3.k,"Election Manifest allows to specify whether to record the Undervote Difference Count for that Contest.",utsutspp, +S3.1.3.k,"Election Manifest allows to specify, for each Contest, whether to record the Undervote Difference Count for that Contest.",utsp, +S3.1.3.k,"If a Contest is specified to record Undervote Difference Count, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (saturating) difference (i.e., nonnegative) between the effective Contest Selection Limit and the the sum of the voter selections for that Contest. This value will not exceed the effective Contest Selection Limit.",nyi, +S3.1.3.k,"If a Contest is specified to record Undervote Difference Count, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.",nyi, +S3.1.3.l,"An 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit.",nyi, +S3.1.3.l,"Election Manifest allows to specify, for each Contest, whether to record the Overvoted Contest Condition for that contest.",nyi, +S3.1.3.l,"If not specified, EGRI does not record the Overvoted Contest Condition.",nyi, +S3.1.3.l,"If a Contest is specified to record Overvoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Contest Condition and `0` otherwise.",nyi, +S3.1.3.l,"If a Contest is specified to record Overvoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.",nyi, +S3.1.3.l,"'Overvoted Option Condition' is a per-contest-option state in which the selection count is strictly greater than the option selection limit",nyi, +S3.1.3.l,"Election Manifest allows to specify, for each Contest Option, whether to record the Overvoted Option Condition for that Option.",nyi, +S3.1.3.l,"If not specified, EGRI does not record the Overvoted Option Condition.",nyi, +S3.1.3.l,"If a Contest Option is specified to record Overvoted Option Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields, any other Contest Data Fields recording Overvoted Option Condition for lower-index Options, and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Option Condition and `0` otherwise.",nyi, +S3.1.3.l,"If a Contest is specified to record Overvoted Option Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.",nyi, +S3.1.3.m,"'Null Voted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limit",nyi, +S3.1.3.m,"Election Manifest allows to specify, for each Contest, whether to record the Null Voted Contest Condition for that Contest.",nyi, +S3.1.3.m,"If not specified, EGRI does not record the Null Voted Contest Condition.",nyi, +S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Null Voted Contest Condition and `0` otherwise.",nyi, +S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.",nyi, +S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.",nyi, +S3.1.3.n,"Election Manifest allows to specify, for each Contest, whether to record the Total Number Of Write-ins selected for that Contest.",nyi, +S3.1.3.n,"If not specified, EGRI does not record the Total Number Of Write-ins.",nyi, +S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (nonnegative integer) total number of write-ins the voter selected for that Contest. This value must not exceed the effective Contest Selection Limit.",nyi, +S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.",nyi, +S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.",nyi, +S3.1.3.n,"EGRI may emit a diagnostic message if any contest specifies a number of write-in fields greater than the contest selection limit",nyi, +S3.1.3.n,"Election Manifest allows to specify, for each Contest, the number of write-in fields available to the voter.",nyi, +S3.1.3.n,"If a Contest is specified to supply a nonzero number of write-in fields, the Contest has that number of additional 'write-in N' voter-selectable Contest Options.",nyi, +S3.1.3.n,"Any such write-in fields are to be labeled by the system as ""write-in N"", where 'N' is the 1-based index value of the write-in field. (This will be greater than the Contest Option Field Index value by the number of non-write-in voter-Selectable Options.)",nyi, +S3.1.3.g,"The Election Manifest, for each additional 'write-in N' voter-selectable Contest Option, may specify a nonnegative integer Option Selection Limit with the same defaults and other semantics as non-write-in Contest Options. (ref: S3.1.3.g)",nyi, +S3.1.3.n,"""Written-in"" means that the election system has recorded some voter-supplied, non-empty, non-blank data associated with a contest and write-in field index value",nyi, +S3.1.3.n,"Every write-in field that *was not* written-in MUST be assigned a value of `0`.",nyi, +S3.1.3.n,"Every write-in field that *was* written-in MUST be assigned a value of `1` or greater, and subject to the same rules for Effective Option Selection Limit as other voter-Selectable Options.",nyi, +S3.1.3.n,"The value of a write-in field counts against the effective Contest Selection Limit",nyi, +S3.1.3.n,"For every Write-in field, an additional Range Proof is recorded that the encrypted value is either `0` or `1`.",nyi, +S3.1.3.n,"Josh 2025-03-18: A write-in field can not have an effective Option Selection Limit greater than `1`.",nyi, +S3.1.3.o,"A canonical byte representation is defined for the Election Manifest.",utsp, +S3.1.3.o,"EGRI allows to write the Election Manifest canonical representation to a file.",ics, +S3.1.4.a,"EGRI computes H_B from H_P and the Election Manifest canonical representation as specified in EG DS v2.1.0 eq. 5.",utsp, +S3.1.4.b.verif1,"TODO: Verification 1","", +S3.2.a,"EGRI allows to specify the number of Guardians `n` prior to a Key Ceremony.",uts, +S3.2.a,"EGRI allows to specify the quorum value `k` prior to a Key Ceremony.",uts, +S3.2.a,"EGRI rejects values of `n` less than `1`.",utsp, +S3.2.a,"EGRI rejects values of `n` greater than `2^31 - 1`.",utsp, +S3.2.a,"EGRI rejects values of `k` less than `1`.",utsp, +S3.2.a,"EGRI rejects values of `k` greater than `n`.",ics, +S3.2.c,"(Ref: S2.b) Guardians can generate individual public-secret key pairs",utep, +S3.2.c,"(Ref: S2.b) Guardians can exchange shares of secret keys",ute, +S3.2.c,"(Ref: S3.a.a.b) ""EGRI requires that a complete new set of keys must be generated if even a single guardian is replaced""",na, +S3.2.c,"EGRI rejects any election data unless 1 <= k <= n < 2^31",ics, +S3.2.c,"Each Guardian is associated with a human readable identifier referred to as the 'Guardian Name'.",utep, +S3.2.c,"EGRI accepts Guardian labels composed of printable characters and (internal, non-contiguous) 0x20 space characters.",utsp, +S3.2.c,"EGRI rejects Guardian labels that contain line break characters.",utsp, +S3.2.c,"EGRI rejects Guardian labels that have leading or trailing whitespace.",utsp, +S3.2.c,"EGRI rejects Guardian labels that contain contiguous sequences of whitespace other than a single 0x20 space.",utsp, +S3.2.c,"EGRI rejects Guardian labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs).",utsp, +S3.2.c,"EGRI rejects Guardian labels having no printable characters",utsp, +S3.2.c,"EGRI rejects Guardian labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics,"difficult to test because the Rust std lib rejects malformed UTF-8" +S3.2.c,"Ballots are encrypted using threshold encryption",ute, +S3.2.c,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.""",ute, +S3.2.c,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.""",ute, +S3.2.c,"(Ref: S2.b) Every guardian can Shamir-share their secret keys during key generation",uts, +S3.2.c,"(Ref: S2.c.c) Voter Selections can be encrypted to the Joint (Vote|Ballot Data) Encryption Public Keys.",ute, +S3.2.c,"(Ref: S3.a.f.c) ""EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.""",ute, +S3.2.c,"(Ref: S3.a.f.c) ""EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.""",ute, +S3.2.c,"(Ref: S3.a.f.d) ""Fewer than `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys can not produce a full decryption of any tally""",na, +S3.2.1,"(Ref: S2.b) Guardians can exchange shares of secret keys",uts, +S3.2.1,"Receiving Guardians can confirm that the key shares they receive are correct",uts, +S3.2.1,"Every Guardian can generate an independent public-secret key pair by generating a random integer secret s_i in Z_q",utsp, +S3.2.1,"Every Guardian can form their Guardian public key as K_i = g^{s_i} mod p",utsp, +S3.2.1,"The Election Record contains every Guardian's public key",nyi, +S3.2.1,"The Election Record contains, for every Guardian public key, a non-interactive zero-knowledge Schnorr proof of knowledge of the associated secret key",ics, +S3.2.1,"The Joint Vote Encryption Public Key `K` is computed as \prod_{i=1}^n K_i mod p",utsp, +S3.2.1,"Every guardian G_i can generate k - 1 random polynomial coefficients a_{i,j} such that 0 < j < k and 0 <= a_{i,j} < q",utsp, +S3.2.1,"Every guardian G_i can generate and publish commitments K_{i,j} = g^{a_{i,j}} mod p to each of its polynomial coefficients",utsp, +S3.2.1,"Guardian polynomial coefficients are generated and managed in precisely the same fashion as secret keys",na, +S3.2.1,"Individual Ballots can be homomorphically combined into a single Aggregate Ballot",ute, +S3.2.1,"The single Aggregate Ballot consists of an encryption of the tally for each contest (e.g. selection option) field",ute, +S3.2.1,"Any set of k guardians' private keys is sufficient to complete decryption of the tally for each contest (e.g. selection option) field",ics, +S3.2.1,"EGRI enables any set of `k` distinct Guardian Vote Encryption Secret Keys to complete the decryption of the tally for each Contest Option and additional Contest Data Fields. (Ref: S3.a.f.c)",ute, +S3.2.1,"No set of fewer than `k` distinct Guardian Vote Encryption Secret Keys is sufficient to complete the the decryption of the tally for any Contest Option or additional Contest Data Field.",na, +S3.2.2.a,"(Ref: S2.b) Key Ceremony",ace, +S3.2.2.a,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Vote Encryption (Public|Secret) Key pair prior to the start of voting or RLA.""",ace, +S3.2.2.a,"EGRI enables a Guardian `i` to generate and store `k` secret polynomial coefficients (indexed as `0 <= j < k`) by sampling a CSRNG uniformly as `0 < a_{i,j} < q` for their Guardian Vote Encryption Key pair.","", +S3.2.2.a,"EGRI enables a Guardian `i` to generate, store, and publish a Schnorr proof of knowledge for each of its k secret polynomial coefficients (indexed as `0 <= j < k`) for their Guardian Vote Encryption Key pair.","", +S3.2.2.b,"(Ref: S2.b) Key Ceremony",ace, +S3.2.2.b,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Ballot Data Encryption (Public|Secret) Key pair prior to the start of voting or RLA.""",ace, +S3.2.2.b,"EGRI enables a Guardian `i` to generate and store `k` secret polynomial coefficients (indexed as `0 <= j < k`) by sampling a CSRNG uniformly as `0 < a_{i,j} < q` for their Guardian Ballot Data Encryption Key pair.","", +S3.2.2.b,"EGRI enables a Guardian `i` to generate, store, and publish a Schnorr proof of knowledge for each of its k secret polynomial coefficients (indexed as `0 <= j < k`) for their Guardian Vote Encryption Key pair.","", +S3.2.2.c,"(Ref: S2.b) Key Ceremony",ace, +S3.2.2.c,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Guardian Communication (Public|Secret) Key pair prior to the start of voting or RLA.""",ace, +S3.2.2.c,"EGRI enables a Guardian `i` to generate and store a uniformly random value $\zeta_i \in \Z_q$ as its Guardian Communication Secret Key","", +S3.2.2.c,"EGRI enables a Guardian `i` to derive and store Guardian Communication Public Key as \kappa_i = g^{\zeta_i} \bmod p. eq. 9","", +S3.2.2.d,"After having generated its own Vote Encryption Secret Key and Guardian Communication Secret Key, EGRI enables a Guardian `i` to generate an NIZK proof of knowledge of secrets $a_{i,j}$ and of $\zeta_i$ as specified in EG DS v2.1.0 eq. 10 - 11","", +S3.2.2.d,"EGRI enables a Guardian to publish its NIZK proof of knowledge of secrets $a_{i,j}$ and of $\zeta_i$.","", +S3.2.2.e,"After having generated its own Ballot Data Encryption Secret Key and Guardian Communication Secret Key, EGRI enables a Guardian `i` to generate and publish an NIZK proof of knowledge of secrets $\hat{a}_{i,j}$ and of $\zeta_i$ as specified in EG DS v2.1.0 eq. 10 - 11","", +S3.2.2.e,"EGRI enables a Guardian to publish its NIZK proof of knowledge of secrets $\hat{a}_{i,j}$ and of $\zeta_i$.","", +S3.2.2.f.verif2,"TODO: Verification 2 Note: Required for (Ref: S3.2.2.o Details of Key Generation - Share verification and the guardian record)",ace, +S3.2.2.i,"(Ref: S2.b) Key Ceremony",ace, +S3.2.2.i,"(Ref: S2.b) EGRI enables a Guardian `i` to encrypt to another Guardian `l` Guardian Communication Public Key a share of each of its own (Vote|Ballot Data) Encryption Secret Keys, $P_i(\ell)$ and $\hat{P}_i(\ell)$, as specified in EG DS v2.1.0 eq. 15 - 22",ace, +S3.2.2.i,"(Ref: S2.b) EGRI enables a Guardian to publish the encryption $E_\ell(P_i(\ell), \hat{P}_i(\ell))$ of its secret key shares $P_i(\ell)$ and $\hat{P}_i(\ell)$ for every other guardian $G_\ell$",ace, +S3.2.2.j,"(Ref: S3.2.2.i) EGRI enables a receiving Guardian `l` to verify or refute the Schnorr proof $C_{i,\ell,2} = (\bar c_{i,\ell},\bar v_{i,\ell})$ from sending Guardian `i` as part of the share of each of Guardian `i`'s (Vote|Ballot Data) Encryption Secret Keys as specified in EG DS v2.1.0 eq. 22.",ace, +S3.2.2.j,"If the Schnorr proof successfully verifies, EGRI enables receiving Guardian `l` to decrypt the ciphertext as specified in EG DS v2.1.0 eq. 23.","", +S3.2.2.k,"EGRI enables a Guardian `i` to compute its share $z_i = P(i)$ of the Joint Vote Encryption Secret key as specified in EG DS v2.1.0 eq. 24.","", +S3.2.2.k,"EGRI enables a Guardian `i` to compute its share $z_i = P(i)$ of the Joint Ballot Data Encryption Secret key as specified in EG DS v2.1.0 eq. 24.","", +S3.2.2.l,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.""",ace, +S3.2.2.l,"EGRI enables the computation of the Joint Vote Encryption Public Key specified in eq. 25.",utep, +S3.2.2.m,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.""",ace, +S3.2.2.m,"EGRI enables the computation of the Joint Ballot Data Encryption Public Key specified in eq. 26.",utep, +S3.2.2.n.verif3,"TODO: Verification 3 Note: Required for (Ref: S3.2.2.o Details of Key Generation - Share verification and the guardian record)","", +S3.2.2.o,"There is a defined Election Data Object called the 'Preliminary Guardian Record'.","", +S3.2.2.o,"EGRI enables an Election Administrator to produce the Preliminary Guardian Record from data generated during the Key Ceremony.","", +S3.2.2.o,"The Preliminary Guardian Record contains the Election Parameters $p$, $q$, $g$, $n$, and $k$; the Joint (Vote|Ballot Data) Encryption Public Keys $K$ and $\hat K$; all $K_{i,j}$ and $\hat{K}_{i,j}$ values from all Guardians (for $1\leq i \leq n$ and $0 \leq j < k$), the Guardian Communication Public Keys $\kappa_i$, together with all Schnorr proofs $(c_{i}, v_{i,0}, v_{i,1}, \dots, v_{i, k})$ and $(\hat{c}_{i}, \hat{v}_{i,0}, \hat{v}_{i,1}, \dots, \hat{v}_{i, k})$.","", +S3.2.2.o,"EGRI enables a Guardian `l` to verify the Preliminary Guardian Record as specified in EG DS v2.1.0 eq. 27 - 29. Note: This includes (Ref: S3.2.2.f.verif2 Verification 2) and (Ref: S3.2.2.n.verif3 Verification 3).","", +S3.2.2.o,"(Ref: S2.b) ""EGRI enables a Guardian to ""complain and halt the protocol""""",ace, +S3.2.2.o,"(Ref: S2.b) ""EGRI enables, supports, or facilitates the initiation of a 'Key Ceremony Irregularity Investigation' subsequent to a Guardian complaint of irregularity.""",ace, +S3.2.2.o,"EGRI enables to deterimine that all Guardians have confirmed that all verification steps have passed successfully.","", +S3.2.2.o,"There is a defined Election Data Object called the 'Guardian Record'.","", +S3.2.2.o,"EGRI enable publishing the Guardian Record as part of the Election Record.","", +S3.2.2.o,"EGRI enables a Guardian to retain its own indepent copy of the Preliminary Guardian Record for later comparison with the Guardian Record as published in the Election Record.","", +S3.2.2.o,"EGRI enables a Guardian to verify that the Guardian Record as published in the Election Record contain the same values for $K$ and $\hat{K}$, the Joint (Vote|Ballot Data) Encryption Public Key as that they verified in the Preliminary Guardian Record.","", +S3.2.2.o,"EGRI enables a Guardian to retain only its secret values $z_i = P(i)$ and $\hat{z}_i = \hat{P}(i)$ for later use in the decryption process and to destroy all other secret values.","", +S3.2.2.o,"EGRI enables a Guardian to destroy its initial (Vote|Ballot Data) Encryption Secret Keys $s_i$ and $\hat{s}_i$.","", +S3.2.3.a,"EGRI computes H_E from H_B and the Joint (Vote|Ballot Data) Encryption Public Keys as specified in EGDS v2.1.0 eq. 30.",utsp, +S3.2.3.b.verif4,"TODO: Verification 4","", +????,"EGRI rejects any set of Guardians having any CoefficientCommitment the same.","", +????,"EGRI rejects any Guardian having any CoefficientCommitment the same.","", +S3.3,"An ElectionGuard `Ballot` is an Election Data Object that is created encrypted. The term `encrypted Ballot` is technicaly redundant.",utsp, +S3.3,"The concept of a traditional, e.g. paper, ""ballot"" is referred to as `Voter Selections`.","", +S3.3,"In specific cases, such as a challenged, an ElectionGuard `Ballot` may be decrypted and the plaintext of the constest data fields published. But the term `Ballot` still as always refers to the encrypted form.","", +S3.3,"Election systems may assign a Ballot an ID label for reference and retrival purposes. This ID may be randomly generated or derived from a combination of information guaranteed unique. In no case will this ID label encode or be derived from data that wouldn't be recorded in the Ballot in-the-clear anyway.","", +S3.3.1.j,"EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EGDS v2.1.0 eq. 31 pg. 28.",utsp, +S3.3.1.j,"EGRI may pre-compute the encryption of a Contest (Option or Additional) Data Field value of '0', and then convert it to an encryption of `1` if needed.","", +S3.3.2.a,"EGRI computes for each Ballot a Selection Encryption Identifier `id_B`.","", +S3.3.2.a,"EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32.","", +S3.3.2.b.verif5,"TODO: Verification 5","", +S3.3.3,"EGRI selects for each Ballot a Ballot Nonce by sampling a CSRNG uniformly as `0 < id_B < 2^256`.","", +S3.3.3,"EGRI computes for each Contest appearing on the Ballot, for each Contest (Option or Additional) Data Field, a Contest Data Field Nonce as specified in EG DS v2.1.0 eq. 33. Note: This uses the ""mod q"" version of H.","", +S3.3.4,"EGRI for each Ballot encrypts (with a Schnorr proof of knowledge) the Ballot Nonce `id_B` to the Joint Ballot Data Encryption Public Key as specified in EG DS v2.1.0 eq. 34 - 38.","", +S3.3.4,"EGRI attempts, to the greatest extent practical, to erase from hardware memory the Ballot Nonce `id_B` (and any information which could reconstruct it e.g., RNG state) immediately after use.","", +S3.3.5.b,"Ref: S3.1.3.g","", +S3.3.5.c,"Ref: S3.1.3.i Election Parameters and the Election Manifest - Undervotes","", +S3.3.5.c,"Ref: S3.1.3.j Election Parameters and the Election Manifest - Counting undervoted contests","", +S3.3.5.c,"Ref: S3.1.3.k Election Parameters and the Election Manifest - Undervote difference count","", +S3.3.5.c,"Ref: S3.1.3.m Election Parameters and the Election Manifest - Null votes","", +S3.3.5.d,"(Ref: S3.1.3.l) ""An 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit.""",ace, +S3.3.5.d,"When an Overvoted Contest Condition has occurred, all Contest Option Data Fields for that Contest are system-assigned the value of `0`.","", +S3.3.5.d,"When an Overvoted Contest Condition has occurred, this fact MAY be encoded into a (non-tallied) data field and encrypted to the Joint Ballot Data Encryption Public Keys.","", +S3.3.5.d,"When an Overvoted Contest Condition has occurred, the specific voter selections for this contest MAY be encoded into a (non-tallied) Contest Data Field and encrypted to the Joint Ballot Data Encryption Public Keys, much like write-in data.","", +S3.3.5.d,"Ref: S3.1.3.l Election Parameters and the Election Manifest - Overvotes","", +S3.3.5.e,"For every Ballot, EGRI allows, for every Contest Option Data Field, to prove that the Ciphertext is an encryption of a nonnegative value less than or equal to the effective Option Selection Limit (e.g., an encryption of `0` or `1`).","", +S3.3.5.e,"For every Ballot, EGRI allows, for every Contest Additional Data Field, to prove that the Ciphertext is an encryption of a legitimate value (as specified for that particular Contest Additional Data Field).","", +S3.3.5.e,"For every Ballot, EGRI allows, for every Contest, to prove that the sum of all Contest Option values is an encryption of a nonnegative value less than or equal to the effective Contest Selection Limit.","", +S3.3.5.f,"EGRI uses range proofs to prove well-formedness of an individual selection when it is allowed for a Contest Option on a Ballot to be assigned values greater than `1`","", +S3.3.5.f,"EGRI uses range proofs for individual Contest Options","", +S3.3.5.f,"EGRI uses range proofs for the Contest selection sum","", +S3.3.5.f,"EGRI can use range proofs to enable cumulative voting","", +S3.3.5.f,"EGRI can use range proofs to enable score voting","", +S3.3.5.f,"EGRI can use range proofs to enable STAR-voting","", +S3.3.5.f,"EGRI can use range proofs to enable Borda count","", +S3.3.7.c,"(Ref: S3.2.3.j) ""EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EG DS v2.1.0 eq. 31.""",ace, +S3.3.7.c,"EGRI encrypts a Contest (Option or Additional) Data Field having a value of `0` as $(\alpha,\beta)=(g^\xi \bmod p,\ K^\xi \bmod p)$.","", +S3.3.7.c,"EGRI computes an NIZK proof that (\alpha,\beta) is an encryption of `0` or `1` as specified in EG DS v2.1.0 eq. 39 - 47.","", +S3.3.7.d,"(Ref: S3.2.3.j) ""EGRI may pre-compute the encryption of a Contest (Option or Additional) Data Field value of '0', and then convert it to an encryption of `1` if needed.""",ace, +S3.3.7.d,"(Ref: S3.2.3.j) ""EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EG DS v2.1.0 eq. 31.""",ace, +S3.3.7.d,"EGRI encrypts a Contest (Option or Additional) Data Field having a value of `1` as $(\alpha,\beta) = (g^\xi \bmod p,\ K^{\xi+1} \bmod p)$.","", +S3.3.7.e,"When the effective Option Selection Limit is a value other than `1`, EGRI computes an NIZK proof that (\alpha,\beta) is an encryption of `0` or `1` as specified in EG DS v2.1.0 eq. 48 - 56.","", +S3.3.7.g,"When the effective Option Selection Limit is a value other than `0` or `1`, EGRI computes an NIZK range proof as specified in EG DS v2.1.0 eq. 57 - 61.","", +S3.3.7.j.verif6,"TODO: Verification 6","", +S3.3.8.b,"EGRI computes an NIZK range proof that the homomorphic combination (i.e., sum) of all Contest Options Fields represents the encryption of a nonnegative integer value not greater than the effective Contest Selection Limit as specified in EG DS v2.1.0 eq. 62.","", +S3.3.8.c.verif7,"TODO: Verification 7","", +S3.3.9.b,"Ref: S3.1.3.j ""Undervoted Contest Condition""","", +S3.3.9.b,"Ref: S3.1.3.j ""Undervote Difference Count""","", +S3.3.9.c,"Ref: S3.1.3.l ""Overvoted Contest Condition""","", +S3.3.9.c,"Ref: S3.1.3.l ""Overvoted Option Condition""","", +S3.3.9.d,"Ref: S3.1.3.m 'Null Voted Contest Condition'","", +S3.3.9.e,"Ref: S3.1.3.n Write-ins","", +S3.3.10.c,"If the Election Manifest specifies that Ballots record for a Contest any non-homomorphically tallied additional or supplemental Contest Data Fields, EGRI encodes their values using an unambiguous fixed-length encoding and encrypts it as specified in EG DS v2.1.0 eq. 63 - 69.","", +S3.4.1,"EGRI computes the Contest Hash value $\chi_l$ as specified in EG DS v2.1.0 eq. 70.","", +S3.4.2,"EGRI computes the Confirmation Code `H_C` as specified in EG DS v2.1.0 eq. 71.","", +S3.4.3,"EGRI computes the Voting Device Information Hash value `H_DI` as specified in EG DS v2.1.0 eq. 72.","", +S3.4.4.b,"In the case of 'No chaining', EGRI computes the Chaining Field `B_C` as specified in EG DS v2.1.0 eq. 73.","", +S3.4.4.c,"In the case of 'Simple chaining', EGRI computes the Chaining Fields and Chaining Value as specified in EG DS v2.1.0 eq. 74 - 78.","", +S3.4.4.d,"EGRI enables a method of recording the state of a Ballot as one of `VoterSelectionsEncrypted`, `Cast`, `Spoiled`, `Challenged`, or `ChallengedDecrypted`.","", +S3.4.4.d,"EGRI refuses to decrypt a ballot in the `Cast` state.","", +S3.4.4.d,"EGRI refuses to decrypt a ballot in the `Spoiled` state.","", +S3.4.4.d,"EGRI includes in any tally operation only Ballots in the `Cast` state.","", +S3.4.4.d,"EGRI enables decrypting a ballot in the `Challenged` state.","", +S3.4.4.d,"EGRI refuses to change the state of a Ballot from the `Cast` state.","", +S3.4.4.d,"EGRI refuses to change the state of a Ballot from the `Spoiled` state.","", +S3.4.4.d,"EGRI refuses to change the state of a Ballot from the `ChallengedDecrypted` state.","", +S3.4.4.e.verif8,"TODO: Verification 8","", +S3.5.a,"EGRI enables publishing all Ballots (with proofs) in the Election Record at the conclusion of voting.","", +S3.5.a,"EGRI enables computing an Aggregate Ballot as specified in EG DS v2.1.0 eq. 79.","", +S3.5.b.verif9,"Every Ballot that fails to specify a Ballot Weight that is an integer inclusively between 1 and the maximum specified in the Election Manifest is considered invalid.","", +S3.5.c,"The Election Manifest specifies a maximum Ballot Weight as a small positive integer.","", +S3.5.c,"The condition ""If weights are used"" means the Election Manifest specifies a maximum Ballot Weight greater than `1`.","", +S3.5.c,"Every EGRI API receiving voter selections for encryption receives a value for the corresponding Ballot Weight and rejects the voter selections if the supplied Ballot Weight is not an integer inclusively between 1 and the maximum specified in the Election Manifest.","", +S3.5.c,"Every Ballot records its associated Ballot Weight.","", +S3.5.c,"Despite the language ""explicitly associated with an identified voter"", EGRI does not represent the concept of ""voter"". If an external system is used to track an association between Ballots and actual voters, the impact on voter privacy and coersion resistance must be considered extremely carefully.","", +S3.5.c,"EGRI enables computing an Aggregate Ballot from Weighted Ballots as specified in EG DS v2.1.0 eq. 80.","", +S3.5.c,"When computing an Aggregate Ballot, the aggregation function must, using access to the full and complete Election Record information to that point in time, verify that every Ballot to be aggregated has never been decrypted, is not in a 'Challeged' or 'Spoiled' state, etc.","", +S3.5.c,"If such a (decrypted, challenged, spoiled, etc) Ballot were somehow to be supplied to the aggregation function, it MUST be excluded from the tally, a diagnostic message emitted, and the offending Ballot identifier(s) returned separately.","", +S3.6.1,"EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Vote Encryption Secret Key. Josh 2025-03-19: This will only ever include the set of `Challenged` Ballots and the aggregate encrypted tallies.","", +S3.6.1,"EGRI enables to communicate to the Guardians the set of Ciphertexts marked for decryption (i.e., challenged Ballots and aggregate encrypted tallies).","", +S3.6.1,"Josh 2025-03-19: Multiple Tallies may be conducted for a single election.","", +S3.6.1,"Whenever multiple Tallies are conducted for a single election, subsequent tallies should extend the current Election Record. This does not introduce a requirement for EGRI to implement any form of rollback protection.","", +S3.6.1,"EGRS enables a Guardian to verify or refute that all Ciphertexts are correct prior to decryption. Ref: S6.2.3.verif4, S6.2.4.verif5, S6.2.4.verif6, S6.2.4.verif7, S6.2.4.verif8, S6.2.5.verif9","", +S3.6.1,"EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.","", +S3.6.1,"EGRS does not assist a Guardian to participate in the decryption of any Ciphertexts which does not both 1. belong to a set that has been marked for decryption and 2. been verified correct by that Guardian.","", +S3.6.2,"(Ref: S3.6.1) ""EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that have both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.""",ace, +S3.6.2,"(Ref: S3.6.1) ""EGRS refusues to assist a Guardian to participate in the decryption of any Ciphertexts unless they both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.""",ace, +S3.6.2,"(Ref: S3.a.f.c) ""EGRI enables a Guardian to compute a verifiable partial decryption...""",ace, +S3.6.2,"(Ref: S3.a.f.c) ""EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies...""",ace, +S3.6.2,"EGRI enables a quorum of `k` distinct Guardians to compute `T = K^t`, for every Contest (Option or Additional) Data Field in the election. as specified in EG DS v2.1.0 eq. 81 - 82.","", +S3.6.2,"EGRI enables a quorum of `k` distinct Guardians to publish `T` in the Tallies of the Election Record, for every Contest (Option or Additional) Data Field in the election. Note: This is to handle scenarios in which the discrete log of `T` ends up needing more computation than the Guardian quorum has on hand at the time.","", +S3.6.2,"EGRI enables the practical determination of the tally `t` from `T = K^t`, for every Contest (Option or Additional) Data Field in the election.","", +S3.6.2,"EGRI enables the publication of the tally `t`, for every Contest (Option or Additional) Data Field in the election, in the Tallies of the Election Record.","", +S3.6.3,"EGRI enables a quorum of `k` distinct Guardians to compute `T = K^t`, for every Contest (Option or Additional) Data Field in the election. as specified in EG DS v2.1.0 eq. 81 - 82.","", +S3.6.3,"EGRI enables a quorum of `k` distinct Guardians to publish `T` in the Tallies of the Election Record, for every Contest (Option or Additional) Data Field in the election. Note: This is to handle scenarios in which the discrete log of `T` ends up needing more computation than the Guardian quorum has on hand at the time.","", +S3.6.3,"EGRI enables the practical determination of the tally `t` from `T = K^t`, for every Contest (Option or Additional) Data Field in the election.","", +S3.6.3,"EGRI enables the publication of the tally `t`, for every Contest (Option or Additional) Data Field in the election, in the Tallies of the Election Record.","", +S3.6.4.a,"EGRI enables the combination of partial decryptions as specified in EG DS v2.1.0 eq. 85 - 86.","", +S3.6.5.b,"EGRI enables available Guardians to jointly prove that they have shared knowledge of $s\in \Z_q$ such that $M = A^{s} \bmod p$ and $K = g^{s} \bmod p$ as specified in EG DS v2.1.0 eq. 87 - 93.","", +S3.6.5.d.verif10,"(Ref: S6.2.5.verif10) Verification 10",nyi, +S3.6.5.f.verif11,"(Ref: S6.2.5.verif11) Verification 11",nyi, +S3.6.6.a,"EGRI decrypts optional contest data as specified in EG DS v2.1.0 eq. 96 - 97.","", +S3.6.6.b,"EGRI computes NIZK proof of Decryption of Contest Data as specified in EG DS v2.1.0 eq. 98 - 106.","", +S3.6.6.c.verif12,"TODO: Verification 12","", +S3.6.7.b,"EGRI decrypts challenged Ballots Ballot Nonces as specified in EG DS v2.1.0 eq. 107 - 108.","", +S3.6.7.c,"EGRI decrypts challenged Ballots from encryption nonces as specified in EG DS v2.1.0 eq. 109 - 111.","", +S3.6.7.d,"TODO: Decryption of Challenged Ballots - Verifying decryption with nonces.","", +S3.6.7.e.verif13,"TODO: Verification 13","", +S3.6.7.f.verif14,"TODO: Verification 14","", +S3.7,"(Ref: S2.b) ""EGRI enables production of an 'Election Record'""",nyi, +S3.7,"The Election Record is not a static fixed format. Different information is added and modified at different times.",nyi, +S3.7,"The Election Record format must support appending new and updated information without invalidating signatures made previously.",nyi, +S3.7,"The Election Record format must support inclusion-by-reference (eg a Ballot ID and hash value) of batches of Ballots. Rationale: For all but very small elections, it will not be practical to hold all Ballots in a single file. Neither will it be practical to place every Ballot in a separate file in a single directory.",nyi, +S3.7,"The Election Record records any information necessary and sufficient to uniquely identify and describe the election, such as date, location, election type, etc. that is not otherwise present in the Election Manifest.",nyi, +S3.7,"The Election Record records the Election Manifest",nyi, +S3.7,"The Election Record records the Fixed Parameters `p`, `q`, `g`, and `r`.",nyi, +S3.7,"The Election Record records the Varying Parameters `n` and `k`.",nyi, +S3.7,"The Election Record records `H_P`",nyi, +S3.7,"The Election Record records `H_B`",nyi, +S3.7,"The Election Record records for every Guardian `1..n`: a name or label",nyi, +S3.7,"The Election Record records for every Guardian `1..n`: the polynomial coefficients commitments",nyi, +S3.7,"The Election Record records for every Guardian `1..n`: the polynomial coefficients the proofs of knowledge",nyi, +S3.7,"The Election Record records for every Guardian `1..n`: the Guardian Communication Public Key",nyi, +S3.7,"The Election Record records the Joint Vote Encryption Public Key",nyi, +S3.7,"The Election Record records the Joint Ballot Data Encryption Public Key",nyi, +S3.7,"The Election Record records `H_E`",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier $\id_B$,",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier hash $\HH_I$,",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: all of the encrypted selections on each ballot,",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the proofs that each such value is an encryption of either zero or one (or more generally, the proofs that these values satisfy the respective option selection limits),",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the effective Contest Selection Limit",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, for every Option Field, the effective Option Selection Limit",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the stated and effective selection limits applied to each contest,",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the proof that the number of selections made does not exceed the selection limit,",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight as originally submitted with the Voter Selections",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight already applied to the Ciphertexts",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Style index,",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: device information uniquely identifying the device used for encrypting of voter selections to produce the Ballot",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the date and time of the ballot encryption (as reported by the encrypting device) with no more than 1 second granularity",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the confirmation code produced for the Ballot",nyi, +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the state of the ballot `Cast` or `Challenged`",nyi, +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: all the information included for the Ballot in its `Challenged` state",nyi, +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the selections made for every Contest Option",nyi, +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the decrypted plaintext values of every Contest (Option or Additional) Data Field",nyi, +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the proofs of correct decryption for every Contest (Option or Additional) Data Field and/or the Contest Data Field nonce.",nyi, +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the date and time of the overall ballot decryption (as reported by the decrypting device)",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: some identifier uniquely identifying the Ballot Decryption Operation",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: the set of Ballots ""marked for decryption""",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: the hash of the ElectionRecord at the point the Ballot Decryption Operation was initiated",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the Guardian Index",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the date and time their participation was initiated",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the hash of their view of the ElectionRecord at the instant this operation was initiated",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: device information uniquely identifying the device used for their secret key operation",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian:",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian:",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation:",nyi, +S3.7,"The Election Record records for every Ballot Decryption Operation:",nyi, +S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state:",nyi, +S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the Guardian Index",nyi, +S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the date and time of the partial decryption",nyi, +S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: their partial decryptions",nyi, +S3.7,"The Election Record records for every Tally: for every Contest: identifiers of the set of Ballots in the `Cast` state",nyi, +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the encrypted tally (eq. 79 pg 44)",nyi, +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the full verifiable decryption of the encrypted tally",nyi, +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the tally exponent",nyi, +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: plaintext representation of the tally",nyi, +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: proof of correct decryption of the tally",nyi, +S3.7,"The Election Record records information uniquely identifying each device used for encryption of voter selections to produce any Ballot",nyi, +S3.7,"The Election Record records an ordered list of the Ballots encrypted by each device.",nyi, +S3.7,"The Election Record records the encrypted contest data, when available.",nyi, +S3.7,"EGRS enables the Election Record to be produced in a format suitable for digitally signing.",nyi, +S3.7,"EGRS enables the Election Record to be published in a format suitable to the needs of Verifier applications. For example, a directory of files accessible via HTTPS supporting efficient random access.",nyi, +S3.7,"EGRS enables the Election Record to be published in a format suitable for archiving or bulk downloading by any interested individuals. For example, the compressed .zip and/or .tar.gz formats.",nyi, +S4,"EGRI Preencrypted Ballots enable the vote-by-mail scenario.",na, +S4,"EGRI Preencrypted Ballots enable the ""precincts with central count or minimal in-precinct equipment"" scenario.",na, +S4,"EGRI Preencrypted Ballots enable back-ups for precincts which ordinarily perform encryption on demand.",na, +S4,"EGRI enables ordinary and Preencrypted Ballots to be tallied together.",nyi, +S4,"When ordinary and Preencrypted Ballots are tallied together, it does not reveal which votes came from which mode.",nyi, +S4,"The Election Manifest contains a configuration setting indicating whether Preencrypted Ballots are enabled for that election.",nyi, +S4,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI allows to produce data necessary to print Preencrypted Ballots. Note: Referred to in the EGRS as the ""Ballot Encryption Tool"".",nyi, +S4,"EGRI refuses to generate any Preencrypted Ballots unless the Election Manifest indicates Preencrypted Ballots are enabled.",nyi, +S4,"EGRI allows to record Voter Selections made on Preencrypted Ballots, if the Election Manifest indicates Preencrypted Ballots are enabled.",nyi, +S4,"If the Election Manifest indicates Preencrypted Ballots are not enabled, EGRI refuses to record any Voter Selections made on Preencrypted Ballots.",nyi, +S4,"For each Preencrypted Ballot generated, EGRI computes the Selection Encryption Identifier `id_B` as described in (Ref: S3.3.2.a).",nyi, +S4,"For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32 (Ref: S3.3.2.a).",nyi, +S4.1,"pg. 57 EGRI `Preencrypted Ballot`, for every Contest in the BallotStyle, for every Contest Option plus an additional null selection, computes a vector `Psi_{i,m}` of length `m`, where `m` is equal to the number of Contest Options and `i` is the Contest Option index. Note1: This is captial `Psi`. Note2: Although `m` is denoted here as a subscript, it is not an index into a second dimension.",nyi, +S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` for `0 <= i <= m`, where `m` is equal to the number of Contest Options, `i = 0` corresponds to the null vote, and for `1 <= i` `i` is the corresponding Option Index.",nyi, +S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `1 <= i` as a length `m` vector having an encryption of `1` at (1-based) position `i`, and encryptions of `0` at all other options. Note: This corresponds to non-null vote cases.",nyi, +S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `i = 0` as a length `m` vector of encryptions of `0`. Note: This corresponds to the null vote case.",nyi, +S4.1,"pg. 57 Note: EGRI `PreencryptedBallot`, when encrypting ContestOption vectors `Psi_{j,m}`, the encryption nonce `xi_{i,j}` is derived from `H_I` and `xi_B` as specified in eq. 121 (Ref: S4.2.1).",nyi, +S4.1,"pg. 57 For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in [eq. 121 pg. 61].",nyi, +S4.1.1,"pg. 58 EGRI computes the Selection Hash `psi_i` for a single Option `i` according to [eq. 115 pg. 58]",nyi, +S4.1.1,"pg. 58 ""In a contest with a selection limit of L, an additional L null vectors are hashed"" ""where all $E_i = (\alpha_i, \beta_i)$ are encyptions of zero and `1 <= l <= L`.""",nyi, +S4.1.1,"pg. 58 EGRI computes the Selection Hash `psi_{m+l}` for an additional L null vector according to [eq. 114 pg. 58]",nyi, +S4.1.2,"pg. 58 EGRI sorts all Selection Hashes in a contest prior to their use in the computation of the Contest Hash.",nyi, +S4.1.2,"pg. 58 EGRI computes the Contest Hash `chi_l` according to [eq. 115 pg. 58]",nyi, +S4.1.3,"pg. 58 EGRI computes the Confirmation Code `H_C` according to [eq. 116 pg. 58]",nyi, +S4.1.4,"pg. 59 EGRI computes Ballot Chaining values for Preencrypted Ballots according to eq. 117-120 pg. 59.",nyi, +S4.1.5.a,"pg. 59 If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI requires that the Election Manifest specify unambiguously the Hash Trimming Function `Omega` to be used for all Preencrypted Ballots generated for the election.",nyi, +S4.1.5.a,"pg. 59 EGRI provides one or more Hash Trimming Functions.",nyi, +S4.1.5.b,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides short codes to indicate undervotes",nyi, +S4.2,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides a ""Ballot Encrypting Tool"" which operates as describedin S4.2",nyi, +S4.2.1,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI produces nonces deterministically as described in eq. 121",nyi, +S4.2.2,"[TODO: Pre-Encrypted Ballots]","", +S4.3,"[TODO: Pre-Encrypted Ballots]",nyi, +S4.3.1,"[TODO: Pre-Encrypted Ballots]",nyi, +S4.4,"[TODO: Pre-Encrypted Ballots]",nyi, +S4.4.1,"[TODO: Pre-Encrypted Ballots]",nyi, +S4.5.a,"[TODO: Pre-Encrypted Ballots]",nyi, +S4.5.b.verif15,"TODO: Verification 15",nyi, +S4.5.c.verif16,"TODO: Verification 16","", +S4.5.d.verif17,"TODO: Verification 17","", +S4.5.e.verif18,"TODO: Verification 18","", +S4.5.f.verif19,"TODO: Verification 19","", +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Two Hex Characters` hash-trimming function `Ω_1`.",ics, +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Four Hex Characters` hash-trimming function `Ω_2`.",ics, +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Letter-Digit` hash-trimming function `Ω_3`.",ics, +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Digit-Letter` hash-trimming function `Ω_4`.",ics, +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 0-255` hash-trimming function `Ω_5`.",ics, +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 1-256` hash-trimming function `Ω_6`.",ics, +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 100-355` hash-trimming function `Ω_7`.",ics, +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 101-356` hash-trimming function `Ω_8`.",ics, +S4.6,"EGRS ElectionManifest PreencryptedBallots feature allows to specify the use of and the complete configuration for an API-user-supplied hash-trimming function.",ics, +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Letter-Digit` hash-trimming function `Ω_3`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Digit-Letter` hash-trimming function `Ω_4`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 0-255` hash-trimming function `Ω_5`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 1-256` hash-trimming function `Ω_6`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 100-355` hash-trimming function `Ω_7`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 101-356` hash-trimming function `Ω_8`.",nyi, +S4.6,"EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for producing short codes.",nyi, +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Letter-Digit` hash-trimming function `Ω_3`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Digit-Letter` hash-trimming function `Ω_4`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 0-255` hash-trimming function `Ω_5`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 1-256` hash-trimming function `Ω_6`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 100-355` hash-trimming function `Ω_7`.",nyi, +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 101-356` hash-trimming function `Ω_8`.",nyi, +S4.6,"EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for consuming short codes.",nyi, +S5,"EGRI uses `HMAC` as the basis for the function `H`.",utsp, +S5.1,"EGRI converts between nonegative integers and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 122 - 123.",utsp, +S5.1.1,"EGRI converts between nonegative integers mod `p` and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 124 - 126.b.",utsp, +S5.1.2,"EGRI converts between nonegative integers mod `q` and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 127 - 129.",utsp, +S5.1.3,"EGRI converts between nonnegative integers less than 2^31 and 4-byte fixed-size arrays using the big endian convention.",utsp, +S5.1.4,"EGRI hashes fixed-length strings as their minimal UTF-8 encoding.",utsp, +S5.1.4,"EGRI hashes varible-length strings as their minimal UTF-8 encoding prefixed by their length in bytes as a sequence of 4 bytes using big endian convention.",utsp, +S5.1.5,"EGRI can read and write files as contiguous sequences of bytes.",nfd,"" +S5.1.5,"EGRI encodes byte lengths as 4-byte big-endian values.",utsp, +S5.1.5,"EGRI accepts byte lengths less than `2^31`.",utsp, +S5.1.5,"EGRI does not produce byte lengths greater than `2^31 - 1`.",utsp, +S5.2,"EGRI implements the function `H` using HMAC and SHA-2-256 as specified in EG DS v2.1.0 eq. 130.",utsp, +S5.3,"EGRI hashes multiple inputs using an unambiguous separation as specified in the relevant section in EG DS v2.1.0",utsp, +S5.4,"EGRI implements the function `H_q` as specified in EG DS v2.1.0 eq. 131 - 132.",utsp, +S5.4,"Iff Fixed Parameter `q` begins with 248 or more consecutive leading `1` valued bits, such as is the case with `q` from the standard parameter set, the function `H_q` can be simplified to `H`.",utsp, +S5.5,"This is expected to be a fully-redundant section, but we could double-check it",utsp, +S5.5.1,"This is expected to be a fully-redundant section, but we could double-check it",ace, +S5.5.2,"This is expected to be a fully-redundant section, but we could double-check it",ace, +S5.5.3,"This is expected to be a fully-redundant section, but we could double-check it",ace, +S5.5.4,"This is expected to be a fully-redundant section, but we could double-check it",ace, +S5.5.5,"This is expected to be a fully-redundant section, but we could double-check it",ace, +S6.1.b,TODO,utsp, +S6.1.c,"EGRI should compute moduluar multiplication on large integers without generating unnecessarily large intermediate values",utsp, +S6.1.d,"EGRI should compute moduluar exponentiation on large integers without generating intermediate values having more digits than there are particles in the universe",utsp, +S6.2.1.verif1,"TODO: Verification 1",ace, +S6.2.2.a.verif2,"TODO: Verification 2",ace, +S6.2.2.b.verif3,"TODO: Verification 3",ace, +S6.2.3.verif4,"TODO: Verification 4",ace, +S6.2.4.a.verif5,"TODO: Verification 5",ace, +S6.2.4.b.verif6,"TODO: Verification 6",ace, +S6.2.4.c.verif7,"TODO: Verification 7",ace, +S6.2.4.d.verif8,"TODO: Verification 8",ace, +S6.2.5.a.verif9,"TODO: Verification 9",ace, +S6.2.5.b.verif10,"TODO: Verification 10",ace, +S6.2.5.c.verif11,"TODO: Verification 11",ace, +S6.2.6.a.verif12,"Verification of Correct Contest Data Decryption",ace, +S6.2.7.a.verif13,"TODO: Verification 13",ace, +S6.2.7.b.verif14,"TODO: Verification 14",ace, +S6.2.8.a.verif15,"TODO: Verification 15",ace, +S6.2.8.b.verif16,"TODO: Verification 16",ace, +S6.2.8.c.verif17,"TODO: Verification 17",ace, +S6.2.8.d.verif18,"TODO: Verification 18",ace, +S6.2.8.e.verif19,"TODO: Verification 19",ace, diff --git a/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.csv b/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.csv new file mode 100644 index 0000000..14218a5 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.csv @@ -0,0 +1,684 @@ +section,xtext,status_code,status_note,ordinal,xtext +S1.a,"EGRI can be used to enable end-to-end (E2E) verifiability.",na,,1,"Not directly applicable to implementation code" +S1.a,"EGRI can be used to enable privacy-enhanced risk-limiting audits (RLAs).",na,,1,"Not directly applicable to implementation code" +S1.a,"EGRI components can be used to empower individual voters to independently verify the accuracy of election results.",na,,1,"Not directly applicable to implementation code" +S1.b,"EGRI allows individual voters to verify that their votes have been accurately recorded.","",,, +S1.b,"EGRI allows voters to verify that all recorded votes have been accurately counted.","",,, +S1.b,"EGRI allows observers to verify that all recorded votes have been accurately counted.","",,, +S1.b,"EGRI provides an E2E-verifiable tally which can be used as the primary tally in an election.","",,, +S1.b,"EGRI provides an E2E-verifiable tally which can be used as a verifiable secondary tally alongside traditional methods.","",,, +S1.b,"EGRI is compatible with in-person voting using an electronic ballot-marking device.",na,,1,"Not directly applicable to implementation code" +S1.b,"EGRI is compatible with in-person voting using an optical scanner capable of reading hand-marked ballots.",na,,1,"Not directly applicable to implementation code" +S1.b,"EGRI is compatible with in-person voting using an optical scanner capable of reading machine-marked ballots.",na,,1,"Not directly applicable to implementation code" +S1.b,"EGRI is compatible with voting by mail.",na,,1,"Not directly applicable to implementation code" +S1.b,"EGRI is compatible with Internet voting.",na,,1,"Not directly applicable to implementation code" +S1.c,"EGRI enables public disclosure of encrypted ballots that can be matched directly to physical ballots selected for RLA auditing.","",,, +S1.c,"EGRI can prove (or fail to prove) that publicly disclosed EG ballots match reported tallies",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S1.c,"EGRI can prove (or fail to prove) that physical ballot records used in an RLA are those produced by the voters",na,,1,"Not directly applicable to implementation code" +S1.d,"EGRI provides a ""detailed implementation specification"" and/or qualifies as a ""well-documented ElectionGuard implementation""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S1.d,"EGRI can be used by independent parties to write ElectionGuard verifiers to confirm the consistency of election artifacts with announced election results.",na,,1,"Not directly applicable to implementation code" +S1.d,"EGRI can be used by independent parties to write verifiers to confirm (or refute) the consistency of election artifacts with announced election results",na,,1,"Not directly applicable to implementation code" +S2.a,"A 'Guardian' role exists with certain duties.",na,,1,"Not directly applicable to implementation code" +S2.a,"EGRI enables a Guardian to protect confidentiality of votes.",na,,1,"Not directly applicable to implementation code" +S2.a,"EGRI enables members of a canvassing board to serve as Guardians.",na,,1,"Not directly applicable to implementation code" +S2.a,"EGRI enables Guardians to manage cryptographic keys.","",,, +S2.a,"Compromised human Guardians cannot compromise the integrity of the election tallies.","",,, +S2.a,"Compromised ""hardware Guardians"" cannot compromise the integrity of the election tallies.","",,, +S2.a,"EGRI enables Guardians to work together to form a public encryption key for homomorphically-tallied vote encryption.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S2.a,"EGRI enables Guardians to work together to form a public encryption key for non-homomorphically-tallied ballot data.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S2.a[x],"EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.","",,, +S2.a,"EGRI allows a quorum of guardians to produce all artifacts required to enable public verification of the tally.","",,, +S2.a,"No set of guardians fewer than a quorum are able to produce the artifacts required to enable public verification of the tally.",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S2.a,"% xnote S3.7 Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: An 'Election Administrator' role exists",na,,1,"Not directly applicable to implementation code" +S2.a,"Note: EG defines protocols",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S2.a,"Note: EG defines procedures",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S2.b,"EGRI enables a Guardian to generate its own Vote Encryption (Public|Secret) Key pair prior to the start of voting or RLA.","",,, +S2.b,"EGRI enables a Guardian to generate its own Ballot Data Encryption (Public|Secret) Key pair prior to the start of voting or RLA.","",,, +S2.b,"EGRI enables a Guardian to generate its own Guardian Communication (Public|Secret) Key pair pair prior to the start of voting or RLA.","",,, +S2.b,"EGRI enables Guardians to employ their Guardian Communication Key pairs to communicate privately with other guardians to exchange information about secret keys.","",,, +S2.b,"EGRI enables Guardians to keep their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys and Key Shares secret.","",,, +S2.b,"EGRI enables Guardians to perform operations using their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys and Key Shares without leaking or exposing them, to the extent practical within the constraints of hardware/os platform.","",,, +S2.b,"EGRI enables potential integration of dedicated hardware tokens or devices with which Guardians could to perform operations using their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys.","",,, +S2.b,"EGRI enables production of an 'Election Record' even if not all guardians are available at that time with","",,, +S2.b,"(=~ S2.a[x]): EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.","",,, +S2.b,"Guardians participate in a 'Key Generation Ceremony'.",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S2.b,"At the beginning of the Key Generation Ceremony, EGRI enables each guardian to publish its public keys and proofs of knowledge of the associated secret keys.",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S2.b,"At the beginning of the Key Generation Ceremony, EGRI enables each Guardian to receive the published keys and proofs of knowledge of the associated secret keys from the other Guardians.",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S2.b,"EGRI enables a Guardian to determine that they have received the public keys and proofs of knowledge of the associated secret keys from all other Guardians, or identify those which have not yet been received.",nyi,,4,"Not yet implemented" +S2.b,"EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Vote Encryption key.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S2.b,"EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Ballot Data Encryption key.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S2.b,"EGRI enables a Guardian to send the encrypted shares of each of its own (two, non-Communication) secret keys to the other Guardian corresponding to the Guardian Communication Public Key to which it was encrypted.","",,, +S2.b,"EGRI enables a Guardian to use its own Guardian Communication Secret Key to decrypt any shares of other guardians secret keys that it has received.","",,, +S2.b,"EGRI enables a Guardian to check for consistency any shares of other guardians secret keys it has received.",nyi,,4,"Not yet implemented" +S2.b,"EGRI enables a Guardian to query or observe which particular shares from all other Guardians it has recieved, decrypted, checked for consistency, and verified, and which are missing, inconsistent, or failed.",nyi,,4,"Not yet implemented" +S2.b,"EGRI enables a Guardian to determine that it has received, decrypted, checked for consistency, and verified shares from all other Guardians.","",,, +S2.b,"Josh 2025-03-18: ""The code should emit a success or failure indicator that a human can use to verify that the [received key shares] have the required properties."".",nyi,,4,"Not yet implemented" +S2.b,"EGRI can emit information sufficicient to enable an ""all greens on a dashboard""-type display for a Guardian to observe the (received, decrypted, checked for consistency, and verified) state of shares from the other Guardians.","",,, +S2.b,"EGRI can emit a formatted message to enable a Guardian to ""announce its completion"" once it it has received, checked for consistency, and verified shares from all other Guardians.","",,, +S2.b,"At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.","",,, +S2.b,"At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.","",,, +S2.b,"EGRI enables a Guardian to notice the irregularity of a failed share verification.","",,, +S2.b,"EGRI enables a Guardian to notice the irregularity of a failed proof verification.","",,, +S2.b,"EGRI enables a Guardian to notice the irregularity of an inconsistent set of keys.","",,, +S2.b,"EGRI enables a Guardian to ""complain and halt the protocol"" subsequent to noticing a failed share verification, a failed proof verification, or an inconsistent set of keys. (Is this published? A communications key message?)","",,, +S2.b,"EGRI enables, supports, or facilitates the initiation of a 'Key Ceremony Irregularity Investigation' subsequent to a Guardian complaint of irregularity.","",,, +S2.b,"Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to distinguish between an 'Actively Misbehaving Guardian' and any other type of error.","",,, +S2.b,"Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to identify an Actively Misbehaving Guardian.","",,, +S2.b,"Sufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to effectively troubleshoot errors other than an Actively Misbehaving Guardian.","",,, +S2.b,"Sufficient data, documentation, tools, and/or other resources are available to enable the Key Ceremony Irregularity Investigation to effectively troubleshoot multiple simultaneous errors, potentially of different types.","",,, +S2.b,"EGRI allows to remove a Guardian subsequent to a Key Ceremony Irregularity Investigation which has identified it as an 'Actively Misbehaving Guardian'.","",,, +S2.b,"EGRI allows to restart the Key Generation Ceremony protocol from scratch subsequent to an Investigation which has identified and removed an Actively Misbehaving Guardian.","",,, +S2.b,"TODO? what if its another type of error? Can you just restart whenever? Any of this published?","",,, +S2.b,"TODO? EGRI enables a Guardian to publish the ciphertext of any encrypted secret key shares that it sent.","",,, +S2.b,"TODO? EGRI enables a Guardian to publish the ciphertext of any encrypted secret key shares that it received.","",,, +S2.b,"TODO? EGRI enables a Guardian to publish the plaintext corresponding to the ciphertext of any encrypted secret key shares that it sent? Only subsequent to the initiation of Key Ceremony Irregularity Investigation?","",,, +S2.b,"TODO? EGRI enables a Guardian to publish the plaintext decrypted from the ciphertext of any encrypted shares that it received? Only subsequent to the initiation of Key Ceremony Irregularity Investigation?","",,, +S2.b,"Note: The Guardians and Election Administrator together have a duty to successfully complete key generation and conclude the Key Generation Ceremony.","",,, +S2.c.a[i],"After a voter completes the process of making selections, the election system can make a single call to the EGRI API with the voter selections to encrypt and record the EG Ballot and return a confirmation code for the voter.","",,, +S2.c.a,"Other than the single call to the EGRI API described in (S2.c.a[i]), there is no other point at which an existing election system must interface with EGRI.","",,, +S2.c.a,"An RLA system can make the single call to the EGRI API described in (S2.c.a[i]), but any confirmation code returned may be discarded.","",,, +S2.c.b,"EGRI can accept can accept voter selections in batches directly. This is to support certain vote-by-mail scenarios and RLA.","",,, +S2.c.b,"EGRI records which Ballots have been decrypted ensures that they can never be ""cast"" or participate in any future Tally. Note that this only applies to homomorphically tallied fields and not, say, decrypting the Ciphertext of a non-homomorphically tallied write-in text field.","",,, +S2.c.b,"A decrypted ballot should never be 'Cast'. Josh 2025-03-19: Univeral knowledge of all challenged ballots is not required, just checking whatever local store is available is sufficient.","",,, +S2.c.b,"Implementation Note: After obtaining their confirmation code, voters should have an opportunity to 'challenge' their ballots in order to view the decrypted selections of their EG Ballot to verify that the encryptions were correct. The voter must be informed that the challenged ballot and decrypted selections will eventually be published as part of the public Election Record.","",,, +S2.c.b,"Implementation note: In an RLA, presumably the non-EG ballots were already cast. Some of the resulting EG Ballots may be anonymized, challenged, and decrypted.","",,, +S2.c.c,"EGRI produces EG Ballots that contain encrypted voter selections.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S2.c.c,"EGRI produces EG Ballots that may contain encrypted additional data fields for some or all Contests.","",,, +S2.c.c,"EGRI produces EG Ballots that contain a Ballot Nonce, encrypted to the Joint Ballot Data Encryption Public Key.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S2.c.c,"EGRI records the EG Ballots for later publication as part of the Election Record.","",,, +S2.c.c,"EGRI produces EG Ballots that non-interactive zero-knowledge (NIZK) proofs of their well-formedness.","",,, +S2.c.c,"There can exist no well-formed ballot which is not legitimate, by definition.","",,, +S2.c.c,"EGRI produces EG Ballots with NIZK proofs proving that, for each contest option, no more than the allowed number of votes were recorded. (Option Selection Limit)","",,, +S2.c.c,"EGRI produces EG Ballots with NIZK proofs proving that, for each contest, no more than the total allowed number of votes were recorded. (Contest Selection Limit)","",,, +S2.c.c,"EGRI produces EG Ballots with Contests having additional data fields may contain NIZK proofs similar to those of voter selectable options or other properties.","",,, +S2.c.c,"EGRI allows to combine EG Ballots into an Aggregate Ballot.","",,, +S2.c.c,"EGRI allows to combine EG Ballots into a single Aggregate Ballot.","",,, +S2.c.c,"An Aggregate Ballot formed by combining EG Ballots contains, for every option in every contest, the tally for that option encrypted to the Joint Vote Public Key.","",,, +S2.d,"Each guardian can compute from the previously shared secret key fragments a share of the secret decryption key","",,, +S2.d,"Guardians can independently use their share of the secret decryption key to jointly decrypt the election tallies","",,, +S2.d,"Guardians can independently use their share of the secret decryption key to generate associated verification data","",,, +S2.d,"Only a quorum of guardians is sufficient to complete decryption","",,, +S2.d,"Only a quorum of guardians is sufficient to generate the verification data","",,, +S2.e.a,"An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the well-formedness of every EG Ballot.","",,, +S2.e.a,"An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the well-formedness the correct aggregation of EG Ballots.","",,, +S2.e.a,"An observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the accurate decryption of election tallies.","",,, +S2.e.b,"A verifier can be independently implemented which performs the Guardian Key verification steps S6.2.2.verif2 and S6.2.2.verif3 .","",,, +S2.e.b,"A verifier can be independently implemented that fully verifies the integrity of an Election Record without performing either of the Guardian Key verification steps S6.2.2.verif2 and S6.2.2.verif3 .","",,, +S2.e.c,"A verifier can be independently implemented which performs the EG Ballot correctness verification steps S6.2.7.verif13, S6.2.7.verif14, S6.2.8.verif17, S6.2.8.verif18, and S6.2.8.verif19","",,, +S2.e.d,"A verifier can be independently implemented which performs the Election Record verification steps S6.2.1.verif1, S6.2.3.verif4, S6.2.4.verif5, S6.2.4.verif6, S6.2.4.verif7, S6.2.4.verif8, S6.2.5.verif9, S6.2.5.verif10, S6.2.5.verif11, S6.2.6.verif12","",,, +S3.a.a.a,"EGRI enables selecting Fixed Parameters in advance of an election",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.a.a,"EGRI enables using the same Fixed Parameters across multiple elections",na,,1,"Not directly applicable to implementation code" +S3.a.a.a,"EGRI supplies a standard set of Fixed Parameters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.a.a,"EGRI enables defining Contests",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.a.a,"EGRI enables defining selectable Contest Options",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.a.a,"EGRI enables defining Ballot Styles",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.a.b,"(ref: S2.b) EGRI enables Guardians to generate individual public-secret key pairs",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.a.a.b,"(ref: S2.b) EGRI enables Guardians to exchange shares of secret keys",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.a.a.b,"EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)","",,, +S3.a.a.b,"EGRI requires that a complete new set of keys must be generated if even a single guardian is replaced (TODO: How?)","",,, +S3.a.a.b,"EGRI enables Guardians to destroy their secret keys.",nyi,,4,"Not yet implemented" +S3.a.a.b,"Josh 2025-03-18: ""There is a point where the rejection of the keys can happen""","",,, +S3.a.a.c,"[No specific requirement]",na,,1,"Not directly applicable to implementation code" +S3.a.a.d,"(ref: S2.c.c) Guardians can use their secret key shares to jointly produce election tallies together with verifiable artifacts that prove that the tallies are correct.","",,, +S3.a.c.a,"Encryption of votes in ElectionGuard is performed using the DPP vote encryption method of Devillez, Pereira, and Peters (2022)","",,, +S3.a.c.b,"EGRI ensures that P is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.c.b,"EGRI ensures that Q is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.c.b,"EGRI ensures that G is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.c.b,"EGRI verifies that P is prime before any processing. Note that this may be a simple comparison with the standard parameter value.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.c.b,"EGRI verifies that Q is prime before any processing. Note that this may be a simple comparison with the standard parameter value.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.c.b,"EGRI verifies that Q is is not a divisor of r=(p-1)/q before any processing. Note that this may be a simple comparison with the standard parameter value.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.c.b,"EGRI verifies that G is a generator $g$ of the order $q$ subgroup $\Z_p^r$ before any processing. Note that this may be a simple comparison with the standard parameter value.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.a.c.c,"EGRI enables a Guardian to (ref: S.2.b) generate its own Vote or Ballot Data Encryption Secret Key by selecting a random $s\in\Z_q$.","",,, +S3.a.c.c,"EGRI enables a Guardian to (ref: S.2.b) derive its Vote or Ballot Data Encryption Public Key as $K=g^s \bmod p$.","",,, +S3.a.c.c,"EGRI enables a Guardian to (ref: S.2.b) publish its Vote or Ballot Data Encryption Public Key.","",,, +S3.a.c.c,"No entity is ever in possession of a secret key that can be used to decrypt votes","",,, +S3.a.c.d,"Values are encrypted to a Vote or Ballot Data Encryption Public Key by selecting a random nonce $\xi\in\Z_q$ and forming the pair $(\alpha, \beta) = (g^\xi \bmod p, K^m\cdot K^\xi \bmod p) = (g^\xi \bmod p, K^{m+\xi} \bmod p)$ .","",,, +S3.a.c.e,"Ciphertexts encrypted to a Vote or Ballot Data Encryption Public Key can be decrypted using the corresponding Secret Key $s$ as $\beta/\alpha^s\bmod p = K^m \bmod p$ using exhaustive search, a pre-computed table, or Shanks's baby-step giant-step method.","",,, +S3.a.c.e,"The value of $K^m$ can be computed from the encryption nonce $\xi$","",,, +S3.a.c.e,"The value of $m$ can be computed from the encryption nonce $\xi$","",,, +S3.a.c.e,"An encryption of one is used to indicate that an option is selected","",,, +S3.a.c.e,"An encryption of zero is used to indicate that an option is not selected","",,, +S3.a.d,"All the encryptions of a single option across ballots can be multiplied to form an encryption of the sum of the individual values","",,, +S3.a.d,"An encryption of some integer other than `0` or `1` is used for certain voting methods","",,, +S3.a.d,"The product of the encryptions of a single contest option across ballots is an encryption of the tally (S2.c.c)","",,, +S3.a.d,"Every option can be demonstrated to be an encryption of either `0` or `1` (for typical voting methods)","",,, +S3.a.d,"Every option can be demonstrated to be an encryption of some integer other than `0` or `1` (for certain voting methods)","",,, +S3.a.d,"The product of the encrypted options of a single ballot can be used to show that no more options were selected than permitted","",,, +S3.a.d,"When an option can receive multiple or weighted votes, the product of the encrypted options of a single ballot can be used to show that only the permitted number of votes or permitted sum of weights were used","",,, +S3.a.d,"A holder of a nonce $\xi$ can prove to a third party that a pair $(\alpha, \beta)$ is an encryption of $m$ without access to the secret $s$ or revealing the nonce $\xi$","",,, +S3.a.e,"EGRI enables the use of NIZK proofs to demonstrate that keys are properly chosen.","",,, +S3.a.e,"EGRI enables the use of NIZK proofs to demonstrate that ballots are well-formed.","",,, +S3.a.e,"EGRI enables the use of NIZK proofs to demonstrate that decryptions match claimed values.","",,, +S3.a.f.a,"EGRI enables the use of threshold encryption for encryption of ballots and other data.","",,, +S3.a.f.a,"(ref S2.b) Combining individual Guardian (Vote|Ballot Data) Public Keys into a single Joint (Vote|Ballot Data) Public Key.","",,, +S3.a.f.a,"Threshold encryption offers a homomorphic property that allows individual encrypted votes to be combined to form encrypted tallies.","",,, +S3.a.f.b,"Ref S2.b: ""EGRI enables a Guardian to generate its own public-secret (Vote|Ballot Data) Encryption Key pair...""","",,, +S3.a.f.b,"Ref S2.b: ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian (Vote|Ballot Data) Encryption Public keys to form the Joint (Vote|Ballot Data) Encryption Public Key.""","",,, +S3.a.f.b,"(=~ S2.a[x]): EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.","",,, +S3.a.f.c,"EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.","",,, +S3.a.f.c,"EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.","",,, +S3.a.f.d,"(Ref: S2.b) Key Ceremony","",,, +S3.a.f.d,"(Ref: S3.a.f.c) ""EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.""","",,, +S3.a.f.d,"(Ref: S3.a.f.c) ""EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.""","",,, +S3.a.g,"EGRI provides a non-homomorphic encryption method known as ""Hashed ElGamal"" for encrypting data other than votes","",,, +S3.a.g,"EGRI provides a signature method known as ""Signed Hashed ElGamal"" for encrypting data other than votes","",,, +S3.a.g,"EGRI enables a user to generate a Hashed ElGamal (Public|Secret) Key pair for Hashed ElGamal encryption","",,, +S3.a.g,"EGRI enables a user to encrypt data to a Hashed ElGamal Public Key","",,, +S3.a.g,"Any data encrypted to a Hashed ElGamal Public Key cannot be decrypted without the Hashed ElGamal Secret Key","",,, +S3.a.g,"EGRI enables using a Hashed ElGamal Secret Key to decrypt data encrypted to the corresponding Hashed ElGamal Public Key","",,, +S3.a.g,"Hashed ElGamal encryption does not have the data size restrictions imposed by the vote encryption","",,, +S3.a.g,"Hashed ElGamal encryption uses a KDF based on HMAC-SHA-2-256 to generate a key stream XORed with the data","",,, +S3.a.g,"EGRI enables a user to generate a Signing Hashed ElGamal (Public|Secret) Key pair for Signing data encrypted with Hashed ElGamal","",,, +S3.a.g,"EGRI enables using a Signing Hashed ElGamal Secret Key to sign data that has been encrypted with Hashed ElGamal","",,, +S3.a.g,"EGRI enables using a Signing Hashed ElGamal Public Key to verify or refute a signature made with Signed Hashed ElGamal","",,, +S3.a.g,"(Ref: S2.b) ""EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key a share of each of its own (two, non-Communication) secret keys.""","",,, +S3.a.g,"EGRI allows to encrypt other (non-homomorphically-tallied) Ballot data to the Joint Ballot Data Encryption Public Key","",,, +S3.a.g,"EGRI allows to encrypt voter selection contest write-in Ballot data to the Joint Ballot Data Encryption Public Key","",,, +S3.a.g,"EGRI allows to encrypt other auxiliary data attached to an encrypted Ballot to the Joint Ballot Data Encryption Public Key","",,, +S3.a.g,"EGRI allows to encrypt voter selection contest write-in Ballot data to the Administrative Public Key.","",,, +S3.a.g,"EGRI allows to encrypt other auxiliary data attached to an encrypted Ballot to the Administrative Public Key.","",,, +S3.a.g,"EGRI allows to encrypt other (non-homomorphically-tallied) Ballot data to the Administrative Public Key.","",,, +S3.1,"EGRI uses integer-based encryption","",,, +S3.1,"EGRI does not use elliptic curves","",,, +S3.1,"The encryption scheme used to encrypt votes is defined by primes p and q.","",,, +S3.1,"ref:S3.a.c.b ""EGRI verifies that (P|Q|G) is prime before any processing...""","",,, +S3.1.1.a,"The value of p matches the specific value given in EG DS v2.1.0 S3.1.1","",,, +S3.1.1.a,"The value of q matches the specific value given in EG DS v2.1.0 S3.1.1","",,, +S3.1.1.a,"The value of r matches the specific value given in EG DS v2.1.0 S3.1.1","",,, +S3.1.1.a,"The value of g matches the specific value given in EG DS v2.1.0 S3.1.1","",,, +S3.1.1.b.n3_1,"Nonstandard parameters are outside the scope of these requirements and thus not permitted.","",,, +S3.1.1.b.n3_1,"EGDS must reject any data bearing non-standard parameters.","",,, +S3.1.2,"Varying Parameter n is the number of Guardians","",,, +S3.1.2,"Varying Parameter k is the number of Guardians required to form a quorum for decryption.","",,, +S3.1.2,"EGRI computes H_P from Fixed Parameters p, q, and g, and Varying Parameters n and k as specified in EG DS v2.1.0 eq. 4","",,, +S3.1.3.a,"EGRI accepts an Election Manifest",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"The Election Manifest is a single file",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"The Election Manifest describes the modalities of the election and specifies a wide range of data about it",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"The Election Manifest contains data that makes it unique for each election",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"EGRI rejects any Election Manifest that does not contain data that makes it unique for each election",nfd,"Ref: S3.1.3.a: ""The manifest file must contain data that makes it unique for each election."" QUESTION: How could this possibly be implemented?",2,"Needs further discussion" +S3.1.3.a,"The Election Manifest lists all contests in an election",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"The Election Manifest for each Contest, specifies the Selectable Options (e.g., candiates)",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"The Election Manifest for each Contest, specifies a label unique across all contests in the election",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"EGRI rejects any Election Manifest containing duplicate labels for Contests",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"The Election Manifest for each ContestOption in each Contest, specifies a label unique across all Selectable Options in that contest",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.a,"EGRI rejects any Election Manifest containing duplicate labels for ContestOptions in any Contest",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI accepts ElectionManifest labels composed of printable characters and (internal, non-contiguous) 0x20 space characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ElectionManifest labels that contain line break characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ElectionManifest labels that have leading or trailing whitespace",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ElectionManifest labels that contain contiguous sequences of whitespace other than a single 0x20 space",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ElectionManifest labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ElectionManifest labels having no printable characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ElectionManifest labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics,"difficult to test because the Rust std lib rejects malformed UTF-8",50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.b,"EGRI accepts Contest labels composed of printable characters and (internal, non-contiguous) 0x20 space characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects Contest labels that contain line break characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects Contest labels that have leading or trailing whitespace",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects Contest labels that contain contiguous sequences of whitespace other than a single 0x20 space",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects Contest labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects Contest labels having no printable characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects Contest labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.b,"EGRI accepts ContestOption labels composed of printable characters and (internal, non-contiguous) 0x20 space characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ContestOption labels that contain line break characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ContestOption labels that have leading or trailing whitespace",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ContestOption labels that contain contiguous sequences of whitespace other than a single 0x20 space",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ContestOption labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ContestOption labels having no printable characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects ContestOption labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.b,"EGRI accepts BallotStyle labels composed of printable characters and (internal, non-contiguous) 0x20 space characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects BallotStyle labels that contain line break characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects BallotStyle labels that have leading or trailing whitespace",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects BallotStyle labels that contain contiguous sequences of whitespace other than a single 0x20 space",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects BallotStyle labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects BallotStyle labels having no printable characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.b,"EGRI rejects BallotStyle labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.c,"Index values are integers in the range 1 <= i < 2^31",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.d,"Every Contest in the Election Manifest is uniquely identified by a Contest Index value assigned starting at 1 and continuing in the order defined in the Election Manifest.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.e,"Every Selectable Option in every Contest is identified uniquely (within the Contest) by an Option Index value assigned starting at 1 (anew for each Contest) and continuing in the order defined in the in the Election Manifest.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.e,"If additional data fields are present for a Contest, they are assigned Indices continuing after the Selectable Options.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.f,"The Election Manifest defines a single ordered list of all Ballot Styles in the election.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.f,"Every Ballot Style in the Election Manifest is uniquely identified by a Ballot Style Index value assigned starting at 1 and continuing in the order defined in the Election Manifest.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.f,"The first Ballot Style within the election manifest has index value 1.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.f,"Every Ballot Style defines a label unique across all Ballot Styles in the manifest",nyi,,4,"Not yet implemented" +S3.1.3.f,"Every Ballot Style defines a single list of Contest Indices.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.f,"Order of occurence is not significant within a Ballot Style Contest Index list.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.f,"EGRI rejects any Election Manifest having a Ballot Style with a Contest Index list containing a Contest Index that does not refer to a contest in the Election Manifest.",uts,,65,"Unit tests exercising a portion of code paths considered sufficient exist" +S3.1.3.f,"EGRI rejects any Election Manifest listing the same Contest Index more than once within the same Ballot Style. (It is common for a Contest Index to be listed in multiple Ballot Styles.)",sci,,, +S3.1.3.g,"The Election Manifest, for each Contest, may specify a nonnegative integer Contest Selection Limit.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.g,"For any Contest for which a Contest Selection Limit is not specfied, an effective value of 1 is used as the default.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.g,"The effective Contest Selection Limit limits the maximum total value (i.e., number of votes or voter selections) that can be assigned over all Selectable Options in that contest.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.g,"EGRI may warn if any effective Contest Selection Limit is zero",nyi,,4,"Not yet implemented" +S3.1.3.g,"The Election Manifest, for each Selectable Option in each Contest, may specify a nonnegative integer Option Selection Limit.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.g,"For any Selectable Option for which a Option Selection Limit is not specfied, an effective value of 1 is used as the default.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.g,"EGRI may warn if any effective Option Selection Limit is zero",nyi,,4,"Not yet implemented" +S3.1.3.g,"The effective Option Selection Limit limits the maximum value (i.e., number of votes) that can be assigned to that particular Selectable Option.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.g,"The effective Selection Limit limits the maximum value that can be assigned to that Selectable Option",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.g,"If an effective (specified or defaulted) Option Selection Limit exceeds the effective (specified or defaulted) Contest Selection Limit, EGRI applies the smaller value. EGRI may emit a diagnostic message in this case.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.g,"If the sum of all effective (specified or defaulted) Option Selection Limits for a Contest exceeds that Contest's effective (specified or defaulted) Contest Selection Limit, EGRI applies the smaller value. This is an ordinary case for which EGRI should not emit a specific diagnostic message.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.3.h,"Election Manifest offers optional data fields for accompanying data.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied at the top level.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Contests. Note: These are different from Additional Contest Data Fields which may be recorded after the Selectable Option Fields on a Ballot.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Selectable Options.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Ballot Styles.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest Accompanying Data Fields may go beyond short labels.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest Accompanying Data Fields may be non-unique.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the jurisdiction.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election device manufacturers.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about software versions.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election date.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election location.",nfd,"",2,"Needs further discussion" +S3.1.3.h,"EGRI allows an Election Manifest to specify many other things, including things not listed here.",nfd,"",2,"Needs further discussion" +S3.1.3.j,"'Undervoted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limit",nyi,,4,"Not yet implemented" +S3.1.3.j,"Election Manifest allows to specify whether to record Undervoted Contest Conditions (unless the Contest specifies otherwise).",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.j,"Election Manifest allows to specify, for each Contest, whether to record the Undervoted Contest Condition for that Contest.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification (None currently). The value of this additional Contest Data Field is `1` to record the Undervoted Contest Condition and `0` otherwise.",nyi,,4,"Not yet implemented" +S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.",nyi,,4,"Not yet implemented" +S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.",nyi,,4,"Not yet implemented" +S3.1.3.k,"Election Manifest allows to specify whether to record the Undervote Difference Count for that Contest.",utsutspp,,, +S3.1.3.k,"Election Manifest allows to specify, for each Contest, whether to record the Undervote Difference Count for that Contest.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.k,"If a Contest is specified to record Undervote Difference Count, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (saturating) difference (i.e., nonnegative) between the effective Contest Selection Limit and the the sum of the voter selections for that Contest. This value will not exceed the effective Contest Selection Limit.",nyi,,4,"Not yet implemented" +S3.1.3.k,"If a Contest is specified to record Undervote Difference Count, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.",nyi,,4,"Not yet implemented" +S3.1.3.l,"An 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit.",nyi,,4,"Not yet implemented" +S3.1.3.l,"Election Manifest allows to specify, for each Contest, whether to record the Overvoted Contest Condition for that contest.",nyi,,4,"Not yet implemented" +S3.1.3.l,"If not specified, EGRI does not record the Overvoted Contest Condition.",nyi,,4,"Not yet implemented" +S3.1.3.l,"If a Contest is specified to record Overvoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Contest Condition and `0` otherwise.",nyi,,4,"Not yet implemented" +S3.1.3.l,"If a Contest is specified to record Overvoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.",nyi,,4,"Not yet implemented" +S3.1.3.l,"'Overvoted Option Condition' is a per-contest-option state in which the selection count is strictly greater than the option selection limit",nyi,,4,"Not yet implemented" +S3.1.3.l,"Election Manifest allows to specify, for each Contest Option, whether to record the Overvoted Option Condition for that Option.",nyi,,4,"Not yet implemented" +S3.1.3.l,"If not specified, EGRI does not record the Overvoted Option Condition.",nyi,,4,"Not yet implemented" +S3.1.3.l,"If a Contest Option is specified to record Overvoted Option Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields, any other Contest Data Fields recording Overvoted Option Condition for lower-index Options, and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Option Condition and `0` otherwise.",nyi,,4,"Not yet implemented" +S3.1.3.l,"If a Contest is specified to record Overvoted Option Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.",nyi,,4,"Not yet implemented" +S3.1.3.m,"'Null Voted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limit",nyi,,4,"Not yet implemented" +S3.1.3.m,"Election Manifest allows to specify, for each Contest, whether to record the Null Voted Contest Condition for that Contest.",nyi,,4,"Not yet implemented" +S3.1.3.m,"If not specified, EGRI does not record the Null Voted Contest Condition.",nyi,,4,"Not yet implemented" +S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Null Voted Contest Condition and `0` otherwise.",nyi,,4,"Not yet implemented" +S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.",nyi,,4,"Not yet implemented" +S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.",nyi,,4,"Not yet implemented" +S3.1.3.n,"Election Manifest allows to specify, for each Contest, whether to record the Total Number Of Write-ins selected for that Contest.",nyi,,4,"Not yet implemented" +S3.1.3.n,"If not specified, EGRI does not record the Total Number Of Write-ins.",nyi,,4,"Not yet implemented" +S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (nonnegative integer) total number of write-ins the voter selected for that Contest. This value must not exceed the effective Contest Selection Limit.",nyi,,4,"Not yet implemented" +S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.",nyi,,4,"Not yet implemented" +S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.",nyi,,4,"Not yet implemented" +S3.1.3.n,"EGRI may emit a diagnostic message if any contest specifies a number of write-in fields greater than the contest selection limit",nyi,,4,"Not yet implemented" +S3.1.3.n,"Election Manifest allows to specify, for each Contest, the number of write-in fields available to the voter.",nyi,,4,"Not yet implemented" +S3.1.3.n,"If a Contest is specified to supply a nonzero number of write-in fields, the Contest has that number of additional 'write-in N' voter-selectable Contest Options.",nyi,,4,"Not yet implemented" +S3.1.3.n,"Any such write-in fields are to be labeled by the system as ""write-in N"", where 'N' is the 1-based index value of the write-in field. (This will be greater than the Contest Option Field Index value by the number of non-write-in voter-Selectable Options.)",nyi,,4,"Not yet implemented" +S3.1.3.g,"The Election Manifest, for each additional 'write-in N' voter-selectable Contest Option, may specify a nonnegative integer Option Selection Limit with the same defaults and other semantics as non-write-in Contest Options. (ref: S3.1.3.g)",nyi,,4,"Not yet implemented" +S3.1.3.n,"""Written-in"" means that the election system has recorded some voter-supplied, non-empty, non-blank data associated with a contest and write-in field index value",nyi,,4,"Not yet implemented" +S3.1.3.n,"Every write-in field that *was not* written-in MUST be assigned a value of `0`.",nyi,,4,"Not yet implemented" +S3.1.3.n,"Every write-in field that *was* written-in MUST be assigned a value of `1` or greater, and subject to the same rules for Effective Option Selection Limit as other voter-Selectable Options.",nyi,,4,"Not yet implemented" +S3.1.3.n,"The value of a write-in field counts against the effective Contest Selection Limit",nyi,,4,"Not yet implemented" +S3.1.3.n,"For every Write-in field, an additional Range Proof is recorded that the encrypted value is either `0` or `1`.",nyi,,4,"Not yet implemented" +S3.1.3.n,"Josh 2025-03-18: A write-in field can not have an effective Option Selection Limit greater than `1`.",nyi,,4,"Not yet implemented" +S3.1.3.o,"A canonical byte representation is defined for the Election Manifest.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.3.o,"EGRI allows to write the Election Manifest canonical representation to a file.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.1.4.a,"EGRI computes H_B from H_P and the Election Manifest canonical representation as specified in EG DS v2.1.0 eq. 5.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.1.4.b.verif1,"TODO: Verification 1","",,, +S3.2.a,"EGRI allows to specify the number of Guardians `n` prior to a Key Ceremony.",uts,,65,"Unit tests exercising a portion of code paths considered sufficient exist" +S3.2.a,"EGRI allows to specify the quorum value `k` prior to a Key Ceremony.",uts,,65,"Unit tests exercising a portion of code paths considered sufficient exist" +S3.2.a,"EGRI rejects values of `n` less than `1`.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.a,"EGRI rejects values of `n` greater than `2^31 - 1`.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.a,"EGRI rejects values of `k` less than `1`.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.a,"EGRI rejects values of `k` greater than `n`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.2.c,"(Ref: S2.b) Guardians can generate individual public-secret key pairs",utep,,60,"At least one unit test exercising a relevant code path is passing" +S3.2.c,"(Ref: S2.b) Guardians can exchange shares of secret keys",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.c,"(Ref: S3.a.a.b) ""EGRI requires that a complete new set of keys must be generated if even a single guardian is replaced""",na,,1,"Not directly applicable to implementation code" +S3.2.c,"EGRI rejects any election data unless 1 <= k <= n < 2^31",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.2.c,"Each Guardian is associated with a human readable identifier referred to as the 'Guardian Name'.",utep,,60,"At least one unit test exercising a relevant code path is passing" +S3.2.c,"EGRI accepts Guardian labels composed of printable characters and (internal, non-contiguous) 0x20 space characters.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.c,"EGRI rejects Guardian labels that contain line break characters.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.c,"EGRI rejects Guardian labels that have leading or trailing whitespace.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.c,"EGRI rejects Guardian labels that contain contiguous sequences of whitespace other than a single 0x20 space.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.c,"EGRI rejects Guardian labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs).",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.c,"EGRI rejects Guardian labels having no printable characters",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.c,"EGRI rejects Guardian labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)",ics,"difficult to test because the Rust std lib rejects malformed UTF-8",50,"Implementation code exists covering a portion of code paths considered substantial" +S3.2.c,"Ballots are encrypted using threshold encryption",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.c,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.""",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.c,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.""",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.c,"(Ref: S2.b) Every guardian can Shamir-share their secret keys during key generation",uts,,65,"Unit tests exercising a portion of code paths considered sufficient exist" +S3.2.c,"(Ref: S2.c.c) Voter Selections can be encrypted to the Joint (Vote|Ballot Data) Encryption Public Keys.",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.c,"(Ref: S3.a.f.c) ""EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.""",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.c,"(Ref: S3.a.f.c) ""EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.""",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.c,"(Ref: S3.a.f.d) ""Fewer than `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys can not produce a full decryption of any tally""",na,,1,"Not directly applicable to implementation code" +S3.2.1,"(Ref: S2.b) Guardians can exchange shares of secret keys",uts,,65,"Unit tests exercising a portion of code paths considered sufficient exist" +S3.2.1,"Receiving Guardians can confirm that the key shares they receive are correct",uts,,65,"Unit tests exercising a portion of code paths considered sufficient exist" +S3.2.1,"Every Guardian can generate an independent public-secret key pair by generating a random integer secret s_i in Z_q",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.1,"Every Guardian can form their Guardian public key as K_i = g^{s_i} mod p",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.1,"The Election Record contains every Guardian's public key",nyi,,4,"Not yet implemented" +S3.2.1,"The Election Record contains, for every Guardian public key, a non-interactive zero-knowledge Schnorr proof of knowledge of the associated secret key",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.2.1,"The Joint Vote Encryption Public Key `K` is computed as \prod_{i=1}^n K_i mod p",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.1,"Every guardian G_i can generate k - 1 random polynomial coefficients a_{i,j} such that 0 < j < k and 0 <= a_{i,j} < q",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.1,"Every guardian G_i can generate and publish commitments K_{i,j} = g^{a_{i,j}} mod p to each of its polynomial coefficients",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.1,"Guardian polynomial coefficients are generated and managed in precisely the same fashion as secret keys",na,,1,"Not directly applicable to implementation code" +S3.2.1,"Individual Ballots can be homomorphically combined into a single Aggregate Ballot",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.1,"The single Aggregate Ballot consists of an encryption of the tally for each contest (e.g. selection option) field",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.1,"Any set of k guardians' private keys is sufficient to complete decryption of the tally for each contest (e.g. selection option) field",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S3.2.1,"EGRI enables any set of `k` distinct Guardian Vote Encryption Secret Keys to complete the decryption of the tally for each Contest Option and additional Contest Data Fields. (Ref: S3.a.f.c)",ute,,55,"At least one unit test exercising a relevant code path exists" +S3.2.1,"No set of fewer than `k` distinct Guardian Vote Encryption Secret Keys is sufficient to complete the the decryption of the tally for any Contest Option or additional Contest Data Field.",na,,1,"Not directly applicable to implementation code" +S3.2.2.a,"(Ref: S2.b) Key Ceremony",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.a,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Vote Encryption (Public|Secret) Key pair prior to the start of voting or RLA.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.a,"EGRI enables a Guardian `i` to generate and store `k` secret polynomial coefficients (indexed as `0 <= j < k`) by sampling a CSRNG uniformly as `0 < a_{i,j} < q` for their Guardian Vote Encryption Key pair.","",,, +S3.2.2.a,"EGRI enables a Guardian `i` to generate, store, and publish a Schnorr proof of knowledge for each of its k secret polynomial coefficients (indexed as `0 <= j < k`) for their Guardian Vote Encryption Key pair.","",,, +S3.2.2.b,"(Ref: S2.b) Key Ceremony",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.b,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Ballot Data Encryption (Public|Secret) Key pair prior to the start of voting or RLA.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.b,"EGRI enables a Guardian `i` to generate and store `k` secret polynomial coefficients (indexed as `0 <= j < k`) by sampling a CSRNG uniformly as `0 < a_{i,j} < q` for their Guardian Ballot Data Encryption Key pair.","",,, +S3.2.2.b,"EGRI enables a Guardian `i` to generate, store, and publish a Schnorr proof of knowledge for each of its k secret polynomial coefficients (indexed as `0 <= j < k`) for their Guardian Vote Encryption Key pair.","",,, +S3.2.2.c,"(Ref: S2.b) Key Ceremony",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.c,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Guardian Communication (Public|Secret) Key pair prior to the start of voting or RLA.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.c,"EGRI enables a Guardian `i` to generate and store a uniformly random value $\zeta_i \in \Z_q$ as its Guardian Communication Secret Key","",,, +S3.2.2.c,"EGRI enables a Guardian `i` to derive and store Guardian Communication Public Key as \kappa_i = g^{\zeta_i} \bmod p. eq. 9","",,, +S3.2.2.d,"After having generated its own Vote Encryption Secret Key and Guardian Communication Secret Key, EGRI enables a Guardian `i` to generate an NIZK proof of knowledge of secrets $a_{i,j}$ and of $\zeta_i$ as specified in EG DS v2.1.0 eq. 10 - 11","",,, +S3.2.2.d,"EGRI enables a Guardian to publish its NIZK proof of knowledge of secrets $a_{i,j}$ and of $\zeta_i$.","",,, +S3.2.2.e,"After having generated its own Ballot Data Encryption Secret Key and Guardian Communication Secret Key, EGRI enables a Guardian `i` to generate and publish an NIZK proof of knowledge of secrets $\hat{a}_{i,j}$ and of $\zeta_i$ as specified in EG DS v2.1.0 eq. 10 - 11","",,, +S3.2.2.e,"EGRI enables a Guardian to publish its NIZK proof of knowledge of secrets $\hat{a}_{i,j}$ and of $\zeta_i$.","",,, +S3.2.2.f.verif2,"TODO: Verification 2 Note: Required for (Ref: S3.2.2.o Details of Key Generation - Share verification and the guardian record)",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.i,"(Ref: S2.b) Key Ceremony",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.i,"(Ref: S2.b) EGRI enables a Guardian `i` to encrypt to another Guardian `l` Guardian Communication Public Key a share of each of its own (Vote|Ballot Data) Encryption Secret Keys, $P_i(\ell)$ and $\hat{P}_i(\ell)$, as specified in EG DS v2.1.0 eq. 15 - 22",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.i,"(Ref: S2.b) EGRI enables a Guardian to publish the encryption $E_\ell(P_i(\ell), \hat{P}_i(\ell))$ of its secret key shares $P_i(\ell)$ and $\hat{P}_i(\ell)$ for every other guardian $G_\ell$",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.j,"(Ref: S3.2.2.i) EGRI enables a receiving Guardian `l` to verify or refute the Schnorr proof $C_{i,\ell,2} = (\bar c_{i,\ell},\bar v_{i,\ell})$ from sending Guardian `i` as part of the share of each of Guardian `i`'s (Vote|Ballot Data) Encryption Secret Keys as specified in EG DS v2.1.0 eq. 22.",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.j,"If the Schnorr proof successfully verifies, EGRI enables receiving Guardian `l` to decrypt the ciphertext as specified in EG DS v2.1.0 eq. 23.","",,, +S3.2.2.k,"EGRI enables a Guardian `i` to compute its share $z_i = P(i)$ of the Joint Vote Encryption Secret key as specified in EG DS v2.1.0 eq. 24.","",,, +S3.2.2.k,"EGRI enables a Guardian `i` to compute its share $z_i = P(i)$ of the Joint Ballot Data Encryption Secret key as specified in EG DS v2.1.0 eq. 24.","",,, +S3.2.2.l,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.l,"EGRI enables the computation of the Joint Vote Encryption Public Key specified in eq. 25.",utep,,60,"At least one unit test exercising a relevant code path is passing" +S3.2.2.m,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.m,"EGRI enables the computation of the Joint Ballot Data Encryption Public Key specified in eq. 26.",utep,,60,"At least one unit test exercising a relevant code path is passing" +S3.2.2.n.verif3,"TODO: Verification 3 Note: Required for (Ref: S3.2.2.o Details of Key Generation - Share verification and the guardian record)","",,, +S3.2.2.o,"There is a defined Election Data Object called the 'Preliminary Guardian Record'.","",,, +S3.2.2.o,"EGRI enables an Election Administrator to produce the Preliminary Guardian Record from data generated during the Key Ceremony.","",,, +S3.2.2.o,"The Preliminary Guardian Record contains the Election Parameters $p$, $q$, $g$, $n$, and $k$; the Joint (Vote|Ballot Data) Encryption Public Keys $K$ and $\hat K$; all $K_{i,j}$ and $\hat{K}_{i,j}$ values from all Guardians (for $1\leq i \leq n$ and $0 \leq j < k$), the Guardian Communication Public Keys $\kappa_i$, together with all Schnorr proofs $(c_{i}, v_{i,0}, v_{i,1}, \dots, v_{i, k})$ and $(\hat{c}_{i}, \hat{v}_{i,0}, \hat{v}_{i,1}, \dots, \hat{v}_{i, k})$.","",,, +S3.2.2.o,"EGRI enables a Guardian `l` to verify the Preliminary Guardian Record as specified in EG DS v2.1.0 eq. 27 - 29. Note: This includes (Ref: S3.2.2.f.verif2 Verification 2) and (Ref: S3.2.2.n.verif3 Verification 3).","",,, +S3.2.2.o,"(Ref: S2.b) ""EGRI enables a Guardian to ""complain and halt the protocol""""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.o,"(Ref: S2.b) ""EGRI enables, supports, or facilitates the initiation of a 'Key Ceremony Irregularity Investigation' subsequent to a Guardian complaint of irregularity.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.2.2.o,"EGRI enables to deterimine that all Guardians have confirmed that all verification steps have passed successfully.","",,, +S3.2.2.o,"There is a defined Election Data Object called the 'Guardian Record'.","",,, +S3.2.2.o,"EGRI enable publishing the Guardian Record as part of the Election Record.","",,, +S3.2.2.o,"EGRI enables a Guardian to retain its own indepent copy of the Preliminary Guardian Record for later comparison with the Guardian Record as published in the Election Record.","",,, +S3.2.2.o,"EGRI enables a Guardian to verify that the Guardian Record as published in the Election Record contain the same values for $K$ and $\hat{K}$, the Joint (Vote|Ballot Data) Encryption Public Key as that they verified in the Preliminary Guardian Record.","",,, +S3.2.2.o,"EGRI enables a Guardian to retain only its secret values $z_i = P(i)$ and $\hat{z}_i = \hat{P}(i)$ for later use in the decryption process and to destroy all other secret values.","",,, +S3.2.2.o,"EGRI enables a Guardian to destroy its initial (Vote|Ballot Data) Encryption Secret Keys $s_i$ and $\hat{s}_i$.","",,, +S3.2.3.a,"EGRI computes H_E from H_B and the Joint (Vote|Ballot Data) Encryption Public Keys as specified in EGDS v2.1.0 eq. 30.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.2.3.b.verif4,"TODO: Verification 4","",,, +????,"EGRI rejects any set of Guardians having any CoefficientCommitment the same.","",,, +????,"EGRI rejects any Guardian having any CoefficientCommitment the same.","",,, +S3.3,"An ElectionGuard `Ballot` is an Election Data Object that is created encrypted. The term `encrypted Ballot` is technicaly redundant.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.3,"The concept of a traditional, e.g. paper, ""ballot"" is referred to as `Voter Selections`.","",,, +S3.3,"In specific cases, such as a challenged, an ElectionGuard `Ballot` may be decrypted and the plaintext of the constest data fields published. But the term `Ballot` still as always refers to the encrypted form.","",,, +S3.3,"Election systems may assign a Ballot an ID label for reference and retrival purposes. This ID may be randomly generated or derived from a combination of information guaranteed unique. In no case will this ID label encode or be derived from data that wouldn't be recorded in the Ballot in-the-clear anyway.","",,, +S3.3.1.j,"EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EGDS v2.1.0 eq. 31 pg. 28.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S3.3.1.j,"EGRI may pre-compute the encryption of a Contest (Option or Additional) Data Field value of '0', and then convert it to an encryption of `1` if needed.","",,, +S3.3.2.a,"EGRI computes for each Ballot a Selection Encryption Identifier `id_B`.","",,, +S3.3.2.a,"EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32.","",,, +S3.3.2.b.verif5,"TODO: Verification 5","",,, +S3.3.3,"EGRI selects for each Ballot a Ballot Nonce by sampling a CSRNG uniformly as `0 < id_B < 2^256`.","",,, +S3.3.3,"EGRI computes for each Contest appearing on the Ballot, for each Contest (Option or Additional) Data Field, a Contest Data Field Nonce as specified in EG DS v2.1.0 eq. 33. Note: This uses the ""mod q"" version of H.","",,, +S3.3.4,"EGRI for each Ballot encrypts (with a Schnorr proof of knowledge) the Ballot Nonce `id_B` to the Joint Ballot Data Encryption Public Key as specified in EG DS v2.1.0 eq. 34 - 38.","",,, +S3.3.4,"EGRI attempts, to the greatest extent practical, to erase from hardware memory the Ballot Nonce `id_B` (and any information which could reconstruct it e.g., RNG state) immediately after use.","",,, +S3.3.5.b,"Ref: S3.1.3.g","",,, +S3.3.5.c,"Ref: S3.1.3.i Election Parameters and the Election Manifest - Undervotes","",,, +S3.3.5.c,"Ref: S3.1.3.j Election Parameters and the Election Manifest - Counting undervoted contests","",,, +S3.3.5.c,"Ref: S3.1.3.k Election Parameters and the Election Manifest - Undervote difference count","",,, +S3.3.5.c,"Ref: S3.1.3.m Election Parameters and the Election Manifest - Null votes","",,, +S3.3.5.d,"(Ref: S3.1.3.l) ""An 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.3.5.d,"When an Overvoted Contest Condition has occurred, all Contest Option Data Fields for that Contest are system-assigned the value of `0`.","",,, +S3.3.5.d,"When an Overvoted Contest Condition has occurred, this fact MAY be encoded into a (non-tallied) data field and encrypted to the Joint Ballot Data Encryption Public Keys.","",,, +S3.3.5.d,"When an Overvoted Contest Condition has occurred, the specific voter selections for this contest MAY be encoded into a (non-tallied) Contest Data Field and encrypted to the Joint Ballot Data Encryption Public Keys, much like write-in data.","",,, +S3.3.5.d,"Ref: S3.1.3.l Election Parameters and the Election Manifest - Overvotes","",,, +S3.3.5.e,"For every Ballot, EGRI allows, for every Contest Option Data Field, to prove that the Ciphertext is an encryption of a nonnegative value less than or equal to the effective Option Selection Limit (e.g., an encryption of `0` or `1`).","",,, +S3.3.5.e,"For every Ballot, EGRI allows, for every Contest Additional Data Field, to prove that the Ciphertext is an encryption of a legitimate value (as specified for that particular Contest Additional Data Field).","",,, +S3.3.5.e,"For every Ballot, EGRI allows, for every Contest, to prove that the sum of all Contest Option values is an encryption of a nonnegative value less than or equal to the effective Contest Selection Limit.","",,, +S3.3.5.f,"EGRI uses range proofs to prove well-formedness of an individual selection when it is allowed for a Contest Option on a Ballot to be assigned values greater than `1`","",,, +S3.3.5.f,"EGRI uses range proofs for individual Contest Options","",,, +S3.3.5.f,"EGRI uses range proofs for the Contest selection sum","",,, +S3.3.5.f,"EGRI can use range proofs to enable cumulative voting","",,, +S3.3.5.f,"EGRI can use range proofs to enable score voting","",,, +S3.3.5.f,"EGRI can use range proofs to enable STAR-voting","",,, +S3.3.5.f,"EGRI can use range proofs to enable Borda count","",,, +S3.3.7.c,"(Ref: S3.2.3.j) ""EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EG DS v2.1.0 eq. 31.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.3.7.c,"EGRI encrypts a Contest (Option or Additional) Data Field having a value of `0` as $(\alpha,\beta)=(g^\xi \bmod p,\ K^\xi \bmod p)$.","",,, +S3.3.7.c,"EGRI computes an NIZK proof that (\alpha,\beta) is an encryption of `0` or `1` as specified in EG DS v2.1.0 eq. 39 - 47.","",,, +S3.3.7.d,"(Ref: S3.2.3.j) ""EGRI may pre-compute the encryption of a Contest (Option or Additional) Data Field value of '0', and then convert it to an encryption of `1` if needed.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.3.7.d,"(Ref: S3.2.3.j) ""EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EG DS v2.1.0 eq. 31.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.3.7.d,"EGRI encrypts a Contest (Option or Additional) Data Field having a value of `1` as $(\alpha,\beta) = (g^\xi \bmod p,\ K^{\xi+1} \bmod p)$.","",,, +S3.3.7.e,"When the effective Option Selection Limit is a value other than `1`, EGRI computes an NIZK proof that (\alpha,\beta) is an encryption of `0` or `1` as specified in EG DS v2.1.0 eq. 48 - 56.","",,, +S3.3.7.g,"When the effective Option Selection Limit is a value other than `0` or `1`, EGRI computes an NIZK range proof as specified in EG DS v2.1.0 eq. 57 - 61.","",,, +S3.3.7.j.verif6,"TODO: Verification 6","",,, +S3.3.8.b,"EGRI computes an NIZK range proof that the homomorphic combination (i.e., sum) of all Contest Options Fields represents the encryption of a nonnegative integer value not greater than the effective Contest Selection Limit as specified in EG DS v2.1.0 eq. 62.","",,, +S3.3.8.c.verif7,"TODO: Verification 7","",,, +S3.3.9.b,"Ref: S3.1.3.j ""Undervoted Contest Condition""","",,, +S3.3.9.b,"Ref: S3.1.3.j ""Undervote Difference Count""","",,, +S3.3.9.c,"Ref: S3.1.3.l ""Overvoted Contest Condition""","",,, +S3.3.9.c,"Ref: S3.1.3.l ""Overvoted Option Condition""","",,, +S3.3.9.d,"Ref: S3.1.3.m 'Null Voted Contest Condition'","",,, +S3.3.9.e,"Ref: S3.1.3.n Write-ins","",,, +S3.3.10.c,"If the Election Manifest specifies that Ballots record for a Contest any non-homomorphically tallied additional or supplemental Contest Data Fields, EGRI encodes their values using an unambiguous fixed-length encoding and encrypts it as specified in EG DS v2.1.0 eq. 63 - 69.","",,, +S3.4.1,"EGRI computes the Contest Hash value $\chi_l$ as specified in EG DS v2.1.0 eq. 70.","",,, +S3.4.2,"EGRI computes the Confirmation Code `H_C` as specified in EG DS v2.1.0 eq. 71.","",,, +S3.4.3,"EGRI computes the Voting Device Information Hash value `H_DI` as specified in EG DS v2.1.0 eq. 72.","",,, +S3.4.4.b,"In the case of 'No chaining', EGRI computes the Chaining Field `B_C` as specified in EG DS v2.1.0 eq. 73.","",,, +S3.4.4.c,"In the case of 'Simple chaining', EGRI computes the Chaining Fields and Chaining Value as specified in EG DS v2.1.0 eq. 74 - 78.","",,, +S3.4.4.d,"EGRI enables a method of recording the state of a Ballot as one of `VoterSelectionsEncrypted`, `Cast`, `Spoiled`, `Challenged`, or `ChallengedDecrypted`.","",,, +S3.4.4.d,"EGRI refuses to decrypt a ballot in the `Cast` state.","",,, +S3.4.4.d,"EGRI refuses to decrypt a ballot in the `Spoiled` state.","",,, +S3.4.4.d,"EGRI includes in any tally operation only Ballots in the `Cast` state.","",,, +S3.4.4.d,"EGRI enables decrypting a ballot in the `Challenged` state.","",,, +S3.4.4.d,"EGRI refuses to change the state of a Ballot from the `Cast` state.","",,, +S3.4.4.d,"EGRI refuses to change the state of a Ballot from the `Spoiled` state.","",,, +S3.4.4.d,"EGRI refuses to change the state of a Ballot from the `ChallengedDecrypted` state.","",,, +S3.4.4.e.verif8,"TODO: Verification 8","",,, +S3.5.a,"EGRI enables publishing all Ballots (with proofs) in the Election Record at the conclusion of voting.","",,, +S3.5.a,"EGRI enables computing an Aggregate Ballot as specified in EG DS v2.1.0 eq. 79.","",,, +S3.5.b.verif9,"Every Ballot that fails to specify a Ballot Weight that is an integer inclusively between 1 and the maximum specified in the Election Manifest is considered invalid.","",,, +S3.5.c,"The Election Manifest specifies a maximum Ballot Weight as a small positive integer.","",,, +S3.5.c,"The condition ""If weights are used"" means the Election Manifest specifies a maximum Ballot Weight greater than `1`.","",,, +S3.5.c,"Every EGRI API receiving voter selections for encryption receives a value for the corresponding Ballot Weight and rejects the voter selections if the supplied Ballot Weight is not an integer inclusively between 1 and the maximum specified in the Election Manifest.","",,, +S3.5.c,"Every Ballot records its associated Ballot Weight.","",,, +S3.5.c,"Despite the language ""explicitly associated with an identified voter"", EGRI does not represent the concept of ""voter"". If an external system is used to track an association between Ballots and actual voters, the impact on voter privacy and coersion resistance must be considered extremely carefully.","",,, +S3.5.c,"EGRI enables computing an Aggregate Ballot from Weighted Ballots as specified in EG DS v2.1.0 eq. 80.","",,, +S3.5.c,"When computing an Aggregate Ballot, the aggregation function must, using access to the full and complete Election Record information to that point in time, verify that every Ballot to be aggregated has never been decrypted, is not in a 'Challeged' or 'Spoiled' state, etc.","",,, +S3.5.c,"If such a (decrypted, challenged, spoiled, etc) Ballot were somehow to be supplied to the aggregation function, it MUST be excluded from the tally, a diagnostic message emitted, and the offending Ballot identifier(s) returned separately.","",,, +S3.6.1,"EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Vote Encryption Secret Key. Josh 2025-03-19: This will only ever include the set of `Challenged` Ballots and the aggregate encrypted tallies.","",,, +S3.6.1,"EGRI enables to communicate to the Guardians the set of Ciphertexts marked for decryption (i.e., challenged Ballots and aggregate encrypted tallies).","",,, +S3.6.1,"Josh 2025-03-19: Multiple Tallies may be conducted for a single election.","",,, +S3.6.1,"Whenever multiple Tallies are conducted for a single election, subsequent tallies should extend the current Election Record. This does not introduce a requirement for EGRI to implement any form of rollback protection.","",,, +S3.6.1,"EGRS enables a Guardian to verify or refute that all Ciphertexts are correct prior to decryption. Ref: S6.2.3.verif4, S6.2.4.verif5, S6.2.4.verif6, S6.2.4.verif7, S6.2.4.verif8, S6.2.5.verif9","",,, +S3.6.1,"EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.","",,, +S3.6.1,"EGRS does not assist a Guardian to participate in the decryption of any Ciphertexts which does not both 1. belong to a set that has been marked for decryption and 2. been verified correct by that Guardian.","",,, +S3.6.2,"(Ref: S3.6.1) ""EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that have both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.6.2,"(Ref: S3.6.1) ""EGRS refusues to assist a Guardian to participate in the decryption of any Ciphertexts unless they both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.6.2,"(Ref: S3.a.f.c) ""EGRI enables a Guardian to compute a verifiable partial decryption...""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.6.2,"(Ref: S3.a.f.c) ""EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies...""",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S3.6.2,"EGRI enables a quorum of `k` distinct Guardians to compute `T = K^t`, for every Contest (Option or Additional) Data Field in the election. as specified in EG DS v2.1.0 eq. 81 - 82.","",,, +S3.6.2,"EGRI enables a quorum of `k` distinct Guardians to publish `T` in the Tallies of the Election Record, for every Contest (Option or Additional) Data Field in the election. Note: This is to handle scenarios in which the discrete log of `T` ends up needing more computation than the Guardian quorum has on hand at the time.","",,, +S3.6.2,"EGRI enables the practical determination of the tally `t` from `T = K^t`, for every Contest (Option or Additional) Data Field in the election.","",,, +S3.6.2,"EGRI enables the publication of the tally `t`, for every Contest (Option or Additional) Data Field in the election, in the Tallies of the Election Record.","",,, +S3.6.3,"EGRI enables a quorum of `k` distinct Guardians to compute `T = K^t`, for every Contest (Option or Additional) Data Field in the election. as specified in EG DS v2.1.0 eq. 81 - 82.","",,, +S3.6.3,"EGRI enables a quorum of `k` distinct Guardians to publish `T` in the Tallies of the Election Record, for every Contest (Option or Additional) Data Field in the election. Note: This is to handle scenarios in which the discrete log of `T` ends up needing more computation than the Guardian quorum has on hand at the time.","",,, +S3.6.3,"EGRI enables the practical determination of the tally `t` from `T = K^t`, for every Contest (Option or Additional) Data Field in the election.","",,, +S3.6.3,"EGRI enables the publication of the tally `t`, for every Contest (Option or Additional) Data Field in the election, in the Tallies of the Election Record.","",,, +S3.6.4.a,"EGRI enables the combination of partial decryptions as specified in EG DS v2.1.0 eq. 85 - 86.","",,, +S3.6.5.b,"EGRI enables available Guardians to jointly prove that they have shared knowledge of $s\in \Z_q$ such that $M = A^{s} \bmod p$ and $K = g^{s} \bmod p$ as specified in EG DS v2.1.0 eq. 87 - 93.","",,, +S3.6.5.d.verif10,"(Ref: S6.2.5.verif10) Verification 10",nyi,,4,"Not yet implemented" +S3.6.5.f.verif11,"(Ref: S6.2.5.verif11) Verification 11",nyi,,4,"Not yet implemented" +S3.6.6.a,"EGRI decrypts optional contest data as specified in EG DS v2.1.0 eq. 96 - 97.","",,, +S3.6.6.b,"EGRI computes NIZK proof of Decryption of Contest Data as specified in EG DS v2.1.0 eq. 98 - 106.","",,, +S3.6.6.c.verif12,"TODO: Verification 12","",,, +S3.6.7.b,"EGRI decrypts challenged Ballots Ballot Nonces as specified in EG DS v2.1.0 eq. 107 - 108.","",,, +S3.6.7.c,"EGRI decrypts challenged Ballots from encryption nonces as specified in EG DS v2.1.0 eq. 109 - 111.","",,, +S3.6.7.d,"TODO: Decryption of Challenged Ballots - Verifying decryption with nonces.","",,, +S3.6.7.e.verif13,"TODO: Verification 13","",,, +S3.6.7.f.verif14,"TODO: Verification 14","",,, +S3.7,"(Ref: S2.b) ""EGRI enables production of an 'Election Record'""",nyi,,4,"Not yet implemented" +S3.7,"The Election Record is not a static fixed format. Different information is added and modified at different times.",nyi,,4,"Not yet implemented" +S3.7,"The Election Record format must support appending new and updated information without invalidating signatures made previously.",nyi,,4,"Not yet implemented" +S3.7,"The Election Record format must support inclusion-by-reference (eg a Ballot ID and hash value) of batches of Ballots. Rationale: For all but very small elections, it will not be practical to hold all Ballots in a single file. Neither will it be practical to place every Ballot in a separate file in a single directory.",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records any information necessary and sufficient to uniquely identify and describe the election, such as date, location, election type, etc. that is not otherwise present in the Election Manifest.",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records the Election Manifest",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records the Fixed Parameters `p`, `q`, `g`, and `r`.",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records the Varying Parameters `n` and `k`.",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records `H_P`",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records `H_B`",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Guardian `1..n`: a name or label",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Guardian `1..n`: the polynomial coefficients commitments",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Guardian `1..n`: the polynomial coefficients the proofs of knowledge",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Guardian `1..n`: the Guardian Communication Public Key",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records the Joint Vote Encryption Public Key",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records the Joint Ballot Data Encryption Public Key",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records `H_E`",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier $\id_B$,",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier hash $\HH_I$,",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: all of the encrypted selections on each ballot,",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the proofs that each such value is an encryption of either zero or one (or more generally, the proofs that these values satisfy the respective option selection limits),",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the effective Contest Selection Limit",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, for every Option Field, the effective Option Selection Limit",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the stated and effective selection limits applied to each contest,",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the proof that the number of selections made does not exceed the selection limit,",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight as originally submitted with the Voter Selections",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight already applied to the Ciphertexts",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Style index,",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: device information uniquely identifying the device used for encrypting of voter selections to produce the Ballot",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the date and time of the ballot encryption (as reported by the encrypting device) with no more than 1 second granularity",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the confirmation code produced for the Ballot",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the state of the ballot `Cast` or `Challenged`",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: all the information included for the Ballot in its `Challenged` state",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the selections made for every Contest Option",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the decrypted plaintext values of every Contest (Option or Additional) Data Field",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the proofs of correct decryption for every Contest (Option or Additional) Data Field and/or the Contest Data Field nonce.",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the date and time of the overall ballot decryption (as reported by the decrypting device)",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: some identifier uniquely identifying the Ballot Decryption Operation",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: the set of Ballots ""marked for decryption""",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: the hash of the ElectionRecord at the point the Ballot Decryption Operation was initiated",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the Guardian Index",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the date and time their participation was initiated",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the hash of their view of the ElectionRecord at the instant this operation was initiated",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: device information uniquely identifying the device used for their secret key operation",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian:",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian:",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation:",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Ballot Decryption Operation:",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state:",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the Guardian Index",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the date and time of the partial decryption",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: their partial decryptions",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Tally: for every Contest: identifiers of the set of Ballots in the `Cast` state",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the encrypted tally (eq. 79 pg 44)",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the full verifiable decryption of the encrypted tally",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the tally exponent",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: plaintext representation of the tally",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: proof of correct decryption of the tally",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records information uniquely identifying each device used for encryption of voter selections to produce any Ballot",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records an ordered list of the Ballots encrypted by each device.",nyi,,4,"Not yet implemented" +S3.7,"The Election Record records the encrypted contest data, when available.",nyi,,4,"Not yet implemented" +S3.7,"EGRS enables the Election Record to be produced in a format suitable for digitally signing.",nyi,,4,"Not yet implemented" +S3.7,"EGRS enables the Election Record to be published in a format suitable to the needs of Verifier applications. For example, a directory of files accessible via HTTPS supporting efficient random access.",nyi,,4,"Not yet implemented" +S3.7,"EGRS enables the Election Record to be published in a format suitable for archiving or bulk downloading by any interested individuals. For example, the compressed .zip and/or .tar.gz formats.",nyi,,4,"Not yet implemented" +S4,"EGRI Preencrypted Ballots enable the vote-by-mail scenario.",na,,1,"Not directly applicable to implementation code" +S4,"EGRI Preencrypted Ballots enable the ""precincts with central count or minimal in-precinct equipment"" scenario.",na,,1,"Not directly applicable to implementation code" +S4,"EGRI Preencrypted Ballots enable back-ups for precincts which ordinarily perform encryption on demand.",na,,1,"Not directly applicable to implementation code" +S4,"EGRI enables ordinary and Preencrypted Ballots to be tallied together.",nyi,,4,"Not yet implemented" +S4,"When ordinary and Preencrypted Ballots are tallied together, it does not reveal which votes came from which mode.",nyi,,4,"Not yet implemented" +S4,"The Election Manifest contains a configuration setting indicating whether Preencrypted Ballots are enabled for that election.",nyi,,4,"Not yet implemented" +S4,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI allows to produce data necessary to print Preencrypted Ballots. Note: Referred to in the EGRS as the ""Ballot Encryption Tool"".",nyi,,4,"Not yet implemented" +S4,"EGRI refuses to generate any Preencrypted Ballots unless the Election Manifest indicates Preencrypted Ballots are enabled.",nyi,,4,"Not yet implemented" +S4,"EGRI allows to record Voter Selections made on Preencrypted Ballots, if the Election Manifest indicates Preencrypted Ballots are enabled.",nyi,,4,"Not yet implemented" +S4,"If the Election Manifest indicates Preencrypted Ballots are not enabled, EGRI refuses to record any Voter Selections made on Preencrypted Ballots.",nyi,,4,"Not yet implemented" +S4,"For each Preencrypted Ballot generated, EGRI computes the Selection Encryption Identifier `id_B` as described in (Ref: S3.3.2.a).",nyi,,4,"Not yet implemented" +S4,"For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32 (Ref: S3.3.2.a).",nyi,,4,"Not yet implemented" +S4.1,"pg. 57 EGRI `Preencrypted Ballot`, for every Contest in the BallotStyle, for every Contest Option plus an additional null selection, computes a vector `Psi_{i,m}` of length `m`, where `m` is equal to the number of Contest Options and `i` is the Contest Option index. Note1: This is captial `Psi`. Note2: Although `m` is denoted here as a subscript, it is not an index into a second dimension.",nyi,,4,"Not yet implemented" +S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` for `0 <= i <= m`, where `m` is equal to the number of Contest Options, `i = 0` corresponds to the null vote, and for `1 <= i` `i` is the corresponding Option Index.",nyi,,4,"Not yet implemented" +S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `1 <= i` as a length `m` vector having an encryption of `1` at (1-based) position `i`, and encryptions of `0` at all other options. Note: This corresponds to non-null vote cases.",nyi,,4,"Not yet implemented" +S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `i = 0` as a length `m` vector of encryptions of `0`. Note: This corresponds to the null vote case.",nyi,,4,"Not yet implemented" +S4.1,"pg. 57 Note: EGRI `PreencryptedBallot`, when encrypting ContestOption vectors `Psi_{j,m}`, the encryption nonce `xi_{i,j}` is derived from `H_I` and `xi_B` as specified in eq. 121 (Ref: S4.2.1).",nyi,,4,"Not yet implemented" +S4.1,"pg. 57 For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in [eq. 121 pg. 61].",nyi,,4,"Not yet implemented" +S4.1.1,"pg. 58 EGRI computes the Selection Hash `psi_i` for a single Option `i` according to [eq. 115 pg. 58]",nyi,,4,"Not yet implemented" +S4.1.1,"pg. 58 ""In a contest with a selection limit of L, an additional L null vectors are hashed"" ""where all $E_i = (\alpha_i, \beta_i)$ are encyptions of zero and `1 <= l <= L`.""",nyi,,4,"Not yet implemented" +S4.1.1,"pg. 58 EGRI computes the Selection Hash `psi_{m+l}` for an additional L null vector according to [eq. 114 pg. 58]",nyi,,4,"Not yet implemented" +S4.1.2,"pg. 58 EGRI sorts all Selection Hashes in a contest prior to their use in the computation of the Contest Hash.",nyi,,4,"Not yet implemented" +S4.1.2,"pg. 58 EGRI computes the Contest Hash `chi_l` according to [eq. 115 pg. 58]",nyi,,4,"Not yet implemented" +S4.1.3,"pg. 58 EGRI computes the Confirmation Code `H_C` according to [eq. 116 pg. 58]",nyi,,4,"Not yet implemented" +S4.1.4,"pg. 59 EGRI computes Ballot Chaining values for Preencrypted Ballots according to eq. 117-120 pg. 59.",nyi,,4,"Not yet implemented" +S4.1.5.a,"pg. 59 If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI requires that the Election Manifest specify unambiguously the Hash Trimming Function `Omega` to be used for all Preencrypted Ballots generated for the election.",nyi,,4,"Not yet implemented" +S4.1.5.a,"pg. 59 EGRI provides one or more Hash Trimming Functions.",nyi,,4,"Not yet implemented" +S4.1.5.b,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides short codes to indicate undervotes",nyi,,4,"Not yet implemented" +S4.2,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides a ""Ballot Encrypting Tool"" which operates as describedin S4.2",nyi,,4,"Not yet implemented" +S4.2.1,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI produces nonces deterministically as described in eq. 121",nyi,,4,"Not yet implemented" +S4.2.2,"[TODO: Pre-Encrypted Ballots]","",,, +S4.3,"[TODO: Pre-Encrypted Ballots]",nyi,,4,"Not yet implemented" +S4.3.1,"[TODO: Pre-Encrypted Ballots]",nyi,,4,"Not yet implemented" +S4.4,"[TODO: Pre-Encrypted Ballots]",nyi,,4,"Not yet implemented" +S4.4.1,"[TODO: Pre-Encrypted Ballots]",nyi,,4,"Not yet implemented" +S4.5.a,"[TODO: Pre-Encrypted Ballots]",nyi,,4,"Not yet implemented" +S4.5.b.verif15,"TODO: Verification 15",nyi,,4,"Not yet implemented" +S4.5.c.verif16,"TODO: Verification 16","",,, +S4.5.d.verif17,"TODO: Verification 17","",,, +S4.5.e.verif18,"TODO: Verification 18","",,, +S4.5.f.verif19,"TODO: Verification 19","",,, +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Two Hex Characters` hash-trimming function `Ω_1`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Four Hex Characters` hash-trimming function `Ω_2`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Letter-Digit` hash-trimming function `Ω_3`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Digit-Letter` hash-trimming function `Ω_4`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 0-255` hash-trimming function `Ω_5`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 1-256` hash-trimming function `Ω_6`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 100-355` hash-trimming function `Ω_7`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 101-356` hash-trimming function `Ω_8`.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS ElectionManifest PreencryptedBallots feature allows to specify the use of and the complete configuration for an API-user-supplied hash-trimming function.",ics,,50,"Implementation code exists covering a portion of code paths considered substantial" +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Letter-Digit` hash-trimming function `Ω_3`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Digit-Letter` hash-trimming function `Ω_4`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 0-255` hash-trimming function `Ω_5`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 1-256` hash-trimming function `Ω_6`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 100-355` hash-trimming function `Ω_7`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 101-356` hash-trimming function `Ω_8`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for producing short codes.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Letter-Digit` hash-trimming function `Ω_3`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Digit-Letter` hash-trimming function `Ω_4`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 0-255` hash-trimming function `Ω_5`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 1-256` hash-trimming function `Ω_6`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 100-355` hash-trimming function `Ω_7`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 101-356` hash-trimming function `Ω_8`.",nyi,,4,"Not yet implemented" +S4.6,"EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for consuming short codes.",nyi,,4,"Not yet implemented" +S5,"EGRI uses `HMAC` as the basis for the function `H`.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1,"EGRI converts between nonegative integers and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 122 - 123.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1.1,"EGRI converts between nonegative integers mod `p` and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 124 - 126.b.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1.2,"EGRI converts between nonegative integers mod `q` and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 127 - 129.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1.3,"EGRI converts between nonnegative integers less than 2^31 and 4-byte fixed-size arrays using the big endian convention.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1.4,"EGRI hashes fixed-length strings as their minimal UTF-8 encoding.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1.4,"EGRI hashes varible-length strings as their minimal UTF-8 encoding prefixed by their length in bytes as a sequence of 4 bytes using big endian convention.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1.5,"EGRI can read and write files as contiguous sequences of bytes.",nfd,"",2,"Needs further discussion" +S5.1.5,"EGRI encodes byte lengths as 4-byte big-endian values.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1.5,"EGRI accepts byte lengths less than `2^31`.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.1.5,"EGRI does not produce byte lengths greater than `2^31 - 1`.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.2,"EGRI implements the function `H` using HMAC and SHA-2-256 as specified in EG DS v2.1.0 eq. 130.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.3,"EGRI hashes multiple inputs using an unambiguous separation as specified in the relevant section in EG DS v2.1.0",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.4,"EGRI implements the function `H_q` as specified in EG DS v2.1.0 eq. 131 - 132.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.4,"Iff Fixed Parameter `q` begins with 248 or more consecutive leading `1` valued bits, such as is the case with `q` from the standard parameter set, the function `H_q` can be simplified to `H`.",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.5,"This is expected to be a fully-redundant section, but we could double-check it",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S5.5.1,"This is expected to be a fully-redundant section, but we could double-check it",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S5.5.2,"This is expected to be a fully-redundant section, but we could double-check it",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S5.5.3,"This is expected to be a fully-redundant section, but we could double-check it",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S5.5.4,"This is expected to be a fully-redundant section, but we could double-check it",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S5.5.5,"This is expected to be a fully-redundant section, but we could double-check it",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.1.b,TODO,utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S6.1.c,"EGRI should compute moduluar multiplication on large integers without generating unnecessarily large intermediate values",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S6.1.d,"EGRI should compute moduluar exponentiation on large integers without generating intermediate values having more digits than there are particles in the universe",utsp,,75,"Unit tests exercising a portion of code paths considered sufficient are passing" +S6.2.1.verif1,"TODO: Verification 1",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.2.a.verif2,"TODO: Verification 2",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.2.b.verif3,"TODO: Verification 3",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.3.verif4,"TODO: Verification 4",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.4.a.verif5,"TODO: Verification 5",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.4.b.verif6,"TODO: Verification 6",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.4.c.verif7,"TODO: Verification 7",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.4.d.verif8,"TODO: Verification 8",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.5.a.verif9,"TODO: Verification 9",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.5.b.verif10,"TODO: Verification 10",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.5.c.verif11,"TODO: Verification 11",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.6.a.verif12,"Verification of Correct Contest Data Decryption",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.7.a.verif13,"TODO: Verification 13",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.7.b.verif14,"TODO: Verification 14",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.8.a.verif15,"TODO: Verification 15",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.8.b.verif16,"TODO: Verification 16",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.8.c.verif17,"TODO: Verification 17",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.8.d.verif18,"TODO: Verification 18",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" +S6.2.8.e.verif19,"TODO: Verification 19",ace,,3,"Applicable to implementation code, but more directly covered elsewhere" diff --git a/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.status-lte-ics.csv b/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.status-lte-ics.csv new file mode 100644 index 0000000..1cbc932 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.status-lte-ics.csv @@ -0,0 +1,291 @@ +status_code,ordinal,section,xtext,status_note +na,1,S1.a,"EGRI can be used to enable end-to-end (E2E) verifiability.", +na,1,S1.a,"EGRI can be used to enable privacy-enhanced risk-limiting audits (RLAs).", +na,1,S1.a,"EGRI components can be used to empower individual voters to independently verify the accuracy of election results.", +na,1,S1.b,"EGRI is compatible with in-person voting using an electronic ballot-marking device.", +na,1,S1.b,"EGRI is compatible with in-person voting using an optical scanner capable of reading hand-marked ballots.", +na,1,S1.b,"EGRI is compatible with in-person voting using an optical scanner capable of reading machine-marked ballots.", +na,1,S1.b,"EGRI is compatible with voting by mail.", +na,1,S1.b,"EGRI is compatible with Internet voting.", +ace,3,S1.c,"EGRI can prove (or fail to prove) that publicly disclosed EG ballots match reported tallies", +na,1,S1.c,"EGRI can prove (or fail to prove) that physical ballot records used in an RLA are those produced by the voters", +ace,3,S1.d,"EGRI provides a ""detailed implementation specification"" and/or qualifies as a ""well-documented ElectionGuard implementation""", +na,1,S1.d,"EGRI can be used by independent parties to write ElectionGuard verifiers to confirm the consistency of election artifacts with announced election results.", +na,1,S1.d,"EGRI can be used by independent parties to write verifiers to confirm (or refute) the consistency of election artifacts with announced election results", +na,1,S2.a,"A 'Guardian' role exists with certain duties.", +na,1,S2.a,"EGRI enables a Guardian to protect confidentiality of votes.", +na,1,S2.a,"EGRI enables members of a canvassing board to serve as Guardians.", +ace,3,S2.a,"No set of guardians fewer than a quorum are able to produce the artifacts required to enable public verification of the tally.", +na,1,S2.a,"% xnote S3.7 Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: An 'Election Administrator' role exists", +ace,3,S2.a,"Note: EG defines protocols", +ace,3,S2.a,"Note: EG defines procedures", +ace,3,S2.b,"Guardians participate in a 'Key Generation Ceremony'.", +ace,3,S2.b,"At the beginning of the Key Generation Ceremony, EGRI enables each guardian to publish its public keys and proofs of knowledge of the associated secret keys.", +ace,3,S2.b,"At the beginning of the Key Generation Ceremony, EGRI enables each Guardian to receive the published keys and proofs of knowledge of the associated secret keys from the other Guardians.", +nyi,4,S2.b,"EGRI enables a Guardian to determine that they have received the public keys and proofs of knowledge of the associated secret keys from all other Guardians, or identify those which have not yet been received.", +ics,50,S2.b,"EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Vote Encryption key.", +ics,50,S2.b,"EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Ballot Data Encryption key.", +nyi,4,S2.b,"EGRI enables a Guardian to check for consistency any shares of other guardians secret keys it has received.", +nyi,4,S2.b,"EGRI enables a Guardian to query or observe which particular shares from all other Guardians it has recieved, decrypted, checked for consistency, and verified, and which are missing, inconsistent, or failed.", +nyi,4,S2.b,"Josh 2025-03-18: ""The code should emit a success or failure indicator that a human can use to verify that the [received key shares] have the required properties."".", +na,1,S3.a.a.a,"EGRI enables using the same Fixed Parameters across multiple elections", +ics,50,S3.a.a.b,"(ref: S2.b) EGRI enables Guardians to generate individual public-secret key pairs", +ics,50,S3.a.a.b,"(ref: S2.b) EGRI enables Guardians to exchange shares of secret keys", +nyi,4,S3.a.a.b,"EGRI enables Guardians to destroy their secret keys.", +na,1,S3.a.a.c,"[No specific requirement]", +nfd,2,S3.1.3.a,"EGRI rejects any Election Manifest that does not contain data that makes it unique for each election","Ref: S3.1.3.a: ""The manifest file must contain data that makes it unique for each election."" QUESTION: How could this possibly be implemented?" +ics,50,S3.1.3.b,"EGRI rejects ElectionManifest labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)","difficult to test because the Rust std lib rejects malformed UTF-8" +ics,50,S3.1.3.b,"EGRI rejects Contest labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)", +ics,50,S3.1.3.b,"EGRI rejects ContestOption labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)", +ics,50,S3.1.3.b,"EGRI rejects BallotStyle labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)", +ics,50,S3.1.3.e,"If additional data fields are present for a Contest, they are assigned Indices continuing after the Selectable Options.", +nyi,4,S3.1.3.f,"Every Ballot Style defines a label unique across all Ballot Styles in the manifest", +ics,50,S3.1.3.f,"Order of occurence is not significant within a Ballot Style Contest Index list.", +ics,50,S3.1.3.g,"For any Contest for which a Contest Selection Limit is not specfied, an effective value of 1 is used as the default.", +ics,50,S3.1.3.g,"The effective Contest Selection Limit limits the maximum total value (i.e., number of votes or voter selections) that can be assigned over all Selectable Options in that contest.", +nyi,4,S3.1.3.g,"EGRI may warn if any effective Contest Selection Limit is zero", +nyi,4,S3.1.3.g,"EGRI may warn if any effective Option Selection Limit is zero", +ics,50,S3.1.3.g,"The effective Option Selection Limit limits the maximum value (i.e., number of votes) that can be assigned to that particular Selectable Option.", +ics,50,S3.1.3.g,"The effective Selection Limit limits the maximum value that can be assigned to that Selectable Option", +ics,50,S3.1.3.g,"If an effective (specified or defaulted) Option Selection Limit exceeds the effective (specified or defaulted) Contest Selection Limit, EGRI applies the smaller value. EGRI may emit a diagnostic message in this case.", +ics,50,S3.1.3.g,"If the sum of all effective (specified or defaulted) Option Selection Limits for a Contest exceeds that Contest's effective (specified or defaulted) Contest Selection Limit, EGRI applies the smaller value. This is an ordinary case for which EGRI should not emit a specific diagnostic message.", +nfd,2,S3.1.3.h,"Election Manifest offers optional data fields for accompanying data.","" +nfd,2,S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied at the top level.","" +nfd,2,S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Contests. Note: These are different from Additional Contest Data Fields which may be recorded after the Selectable Option Fields on a Ballot.","" +nfd,2,S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Selectable Options.","" +nfd,2,S3.1.3.h,"Election Manifest allows Accompanying Data Fields to be applied to Ballot Styles.","" +nfd,2,S3.1.3.h,"Election Manifest Accompanying Data Fields may go beyond short labels.","" +nfd,2,S3.1.3.h,"Election Manifest Accompanying Data Fields may be non-unique.","" +nfd,2,S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election.","" +nfd,2,S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the jurisdiction.","" +nfd,2,S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election device manufacturers.","" +nfd,2,S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about software versions.","" +nfd,2,S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election date.","" +nfd,2,S3.1.3.h,"Election Manifest Accompanying Data Fields may describe general information about the election location.","" +nfd,2,S3.1.3.h,"EGRI allows an Election Manifest to specify many other things, including things not listed here.","" +nyi,4,S3.1.3.j,"'Undervoted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limit", +nyi,4,S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification (None currently). The value of this additional Contest Data Field is `1` to record the Undervoted Contest Condition and `0` otherwise.", +nyi,4,S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.", +nyi,4,S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.", +nyi,4,S3.1.3.k,"If a Contest is specified to record Undervote Difference Count, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (saturating) difference (i.e., nonnegative) between the effective Contest Selection Limit and the the sum of the voter selections for that Contest. This value will not exceed the effective Contest Selection Limit.", +nyi,4,S3.1.3.k,"If a Contest is specified to record Undervote Difference Count, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.", +nyi,4,S3.1.3.l,"An 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit.", +nyi,4,S3.1.3.l,"Election Manifest allows to specify, for each Contest, whether to record the Overvoted Contest Condition for that contest.", +nyi,4,S3.1.3.l,"If not specified, EGRI does not record the Overvoted Contest Condition.", +nyi,4,S3.1.3.l,"If a Contest is specified to record Overvoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Contest Condition and `0` otherwise.", +nyi,4,S3.1.3.l,"If a Contest is specified to record Overvoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.", +nyi,4,S3.1.3.l,"'Overvoted Option Condition' is a per-contest-option state in which the selection count is strictly greater than the option selection limit", +nyi,4,S3.1.3.l,"Election Manifest allows to specify, for each Contest Option, whether to record the Overvoted Option Condition for that Option.", +nyi,4,S3.1.3.l,"If not specified, EGRI does not record the Overvoted Option Condition.", +nyi,4,S3.1.3.l,"If a Contest Option is specified to record Overvoted Option Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields, any other Contest Data Fields recording Overvoted Option Condition for lower-index Options, and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Option Condition and `0` otherwise.", +nyi,4,S3.1.3.l,"If a Contest is specified to record Overvoted Option Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.", +nyi,4,S3.1.3.m,"'Null Voted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limit", +nyi,4,S3.1.3.m,"Election Manifest allows to specify, for each Contest, whether to record the Null Voted Contest Condition for that Contest.", +nyi,4,S3.1.3.m,"If not specified, EGRI does not record the Null Voted Contest Condition.", +nyi,4,S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Null Voted Contest Condition and `0` otherwise.", +nyi,4,S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.", +nyi,4,S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.", +nyi,4,S3.1.3.n,"Election Manifest allows to specify, for each Contest, whether to record the Total Number Of Write-ins selected for that Contest.", +nyi,4,S3.1.3.n,"If not specified, EGRI does not record the Total Number Of Write-ins.", +nyi,4,S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (nonnegative integer) total number of write-ins the voter selected for that Contest. This value must not exceed the effective Contest Selection Limit.", +nyi,4,S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.", +nyi,4,S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.", +nyi,4,S3.1.3.n,"EGRI may emit a diagnostic message if any contest specifies a number of write-in fields greater than the contest selection limit", +nyi,4,S3.1.3.n,"Election Manifest allows to specify, for each Contest, the number of write-in fields available to the voter.", +nyi,4,S3.1.3.n,"If a Contest is specified to supply a nonzero number of write-in fields, the Contest has that number of additional 'write-in N' voter-selectable Contest Options.", +nyi,4,S3.1.3.n,"Any such write-in fields are to be labeled by the system as ""write-in N"", where 'N' is the 1-based index value of the write-in field. (This will be greater than the Contest Option Field Index value by the number of non-write-in voter-Selectable Options.)", +nyi,4,S3.1.3.g,"The Election Manifest, for each additional 'write-in N' voter-selectable Contest Option, may specify a nonnegative integer Option Selection Limit with the same defaults and other semantics as non-write-in Contest Options. (ref: S3.1.3.g)", +nyi,4,S3.1.3.n,"""Written-in"" means that the election system has recorded some voter-supplied, non-empty, non-blank data associated with a contest and write-in field index value", +nyi,4,S3.1.3.n,"Every write-in field that *was not* written-in MUST be assigned a value of `0`.", +nyi,4,S3.1.3.n,"Every write-in field that *was* written-in MUST be assigned a value of `1` or greater, and subject to the same rules for Effective Option Selection Limit as other voter-Selectable Options.", +nyi,4,S3.1.3.n,"The value of a write-in field counts against the effective Contest Selection Limit", +nyi,4,S3.1.3.n,"For every Write-in field, an additional Range Proof is recorded that the encrypted value is either `0` or `1`.", +nyi,4,S3.1.3.n,"Josh 2025-03-18: A write-in field can not have an effective Option Selection Limit greater than `1`.", +ics,50,S3.1.3.o,"EGRI allows to write the Election Manifest canonical representation to a file.", +ics,50,S3.2.a,"EGRI rejects values of `k` greater than `n`.", +na,1,S3.2.c,"(Ref: S3.a.a.b) ""EGRI requires that a complete new set of keys must be generated if even a single guardian is replaced""", +ics,50,S3.2.c,"EGRI rejects any election data unless 1 <= k <= n < 2^31", +ics,50,S3.2.c,"EGRI rejects Guardian labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)","difficult to test because the Rust std lib rejects malformed UTF-8" +na,1,S3.2.c,"(Ref: S3.a.f.d) ""Fewer than `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys can not produce a full decryption of any tally""", +nyi,4,S3.2.1,"The Election Record contains every Guardian's public key", +ics,50,S3.2.1,"The Election Record contains, for every Guardian public key, a non-interactive zero-knowledge Schnorr proof of knowledge of the associated secret key", +na,1,S3.2.1,"Guardian polynomial coefficients are generated and managed in precisely the same fashion as secret keys", +ics,50,S3.2.1,"Any set of k guardians' private keys is sufficient to complete decryption of the tally for each contest (e.g. selection option) field", +na,1,S3.2.1,"No set of fewer than `k` distinct Guardian Vote Encryption Secret Keys is sufficient to complete the the decryption of the tally for any Contest Option or additional Contest Data Field.", +ace,3,S3.2.2.a,"(Ref: S2.b) Key Ceremony", +ace,3,S3.2.2.a,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Vote Encryption (Public|Secret) Key pair prior to the start of voting or RLA.""", +ace,3,S3.2.2.b,"(Ref: S2.b) Key Ceremony", +ace,3,S3.2.2.b,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Ballot Data Encryption (Public|Secret) Key pair prior to the start of voting or RLA.""", +ace,3,S3.2.2.c,"(Ref: S2.b) Key Ceremony", +ace,3,S3.2.2.c,"(Ref: S2.b) ""EGRI enables a Guardian to generate its own Guardian Communication (Public|Secret) Key pair prior to the start of voting or RLA.""", +ace,3,S3.2.2.f.verif2,"TODO: Verification 2 Note: Required for (Ref: S3.2.2.o Details of Key Generation - Share verification and the guardian record)", +ace,3,S3.2.2.i,"(Ref: S2.b) Key Ceremony", +ace,3,S3.2.2.i,"(Ref: S2.b) EGRI enables a Guardian `i` to encrypt to another Guardian `l` Guardian Communication Public Key a share of each of its own (Vote|Ballot Data) Encryption Secret Keys, $P_i(\ell)$ and $\hat{P}_i(\ell)$, as specified in EG DS v2.1.0 eq. 15 - 22", +ace,3,S3.2.2.i,"(Ref: S2.b) EGRI enables a Guardian to publish the encryption $E_\ell(P_i(\ell), \hat{P}_i(\ell))$ of its secret key shares $P_i(\ell)$ and $\hat{P}_i(\ell)$ for every other guardian $G_\ell$", +ace,3,S3.2.2.j,"(Ref: S3.2.2.i) EGRI enables a receiving Guardian `l` to verify or refute the Schnorr proof $C_{i,\ell,2} = (\bar c_{i,\ell},\bar v_{i,\ell})$ from sending Guardian `i` as part of the share of each of Guardian `i`'s (Vote|Ballot Data) Encryption Secret Keys as specified in EG DS v2.1.0 eq. 22.", +ace,3,S3.2.2.l,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.""", +ace,3,S3.2.2.m,"(Ref: S2.b) ""At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.""", +ace,3,S3.2.2.o,"(Ref: S2.b) ""EGRI enables a Guardian to ""complain and halt the protocol""""", +ace,3,S3.2.2.o,"(Ref: S2.b) ""EGRI enables, supports, or facilitates the initiation of a 'Key Ceremony Irregularity Investigation' subsequent to a Guardian complaint of irregularity.""", +ace,3,S3.3.5.d,"(Ref: S3.1.3.l) ""An 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit.""", +ace,3,S3.3.7.c,"(Ref: S3.2.3.j) ""EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EG DS v2.1.0 eq. 31.""", +ace,3,S3.3.7.d,"(Ref: S3.2.3.j) ""EGRI may pre-compute the encryption of a Contest (Option or Additional) Data Field value of '0', and then convert it to an encryption of `1` if needed.""", +ace,3,S3.3.7.d,"(Ref: S3.2.3.j) ""EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EG DS v2.1.0 eq. 31.""", +ace,3,S3.6.2,"(Ref: S3.6.1) ""EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that have both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.""", +ace,3,S3.6.2,"(Ref: S3.6.1) ""EGRS refusues to assist a Guardian to participate in the decryption of any Ciphertexts unless they both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.""", +ace,3,S3.6.2,"(Ref: S3.a.f.c) ""EGRI enables a Guardian to compute a verifiable partial decryption...""", +ace,3,S3.6.2,"(Ref: S3.a.f.c) ""EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies...""", +nyi,4,S3.6.5.d.verif10,"(Ref: S6.2.5.verif10) Verification 10", +nyi,4,S3.6.5.f.verif11,"(Ref: S6.2.5.verif11) Verification 11", +nyi,4,S3.7,"(Ref: S2.b) ""EGRI enables production of an 'Election Record'""", +nyi,4,S3.7,"The Election Record is not a static fixed format. Different information is added and modified at different times.", +nyi,4,S3.7,"The Election Record format must support appending new and updated information without invalidating signatures made previously.", +nyi,4,S3.7,"The Election Record format must support inclusion-by-reference (eg a Ballot ID and hash value) of batches of Ballots. Rationale: For all but very small elections, it will not be practical to hold all Ballots in a single file. Neither will it be practical to place every Ballot in a separate file in a single directory.", +nyi,4,S3.7,"The Election Record records any information necessary and sufficient to uniquely identify and describe the election, such as date, location, election type, etc. that is not otherwise present in the Election Manifest.", +nyi,4,S3.7,"The Election Record records the Election Manifest", +nyi,4,S3.7,"The Election Record records the Fixed Parameters `p`, `q`, `g`, and `r`.", +nyi,4,S3.7,"The Election Record records the Varying Parameters `n` and `k`.", +nyi,4,S3.7,"The Election Record records `H_P`", +nyi,4,S3.7,"The Election Record records `H_B`", +nyi,4,S3.7,"The Election Record records for every Guardian `1..n`: a name or label", +nyi,4,S3.7,"The Election Record records for every Guardian `1..n`: the polynomial coefficients commitments", +nyi,4,S3.7,"The Election Record records for every Guardian `1..n`: the polynomial coefficients the proofs of knowledge", +nyi,4,S3.7,"The Election Record records for every Guardian `1..n`: the Guardian Communication Public Key", +nyi,4,S3.7,"The Election Record records the Joint Vote Encryption Public Key", +nyi,4,S3.7,"The Election Record records the Joint Ballot Data Encryption Public Key", +nyi,4,S3.7,"The Election Record records `H_E`", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier $\id_B$,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier hash $\HH_I$,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: all of the encrypted selections on each ballot,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the proofs that each such value is an encryption of either zero or one (or more generally, the proofs that these values satisfy the respective option selection limits),", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the effective Contest Selection Limit", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, for every Option Field, the effective Option Selection Limit", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the stated and effective selection limits applied to each contest,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the proof that the number of selections made does not exceed the selection limit,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight as originally submitted with the Voter Selections", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight already applied to the Ciphertexts", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Style index,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: device information uniquely identifying the device used for encrypting of voter selections to produce the Ballot", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the date and time of the ballot encryption (as reported by the encrypting device) with no more than 1 second granularity", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the confirmation code produced for the Ballot", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the state of the ballot `Cast` or `Challenged`", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: all the information included for the Ballot in its `Challenged` state", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the selections made for every Contest Option", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the decrypted plaintext values of every Contest (Option or Additional) Data Field", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the proofs of correct decryption for every Contest (Option or Additional) Data Field and/or the Contest Data Field nonce.", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the date and time of the overall ballot decryption (as reported by the decrypting device)", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: some identifier uniquely identifying the Ballot Decryption Operation", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: the set of Ballots ""marked for decryption""", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: the hash of the ElectionRecord at the point the Ballot Decryption Operation was initiated", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the Guardian Index", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the date and time their participation was initiated", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the hash of their view of the ElectionRecord at the instant this operation was initiated", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: device information uniquely identifying the device used for their secret key operation", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian:", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian:", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation:", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation:", +nyi,4,S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state:", +nyi,4,S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the Guardian Index", +nyi,4,S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the date and time of the partial decryption", +nyi,4,S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: their partial decryptions", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: identifiers of the set of Ballots in the `Cast` state", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the encrypted tally (eq. 79 pg 44)", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the full verifiable decryption of the encrypted tally", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the tally exponent", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: plaintext representation of the tally", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: proof of correct decryption of the tally", +nyi,4,S3.7,"The Election Record records information uniquely identifying each device used for encryption of voter selections to produce any Ballot", +nyi,4,S3.7,"The Election Record records an ordered list of the Ballots encrypted by each device.", +nyi,4,S3.7,"The Election Record records the encrypted contest data, when available.", +nyi,4,S3.7,"EGRS enables the Election Record to be produced in a format suitable for digitally signing.", +nyi,4,S3.7,"EGRS enables the Election Record to be published in a format suitable to the needs of Verifier applications. For example, a directory of files accessible via HTTPS supporting efficient random access.", +nyi,4,S3.7,"EGRS enables the Election Record to be published in a format suitable for archiving or bulk downloading by any interested individuals. For example, the compressed .zip and/or .tar.gz formats.", +na,1,S4,"EGRI Preencrypted Ballots enable the vote-by-mail scenario.", +na,1,S4,"EGRI Preencrypted Ballots enable the ""precincts with central count or minimal in-precinct equipment"" scenario.", +na,1,S4,"EGRI Preencrypted Ballots enable back-ups for precincts which ordinarily perform encryption on demand.", +nyi,4,S4,"EGRI enables ordinary and Preencrypted Ballots to be tallied together.", +nyi,4,S4,"When ordinary and Preencrypted Ballots are tallied together, it does not reveal which votes came from which mode.", +nyi,4,S4,"The Election Manifest contains a configuration setting indicating whether Preencrypted Ballots are enabled for that election.", +nyi,4,S4,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI allows to produce data necessary to print Preencrypted Ballots. Note: Referred to in the EGRS as the ""Ballot Encryption Tool"".", +nyi,4,S4,"EGRI refuses to generate any Preencrypted Ballots unless the Election Manifest indicates Preencrypted Ballots are enabled.", +nyi,4,S4,"EGRI allows to record Voter Selections made on Preencrypted Ballots, if the Election Manifest indicates Preencrypted Ballots are enabled.", +nyi,4,S4,"If the Election Manifest indicates Preencrypted Ballots are not enabled, EGRI refuses to record any Voter Selections made on Preencrypted Ballots.", +nyi,4,S4,"For each Preencrypted Ballot generated, EGRI computes the Selection Encryption Identifier `id_B` as described in (Ref: S3.3.2.a).", +nyi,4,S4,"For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32 (Ref: S3.3.2.a).", +nyi,4,S4.1,"pg. 57 EGRI `Preencrypted Ballot`, for every Contest in the BallotStyle, for every Contest Option plus an additional null selection, computes a vector `Psi_{i,m}` of length `m`, where `m` is equal to the number of Contest Options and `i` is the Contest Option index. Note1: This is captial `Psi`. Note2: Although `m` is denoted here as a subscript, it is not an index into a second dimension.", +nyi,4,S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` for `0 <= i <= m`, where `m` is equal to the number of Contest Options, `i = 0` corresponds to the null vote, and for `1 <= i` `i` is the corresponding Option Index.", +nyi,4,S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `1 <= i` as a length `m` vector having an encryption of `1` at (1-based) position `i`, and encryptions of `0` at all other options. Note: This corresponds to non-null vote cases.", +nyi,4,S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `i = 0` as a length `m` vector of encryptions of `0`. Note: This corresponds to the null vote case.", +nyi,4,S4.1,"pg. 57 Note: EGRI `PreencryptedBallot`, when encrypting ContestOption vectors `Psi_{j,m}`, the encryption nonce `xi_{i,j}` is derived from `H_I` and `xi_B` as specified in eq. 121 (Ref: S4.2.1).", +nyi,4,S4.1,"pg. 57 For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in [eq. 121 pg. 61].", +nyi,4,S4.1.1,"pg. 58 EGRI computes the Selection Hash `psi_i` for a single Option `i` according to [eq. 115 pg. 58]", +nyi,4,S4.1.1,"pg. 58 ""In a contest with a selection limit of L, an additional L null vectors are hashed"" ""where all $E_i = (\alpha_i, \beta_i)$ are encyptions of zero and `1 <= l <= L`.""", +nyi,4,S4.1.1,"pg. 58 EGRI computes the Selection Hash `psi_{m+l}` for an additional L null vector according to [eq. 114 pg. 58]", +nyi,4,S4.1.2,"pg. 58 EGRI sorts all Selection Hashes in a contest prior to their use in the computation of the Contest Hash.", +nyi,4,S4.1.2,"pg. 58 EGRI computes the Contest Hash `chi_l` according to [eq. 115 pg. 58]", +nyi,4,S4.1.3,"pg. 58 EGRI computes the Confirmation Code `H_C` according to [eq. 116 pg. 58]", +nyi,4,S4.1.4,"pg. 59 EGRI computes Ballot Chaining values for Preencrypted Ballots according to eq. 117-120 pg. 59.", +nyi,4,S4.1.5.a,"pg. 59 If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI requires that the Election Manifest specify unambiguously the Hash Trimming Function `Omega` to be used for all Preencrypted Ballots generated for the election.", +nyi,4,S4.1.5.a,"pg. 59 EGRI provides one or more Hash Trimming Functions.", +nyi,4,S4.1.5.b,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides short codes to indicate undervotes", +nyi,4,S4.2,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides a ""Ballot Encrypting Tool"" which operates as describedin S4.2", +nyi,4,S4.2.1,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI produces nonces deterministically as described in eq. 121", +nyi,4,S4.3,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.3.1,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.4,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.4.1,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.5.a,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.5.b.verif15,"TODO: Verification 15", +ics,50,S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Two Hex Characters` hash-trimming function `Ω_1`.", +ics,50,S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Four Hex Characters` hash-trimming function `Ω_2`.", +ics,50,S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Letter-Digit` hash-trimming function `Ω_3`.", +ics,50,S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Digit-Letter` hash-trimming function `Ω_4`.", +ics,50,S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 0-255` hash-trimming function `Ω_5`.", +ics,50,S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 1-256` hash-trimming function `Ω_6`.", +ics,50,S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 100-355` hash-trimming function `Ω_7`.", +ics,50,S4.6,"EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 101-356` hash-trimming function `Ω_8`.", +ics,50,S4.6,"EGRS ElectionManifest PreencryptedBallots feature allows to specify the use of and the complete configuration for an API-user-supplied hash-trimming function.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Letter-Digit` hash-trimming function `Ω_3`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Digit-Letter` hash-trimming function `Ω_4`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 0-255` hash-trimming function `Ω_5`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 1-256` hash-trimming function `Ω_6`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 100-355` hash-trimming function `Ω_7`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 101-356` hash-trimming function `Ω_8`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for producing short codes.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Letter-Digit` hash-trimming function `Ω_3`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Digit-Letter` hash-trimming function `Ω_4`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 0-255` hash-trimming function `Ω_5`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 1-256` hash-trimming function `Ω_6`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 100-355` hash-trimming function `Ω_7`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 101-356` hash-trimming function `Ω_8`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for consuming short codes.", +nfd,2,S5.1.5,"EGRI can read and write files as contiguous sequences of bytes.","" +ace,3,S5.5.1,"This is expected to be a fully-redundant section, but we could double-check it", +ace,3,S5.5.2,"This is expected to be a fully-redundant section, but we could double-check it", +ace,3,S5.5.3,"This is expected to be a fully-redundant section, but we could double-check it", +ace,3,S5.5.4,"This is expected to be a fully-redundant section, but we could double-check it", +ace,3,S5.5.5,"This is expected to be a fully-redundant section, but we could double-check it", +ace,3,S6.2.1.verif1,"TODO: Verification 1", +ace,3,S6.2.2.a.verif2,"TODO: Verification 2", +ace,3,S6.2.2.b.verif3,"TODO: Verification 3", +ace,3,S6.2.3.verif4,"TODO: Verification 4", +ace,3,S6.2.4.a.verif5,"TODO: Verification 5", +ace,3,S6.2.4.b.verif6,"TODO: Verification 6", +ace,3,S6.2.4.c.verif7,"TODO: Verification 7", +ace,3,S6.2.4.d.verif8,"TODO: Verification 8", +ace,3,S6.2.5.a.verif9,"TODO: Verification 9", +ace,3,S6.2.5.b.verif10,"TODO: Verification 10", +ace,3,S6.2.5.c.verif11,"TODO: Verification 11", +ace,3,S6.2.6.a.verif12,"Verification of Correct Contest Data Decryption", +ace,3,S6.2.7.a.verif13,"TODO: Verification 13", +ace,3,S6.2.7.b.verif14,"TODO: Verification 14", +ace,3,S6.2.8.a.verif15,"TODO: Verification 15", +ace,3,S6.2.8.b.verif16,"TODO: Verification 16", +ace,3,S6.2.8.c.verif17,"TODO: Verification 17", +ace,3,S6.2.8.d.verif18,"TODO: Verification 18", +ace,3,S6.2.8.e.verif19,"TODO: Verification 19", diff --git a/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.status-nyi.csv b/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.status-nyi.csv new file mode 100644 index 0000000..2cbed0c --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.xreqs_statuses.status-nyi.csv @@ -0,0 +1,165 @@ +status_code,ordinal,section,xtext,status_note +nyi,4,S2.b,"EGRI enables a Guardian to determine that they have received the public keys and proofs of knowledge of the associated secret keys from all other Guardians, or identify those which have not yet been received.", +nyi,4,S2.b,"EGRI enables a Guardian to check for consistency any shares of other guardians secret keys it has received.", +nyi,4,S2.b,"EGRI enables a Guardian to query or observe which particular shares from all other Guardians it has recieved, decrypted, checked for consistency, and verified, and which are missing, inconsistent, or failed.", +nyi,4,S2.b,"Josh 2025-03-18: ""The code should emit a success or failure indicator that a human can use to verify that the [received key shares] have the required properties."".", +nyi,4,S3.a.a.b,"EGRI enables Guardians to destroy their secret keys.", +nyi,4,S3.1.3.f,"Every Ballot Style defines a label unique across all Ballot Styles in the manifest", +nyi,4,S3.1.3.g,"EGRI may warn if any effective Contest Selection Limit is zero", +nyi,4,S3.1.3.g,"EGRI may warn if any effective Option Selection Limit is zero", +nyi,4,S3.1.3.j,"'Undervoted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limit", +nyi,4,S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification (None currently). The value of this additional Contest Data Field is `1` to record the Undervoted Contest Condition and `0` otherwise.", +nyi,4,S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.", +nyi,4,S3.1.3.j,"If a Contest is specified to record Undervoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.", +nyi,4,S3.1.3.k,"If a Contest is specified to record Undervote Difference Count, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (saturating) difference (i.e., nonnegative) between the effective Contest Selection Limit and the the sum of the voter selections for that Contest. This value will not exceed the effective Contest Selection Limit.", +nyi,4,S3.1.3.k,"If a Contest is specified to record Undervote Difference Count, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.", +nyi,4,S3.1.3.l,"An 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit.", +nyi,4,S3.1.3.l,"Election Manifest allows to specify, for each Contest, whether to record the Overvoted Contest Condition for that contest.", +nyi,4,S3.1.3.l,"If not specified, EGRI does not record the Overvoted Contest Condition.", +nyi,4,S3.1.3.l,"If a Contest is specified to record Overvoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Contest Condition and `0` otherwise.", +nyi,4,S3.1.3.l,"If a Contest is specified to record Overvoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.", +nyi,4,S3.1.3.l,"'Overvoted Option Condition' is a per-contest-option state in which the selection count is strictly greater than the option selection limit", +nyi,4,S3.1.3.l,"Election Manifest allows to specify, for each Contest Option, whether to record the Overvoted Option Condition for that Option.", +nyi,4,S3.1.3.l,"If not specified, EGRI does not record the Overvoted Option Condition.", +nyi,4,S3.1.3.l,"If a Contest Option is specified to record Overvoted Option Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields, any other Contest Data Fields recording Overvoted Option Condition for lower-index Options, and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Option Condition and `0` otherwise.", +nyi,4,S3.1.3.l,"If a Contest is specified to record Overvoted Option Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.", +nyi,4,S3.1.3.m,"'Null Voted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limit", +nyi,4,S3.1.3.m,"Election Manifest allows to specify, for each Contest, whether to record the Null Voted Contest Condition for that Contest.", +nyi,4,S3.1.3.m,"If not specified, EGRI does not record the Null Voted Contest Condition.", +nyi,4,S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Null Voted Contest Condition and `0` otherwise.", +nyi,4,S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.", +nyi,4,S3.1.3.m,"If a Contest is specified to record Null Voted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.", +nyi,4,S3.1.3.n,"Election Manifest allows to specify, for each Contest, whether to record the Total Number Of Write-ins selected for that Contest.", +nyi,4,S3.1.3.n,"If not specified, EGRI does not record the Total Number Of Write-ins.", +nyi,4,S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (nonnegative integer) total number of write-ins the voter selected for that Contest. This value must not exceed the effective Contest Selection Limit.", +nyi,4,S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.", +nyi,4,S3.1.3.n,"If a Contest is specified to record Total Number Of Write-ins, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.", +nyi,4,S3.1.3.n,"EGRI may emit a diagnostic message if any contest specifies a number of write-in fields greater than the contest selection limit", +nyi,4,S3.1.3.n,"Election Manifest allows to specify, for each Contest, the number of write-in fields available to the voter.", +nyi,4,S3.1.3.n,"If a Contest is specified to supply a nonzero number of write-in fields, the Contest has that number of additional 'write-in N' voter-selectable Contest Options.", +nyi,4,S3.1.3.n,"Any such write-in fields are to be labeled by the system as ""write-in N"", where 'N' is the 1-based index value of the write-in field. (This will be greater than the Contest Option Field Index value by the number of non-write-in voter-Selectable Options.)", +nyi,4,S3.1.3.g,"The Election Manifest, for each additional 'write-in N' voter-selectable Contest Option, may specify a nonnegative integer Option Selection Limit with the same defaults and other semantics as non-write-in Contest Options. (ref: S3.1.3.g)", +nyi,4,S3.1.3.n,"""Written-in"" means that the election system has recorded some voter-supplied, non-empty, non-blank data associated with a contest and write-in field index value", +nyi,4,S3.1.3.n,"Every write-in field that *was not* written-in MUST be assigned a value of `0`.", +nyi,4,S3.1.3.n,"Every write-in field that *was* written-in MUST be assigned a value of `1` or greater, and subject to the same rules for Effective Option Selection Limit as other voter-Selectable Options.", +nyi,4,S3.1.3.n,"The value of a write-in field counts against the effective Contest Selection Limit", +nyi,4,S3.1.3.n,"For every Write-in field, an additional Range Proof is recorded that the encrypted value is either `0` or `1`.", +nyi,4,S3.1.3.n,"Josh 2025-03-18: A write-in field can not have an effective Option Selection Limit greater than `1`.", +nyi,4,S3.2.1,"The Election Record contains every Guardian's public key", +nyi,4,S3.6.5.d.verif10,"(Ref: S6.2.5.verif10) Verification 10", +nyi,4,S3.6.5.f.verif11,"(Ref: S6.2.5.verif11) Verification 11", +nyi,4,S3.7,"(Ref: S2.b) ""EGRI enables production of an 'Election Record'""", +nyi,4,S3.7,"The Election Record is not a static fixed format. Different information is added and modified at different times.", +nyi,4,S3.7,"The Election Record format must support appending new and updated information without invalidating signatures made previously.", +nyi,4,S3.7,"The Election Record format must support inclusion-by-reference (eg a Ballot ID and hash value) of batches of Ballots. Rationale: For all but very small elections, it will not be practical to hold all Ballots in a single file. Neither will it be practical to place every Ballot in a separate file in a single directory.", +nyi,4,S3.7,"The Election Record records any information necessary and sufficient to uniquely identify and describe the election, such as date, location, election type, etc. that is not otherwise present in the Election Manifest.", +nyi,4,S3.7,"The Election Record records the Election Manifest", +nyi,4,S3.7,"The Election Record records the Fixed Parameters `p`, `q`, `g`, and `r`.", +nyi,4,S3.7,"The Election Record records the Varying Parameters `n` and `k`.", +nyi,4,S3.7,"The Election Record records `H_P`", +nyi,4,S3.7,"The Election Record records `H_B`", +nyi,4,S3.7,"The Election Record records for every Guardian `1..n`: a name or label", +nyi,4,S3.7,"The Election Record records for every Guardian `1..n`: the polynomial coefficients commitments", +nyi,4,S3.7,"The Election Record records for every Guardian `1..n`: the polynomial coefficients the proofs of knowledge", +nyi,4,S3.7,"The Election Record records for every Guardian `1..n`: the Guardian Communication Public Key", +nyi,4,S3.7,"The Election Record records the Joint Vote Encryption Public Key", +nyi,4,S3.7,"The Election Record records the Joint Ballot Data Encryption Public Key", +nyi,4,S3.7,"The Election Record records `H_E`", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier $\id_B$,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier hash $\HH_I$,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: all of the encrypted selections on each ballot,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the proofs that each such value is an encryption of either zero or one (or more generally, the proofs that these values satisfy the respective option selection limits),", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the effective Contest Selection Limit", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, for every Option Field, the effective Option Selection Limit", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the stated and effective selection limits applied to each contest,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the proof that the number of selections made does not exceed the selection limit,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight as originally submitted with the Voter Selections", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight already applied to the Ciphertexts", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Style index,", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: device information uniquely identifying the device used for encrypting of voter selections to produce the Ballot", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the date and time of the ballot encryption (as reported by the encrypting device) with no more than 1 second granularity", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the confirmation code produced for the Ballot", +nyi,4,S3.7,"The Election Record records for every Ballot in the `Cast` or `Challenged` state: the state of the ballot `Cast` or `Challenged`", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: all the information included for the Ballot in its `Challenged` state", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the selections made for every Contest Option", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the decrypted plaintext values of every Contest (Option or Additional) Data Field", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the proofs of correct decryption for every Contest (Option or Additional) Data Field and/or the Contest Data Field nonce.", +nyi,4,S3.7,"The Election Record records for every Ballot in the `ChallengedDecrypted` state: the date and time of the overall ballot decryption (as reported by the decrypting device)", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: some identifier uniquely identifying the Ballot Decryption Operation", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: the set of Ballots ""marked for decryption""", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: the hash of the ElectionRecord at the point the Ballot Decryption Operation was initiated", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the Guardian Index", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the date and time their participation was initiated", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the hash of their view of the ElectionRecord at the instant this operation was initiated", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: device information uniquely identifying the device used for their secret key operation", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian:", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian:", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation:", +nyi,4,S3.7,"The Election Record records for every Ballot Decryption Operation:", +nyi,4,S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state:", +nyi,4,S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the Guardian Index", +nyi,4,S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the date and time of the partial decryption", +nyi,4,S3.7,"The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: their partial decryptions", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: identifiers of the set of Ballots in the `Cast` state", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the encrypted tally (eq. 79 pg 44)", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the full verifiable decryption of the encrypted tally", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the tally exponent", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: plaintext representation of the tally", +nyi,4,S3.7,"The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: proof of correct decryption of the tally", +nyi,4,S3.7,"The Election Record records information uniquely identifying each device used for encryption of voter selections to produce any Ballot", +nyi,4,S3.7,"The Election Record records an ordered list of the Ballots encrypted by each device.", +nyi,4,S3.7,"The Election Record records the encrypted contest data, when available.", +nyi,4,S3.7,"EGRS enables the Election Record to be produced in a format suitable for digitally signing.", +nyi,4,S3.7,"EGRS enables the Election Record to be published in a format suitable to the needs of Verifier applications. For example, a directory of files accessible via HTTPS supporting efficient random access.", +nyi,4,S3.7,"EGRS enables the Election Record to be published in a format suitable for archiving or bulk downloading by any interested individuals. For example, the compressed .zip and/or .tar.gz formats.", +nyi,4,S4,"EGRI enables ordinary and Preencrypted Ballots to be tallied together.", +nyi,4,S4,"When ordinary and Preencrypted Ballots are tallied together, it does not reveal which votes came from which mode.", +nyi,4,S4,"The Election Manifest contains a configuration setting indicating whether Preencrypted Ballots are enabled for that election.", +nyi,4,S4,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI allows to produce data necessary to print Preencrypted Ballots. Note: Referred to in the EGRS as the ""Ballot Encryption Tool"".", +nyi,4,S4,"EGRI refuses to generate any Preencrypted Ballots unless the Election Manifest indicates Preencrypted Ballots are enabled.", +nyi,4,S4,"EGRI allows to record Voter Selections made on Preencrypted Ballots, if the Election Manifest indicates Preencrypted Ballots are enabled.", +nyi,4,S4,"If the Election Manifest indicates Preencrypted Ballots are not enabled, EGRI refuses to record any Voter Selections made on Preencrypted Ballots.", +nyi,4,S4,"For each Preencrypted Ballot generated, EGRI computes the Selection Encryption Identifier `id_B` as described in (Ref: S3.3.2.a).", +nyi,4,S4,"For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32 (Ref: S3.3.2.a).", +nyi,4,S4.1,"pg. 57 EGRI `Preencrypted Ballot`, for every Contest in the BallotStyle, for every Contest Option plus an additional null selection, computes a vector `Psi_{i,m}` of length `m`, where `m` is equal to the number of Contest Options and `i` is the Contest Option index. Note1: This is captial `Psi`. Note2: Although `m` is denoted here as a subscript, it is not an index into a second dimension.", +nyi,4,S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` for `0 <= i <= m`, where `m` is equal to the number of Contest Options, `i = 0` corresponds to the null vote, and for `1 <= i` `i` is the corresponding Option Index.", +nyi,4,S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `1 <= i` as a length `m` vector having an encryption of `1` at (1-based) position `i`, and encryptions of `0` at all other options. Note: This corresponds to non-null vote cases.", +nyi,4,S4.1,"pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `i = 0` as a length `m` vector of encryptions of `0`. Note: This corresponds to the null vote case.", +nyi,4,S4.1,"pg. 57 Note: EGRI `PreencryptedBallot`, when encrypting ContestOption vectors `Psi_{j,m}`, the encryption nonce `xi_{i,j}` is derived from `H_I` and `xi_B` as specified in eq. 121 (Ref: S4.2.1).", +nyi,4,S4.1,"pg. 57 For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in [eq. 121 pg. 61].", +nyi,4,S4.1.1,"pg. 58 EGRI computes the Selection Hash `psi_i` for a single Option `i` according to [eq. 115 pg. 58]", +nyi,4,S4.1.1,"pg. 58 ""In a contest with a selection limit of L, an additional L null vectors are hashed"" ""where all $E_i = (\alpha_i, \beta_i)$ are encyptions of zero and `1 <= l <= L`.""", +nyi,4,S4.1.1,"pg. 58 EGRI computes the Selection Hash `psi_{m+l}` for an additional L null vector according to [eq. 114 pg. 58]", +nyi,4,S4.1.2,"pg. 58 EGRI sorts all Selection Hashes in a contest prior to their use in the computation of the Contest Hash.", +nyi,4,S4.1.2,"pg. 58 EGRI computes the Contest Hash `chi_l` according to [eq. 115 pg. 58]", +nyi,4,S4.1.3,"pg. 58 EGRI computes the Confirmation Code `H_C` according to [eq. 116 pg. 58]", +nyi,4,S4.1.4,"pg. 59 EGRI computes Ballot Chaining values for Preencrypted Ballots according to eq. 117-120 pg. 59.", +nyi,4,S4.1.5.a,"pg. 59 If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI requires that the Election Manifest specify unambiguously the Hash Trimming Function `Omega` to be used for all Preencrypted Ballots generated for the election.", +nyi,4,S4.1.5.a,"pg. 59 EGRI provides one or more Hash Trimming Functions.", +nyi,4,S4.1.5.b,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides short codes to indicate undervotes", +nyi,4,S4.2,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides a ""Ballot Encrypting Tool"" which operates as describedin S4.2", +nyi,4,S4.2.1,"If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI produces nonces deterministically as described in eq. 121", +nyi,4,S4.3,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.3.1,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.4,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.4.1,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.5.a,"[TODO: Pre-Encrypted Ballots]", +nyi,4,S4.5.b.verif15,"TODO: Verification 15", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Letter-Digit` hash-trimming function `Ω_3`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Digit-Letter` hash-trimming function `Ω_4`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 0-255` hash-trimming function `Ω_5`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 1-256` hash-trimming function `Ω_6`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 100-355` hash-trimming function `Ω_7`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can produce short codes using the `Number: 101-356` hash-trimming function `Ω_8`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for producing short codes.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Letter-Digit` hash-trimming function `Ω_3`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Digit-Letter` hash-trimming function `Ω_4`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 0-255` hash-trimming function `Ω_5`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 1-256` hash-trimming function `Ω_6`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 100-355` hash-trimming function `Ω_7`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature can consume short codes using the `Number: 101-356` hash-trimming function `Ω_8`.", +nyi,4,S4.6,"EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for consuming short codes.", diff --git a/other/spec-todo/TODO-2.1.txt.db.xtodos.csv b/other/spec-todo/TODO-2.1.txt.db.xtodos.csv new file mode 100644 index 0000000..3fb29e9 --- /dev/null +++ b/other/spec-todo/TODO-2.1.txt.db.xtodos.csv @@ -0,0 +1,94 @@ +line_n,section,xtext +19,S0,"Fix references to EGDS in the source code to always include section and page number" +20,S0,"Fix references to EGDS in the source docs to always include section and page number" +22,S0,"Set panic behavior to abort. See https://gitlab.torproject.org/legacy/trac/-/issues/27199" +23,S0,"Remove eg/src/example*.rs files, migrate to resource_producer" +24,S0,"Migrate away from 'anyhow' in library code" +27,S0,"Q|UG left pad as necessary to ensure length is correct" +28,S0,"MAYB|serialize format wrapped in version" +30,S0,"crate feature for guardian secret operations." +32,S0,"ensure SecretCoefficient is serialized in a fixed-length format" +34,S0,"rewrite build-docs script in sh" +36,S0,"Build on Linux32" +37,S0,"Test on Linux32" +38,S0,"Build on Linux64" +39,S0,"Test on Linux64" +40,S0,"Build on Win64" +41,S0,"Test on Win64" +42,S0,"Test library components for Wasm32" +44,S0,"docs/general: style sheet for markdown, ideally match API docs" +46,S0,"If an overvote occurs, the overvote must be captured, encrypted, and never decrypted." +48,S0,"docs/specs/serialization: data formats section" +49,S0,"docs/specs/serialization: standards and references section" +50,S0,"docs/specs/serialization: election manifest section" +51,S0,"docs/specs/serialization: election record section" +52,S0,"docs/specs/serialization: vendor data section" +54,S0,"docs/api: reference NIST CDF where types clearly correspond. E.g., BallotStyle https://github.com/usnistgov/ElectionResultsReporting/blob/nist-pages/index.md#17_0_2_4_78e0236_1389366224561_797289_2360" +56,S0,"docs/implementation guide/Requirements for election systems vendors: complete" +57,S0,"docs/implementation guide/Requirements for verifier app authors: complete" +58,S0,"docs/implementation guide/roles: consider splitting into separate pages: complete" +59,S0,"docs/implementation guide/roles/Election Administrator: complete" +60,S0,"docs/implementation guide/roles/Election Guardians: complete" +61,S0,"docs/implementation guide/roles/Voters: complete" +62,S0,"docs/implementation guide/roles/Political parties and voter-interest organizations: complete" +63,S0,"docs/implementation guide/roles/Journalists and other media: complete" +64,S0,"docs/implementation guide/Hardware requirements/Gurardian secret key storage: complete" +65,S0,"docs/implementation guide/Hardware requirements/Gurardian secret key operations: complete" +66,S0,"docs/implementation guide/step-by-step/Advance preparation: complete" +67,S0,"docs/implementation guide/step-by-step/Key ceremony: complete" +68,S0,"docs/implementation guide/step-by-step/Tally ceremony: complete" +69,S0,"docs/implementation guide/step-by-step/Publishing: complete" +70,S0,"docs/implementation guide/step-by-step/Verification: complete" +71,S0,"docs/implementation guide/step-by-step/Reporting: complete" +73,S0,"docs/api: use correct logo" +74,S0,"docs/api: complete" +76,S0,"docs: complete, #![warn(missing_docs)]" +78,S0,"docs: upload docs to github pages (see compliance notes)" +80,S0,"security review: ensure that no file leaks info through filesize" +82,S0,"distinguish between PartySelection, BallotMeasureSelection, CandidateSelection" +83,S0,"BallotDefinition doc for write-in option" +84,S0,"would be nice to support a PartySelection type vote, rather than rely on the vendor to give us the correct selections explicitly" +86,S0,"a trait for types that have pub fn validate(&Self, &ElectionParameters)" +88,S0,"docs: more investigation into using rust modules to build documentation along with api, observe how cargo_crev does it with the include_str! macro: https://github.com/crev-dev/cargo-crev/blob/master/cargo-crev/src/doc/mod.rs" +89,S0,"docs: more investigation into using mdbook for all project documentation https://github.com/rust-lang/rust/issues/66249" +90,S0,"docs: cargo-external-doc is nice but doesn't support virtual manifests https://github.com/Geal/cargo-external-doc" +92,S0,"VaryingParameters (n, k) This isn't really an 'index' (ordinal), it's a cardinal number. Maybe we need a general purpose ranged number type." +94,S0,"exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%)" +95,S0,"exe: common parameter: manifest file" +96,S0,"exe: common parameter: others" +98,S0,"persist/file: create artifact directories if they don't exist. q: what about permissions on guardian secret directories?" +100,S0,"perisist: encryption or password protection for guaridan secret key files" +101,S0,"util: read guardian secret key, print info, suppressing secrets" +102,S0,"util: read guardian public key, print info" +104,S0,"persist: define standard election directory layout - look at other implementers and users" +105,S0,"design: key file represents its kind: guardian, ..., ?" +107,S0,"Consider structures defined in JSON schema https://github.com/usnistgov/ElectionResultsReporting/blob/version2/NIST_V2_election_results_reporting.json" +109,S0,"if electionguard.exe fails to read or write a file, check the path to see if it has a leading ~. If so, print a good error message." +111,S0,"test on 32-bit target such as x86 or wasm32" +112,S0,"test on big-endian target such as powerpc64-unknown-linux-gnu, s390x-unknown-linux-gnu, riscv64gc-unknown-linux-gnu, or loongarch64-unknown-linux-gnu" +114,S0,"electionguard-test script: incorporate ballots" +115,S0,"electionguard-test script: incorporate tally" +187,S0,"What is a Preencrypted Ballot called after Voter Selections have been applied?" +1404,S3.a.g,"TODO: Do we need separate Administrative (Public|Secret) Key pairs for encryption and signing operations?" +1405,S3.a.g,"TODO: How is it configured which specific other data is to be encrypted with which of the Joint Ballot Data Encryption Public Key and/or Administrative Public Key?" +2456,S3.1.3n,"If the effective Contest Selection Limit is > 1, could someone evade the Option Selection Limit by both selecting and writing-in the same candidate? Similarly if multiple write-in fields are allowed, couldn't they write-in the same choice multiple times?" +2642,S3.2.c,"(Ref: S3.a.a.b) ""EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)""" +2915,S3.2.2.a,"eq8 pg22 refers to ""the secret key for guardian $G_i$"" ""the public key for guardian $G_i$"" Are these keys ever really used as such in v2.1.0 ?" +3488,S3.2.2.o,"EGRI supports a Guardian `l` in complaining to the Election Administrator and all other Guardians. TODO: In practice this likely implies providing a set of data TBD." +5215,S3.5.b.verif9,"TODO: Verification 9" +5340,S3.6.1,"TODO: Presumably this (""enables a Guardian to review the set of Ciphertexts marked for decryption"") requirement applies to any use of the Guardian Ballot Data Encryption Secret Key as well? -> xreq S3.6.1 EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Ballot Data Encryption Secret Key." +5341,S3.6.1,"Does EGRI need to determine whether write-in fields may actually be relevant to an outcome, or just always decrypt all write-in fields with every Tally?" +5342,S3.6.1,"Presumably, many Ballots will not have any write-in fields actually written-in. Does each Ballot record whether it has a write-in field written-in in a way that avoids the need to decrypt the ballot nonce during the Tally if write-in values are needed?" +5780,S3.6.6.a,"""[write-in] data may need to be decrypted if the tallies record a significant number" +6362,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: the partial decryption of the Ballot Nonce" +6363,S3.7,"The Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: for every Contest, for every Option Field, the partial decryption" +6405,S3.7The,"Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the partial decryption from each available Guardian?" +6587,S4.1,"pg. 58 ""in a contest where a voter is allowed to make multiple selections, multiple pre-encryption vectors can be combined to form a single contest encryption vector with multiple encryptions of one that precisely matches the standard \EG format.""" +6588,S4.1,"pg. 58 QUESTION: How are they ""combined""? E.g., are these multiple encryptions combined such that there is still a single vector `Psi_{i,m}` for Option `i`, or are there additional Option vectors? Note: [S4.1.4 pg. 63] says ""For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected"", which seems to imply they are multiplied." +6589,S4.1,"pg. 58 QUESTION: What about when the effective Option Selection Limit is greater than `1`? Do all possible assignments within the effective (Option and Contest) Selection Limits require distinct Selection Vectors and Selection Hashes? E.g. say a Contest has two options and the Contest and Option Selection Limits are all `5`. How is the selection vector constructed which allows the voter to apply a value of `3` to Option 1 and `2` to Option 2?" +6590,S4.1,"pg. 58 QUESTION: How to these ""multiple pre-encryption vectors"" map to ""the j-th selection vector"" and ""the k-th encryption"" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?" +6639,S4.1.1,"pg. 58 QUESTION: S4.1 pg. 57 designates the ""null form"" explicitly as `Psi_{0,m}`, but (eq. 114 pg. 58) refers to them as `psi_{m+l}` where `1 <= l <= L`. How to these multiple pre-encryption vectors map to ""the j-th selection vector"" and ""the k-th encryption"" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?" +6755,S4.1.4,"pg. 59 EGRS says ""A pre-encrypted ballot’s hash will typically be printed directly on the ballot. Ideally, two copies of the ballot hash will be included on each ballot with one remaining permanently with the ballot and the other in an immediately adjacent location on a removable tab. These removable ballot codes are intended to be retained by voters and can be used as an identifier to allow voters to look up their (encrypted) ballots in the election record and confirm that they include the proper short codes as described below.""" +6996,S4.4,"pg. 62 refers to ""every pre-encrypted ballot listed in the election record as uncast"". QUESTION: Does this imply that every `PreencryptedBallot` generated is recorded in the Election Record?" +6997,S4.4,"pg. 63 states that ""For each uncast ballot, the ballot nonce for that ballot is published in the encryption record."" and [S4.5 pg. 64] refers to ""every pre-encrypted ballot listed in the election record as uncast"". QUESTION: Does this imply that there is a point during the tally process at which generated-but-not-cast `PreencryptedBallots` are no longer accepted? If there are multiple Tallys taken, then it seems really important that this only happen at the final Tally. Someone who submitted a PreencryptedBallot that was somehow delayed in the mail probably does not want their Voter Selections made public." +9095,S7,"TODO: Consider any special features which may be needed for RLAs" diff --git a/other/spec-todo/html/functional_reqs.html b/other/spec-todo/html/functional_reqs.html new file mode 100644 index 0000000..ea6d952 --- /dev/null +++ b/other/spec-todo/html/functional_reqs.html @@ -0,0 +1,752 @@ + + + + + + + functional_reqs.html + + + +
+

[Reload]   index.html +
+

EGDS v2.1.0 Functional Requirements with EGRI Statuses

+

[plain: xreqs.html] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
query row ndoc line nsectiontextstatus codestatus ordinalstatus text
1212S1.aEGRI can be used to enable end-to-end (E2E) verifiability.na1Not directly applicable to implementation code
2216S1.aEGRI can be used to enable privacy-enhanced risk-limiting audits (RLAs).na1Not directly applicable to implementation code
3220S1.aEGRI components can be used to empower individual voters to independently verify the accuracy of election results.na1Not directly applicable to implementation code
4240S1.bEGRI allows individual voters to verify that their votes have been accurately recorded.0
5244S1.bEGRI allows voters to verify that all recorded votes have been accurately counted.0
6248S1.bEGRI allows observers to verify that all recorded votes have been accurately counted.0
7252S1.bEGRI provides an E2E-verifiable tally which can be used as the primary tally in an election.0
8256S1.bEGRI provides an E2E-verifiable tally which can be used as a verifiable secondary tally alongside traditional methods.0
9260S1.bEGRI is compatible with in-person voting using an electronic ballot-marking device.na1Not directly applicable to implementation code
10264S1.bEGRI is compatible with in-person voting using an optical scanner capable of reading hand-marked ballots.na1Not directly applicable to implementation code
11268S1.bEGRI is compatible with in-person voting using an optical scanner capable of reading machine-marked ballots.na1Not directly applicable to implementation code
12272S1.bEGRI is compatible with voting by mail.na1Not directly applicable to implementation code
13276S1.bEGRI is compatible with Internet voting.na1Not directly applicable to implementation code
14295S1.cEGRI enables public disclosure of encrypted ballots that can be matched directly to physical ballots selected for RLA auditing.0
15299S1.cEGRI can prove (or fail to prove) that publicly disclosed EG ballots match reported talliesace3Applicable to implementation code, but more directly covered elsewhere
16303S1.cEGRI can prove (or fail to prove) that physical ballot records used in an RLA are those produced by the votersna1Not directly applicable to implementation code
17311S1.dEGRI provides a "detailed implementation specification" and/or qualifies as a "well-documented ElectionGuard implementation"ace3Applicable to implementation code, but more directly covered elsewhere
18315S1.dEGRI can be used by independent parties to write ElectionGuard verifiers to confirm the consistency of election artifacts with announced election results.na1Not directly applicable to implementation code
19319S1.dEGRI can be used by independent parties to write verifiers to confirm (or refute) the consistency of election artifacts with announced election resultsna1Not directly applicable to implementation code
20350S2.aA 'Guardian' role exists with certain duties.na1Not directly applicable to implementation code
21354S2.aEGRI enables a Guardian to protect confidentiality of votes.na1Not directly applicable to implementation code
22358S2.aEGRI enables members of a canvassing board to serve as Guardians.na1Not directly applicable to implementation code
23362S2.aEGRI enables Guardians to manage cryptographic keys.0
24366S2.aCompromised human Guardians cannot compromise the integrity of the election tallies.0
25370S2.aCompromised "hardware Guardians" cannot compromise the integrity of the election tallies.0
26374S2.aEGRI enables Guardians to work together to form a public encryption key for homomorphically-tallied vote encryption.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
27379S2.aEGRI enables Guardians to work together to form a public encryption key for non-homomorphically-tallied ballot data.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
28384S2.a[x]EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.0
29388S2.aEGRI allows a quorum of guardians to produce all artifacts required to enable public verification of the tally.0
30392S2.aNo set of guardians fewer than a quorum are able to produce the artifacts required to enable public verification of the tally.ace3Applicable to implementation code, but more directly covered elsewhere
31396S2.a% xnote S3.7 Josh 2025-02-25, 2025-03-18: Out of scope for EGRI: An 'Election Administrator' role existsna1Not directly applicable to implementation code
32400S2.aNote: EG defines protocolsace3Applicable to implementation code, but more directly covered elsewhere
33404S2.aNote: EG defines proceduresace3Applicable to implementation code, but more directly covered elsewhere
34432S2.bEGRI enables a Guardian to generate its own Vote Encryption (Public|Secret) Key pair prior to the start of voting or RLA.0
35436S2.bEGRI enables a Guardian to generate its own Ballot Data Encryption (Public|Secret) Key pair prior to the start of voting or RLA.0
36440S2.bEGRI enables a Guardian to generate its own Guardian Communication (Public|Secret) Key pair pair prior to the start of voting or RLA.0
37444S2.bEGRI enables Guardians to employ their Guardian Communication Key pairs to communicate privately with other guardians to exchange information about secret keys.0
38448S2.bEGRI enables Guardians to keep their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys and Key Shares secret.0
39452S2.bEGRI enables Guardians to perform operations using their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys and Key Shares without leaking or exposing them, to the extent practical within the constraints of hardware/os platform.0
40456S2.bEGRI enables potential integration of dedicated hardware tokens or devices with which Guardians could to perform operations using their (Vote Encryption|Ballot Data Encryption|Guardian Communication) Secret Keys.0
41460S2.bEGRI enables production of an 'Election Record' even if not all guardians are available at that time with0
42465S2.b(=~ S2.a[x]): EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.0
43469S2.bGuardians participate in a 'Key Generation Ceremony'.ace3Applicable to implementation code, but more directly covered elsewhere
44473S2.bAt the beginning of the Key Generation Ceremony, EGRI enables each guardian to publish its public keys and proofs of knowledge of the associated secret keys.ace3Applicable to implementation code, but more directly covered elsewhere
45477S2.bAt the beginning of the Key Generation Ceremony, EGRI enables each Guardian to receive the published keys and proofs of knowledge of the associated secret keys from the other Guardians.ace3Applicable to implementation code, but more directly covered elsewhere
46481S2.bEGRI enables a Guardian to determine that they have received the public keys and proofs of knowledge of the associated secret keys from all other Guardians, or identify those which have not yet been received.nyi4Not yet implemented
47485S2.bEGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Vote Encryption key.ics50Implementation code exists covering a portion of code paths considered substantial
48489S2.bEGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key an InterGuardianShare for the Ballot Data Encryption key.ics50Implementation code exists covering a portion of code paths considered substantial
49493S2.bEGRI enables a Guardian to send the encrypted shares of each of its own (two, non-Communication) secret keys to the other Guardian corresponding to the Guardian Communication Public Key to which it was encrypted.0
50497S2.bEGRI enables a Guardian to use its own Guardian Communication Secret Key to decrypt any shares of other guardians secret keys that it has received.0
51501S2.bEGRI enables a Guardian to check for consistency any shares of other guardians secret keys it has received.nyi4Not yet implemented
52505S2.bEGRI enables a Guardian to query or observe which particular shares from all other Guardians it has recieved, decrypted, checked for consistency, and verified, and which are missing, inconsistent, or failed.nyi4Not yet implemented
53509S2.bEGRI enables a Guardian to determine that it has received, decrypted, checked for consistency, and verified shares from all other Guardians.0
54513S2.bJosh 2025-03-18: "The code should emit a success or failure indicator that a human can use to verify that the [received key shares] have the required properties.".nyi4Not yet implemented
55518S2.bEGRI can emit information sufficicient to enable an "all greens on a dashboard"-type display for a Guardian to observe the (received, decrypted, checked for consistency, and verified) state of shares from the other Guardians.0
56522S2.bEGRI can emit a formatted message to enable a Guardian to "announce its completion" once it it has received, checked for consistency, and verified shares from all other Guardians.0
57526S2.bAt the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key.0
58530S2.bAt the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key.0
59534S2.bEGRI enables a Guardian to notice the irregularity of a failed share verification.0
60538S2.bEGRI enables a Guardian to notice the irregularity of a failed proof verification.0
61542S2.bEGRI enables a Guardian to notice the irregularity of an inconsistent set of keys.0
62546S2.bEGRI enables a Guardian to "complain and halt the protocol" subsequent to noticing a failed share verification, a failed proof verification, or an inconsistent set of keys. (Is this published? A communications key message?)0
63550S2.bEGRI enables, supports, or facilitates the initiation of a 'Key Ceremony Irregularity Investigation' subsequent to a Guardian complaint of irregularity.0
64554S2.bSufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to distinguish between an 'Actively Misbehaving Guardian' and any other type of error.0
65558S2.bSufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to identify an Actively Misbehaving Guardian.0
66562S2.bSufficient data, documentation, tools, and/or other resources are available to enable a Key Ceremony Irregularity Investigation to effectively troubleshoot errors other than an Actively Misbehaving Guardian.0
67566S2.bSufficient data, documentation, tools, and/or other resources are available to enable the Key Ceremony Irregularity Investigation to effectively troubleshoot multiple simultaneous errors, potentially of different types.0
68570S2.bEGRI allows to remove a Guardian subsequent to a Key Ceremony Irregularity Investigation which has identified it as an 'Actively Misbehaving Guardian'.0
69574S2.bEGRI allows to restart the Key Generation Ceremony protocol from scratch subsequent to an Investigation which has identified and removed an Actively Misbehaving Guardian.0
70578S2.bTODO? what if its another type of error? Can you just restart whenever? Any of this published?0
71582S2.bTODO? EGRI enables a Guardian to publish the ciphertext of any encrypted secret key shares that it sent.0
72586S2.bTODO? EGRI enables a Guardian to publish the ciphertext of any encrypted secret key shares that it received.0
73590S2.bTODO? EGRI enables a Guardian to publish the plaintext corresponding to the ciphertext of any encrypted secret key shares that it sent? Only subsequent to the initiation of Key Ceremony Irregularity Investigation?0
74594S2.bTODO? EGRI enables a Guardian to publish the plaintext decrypted from the ciphertext of any encrypted shares that it received? Only subsequent to the initiation of Key Ceremony Irregularity Investigation?0
75598S2.bNote: The Guardians and Election Administrator together have a duty to successfully complete key generation and conclude the Key Generation Ceremony.0
76620S2.c.a[i]After a voter completes the process of making selections, the election system can make a single call to the EGRI API with the voter selections to encrypt and record the EG Ballot and return a confirmation code for the voter.0
77624S2.c.aOther than the single call to the EGRI API described in (S2.c.a[i]), there is no other point at which an existing election system must interface with EGRI.0
78629S2.c.aAn RLA system can make the single call to the EGRI API described in (S2.c.a[i]), but any confirmation code returned may be discarded.0
79637S2.c.bEGRI can accept can accept voter selections in batches directly. This is to support certain vote-by-mail scenarios and RLA.0
80641S2.c.bEGRI records which Ballots have been decrypted ensures that they can never be "cast" or participate in any future Tally. Note that this only applies to homomorphically tallied fields and not, say, decrypting the Ciphertext of a non-homomorphically tallied write-in text field.0
81645S2.c.bA decrypted ballot should never be 'Cast'. Josh 2025-03-19: Univeral knowledge of all challenged ballots is not required, just checking whatever local store is available is sufficient.0
82649S2.c.bImplementation Note: After obtaining their confirmation code, voters should have an opportunity to 'challenge' their ballots in order to view the decrypted selections of their EG Ballot to verify that the encryptions were correct. The voter must be informed that the challenged ballot and decrypted selections will eventually be published as part of the public Election Record.0
83653S2.c.bImplementation note: In an RLA, presumably the non-EG ballots were already cast. Some of the resulting EG Ballots may be anonymized, challenged, and decrypted.0
84660S2.c.cEGRI produces EG Ballots that contain encrypted voter selections.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
85665S2.c.cEGRI produces EG Ballots that may contain encrypted additional data fields for some or all Contests.0
86669S2.c.cEGRI produces EG Ballots that contain a Ballot Nonce, encrypted to the Joint Ballot Data Encryption Public Key.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
87674S2.c.cEGRI records the EG Ballots for later publication as part of the Election Record.0
88678S2.c.cEGRI produces EG Ballots that non-interactive zero-knowledge (NIZK) proofs of their well-formedness.0
89682S2.c.cThere can exist no well-formed ballot which is not legitimate, by definition.0
90686S2.c.cEGRI produces EG Ballots with NIZK proofs proving that, for each contest option, no more than the allowed number of votes were recorded. (Option Selection Limit)0
91690S2.c.cEGRI produces EG Ballots with NIZK proofs proving that, for each contest, no more than the total allowed number of votes were recorded. (Contest Selection Limit)0
92694S2.c.cEGRI produces EG Ballots with Contests having additional data fields may contain NIZK proofs similar to those of voter selectable options or other properties.0
93698S2.c.cEGRI allows to combine EG Ballots into an Aggregate Ballot.0
94702S2.c.cEGRI allows to combine EG Ballots into a single Aggregate Ballot.0
95706S2.c.cAn Aggregate Ballot formed by combining EG Ballots contains, for every option in every contest, the tally for that option encrypted to the Joint Vote Public Key.0
96729S2.dEach guardian can compute from the previously shared secret key fragments a share of the secret decryption key0
97733S2.dGuardians can independently use their share of the secret decryption key to jointly decrypt the election tallies0
98737S2.dGuardians can independently use their share of the secret decryption key to generate associated verification data0
99741S2.dOnly a quorum of guardians is sufficient to complete decryption0
100745S2.dOnly a quorum of guardians is sufficient to generate the verification data0
101769S2.e.aAn observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the well-formedness of every EG Ballot.0
102773S2.e.aAn observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the well-formedness the correct aggregation of EG Ballots.0
103777S2.e.aAn observer can use EGRS, EGRI, and/or accompanying materials to independently implement an election verifier that can confirm the accurate decryption of election tallies.0
104805S2.e.bA verifier can be independently implemented which performs the Guardian Key verification steps S6.2.2.verif2 and S6.2.2.verif3 .0
105809S2.e.bA verifier can be independently implemented that fully verifies the integrity of an Election Record without performing either of the Guardian Key verification steps S6.2.2.verif2 and S6.2.2.verif3 .0
106835S2.e.cA verifier can be independently implemented which performs the EG Ballot correctness verification steps S6.2.7.verif13, S6.2.7.verif14, S6.2.8.verif17, S6.2.8.verif18, and S6.2.8.verif190
107861S2.e.dA verifier can be independently implemented which performs the Election Record verification steps S6.2.1.verif1, S6.2.3.verif4, S6.2.4.verif5, S6.2.4.verif6, S6.2.4.verif7, S6.2.4.verif8, S6.2.5.verif9, S6.2.5.verif10, S6.2.5.verif11, S6.2.6.verif120
108907S3.a.a.aEGRI enables selecting Fixed Parameters in advance of an electionutsp75Unit tests exercising a portion of code paths considered sufficient are passing
109912S3.a.a.aEGRI enables using the same Fixed Parameters across multiple electionsna1Not directly applicable to implementation code
110916S3.a.a.aEGRI supplies a standard set of Fixed Parametersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
111921S3.a.a.aEGRI enables defining Contestsutsp75Unit tests exercising a portion of code paths considered sufficient are passing
112926S3.a.a.aEGRI enables defining selectable Contest Optionsutsp75Unit tests exercising a portion of code paths considered sufficient are passing
113931S3.a.a.aEGRI enables defining Ballot Stylesutsp75Unit tests exercising a portion of code paths considered sufficient are passing
114940S3.a.a.b(ref: S2.b) EGRI enables Guardians to generate individual public-secret key pairsics50Implementation code exists covering a portion of code paths considered substantial
115944S3.a.a.b(ref: S2.b) EGRI enables Guardians to exchange shares of secret keysics50Implementation code exists covering a portion of code paths considered substantial
116948S3.a.a.bEGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)0
117952S3.a.a.bEGRI requires that a complete new set of keys must be generated if even a single guardian is replaced (TODO: How?)0
118956S3.a.a.bEGRI enables Guardians to destroy their secret keys.nyi4Not yet implemented
119960S3.a.a.bJosh 2025-03-18: "There is a point where the rejection of the keys can happen"0
120971S3.a.a.c[No specific requirement]na1Not directly applicable to implementation code
121979S3.a.a.d(ref: S2.c.c) Guardians can use their secret key shares to jointly produce election tallies together with verifiable artifacts that prove that the tallies are correct.0
1221032S3.a.c.aEncryption of votes in ElectionGuard is performed using the DPP vote encryption method of Devillez, Pereira, and Peters (2022)0
1231039S3.a.c.bEGRI ensures that P is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
1241044S3.a.c.bEGRI ensures that Q is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
1251049S3.a.c.bEGRI ensures that G is fixed before the (ref: S.2.b) Key Generation Ceremony. Note that this may be a simple comparison with the standard parameter value.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
1261054S3.a.c.bEGRI verifies that P is prime before any processing. Note that this may be a simple comparison with the standard parameter value.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
1271059S3.a.c.bEGRI verifies that Q is prime before any processing. Note that this may be a simple comparison with the standard parameter value.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
1281064S3.a.c.bEGRI verifies that Q is is not a divisor of r=(p-1)/q before any processing. Note that this may be a simple comparison with the standard parameter value.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
1291069S3.a.c.bEGRI verifies that G is a generator $g$ of the order $q$ subgroup $\Z_p^r$ before any processing. Note that this may be a simple comparison with the standard parameter value.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
1301077S3.a.c.cEGRI enables a Guardian to (ref: S.2.b) generate its own Vote or Ballot Data Encryption Secret Key by selecting a random $s\in\Z_q$.0
1311081S3.a.c.cEGRI enables a Guardian to (ref: S.2.b) derive its Vote or Ballot Data Encryption Public Key as $K=g^s \bmod p$.0
1321085S3.a.c.cEGRI enables a Guardian to (ref: S.2.b) publish its Vote or Ballot Data Encryption Public Key.0
1331089S3.a.c.cNo entity is ever in possession of a secret key that can be used to decrypt votes0
1341102S3.a.c.dValues are encrypted to a Vote or Ballot Data Encryption Public Key by selecting a random nonce $\xi\in\Z_q$ and forming the pair $(\alpha, \beta) = (g^\xi \bmod p, K^m\cdot K^\xi \bmod p) = (g^\xi \bmod p, K^{m+\xi} \bmod p)$ .0
1351113S3.a.c.eCiphertexts encrypted to a Vote or Ballot Data Encryption Public Key can be decrypted using the corresponding Secret Key $s$ as $\beta/\alpha^s\bmod p = K^m \bmod p$ using exhaustive search, a pre-computed table, or Shanks's baby-step giant-step method.0
1361117S3.a.c.eThe value of $K^m$ can be computed from the encryption nonce $\xi$0
1371121S3.a.c.eThe value of $m$ can be computed from the encryption nonce $\xi$0
1381125S3.a.c.eAn encryption of one is used to indicate that an option is selected0
1391129S3.a.c.eAn encryption of zero is used to indicate that an option is not selected0
1401164S3.a.dAll the encryptions of a single option across ballots can be multiplied to form an encryption of the sum of the individual values0
1411168S3.a.dAn encryption of some integer other than `0` or `1` is used for certain voting methods0
1421172S3.a.dThe product of the encryptions of a single contest option across ballots is an encryption of the tally (S2.c.c)0
1431176S3.a.dEvery option can be demonstrated to be an encryption of either `0` or `1` (for typical voting methods)0
1441180S3.a.dEvery option can be demonstrated to be an encryption of some integer other than `0` or `1` (for certain voting methods)0
1451184S3.a.dThe product of the encrypted options of a single ballot can be used to show that no more options were selected than permitted0
1461188S3.a.dWhen an option can receive multiple or weighted votes, the product of the encrypted options of a single ballot can be used to show that only the permitted number of votes or permitted sum of weights were used0
1471192S3.a.dA holder of a nonce $\xi$ can prove to a third party that a pair $(\alpha, \beta)$ is an encryption of $m$ without access to the secret $s$ or revealing the nonce $\xi$0
1481221S3.a.eEGRI enables the use of NIZK proofs to demonstrate that keys are properly chosen.0
1491225S3.a.eEGRI enables the use of NIZK proofs to demonstrate that ballots are well-formed.0
1501229S3.a.eEGRI enables the use of NIZK proofs to demonstrate that decryptions match claimed values.0
1511252S3.a.f.aEGRI enables the use of threshold encryption for encryption of ballots and other data.0
1521256S3.a.f.a(ref S2.b) Combining individual Guardian (Vote|Ballot Data) Public Keys into a single Joint (Vote|Ballot Data) Public Key.0
1531260S3.a.f.aThreshold encryption offers a homomorphic property that allows individual encrypted votes to be combined to form encrypted tallies.0
1541267S3.a.f.bRef S2.b: "EGRI enables a Guardian to generate its own public-secret (Vote|Ballot Data) Encryption Key pair..."0
1551271S3.a.f.bRef S2.b: "At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian (Vote|Ballot Data) Encryption Public keys to form the Joint (Vote|Ballot Data) Encryption Public Key."0
1561275S3.a.f.b(=~ S2.a[x]): EGRI uses a Joint Vote Encryption Public Key and a Joint Ballot Data Encryption Public Key formed by the Guardians working together to encrypt individual ballots.0
1571282S3.a.f.cEGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field.0
1581286S3.a.f.cEGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field.0
1591293S3.a.f.d(Ref: S2.b) Key Ceremony0
1601297S3.a.f.d(Ref: S3.a.f.c) "EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field."0
1611301S3.a.f.d(Ref: S3.a.f.c) "EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field."0
1621326S3.a.gEGRI provides a non-homomorphic encryption method known as "Hashed ElGamal" for encrypting data other than votes0
1631330S3.a.gEGRI provides a signature method known as "Signed Hashed ElGamal" for encrypting data other than votes0
1641335S3.a.gEGRI enables a user to generate a Hashed ElGamal (Public|Secret) Key pair for Hashed ElGamal encryption0
1651339S3.a.gEGRI enables a user to encrypt data to a Hashed ElGamal Public Key0
1661343S3.a.gAny data encrypted to a Hashed ElGamal Public Key cannot be decrypted without the Hashed ElGamal Secret Key0
1671347S3.a.gEGRI enables using a Hashed ElGamal Secret Key to decrypt data encrypted to the corresponding Hashed ElGamal Public Key0
1681351S3.a.gHashed ElGamal encryption does not have the data size restrictions imposed by the vote encryption0
1691355S3.a.gHashed ElGamal encryption uses a KDF based on HMAC-SHA-2-256 to generate a key stream XORed with the data0
1701359S3.a.gEGRI enables a user to generate a Signing Hashed ElGamal (Public|Secret) Key pair for Signing data encrypted with Hashed ElGamal0
1711363S3.a.gEGRI enables using a Signing Hashed ElGamal Secret Key to sign data that has been encrypted with Hashed ElGamal0
1721367S3.a.gEGRI enables using a Signing Hashed ElGamal Public Key to verify or refute a signature made with Signed Hashed ElGamal0
1731373S3.a.g(Ref: S2.b) "EGRI enables a Guardian to encrypt to another guardian's Guardian Communication Public Key a share of each of its own (two, non-Communication) secret keys."0
1741377S3.a.gEGRI allows to encrypt other (non-homomorphically-tallied) Ballot data to the Joint Ballot Data Encryption Public Key0
1751381S3.a.gEGRI allows to encrypt voter selection contest write-in Ballot data to the Joint Ballot Data Encryption Public Key0
1761385S3.a.gEGRI allows to encrypt other auxiliary data attached to an encrypted Ballot to the Joint Ballot Data Encryption Public Key0
1771392S3.a.gEGRI allows to encrypt voter selection contest write-in Ballot data to the Administrative Public Key.0
1781396S3.a.gEGRI allows to encrypt other auxiliary data attached to an encrypted Ballot to the Administrative Public Key.0
1791400S3.a.gEGRI allows to encrypt other (non-homomorphically-tallied) Ballot data to the Administrative Public Key.0
1801428S3.1EGRI uses integer-based encryption0
1811432S3.1EGRI does not use elliptic curves0
1821436S3.1The encryption scheme used to encrypt votes is defined by primes p and q.0
1831440S3.1ref:S3.a.c.b "EGRI verifies that (P|Q|G) is prime before any processing..."0
1841548S3.1.1.aThe value of p matches the specific value given in EG DS v2.1.0 S3.1.10
1851552S3.1.1.aThe value of q matches the specific value given in EG DS v2.1.0 S3.1.10
1861556S3.1.1.aThe value of r matches the specific value given in EG DS v2.1.0 S3.1.10
1871560S3.1.1.aThe value of g matches the specific value given in EG DS v2.1.0 S3.1.10
1881583S3.1.1.b.n3_1Nonstandard parameters are outside the scope of these requirements and thus not permitted.0
1891587S3.1.1.b.n3_1EGDS must reject any data bearing non-standard parameters.0
1901617S3.1.2Varying Parameter n is the number of Guardians0
1911621S3.1.2Varying Parameter k is the number of Guardians required to form a quorum for decryption.0
1921625S3.1.2EGRI computes H_P from Fixed Parameters p, q, and g, and Varying Parameters n and k as specified in EG DS v2.1.0 eq. 40
1931649S3.1.3.aEGRI accepts an Election Manifestutsp75Unit tests exercising a portion of code paths considered sufficient are passing
1941654S3.1.3.aThe Election Manifest is a single fileutsp75Unit tests exercising a portion of code paths considered sufficient are passing
1951659S3.1.3.aThe Election Manifest describes the modalities of the election and specifies a wide range of data about itutsp75Unit tests exercising a portion of code paths considered sufficient are passing
1961664S3.1.3.aThe Election Manifest contains data that makes it unique for each electionutsp75Unit tests exercising a portion of code paths considered sufficient are passing
1971669S3.1.3.aEGRI rejects any Election Manifest that does not contain data that makes it unique for each electionnfd2Needs further discussion
1981675S3.1.3.aThe Election Manifest lists all contests in an electionutsp75Unit tests exercising a portion of code paths considered sufficient are passing
1991680S3.1.3.aThe Election Manifest for each Contest, specifies the Selectable Options (e.g., candiates)utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2001685S3.1.3.aThe Election Manifest for each Contest, specifies a label unique across all contests in the electionutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2011690S3.1.3.aEGRI rejects any Election Manifest containing duplicate labels for Contestsutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2021696S3.1.3.aThe Election Manifest for each ContestOption in each Contest, specifies a label unique across all Selectable Options in that contestutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2031701S3.1.3.aEGRI rejects any Election Manifest containing duplicate labels for ContestOptions in any Contestutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2041734S3.1.3.bEGRI accepts ElectionManifest labels composed of printable characters and (internal, non-contiguous) 0x20 space charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2051739S3.1.3.bEGRI rejects ElectionManifest labels that contain line break charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2061744S3.1.3.bEGRI rejects ElectionManifest labels that have leading or trailing whitespaceutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2071749S3.1.3.bEGRI rejects ElectionManifest labels that contain contiguous sequences of whitespace other than a single 0x20 spaceutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2081754S3.1.3.bEGRI rejects ElectionManifest labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2091759S3.1.3.bEGRI rejects ElectionManifest labels having no printable charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2101764S3.1.3.bEGRI rejects ElectionManifest labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)ics50Implementation code exists covering a portion of code paths considered substantial
2111770S3.1.3.bEGRI accepts Contest labels composed of printable characters and (internal, non-contiguous) 0x20 space charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2121775S3.1.3.bEGRI rejects Contest labels that contain line break charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2131780S3.1.3.bEGRI rejects Contest labels that have leading or trailing whitespaceutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2141785S3.1.3.bEGRI rejects Contest labels that contain contiguous sequences of whitespace other than a single 0x20 spaceutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2151790S3.1.3.bEGRI rejects Contest labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2161795S3.1.3.bEGRI rejects Contest labels having no printable charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2171800S3.1.3.bEGRI rejects Contest labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)ics50Implementation code exists covering a portion of code paths considered substantial
2181805S3.1.3.bEGRI accepts ContestOption labels composed of printable characters and (internal, non-contiguous) 0x20 space charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2191810S3.1.3.bEGRI rejects ContestOption labels that contain line break charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2201815S3.1.3.bEGRI rejects ContestOption labels that have leading or trailing whitespaceutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2211820S3.1.3.bEGRI rejects ContestOption labels that contain contiguous sequences of whitespace other than a single 0x20 spaceutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2221825S3.1.3.bEGRI rejects ContestOption labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2231830S3.1.3.bEGRI rejects ContestOption labels having no printable charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2241835S3.1.3.bEGRI rejects ContestOption labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)ics50Implementation code exists covering a portion of code paths considered substantial
2251840S3.1.3.bEGRI accepts BallotStyle labels composed of printable characters and (internal, non-contiguous) 0x20 space charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2261845S3.1.3.bEGRI rejects BallotStyle labels that contain line break charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2271850S3.1.3.bEGRI rejects BallotStyle labels that have leading or trailing whitespaceutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2281855S3.1.3.bEGRI rejects BallotStyle labels that contain contiguous sequences of whitespace other than a single 0x20 spaceutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2291860S3.1.3.bEGRI rejects BallotStyle labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs)utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2301865S3.1.3.bEGRI rejects BallotStyle labels having no printable charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
2311870S3.1.3.bEGRI rejects BallotStyle labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)ics50Implementation code exists covering a portion of code paths considered substantial
2321891S3.1.3.cIndex values are integers in the range 1 <= i < 2^31utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2331914S3.1.3.dEvery Contest in the Election Manifest is uniquely identified by a Contest Index value assigned starting at 1 and continuing in the order defined in the Election Manifest.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2341937S3.1.3.eEvery Selectable Option in every Contest is identified uniquely (within the Contest) by an Option Index value assigned starting at 1 (anew for each Contest) and continuing in the order defined in the in the Election Manifest.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2351942S3.1.3.eIf additional data fields are present for a Contest, they are assigned Indices continuing after the Selectable Options.ics50Implementation code exists covering a portion of code paths considered substantial
2361964S3.1.3.fThe Election Manifest defines a single ordered list of all Ballot Styles in the election.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2371969S3.1.3.fEvery Ballot Style in the Election Manifest is uniquely identified by a Ballot Style Index value assigned starting at 1 and continuing in the order defined in the Election Manifest.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2381975S3.1.3.fThe first Ballot Style within the election manifest has index value 1.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2391980S3.1.3.fEvery Ballot Style defines a label unique across all Ballot Styles in the manifestnyi4Not yet implemented
2401984S3.1.3.fEvery Ballot Style defines a single list of Contest Indices.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2411989S3.1.3.fOrder of occurence is not significant within a Ballot Style Contest Index list.ics50Implementation code exists covering a portion of code paths considered substantial
2421993S3.1.3.fEGRI rejects any Election Manifest having a Ballot Style with a Contest Index list containing a Contest Index that does not refer to a contest in the Election Manifest.uts65Unit tests exercising a portion of code paths considered sufficient exist
2431998S3.1.3.fEGRI rejects any Election Manifest listing the same Contest Index more than once within the same Ballot Style. (It is common for a Contest Index to be listed in multiple Ballot Styles.)sci0
2442020S3.1.3.gThe Election Manifest, for each Contest, may specify a nonnegative integer Contest Selection Limit.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2452025S3.1.3.gFor any Contest for which a Contest Selection Limit is not specfied, an effective value of 1 is used as the default.ics50Implementation code exists covering a portion of code paths considered substantial
2462029S3.1.3.gThe effective Contest Selection Limit limits the maximum total value (i.e., number of votes or voter selections) that can be assigned over all Selectable Options in that contest.ics50Implementation code exists covering a portion of code paths considered substantial
2472033S3.1.3.gEGRI may warn if any effective Contest Selection Limit is zeronyi4Not yet implemented
2482038S3.1.3.gThe Election Manifest, for each Selectable Option in each Contest, may specify a nonnegative integer Option Selection Limit.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2492043S3.1.3.gFor any Selectable Option for which a Option Selection Limit is not specfied, an effective value of 1 is used as the default.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2502048S3.1.3.gEGRI may warn if any effective Option Selection Limit is zeronyi4Not yet implemented
2512052S3.1.3.gThe effective Option Selection Limit limits the maximum value (i.e., number of votes) that can be assigned to that particular Selectable Option.ics50Implementation code exists covering a portion of code paths considered substantial
2522056S3.1.3.gThe effective Selection Limit limits the maximum value that can be assigned to that Selectable Optionics50Implementation code exists covering a portion of code paths considered substantial
2532060S3.1.3.gIf an effective (specified or defaulted) Option Selection Limit exceeds the effective (specified or defaulted) Contest Selection Limit, EGRI applies the smaller value. EGRI may emit a diagnostic message in this case.ics50Implementation code exists covering a portion of code paths considered substantial
2542064S3.1.3.gIf the sum of all effective (specified or defaulted) Option Selection Limits for a Contest exceeds that Contest's effective (specified or defaulted) Contest Selection Limit, EGRI applies the smaller value. This is an ordinary case for which EGRI should not emit a specific diagnostic message.ics50Implementation code exists covering a portion of code paths considered substantial
2552089S3.1.3.hElection Manifest offers optional data fields for accompanying data.nfd2Needs further discussion
2562094S3.1.3.hElection Manifest allows Accompanying Data Fields to be applied at the top level.nfd2Needs further discussion
2572099S3.1.3.hElection Manifest allows Accompanying Data Fields to be applied to Contests. Note: These are different from Additional Contest Data Fields which may be recorded after the Selectable Option Fields on a Ballot.nfd2Needs further discussion
2582104S3.1.3.hElection Manifest allows Accompanying Data Fields to be applied to Selectable Options.nfd2Needs further discussion
2592109S3.1.3.hElection Manifest allows Accompanying Data Fields to be applied to Ballot Styles.nfd2Needs further discussion
2602114S3.1.3.hElection Manifest Accompanying Data Fields may go beyond short labels.nfd2Needs further discussion
2612119S3.1.3.hElection Manifest Accompanying Data Fields may be non-unique.nfd2Needs further discussion
2622124S3.1.3.hElection Manifest Accompanying Data Fields may describe general information about the election.nfd2Needs further discussion
2632129S3.1.3.hElection Manifest Accompanying Data Fields may describe general information about the jurisdiction.nfd2Needs further discussion
2642134S3.1.3.hElection Manifest Accompanying Data Fields may describe general information about the election device manufacturers.nfd2Needs further discussion
2652139S3.1.3.hElection Manifest Accompanying Data Fields may describe general information about software versions.nfd2Needs further discussion
2662144S3.1.3.hElection Manifest Accompanying Data Fields may describe general information about the election date.nfd2Needs further discussion
2672149S3.1.3.hElection Manifest Accompanying Data Fields may describe general information about the election location.nfd2Needs further discussion
2682154S3.1.3.hEGRI allows an Election Manifest to specify many other things, including things not listed here.nfd2Needs further discussion
2692199S3.1.3.j'Undervoted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limitnyi4Not yet implemented
2702203S3.1.3.jElection Manifest allows to specify whether to record Undervoted Contest Conditions (unless the Contest specifies otherwise).utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2712208S3.1.3.jElection Manifest allows to specify, for each Contest, whether to record the Undervoted Contest Condition for that Contest.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2722213S3.1.3.jIf a Contest is specified to record Undervoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification (None currently). The value of this additional Contest Data Field is `1` to record the Undervoted Contest Condition and `0` otherwise.nyi4Not yet implemented
2732217S3.1.3.jIf a Contest is specified to record Undervoted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.nyi4Not yet implemented
2742221S3.1.3.jIf a Contest is specified to record Undervoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.nyi4Not yet implemented
2752245S3.1.3.kElection Manifest allows to specify whether to record the Undervote Difference Count for that Contest.utsutspp0
2762249S3.1.3.kElection Manifest allows to specify, for each Contest, whether to record the Undervote Difference Count for that Contest.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
2772254S3.1.3.kIf a Contest is specified to record Undervote Difference Count, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (saturating) difference (i.e., nonnegative) between the effective Contest Selection Limit and the the sum of the voter selections for that Contest. This value will not exceed the effective Contest Selection Limit.nyi4Not yet implemented
2782258S3.1.3.kIf a Contest is specified to record Undervote Difference Count, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.nyi4Not yet implemented
2792283S3.1.3.lAn 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit.nyi4Not yet implemented
2802287S3.1.3.lElection Manifest allows to specify, for each Contest, whether to record the Overvoted Contest Condition for that contest.nyi4Not yet implemented
2812291S3.1.3.lIf not specified, EGRI does not record the Overvoted Contest Condition.nyi4Not yet implemented
2822295S3.1.3.lIf a Contest is specified to record Overvoted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Contest Condition and `0` otherwise.nyi4Not yet implemented
2832299S3.1.3.lIf a Contest is specified to record Overvoted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.nyi4Not yet implemented
2842304S3.1.3.l'Overvoted Option Condition' is a per-contest-option state in which the selection count is strictly greater than the option selection limitnyi4Not yet implemented
2852308S3.1.3.lElection Manifest allows to specify, for each Contest Option, whether to record the Overvoted Option Condition for that Option.nyi4Not yet implemented
2862312S3.1.3.lIf not specified, EGRI does not record the Overvoted Option Condition.nyi4Not yet implemented
2872316S3.1.3.lIf a Contest Option is specified to record Overvoted Option Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields, any other Contest Data Fields recording Overvoted Option Condition for lower-index Options, and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Overvoted Option Condition and `0` otherwise.nyi4Not yet implemented
2882320S3.1.3.lIf a Contest is specified to record Overvoted Option Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.nyi4Not yet implemented
2892343S3.1.3.m'Null Voted Contest Condition' is a per-contest state in which the sum of the voter selections is strictly less than the contest selection limitnyi4Not yet implemented
2902347S3.1.3.mElection Manifest allows to specify, for each Contest, whether to record the Null Voted Contest Condition for that Contest.nyi4Not yet implemented
2912351S3.1.3.mIf not specified, EGRI does not record the Null Voted Contest Condition.nyi4Not yet implemented
2922355S3.1.3.mIf a Contest is specified to record Null Voted Contest Conditions, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is `1` to record the Null Voted Contest Condition and `0` otherwise.nyi4Not yet implemented
2932359S3.1.3.mIf a Contest is specified to record Null Voted Contest Conditions, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.nyi4Not yet implemented
2942363S3.1.3.mIf a Contest is specified to record Null Voted Contest Conditions, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is either `0` or `1`.nyi4Not yet implemented
2952387S3.1.3.nElection Manifest allows to specify, for each Contest, whether to record the Total Number Of Write-ins selected for that Contest.nyi4Not yet implemented
2962391S3.1.3.nIf not specified, EGRI does not record the Total Number Of Write-ins.nyi4Not yet implemented
2972395S3.1.3.nIf a Contest is specified to record Total Number Of Write-ins, an additional Contest Data Field is recorded, on every Ballot having that Contest in its Ballot Style, after the Contest Option Fields and any other additional Contest Data Fields described earlier in this specification. The value of this additional Contest Data Field is the (nonnegative integer) total number of write-ins the voter selected for that Contest. This value must not exceed the effective Contest Selection Limit.nyi4Not yet implemented
2982399S3.1.3.nIf a Contest is specified to record Total Number Of Write-ins, any value assigned to the additional Contest Data Field does not count against the voter's Contest Selection Limit.nyi4Not yet implemented
2992403S3.1.3.nIf a Contest is specified to record Total Number Of Write-ins, an additional Range Proof is recorded that the encrypted value of the additional Contest Data Field is `0` through the effective Contest Selection Limit, inclusive.nyi4Not yet implemented
3002408S3.1.3.nEGRI may emit a diagnostic message if any contest specifies a number of write-in fields greater than the contest selection limitnyi4Not yet implemented
3012413S3.1.3.nElection Manifest allows to specify, for each Contest, the number of write-in fields available to the voter.nyi4Not yet implemented
3022417S3.1.3.nIf a Contest is specified to supply a nonzero number of write-in fields, the Contest has that number of additional 'write-in N' voter-selectable Contest Options.nyi4Not yet implemented
3032421S3.1.3.nAny such write-in fields are to be labeled by the system as "write-in N", where 'N' is the 1-based index value of the write-in field. (This will be greater than the Contest Option Field Index value by the number of non-write-in voter-Selectable Options.)nyi4Not yet implemented
3042425S3.1.3.gThe Election Manifest, for each additional 'write-in N' voter-selectable Contest Option, may specify a nonnegative integer Option Selection Limit with the same defaults and other semantics as non-write-in Contest Options. (ref: S3.1.3.g)nyi4Not yet implemented
3052429S3.1.3.n"Written-in" means that the election system has recorded some voter-supplied, non-empty, non-blank data associated with a contest and write-in field index valuenyi4Not yet implemented
3062433S3.1.3.nEvery write-in field that *was not* written-in MUST be assigned a value of `0`.nyi4Not yet implemented
3072437S3.1.3.nEvery write-in field that *was* written-in MUST be assigned a value of `1` or greater, and subject to the same rules for Effective Option Selection Limit as other voter-Selectable Options.nyi4Not yet implemented
3082442S3.1.3.nThe value of a write-in field counts against the effective Contest Selection Limitnyi4Not yet implemented
3092446S3.1.3.nFor every Write-in field, an additional Range Proof is recorded that the encrypted value is either `0` or `1`.nyi4Not yet implemented
3102451S3.1.3.nJosh 2025-03-18: A write-in field can not have an effective Option Selection Limit greater than `1`.nyi4Not yet implemented
3112477S3.1.3.oA canonical byte representation is defined for the Election Manifest.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3122482S3.1.3.oEGRI allows to write the Election Manifest canonical representation to a file.ics50Implementation code exists covering a portion of code paths considered substantial
3132511S3.1.4.aEGRI computes H_B from H_P and the Election Manifest canonical representation as specified in EG DS v2.1.0 eq. 5.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3142535S3.1.4.b.verif1TODO: Verification 10
3152557S3.2.aEGRI allows to specify the number of Guardians `n` prior to a Key Ceremony.uts65Unit tests exercising a portion of code paths considered sufficient exist
3162562S3.2.aEGRI allows to specify the quorum value `k` prior to a Key Ceremony.uts65Unit tests exercising a portion of code paths considered sufficient exist
3172567S3.2.aEGRI rejects values of `n` less than `1`.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3182572S3.2.aEGRI rejects values of `n` greater than `2^31 - 1`.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3192577S3.2.aEGRI rejects values of `k` less than `1`.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3202582S3.2.aEGRI rejects values of `k` greater than `n`.ics50Implementation code exists covering a portion of code paths considered substantial
3212632S3.2.c(Ref: S2.b) Guardians can generate individual public-secret key pairsutep60At least one unit test exercising a relevant code path is passing
3222637S3.2.c(Ref: S2.b) Guardians can exchange shares of secret keysute55At least one unit test exercising a relevant code path exists
3232643S3.2.c(Ref: S3.a.a.b) "EGRI requires that a complete new set of keys must be generated if even a single guardian is replaced"na1Not directly applicable to implementation code
3242647S3.2.cEGRI rejects any election data unless 1 <= k <= n < 2^31ics50Implementation code exists covering a portion of code paths considered substantial
3252651S3.2.cEach Guardian is associated with a human readable identifier referred to as the 'Guardian Name'.utep60At least one unit test exercising a relevant code path is passing
3262656S3.2.cEGRI accepts Guardian labels composed of printable characters and (internal, non-contiguous) 0x20 space characters.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3272661S3.2.cEGRI rejects Guardian labels that contain line break characters.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3282666S3.2.cEGRI rejects Guardian labels that have leading or trailing whitespace.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3292671S3.2.cEGRI rejects Guardian labels that contain contiguous sequences of whitespace other than a single 0x20 space.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3302676S3.2.cEGRI rejects Guardian labels that contain special characters (Unicode Category Cc, Cf, Zs, Zl, Zp, or Cs).utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3312681S3.2.cEGRI rejects Guardian labels having no printable charactersutsp75Unit tests exercising a portion of code paths considered sufficient are passing
3322686S3.2.cEGRI rejects Guardian labels that decode to a Unicode malformed surrogate pair (Unicode Category Cs). See [JSON RFC Errata 7603](https://www.rfc-editor.org/errata/eid7603)ics50Implementation code exists covering a portion of code paths considered substantial
3332692S3.2.cBallots are encrypted using threshold encryptionute55At least one unit test exercising a relevant code path exists
3342697S3.2.c(Ref: S2.b) "At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key."ute55At least one unit test exercising a relevant code path exists
3352702S3.2.c(Ref: S2.b) "At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key."ute55At least one unit test exercising a relevant code path exists
3362707S3.2.c(Ref: S2.b) Every guardian can Shamir-share their secret keys during key generationuts65Unit tests exercising a portion of code paths considered sufficient exist
3372712S3.2.c(Ref: S2.c.c) Voter Selections can be encrypted to the Joint (Vote|Ballot Data) Encryption Public Keys.ute55At least one unit test exercising a relevant code path exists
3382717S3.2.c(Ref: S3.a.f.c) "EGRI enables a Guardian to compute a verifiable partial decryption of the tally of every Contest Option Field and additional Contest Data Field."ute55At least one unit test exercising a relevant code path exists
3392722S3.2.c(Ref: S3.a.f.c) "EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies by combining the verifiable partial decryptions of the tallies of every Contest Option Field and additional Contest Data Field."ute55At least one unit test exercising a relevant code path exists
3402727S3.2.c(Ref: S3.a.f.d) "Fewer than `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys can not produce a full decryption of any tally"na1Not directly applicable to implementation code
3412795S3.2.1(Ref: S2.b) Guardians can exchange shares of secret keysuts65Unit tests exercising a portion of code paths considered sufficient exist
3422800S3.2.1Receiving Guardians can confirm that the key shares they receive are correctuts65Unit tests exercising a portion of code paths considered sufficient exist
3432805S3.2.1Every Guardian can generate an independent public-secret key pair by generating a random integer secret s_i in Z_qutsp75Unit tests exercising a portion of code paths considered sufficient are passing
3442810S3.2.1Every Guardian can form their Guardian public key as K_i = g^{s_i} mod putsp75Unit tests exercising a portion of code paths considered sufficient are passing
3452815S3.2.1The Election Record contains every Guardian's public keynyi4Not yet implemented
3462819S3.2.1The Election Record contains, for every Guardian public key, a non-interactive zero-knowledge Schnorr proof of knowledge of the associated secret keyics50Implementation code exists covering a portion of code paths considered substantial
3472823S3.2.1The Joint Vote Encryption Public Key `K` is computed as \prod_{i=1}^n K_i mod putsp75Unit tests exercising a portion of code paths considered sufficient are passing
3482828S3.2.1Every guardian G_i can generate k - 1 random polynomial coefficients a_{i,j} such that 0 < j < k and 0 <= a_{i,j} < qutsp75Unit tests exercising a portion of code paths considered sufficient are passing
3492833S3.2.1Every guardian G_i can generate and publish commitments K_{i,j} = g^{a_{i,j}} mod p to each of its polynomial coefficientsutsp75Unit tests exercising a portion of code paths considered sufficient are passing
3502838S3.2.1Guardian polynomial coefficients are generated and managed in precisely the same fashion as secret keysna1Not directly applicable to implementation code
3512842S3.2.1Individual Ballots can be homomorphically combined into a single Aggregate Ballotute55At least one unit test exercising a relevant code path exists
3522847S3.2.1The single Aggregate Ballot consists of an encryption of the tally for each contest (e.g. selection option) fieldute55At least one unit test exercising a relevant code path exists
3532852S3.2.1Any set of k guardians' private keys is sufficient to complete decryption of the tally for each contest (e.g. selection option) fieldics50Implementation code exists covering a portion of code paths considered substantial
3542856S3.2.1EGRI enables any set of `k` distinct Guardian Vote Encryption Secret Keys to complete the decryption of the tally for each Contest Option and additional Contest Data Fields. (Ref: S3.a.f.c)ute55At least one unit test exercising a relevant code path exists
3552861S3.2.1No set of fewer than `k` distinct Guardian Vote Encryption Secret Keys is sufficient to complete the the decryption of the tally for any Contest Option or additional Contest Data Field.na1Not directly applicable to implementation code
3562917S3.2.2.a(Ref: S2.b) Key Ceremonyace3Applicable to implementation code, but more directly covered elsewhere
3572921S3.2.2.a(Ref: S2.b) "EGRI enables a Guardian to generate its own Vote Encryption (Public|Secret) Key pair prior to the start of voting or RLA."ace3Applicable to implementation code, but more directly covered elsewhere
3582925S3.2.2.aEGRI enables a Guardian `i` to generate and store `k` secret polynomial coefficients (indexed as `0 <= j < k`) by sampling a CSRNG uniformly as `0 < a_{i,j} < q` for their Guardian Vote Encryption Key pair.0
3592929S3.2.2.aEGRI enables a Guardian `i` to generate, store, and publish a Schnorr proof of knowledge for each of its k secret polynomial coefficients (indexed as `0 <= j < k`) for their Guardian Vote Encryption Key pair.0
3602951S3.2.2.b(Ref: S2.b) Key Ceremonyace3Applicable to implementation code, but more directly covered elsewhere
3612955S3.2.2.b(Ref: S2.b) "EGRI enables a Guardian to generate its own Ballot Data Encryption (Public|Secret) Key pair prior to the start of voting or RLA."ace3Applicable to implementation code, but more directly covered elsewhere
3622959S3.2.2.bEGRI enables a Guardian `i` to generate and store `k` secret polynomial coefficients (indexed as `0 <= j < k`) by sampling a CSRNG uniformly as `0 < a_{i,j} < q` for their Guardian Ballot Data Encryption Key pair.0
3632963S3.2.2.bEGRI enables a Guardian `i` to generate, store, and publish a Schnorr proof of knowledge for each of its k secret polynomial coefficients (indexed as `0 <= j < k`) for their Guardian Vote Encryption Key pair.0
3642993S3.2.2.c(Ref: S2.b) Key Ceremonyace3Applicable to implementation code, but more directly covered elsewhere
3652997S3.2.2.c(Ref: S2.b) "EGRI enables a Guardian to generate its own Guardian Communication (Public|Secret) Key pair prior to the start of voting or RLA."ace3Applicable to implementation code, but more directly covered elsewhere
3663001S3.2.2.cEGRI enables a Guardian `i` to generate and store a uniformly random value $\zeta_i \in \Z_q$ as its Guardian Communication Secret Key0
3673005S3.2.2.cEGRI enables a Guardian `i` to derive and store Guardian Communication Public Key as \kappa_i = g^{\zeta_i} \bmod p. eq. 90
3683042S3.2.2.dAfter having generated its own Vote Encryption Secret Key and Guardian Communication Secret Key, EGRI enables a Guardian `i` to generate an NIZK proof of knowledge of secrets $a_{i,j}$ and of $\zeta_i$ as specified in EG DS v2.1.0 eq. 10 - 110
3693046S3.2.2.dEGRI enables a Guardian to publish its NIZK proof of knowledge of secrets $a_{i,j}$ and of $\zeta_i$.0
3703084S3.2.2.eAfter having generated its own Ballot Data Encryption Secret Key and Guardian Communication Secret Key, EGRI enables a Guardian `i` to generate and publish an NIZK proof of knowledge of secrets $\hat{a}_{i,j}$ and of $\zeta_i$ as specified in EG DS v2.1.0 eq. 10 - 110
3713088S3.2.2.eEGRI enables a Guardian to publish its NIZK proof of knowledge of secrets $\hat{a}_{i,j}$ and of $\zeta_i$.0
3723110S3.2.2.f.verif2TODO: Verification 2 Note: Required for (Ref: S3.2.2.o Details of Key Generation - Share verification and the guardian record)ace3Applicable to implementation code, but more directly covered elsewhere
3733238S3.2.2.i(Ref: S2.b) Key Ceremonyace3Applicable to implementation code, but more directly covered elsewhere
3743242S3.2.2.i(Ref: S2.b) EGRI enables a Guardian `i` to encrypt to another Guardian `l` Guardian Communication Public Key a share of each of its own (Vote|Ballot Data) Encryption Secret Keys, $P_i(\ell)$ and $\hat{P}_i(\ell)$, as specified in EG DS v2.1.0 eq. 15 - 22ace3Applicable to implementation code, but more directly covered elsewhere
3753246S3.2.2.i(Ref: S2.b) EGRI enables a Guardian to publish the encryption $E_\ell(P_i(\ell), \hat{P}_i(\ell))$ of its secret key shares $P_i(\ell)$ and $\hat{P}_i(\ell)$ for every other guardian $G_\ell$ace3Applicable to implementation code, but more directly covered elsewhere
3763274S3.2.2.j(Ref: S3.2.2.i) EGRI enables a receiving Guardian `l` to verify or refute the Schnorr proof $C_{i,\ell,2} = (\bar c_{i,\ell},\bar v_{i,\ell})$ from sending Guardian `i` as part of the share of each of Guardian `i`'s (Vote|Ballot Data) Encryption Secret Keys as specified in EG DS v2.1.0 eq. 22.ace3Applicable to implementation code, but more directly covered elsewhere
3773278S3.2.2.jIf the Schnorr proof successfully verifies, EGRI enables receiving Guardian `l` to decrypt the ciphertext as specified in EG DS v2.1.0 eq. 23.0
3783308S3.2.2.kEGRI enables a Guardian `i` to compute its share $z_i = P(i)$ of the Joint Vote Encryption Secret key as specified in EG DS v2.1.0 eq. 24.0
3793312S3.2.2.kEGRI enables a Guardian `i` to compute its share $z_i = P(i)$ of the Joint Ballot Data Encryption Secret key as specified in EG DS v2.1.0 eq. 24.0
3803340S3.2.2.l(Ref: S2.b) "At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Vote Encryption Public keys to form the Joint Vote Encryption Public Key."ace3Applicable to implementation code, but more directly covered elsewhere
3813344S3.2.2.lEGRI enables the computation of the Joint Vote Encryption Public Key specified in eq. 25.utep60At least one unit test exercising a relevant code path is passing
3823373S3.2.2.m(Ref: S2.b) "At the successful completion of the Key Generation Ceremony, EGRI enables the combination of Guardian Ballot Data Encryption Public keys to form the Joint Ballot Data Encryption Public Key."ace3Applicable to implementation code, but more directly covered elsewhere
3833377S3.2.2.mEGRI enables the computation of the Joint Ballot Data Encryption Public Key specified in eq. 26.utep60At least one unit test exercising a relevant code path is passing
3843401S3.2.2.n.verif3TODO: Verification 3 Note: Required for (Ref: S3.2.2.o Details of Key Generation - Share verification and the guardian record)0
3853467S3.2.2.oThere is a defined Election Data Object called the 'Preliminary Guardian Record'.0
3863472S3.2.2.oEGRI enables an Election Administrator to produce the Preliminary Guardian Record from data generated during the Key Ceremony.0
3873476S3.2.2.oThe Preliminary Guardian Record contains the Election Parameters $p$, $q$, $g$, $n$, and $k$; the Joint (Vote|Ballot Data) Encryption Public Keys $K$ and $\hat K$; all $K_{i,j}$ and $\hat{K}_{i,j}$ values from all Guardians (for $1\leq i \leq n$ and $0 \leq j < k$), the Guardian Communication Public Keys $\kappa_i$, together with all Schnorr proofs $(c_{i}, v_{i,0}, v_{i,1}, \dots, v_{i, k})$ and $(\hat{c}_{i}, \hat{v}_{i,0}, \hat{v}_{i,1}, \dots, \hat{v}_{i, k})$.0
3883480S3.2.2.oEGRI enables a Guardian `l` to verify the Preliminary Guardian Record as specified in EG DS v2.1.0 eq. 27 - 29. Note: This includes (Ref: S3.2.2.f.verif2 Verification 2) and (Ref: S3.2.2.n.verif3 Verification 3).0
3893484S3.2.2.o(Ref: S2.b) "EGRI enables a Guardian to "complain and halt the protocol""ace3Applicable to implementation code, but more directly covered elsewhere
3903489S3.2.2.o(Ref: S2.b) "EGRI enables, supports, or facilitates the initiation of a 'Key Ceremony Irregularity Investigation' subsequent to a Guardian complaint of irregularity."ace3Applicable to implementation code, but more directly covered elsewhere
3913493S3.2.2.oEGRI enables to deterimine that all Guardians have confirmed that all verification steps have passed successfully.0
3923497S3.2.2.oThere is a defined Election Data Object called the 'Guardian Record'.0
3933501S3.2.2.oEGRI enable publishing the Guardian Record as part of the Election Record.0
3943506S3.2.2.oEGRI enables a Guardian to retain its own indepent copy of the Preliminary Guardian Record for later comparison with the Guardian Record as published in the Election Record.0
3953510S3.2.2.oEGRI enables a Guardian to verify that the Guardian Record as published in the Election Record contain the same values for $K$ and $\hat{K}$, the Joint (Vote|Ballot Data) Encryption Public Key as that they verified in the Preliminary Guardian Record.0
3963514S3.2.2.oEGRI enables a Guardian to retain only its secret values $z_i = P(i)$ and $\hat{z}_i = \hat{P}(i)$ for later use in the decryption process and to destroy all other secret values.0
3973518S3.2.2.oEGRI enables a Guardian to destroy its initial (Vote|Ballot Data) Encryption Secret Keys $s_i$ and $\hat{s}_i$.0
3983547S3.2.3.aEGRI computes H_E from H_B and the Joint (Vote|Ballot Data) Encryption Public Keys as specified in EGDS v2.1.0 eq. 30.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
3993570S3.2.3.b.verif4TODO: Verification 40
4003592????EGRI rejects any set of Guardians having any CoefficientCommitment the same.0
4013596????EGRI rejects any Guardian having any CoefficientCommitment the same.0
4023601S3.3An ElectionGuard `Ballot` is an Election Data Object that is created encrypted. The term `encrypted Ballot` is technicaly redundant.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
4033607S3.3The concept of a traditional, e.g. paper, "ballot" is referred to as `Voter Selections`.0
4043611S3.3In specific cases, such as a challenged, an ElectionGuard `Ballot` may be decrypted and the plaintext of the constest data fields published. But the term `Ballot` still as always refers to the encrypted form.0
4053615S3.3Election systems may assign a Ballot an ID label for reference and retrival purposes. This ID may be randomly generated or derived from a combination of information guaranteed unique. In no case will this ID label encode or be derived from data that wouldn't be recorded in the Ballot in-the-clear anyway.0
4063655S3.3.1.jEGRI encrypts a Contest (Option or Additional) Data Field value as specified in EGDS v2.1.0 eq. 31 pg. 28.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
4073660S3.3.1.jEGRI may pre-compute the encryption of a Contest (Option or Additional) Data Field value of '0', and then convert it to an encryption of `1` if needed.0
4083691S3.3.2.aEGRI computes for each Ballot a Selection Encryption Identifier `id_B`.0
4093695S3.3.2.aEGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32.0
4103718S3.3.2.b.verif5TODO: Verification 50
4113748S3.3.3EGRI selects for each Ballot a Ballot Nonce by sampling a CSRNG uniformly as `0 < id_B < 2^256`.0
4123752S3.3.3EGRI computes for each Contest appearing on the Ballot, for each Contest (Option or Additional) Data Field, a Contest Data Field Nonce as specified in EG DS v2.1.0 eq. 33. Note: This uses the "mod q" version of H.0
4133850S3.3.4EGRI for each Ballot encrypts (with a Schnorr proof of knowledge) the Ballot Nonce `id_B` to the Joint Ballot Data Encryption Public Key as specified in EG DS v2.1.0 eq. 34 - 38.0
4143854S3.3.4EGRI attempts, to the greatest extent practical, to erase from hardware memory the Ballot Nonce `id_B` (and any information which could reconstruct it e.g., RNG state) immediately after use.0
4153895S3.3.5.bRef: S3.1.3.g0
4163918S3.3.5.cRef: S3.1.3.i Election Parameters and the Election Manifest - Undervotes0
4173922S3.3.5.cRef: S3.1.3.j Election Parameters and the Election Manifest - Counting undervoted contests0
4183926S3.3.5.cRef: S3.1.3.k Election Parameters and the Election Manifest - Undervote difference count0
4193930S3.3.5.cRef: S3.1.3.m Election Parameters and the Election Manifest - Null votes0
4203952S3.3.5.d(Ref: S3.1.3.l) "An 'Overvoted Contest Condition' is considered to have occurred for a specific Contest on a specific Ballot when the the sum of the selections is strictly greater than the contest Selection Limit."ace3Applicable to implementation code, but more directly covered elsewhere
4213956S3.3.5.dWhen an Overvoted Contest Condition has occurred, all Contest Option Data Fields for that Contest are system-assigned the value of `0`.0
4223960S3.3.5.dWhen an Overvoted Contest Condition has occurred, this fact MAY be encoded into a (non-tallied) data field and encrypted to the Joint Ballot Data Encryption Public Keys.0
4233964S3.3.5.dWhen an Overvoted Contest Condition has occurred, the specific voter selections for this contest MAY be encoded into a (non-tallied) Contest Data Field and encrypted to the Joint Ballot Data Encryption Public Keys, much like write-in data.0
4243968S3.3.5.dRef: S3.1.3.l Election Parameters and the Election Manifest - Overvotes0
4253995S3.3.5.eFor every Ballot, EGRI allows, for every Contest Option Data Field, to prove that the Ciphertext is an encryption of a nonnegative value less than or equal to the effective Option Selection Limit (e.g., an encryption of `0` or `1`).0
4263999S3.3.5.eFor every Ballot, EGRI allows, for every Contest Additional Data Field, to prove that the Ciphertext is an encryption of a legitimate value (as specified for that particular Contest Additional Data Field).0
4274003S3.3.5.eFor every Ballot, EGRI allows, for every Contest, to prove that the sum of all Contest Option values is an encryption of a nonnegative value less than or equal to the effective Contest Selection Limit.0
4284027S3.3.5.fEGRI uses range proofs to prove well-formedness of an individual selection when it is allowed for a Contest Option on a Ballot to be assigned values greater than `1`0
4294031S3.3.5.fEGRI uses range proofs for individual Contest Options0
4304035S3.3.5.fEGRI uses range proofs for the Contest selection sum0
4314039S3.3.5.fEGRI can use range proofs to enable cumulative voting0
4324043S3.3.5.fEGRI can use range proofs to enable score voting0
4334047S3.3.5.fEGRI can use range proofs to enable STAR-voting0
4344051S3.3.5.fEGRI can use range proofs to enable Borda count0
4354269S3.3.7.c(Ref: S3.2.3.j) "EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EG DS v2.1.0 eq. 31."ace3Applicable to implementation code, but more directly covered elsewhere
4364273S3.3.7.cEGRI encrypts a Contest (Option or Additional) Data Field having a value of `0` as $(\alpha,\beta)=(g^\xi \bmod p,\ K^\xi \bmod p)$.0
4374277S3.3.7.cEGRI computes an NIZK proof that (\alpha,\beta) is an encryption of `0` or `1` as specified in EG DS v2.1.0 eq. 39 - 47.0
4384300S3.3.7.d(Ref: S3.2.3.j) "EGRI may pre-compute the encryption of a Contest (Option or Additional) Data Field value of '0', and then convert it to an encryption of `1` if needed."ace3Applicable to implementation code, but more directly covered elsewhere
4394304S3.3.7.d(Ref: S3.2.3.j) "EGRI encrypts a Contest (Option or Additional) Data Field value as specified in EG DS v2.1.0 eq. 31."ace3Applicable to implementation code, but more directly covered elsewhere
4404308S3.3.7.dEGRI encrypts a Contest (Option or Additional) Data Field having a value of `1` as $(\alpha,\beta) = (g^\xi \bmod p,\ K^{\xi+1} \bmod p)$.0
4414385S3.3.7.eWhen the effective Option Selection Limit is a value other than `1`, EGRI computes an NIZK proof that (\alpha,\beta) is an encryption of `0` or `1` as specified in EG DS v2.1.0 eq. 48 - 56.0
4424464S3.3.7.gWhen the effective Option Selection Limit is a value other than `0` or `1`, EGRI computes an NIZK range proof as specified in EG DS v2.1.0 eq. 57 - 61.0
4434535S3.3.7.j.verif6TODO: Verification 60
4444587S3.3.8.bEGRI computes an NIZK range proof that the homomorphic combination (i.e., sum) of all Contest Options Fields represents the encryption of a nonnegative integer value not greater than the effective Contest Selection Limit as specified in EG DS v2.1.0 eq. 62.0
4454609S3.3.8.c.verif7TODO: Verification 70
4464657S3.3.9.bRef: S3.1.3.j "Undervoted Contest Condition"0
4474661S3.3.9.bRef: S3.1.3.j "Undervote Difference Count"0
4484689S3.3.9.cRef: S3.1.3.l "Overvoted Contest Condition"0
4494693S3.3.9.cRef: S3.1.3.l "Overvoted Option Condition"0
4504719S3.3.9.dRef: S3.1.3.m 'Null Voted Contest Condition'0
4514744S3.3.9.eRef: S3.1.3.n Write-ins0
4524857S3.3.10.cIf the Election Manifest specifies that Ballots record for a Contest any non-homomorphically tallied additional or supplemental Contest Data Fields, EGRI encodes their values using an unambiguous fixed-length encoding and encrypts it as specified in EG DS v2.1.0 eq. 63 - 69.0
4534919S3.4.1EGRI computes the Contest Hash value $\chi_l$ as specified in EG DS v2.1.0 eq. 70.0
4544948S3.4.2EGRI computes the Confirmation Code `H_C` as specified in EG DS v2.1.0 eq. 71.0
4554977S3.4.3EGRI computes the Voting Device Information Hash value `H_DI` as specified in EG DS v2.1.0 eq. 72.0
4565025S3.4.4.bIn the case of 'No chaining', EGRI computes the Chaining Field `B_C` as specified in EG DS v2.1.0 eq. 73.0
4575087S3.4.4.cIn the case of 'Simple chaining', EGRI computes the Chaining Fields and Chaining Value as specified in EG DS v2.1.0 eq. 74 - 78.0
4585110S3.4.4.dEGRI enables a method of recording the state of a Ballot as one of `VoterSelectionsEncrypted`, `Cast`, `Spoiled`, `Challenged`, or `ChallengedDecrypted`.0
4595114S3.4.4.dEGRI refuses to decrypt a ballot in the `Cast` state.0
4605118S3.4.4.dEGRI refuses to decrypt a ballot in the `Spoiled` state.0
4615122S3.4.4.dEGRI includes in any tally operation only Ballots in the `Cast` state.0
4625126S3.4.4.dEGRI enables decrypting a ballot in the `Challenged` state.0
4635130S3.4.4.dEGRI refuses to change the state of a Ballot from the `Cast` state.0
4645134S3.4.4.dEGRI refuses to change the state of a Ballot from the `Spoiled` state.0
4655138S3.4.4.dEGRI refuses to change the state of a Ballot from the `ChallengedDecrypted` state.0
4665160S3.4.4.e.verif8TODO: Verification 80
4675189S3.5.aEGRI enables publishing all Ballots (with proofs) in the Election Record at the conclusion of voting.0
4685193S3.5.aEGRI enables computing an Aggregate Ballot as specified in EG DS v2.1.0 eq. 79.0
4695216S3.5.b.verif9Every Ballot that fails to specify a Ballot Weight that is an integer inclusively between 1 and the maximum specified in the Election Manifest is considered invalid.0
4705246S3.5.cThe Election Manifest specifies a maximum Ballot Weight as a small positive integer.0
4715250S3.5.cThe condition "If weights are used" means the Election Manifest specifies a maximum Ballot Weight greater than `1`.0
4725254S3.5.cEvery EGRI API receiving voter selections for encryption receives a value for the corresponding Ballot Weight and rejects the voter selections if the supplied Ballot Weight is not an integer inclusively between 1 and the maximum specified in the Election Manifest.0
4735258S3.5.cEvery Ballot records its associated Ballot Weight.0
4745262S3.5.cDespite the language "explicitly associated with an identified voter", EGRI does not represent the concept of "voter". If an external system is used to track an association between Ballots and actual voters, the impact on voter privacy and coersion resistance must be considered extremely carefully.0
4755266S3.5.cEGRI enables computing an Aggregate Ballot from Weighted Ballots as specified in EG DS v2.1.0 eq. 80.0
4765271S3.5.cWhen computing an Aggregate Ballot, the aggregation function must, using access to the full and complete Election Record information to that point in time, verify that every Ballot to be aggregated has never been decrypted, is not in a 'Challeged' or 'Spoiled' state, etc.0
4775275S3.5.cIf such a (decrypted, challenged, spoiled, etc) Ballot were somehow to be supplied to the aggregation function, it MUST be excluded from the tally, a diagnostic message emitted, and the offending Ballot identifier(s) returned separately.0
4785336S3.6.1EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Vote Encryption Secret Key. Josh 2025-03-19: This will only ever include the set of `Challenged` Ballots and the aggregate encrypted tallies.0
4795344S3.6.1EGRI enables to communicate to the Guardians the set of Ciphertexts marked for decryption (i.e., challenged Ballots and aggregate encrypted tallies).0
4805348S3.6.1Josh 2025-03-19: Multiple Tallies may be conducted for a single election.0
4815352S3.6.1Whenever multiple Tallies are conducted for a single election, subsequent tallies should extend the current Election Record. This does not introduce a requirement for EGRI to implement any form of rollback protection.0
4825356S3.6.1EGRS enables a Guardian to verify or refute that all Ciphertexts are correct prior to decryption. Ref: S6.2.3.verif4, S6.2.4.verif5, S6.2.4.verif6, S6.2.4.verif7, S6.2.4.verif8, S6.2.5.verif90
4835361S3.6.1EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian.0
4845365S3.6.1EGRS does not assist a Guardian to participate in the decryption of any Ciphertexts which does not both 1. belong to a set that has been marked for decryption and 2. been verified correct by that Guardian.0
4855406S3.6.2(Ref: S3.6.1) "EGRS enables a Guardian to participate in the decryption of a set of Ciphertexts that have both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian."ace3Applicable to implementation code, but more directly covered elsewhere
4865410S3.6.2(Ref: S3.6.1) "EGRS refusues to assist a Guardian to participate in the decryption of any Ciphertexts unless they both 1. belong to a set that has been marked for decryption and 2. have all been verified correct by that Guardian."ace3Applicable to implementation code, but more directly covered elsewhere
4875414S3.6.2(Ref: S3.a.f.c) "EGRI enables a Guardian to compute a verifiable partial decryption..."ace3Applicable to implementation code, but more directly covered elsewhere
4885418S3.6.2(Ref: S3.a.f.c) "EGRI enables a quorum of `k` distinct Guardian (Vote|Ballot Data) Encryption Secret Keys to form full verifiable decryptions of the election tallies..."ace3Applicable to implementation code, but more directly covered elsewhere
4895423S3.6.2EGRI enables a quorum of `k` distinct Guardians to compute `T = K^t`, for every Contest (Option or Additional) Data Field in the election. as specified in EG DS v2.1.0 eq. 81 - 82.0
4905427S3.6.2EGRI enables a quorum of `k` distinct Guardians to publish `T` in the Tallies of the Election Record, for every Contest (Option or Additional) Data Field in the election. Note: This is to handle scenarios in which the discrete log of `T` ends up needing more computation than the Guardian quorum has on hand at the time.0
4915431S3.6.2EGRI enables the practical determination of the tally `t` from `T = K^t`, for every Contest (Option or Additional) Data Field in the election.0
4925435S3.6.2EGRI enables the publication of the tally `t`, for every Contest (Option or Additional) Data Field in the election, in the Tallies of the Election Record.0
4935474S3.6.3EGRI enables a quorum of `k` distinct Guardians to compute `T = K^t`, for every Contest (Option or Additional) Data Field in the election. as specified in EG DS v2.1.0 eq. 81 - 82.0
4945478S3.6.3EGRI enables a quorum of `k` distinct Guardians to publish `T` in the Tallies of the Election Record, for every Contest (Option or Additional) Data Field in the election. Note: This is to handle scenarios in which the discrete log of `T` ends up needing more computation than the Guardian quorum has on hand at the time.0
4955482S3.6.3EGRI enables the practical determination of the tally `t` from `T = K^t`, for every Contest (Option or Additional) Data Field in the election.0
4965486S3.6.3EGRI enables the publication of the tally `t`, for every Contest (Option or Additional) Data Field in the election, in the Tallies of the Election Record.0
4975523S3.6.4.aEGRI enables the combination of partial decryptions as specified in EG DS v2.1.0 eq. 85 - 86.0
4985641S3.6.5.bEGRI enables available Guardians to jointly prove that they have shared knowledge of $s\in \Z_q$ such that $M = A^{s} \bmod p$ and $K = g^{s} \bmod p$ as specified in EG DS v2.1.0 eq. 87 - 93.0
4995693S3.6.5.d.verif10(Ref: S6.2.5.verif10) Verification 10nyi4Not yet implemented
5005735S3.6.5.f.verif11(Ref: S6.2.5.verif11) Verification 11nyi4Not yet implemented
5015776S3.6.6.aEGRI decrypts optional contest data as specified in EG DS v2.1.0 eq. 96 - 97.0
5025875S3.6.6.bEGRI computes NIZK proof of Decryption of Contest Data as specified in EG DS v2.1.0 eq. 98 - 106.0
5035897S3.6.6.c.verif12TODO: Verification 120
5045963S3.6.7.bEGRI decrypts challenged Ballots Ballot Nonces as specified in EG DS v2.1.0 eq. 107 - 108.0
5056008S3.6.7.cEGRI decrypts challenged Ballots from encryption nonces as specified in EG DS v2.1.0 eq. 109 - 111.0
5066064S3.6.7.dTODO: Decryption of Challenged Ballots - Verifying decryption with nonces.0
5076086S3.6.7.e.verif13TODO: Verification 130
5086108S3.6.7.f.verif14TODO: Verification 140
5096184S3.7(Ref: S2.b) "EGRI enables production of an 'Election Record'"nyi4Not yet implemented
5106188S3.7The Election Record is not a static fixed format. Different information is added and modified at different times.nyi4Not yet implemented
5116192S3.7The Election Record format must support appending new and updated information without invalidating signatures made previously.nyi4Not yet implemented
5126196S3.7The Election Record format must support inclusion-by-reference (eg a Ballot ID and hash value) of batches of Ballots. Rationale: For all but very small elections, it will not be practical to hold all Ballots in a single file. Neither will it be practical to place every Ballot in a separate file in a single directory.nyi4Not yet implemented
5136200S3.7The Election Record records any information necessary and sufficient to uniquely identify and describe the election, such as date, location, election type, etc. that is not otherwise present in the Election Manifest.nyi4Not yet implemented
5146204S3.7The Election Record records the Election Manifestnyi4Not yet implemented
5156208S3.7The Election Record records the Fixed Parameters `p`, `q`, `g`, and `r`.nyi4Not yet implemented
5166212S3.7The Election Record records the Varying Parameters `n` and `k`.nyi4Not yet implemented
5176216S3.7The Election Record records `H_P`nyi4Not yet implemented
5186220S3.7The Election Record records `H_B`nyi4Not yet implemented
5196224S3.7The Election Record records for every Guardian `1..n`: a name or labelnyi4Not yet implemented
5206228S3.7The Election Record records for every Guardian `1..n`: the polynomial coefficients commitmentsnyi4Not yet implemented
5216232S3.7The Election Record records for every Guardian `1..n`: the polynomial coefficients the proofs of knowledgenyi4Not yet implemented
5226236S3.7The Election Record records for every Guardian `1..n`: the Guardian Communication Public Keynyi4Not yet implemented
5236240S3.7The Election Record records the Joint Vote Encryption Public Keynyi4Not yet implemented
5246244S3.7The Election Record records the Joint Ballot Data Encryption Public Keynyi4Not yet implemented
5256248S3.7The Election Record records `H_E`nyi4Not yet implemented
5266252S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier $\id_B$,nyi4Not yet implemented
5276256S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the selection encryption identifier hash $\HH_I$,nyi4Not yet implemented
5286260S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: all of the encrypted selections on each ballot,nyi4Not yet implemented
5296264S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the proofs that each such value is an encryption of either zero or one (or more generally, the proofs that these values satisfy the respective option selection limits),nyi4Not yet implemented
5306268S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the effective Contest Selection Limitnyi4Not yet implemented
5316272S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, for every Option Field, the effective Option Selection Limitnyi4Not yet implemented
5326276S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the stated and effective selection limits applied to each contest,nyi4Not yet implemented
5336280S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: for every Contest, the proof that the number of selections made does not exceed the selection limit,nyi4Not yet implemented
5346284S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight as originally submitted with the Voter Selectionsnyi4Not yet implemented
5356288S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Weight already applied to the Ciphertextsnyi4Not yet implemented
5366292S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the Ballot Style index,nyi4Not yet implemented
5376296S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: device information uniquely identifying the device used for encrypting of voter selections to produce the Ballotnyi4Not yet implemented
5386300S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the date and time of the ballot encryption (as reported by the encrypting device) with no more than 1 second granularitynyi4Not yet implemented
5396304S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the confirmation code produced for the Ballotnyi4Not yet implemented
5406308S3.7The Election Record records for every Ballot in the `Cast` or `Challenged` state: the state of the ballot `Cast` or `Challenged`nyi4Not yet implemented
5416313S3.7The Election Record records for every Ballot in the `ChallengedDecrypted` state: all the information included for the Ballot in its `Challenged` statenyi4Not yet implemented
5426317S3.7The Election Record records for every Ballot in the `ChallengedDecrypted` state: the selections made for every Contest Optionnyi4Not yet implemented
5436321S3.7The Election Record records for every Ballot in the `ChallengedDecrypted` state: the decrypted plaintext values of every Contest (Option or Additional) Data Fieldnyi4Not yet implemented
5446325S3.7The Election Record records for every Ballot in the `ChallengedDecrypted` state: the proofs of correct decryption for every Contest (Option or Additional) Data Field and/or the Contest Data Field nonce.nyi4Not yet implemented
5456329S3.7The Election Record records for every Ballot in the `ChallengedDecrypted` state: the date and time of the overall ballot decryption (as reported by the decrypting device)nyi4Not yet implemented
5466334S3.7The Election Record records for every Ballot Decryption Operation: some identifier uniquely identifying the Ballot Decryption Operationnyi4Not yet implemented
5476338S3.7The Election Record records for every Ballot Decryption Operation: the set of Ballots "marked for decryption"nyi4Not yet implemented
5486342S3.7The Election Record records for every Ballot Decryption Operation: the hash of the ElectionRecord at the point the Ballot Decryption Operation was initiatednyi4Not yet implemented
5496346S3.7The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the Guardian Indexnyi4Not yet implemented
5506350S3.7The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the date and time their participation was initiatednyi4Not yet implemented
5516354S3.7The Election Record records for every Ballot Decryption Operation: for every participating Guardian: the hash of their view of the ElectionRecord at the instant this operation was initiatednyi4Not yet implemented
5526358S3.7The Election Record records for every Ballot Decryption Operation: for every participating Guardian: device information uniquely identifying the device used for their secret key operationnyi4Not yet implemented
5536364S3.7The Election Record records for every Ballot Decryption Operation: for every participating Guardian:nyi4Not yet implemented
5546368S3.7The Election Record records for every Ballot Decryption Operation: for every participating Guardian:nyi4Not yet implemented
5556372S3.7The Election Record records for every Ballot Decryption Operation:nyi4Not yet implemented
5566376S3.7The Election Record records for every Ballot Decryption Operation:nyi4Not yet implemented
5576380S3.7The Election Record records for every set of Ballots in the `ChallengedDecrypted` state:nyi4Not yet implemented
5586384S3.7The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the Guardian Indexnyi4Not yet implemented
5596388S3.7The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: the date and time of the partial decryptionnyi4Not yet implemented
5606392S3.7The Election Record records for every set of Ballots in the `ChallengedDecrypted` state: for each Guardian participating in the Decryption: their partial decryptionsnyi4Not yet implemented
5616397S3.7The Election Record records for every Tally: for every Contest: identifiers of the set of Ballots in the `Cast` statenyi4Not yet implemented
5626401S3.7The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the encrypted tally (eq. 79 pg 44)nyi4Not yet implemented
5636406S3.7The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the full verifiable decryption of the encrypted tallynyi4Not yet implemented
5646410S3.7The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the tally exponentnyi4Not yet implemented
5656414S3.7The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: plaintext representation of the tallynyi4Not yet implemented
5666418S3.7The Election Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: proof of correct decryption of the tallynyi4Not yet implemented
5676422S3.7The Election Record records information uniquely identifying each device used for encryption of voter selections to produce any Ballotnyi4Not yet implemented
5686426S3.7The Election Record records an ordered list of the Ballots encrypted by each device.nyi4Not yet implemented
5696430S3.7The Election Record records the encrypted contest data, when available.nyi4Not yet implemented
5706434S3.7EGRS enables the Election Record to be produced in a format suitable for digitally signing.nyi4Not yet implemented
5716439S3.7EGRS enables the Election Record to be published in a format suitable to the needs of Verifier applications. For example, a directory of files accessible via HTTPS supporting efficient random access.nyi4Not yet implemented
5726443S3.7EGRS enables the Election Record to be published in a format suitable for archiving or bulk downloading by any interested individuals. For example, the compressed .zip and/or .tar.gz formats.nyi4Not yet implemented
5736475S4EGRI Preencrypted Ballots enable the vote-by-mail scenario.na1Not directly applicable to implementation code
5746479S4EGRI Preencrypted Ballots enable the "precincts with central count or minimal in-precinct equipment" scenario.na1Not directly applicable to implementation code
5756483S4EGRI Preencrypted Ballots enable back-ups for precincts which ordinarily perform encryption on demand.na1Not directly applicable to implementation code
5766487S4EGRI enables ordinary and Preencrypted Ballots to be tallied together.nyi4Not yet implemented
5776491S4When ordinary and Preencrypted Ballots are tallied together, it does not reveal which votes came from which mode.nyi4Not yet implemented
5786495S4The Election Manifest contains a configuration setting indicating whether Preencrypted Ballots are enabled for that election.nyi4Not yet implemented
5796499S4If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI allows to produce data necessary to print Preencrypted Ballots. Note: Referred to in the EGRS as the "Ballot Encryption Tool".nyi4Not yet implemented
5806503S4EGRI refuses to generate any Preencrypted Ballots unless the Election Manifest indicates Preencrypted Ballots are enabled.nyi4Not yet implemented
5816507S4EGRI allows to record Voter Selections made on Preencrypted Ballots, if the Election Manifest indicates Preencrypted Ballots are enabled.nyi4Not yet implemented
5826511S4If the Election Manifest indicates Preencrypted Ballots are not enabled, EGRI refuses to record any Voter Selections made on Preencrypted Ballots.nyi4Not yet implemented
5836515S4For each Preencrypted Ballot generated, EGRI computes the Selection Encryption Identifier `id_B` as described in (Ref: S3.3.2.a).nyi4Not yet implemented
5846519S4For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in EG DS v2.1.0 eq. 32 (Ref: S3.3.2.a).nyi4Not yet implemented
5856561S4.1pg. 57 EGRI `Preencrypted Ballot`, for every Contest in the BallotStyle, for every Contest Option plus an additional null selection, computes a vector `Psi_{i,m}` of length `m`, where `m` is equal to the number of Contest Options and `i` is the Contest Option index. Note1: This is captial `Psi`. Note2: Although `m` is denoted here as a subscript, it is not an index into a second dimension.nyi4Not yet implemented
5866565S4.1pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` for `0 <= i <= m`, where `m` is equal to the number of Contest Options, `i = 0` corresponds to the null vote, and for `1 <= i` `i` is the corresponding Option Index.nyi4Not yet implemented
5876569S4.1pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `1 <= i` as a length `m` vector having an encryption of `1` at (1-based) position `i`, and encryptions of `0` at all other options. Note: This corresponds to non-null vote cases.nyi4Not yet implemented
5886573S4.1pg. 57 EGRI `PreencryptedBallot` computes `Psi_{i,m}` when `i = 0` as a length `m` vector of encryptions of `0`. Note: This corresponds to the null vote case.nyi4Not yet implemented
5896578S4.1pg. 57 Note: EGRI `PreencryptedBallot`, when encrypting ContestOption vectors `Psi_{j,m}`, the encryption nonce `xi_{i,j}` is derived from `H_I` and `xi_B` as specified in eq. 121 (Ref: S4.2.1).nyi4Not yet implemented
5906582S4.1pg. 57 For each Preencrypted Ballot generated, EGRI computes `H_I` from `H_E` and the Selection Encryption Identifier `id_B` as specified in [eq. 121 pg. 61].nyi4Not yet implemented
5916616S4.1.1pg. 58 EGRI computes the Selection Hash `psi_i` for a single Option `i` according to [eq. 115 pg. 58]nyi4Not yet implemented
5926630S4.1.1pg. 58 "In a contest with a selection limit of L, an additional L null vectors are hashed" "where all $E_i = (\alpha_i, \beta_i)$ are encyptions of zero and `1 <= l <= L`."nyi4Not yet implemented
5936634S4.1.1pg. 58 EGRI computes the Selection Hash `psi_{m+l}` for an additional L null vector according to [eq. 114 pg. 58]nyi4Not yet implemented
5946666S4.1.2pg. 58 EGRI sorts all Selection Hashes in a contest prior to their use in the computation of the Contest Hash.nyi4Not yet implemented
5956670S4.1.2pg. 58 EGRI computes the Contest Hash `chi_l` according to [eq. 115 pg. 58]nyi4Not yet implemented
5966698S4.1.3pg. 58 EGRI computes the Confirmation Code `H_C` according to [eq. 116 pg. 58]nyi4Not yet implemented
5976750S4.1.4pg. 59 EGRI computes Ballot Chaining values for Preencrypted Ballots according to eq. 117-120 pg. 59.nyi4Not yet implemented
5986777S4.1.5.apg. 59 If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI requires that the Election Manifest specify unambiguously the Hash Trimming Function `Omega` to be used for all Preencrypted Ballots generated for the election.nyi4Not yet implemented
5996781S4.1.5.apg. 59 EGRI provides one or more Hash Trimming Functions.nyi4Not yet implemented
6006807S4.1.5.bIf the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides short codes to indicate undervotesnyi4Not yet implemented
6016852S4.2If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI provides a "Ballot Encrypting Tool" which operates as describedin S4.2nyi4Not yet implemented
6026883S4.2.1If the Election Manifest indicates Preencrypted Ballots are enabled, EGRI produces nonces deterministically as described in eq. 121nyi4Not yet implemented
6036907S4.2.2[TODO: Pre-Encrypted Ballots]0
6046933S4.3[TODO: Pre-Encrypted Ballots]nyi4Not yet implemented
6056959S4.3.1[TODO: Pre-Encrypted Ballots]nyi4Not yet implemented
6066991S4.4[TODO: Pre-Encrypted Ballots]nyi4Not yet implemented
6077020S4.4.1[TODO: Pre-Encrypted Ballots]nyi4Not yet implemented
6087067S4.5.a[TODO: Pre-Encrypted Ballots]nyi4Not yet implemented
6097091S4.5.b.verif15TODO: Verification 15nyi4Not yet implemented
6107115S4.5.c.verif16TODO: Verification 160
6117146S4.5.d.verif17TODO: Verification 170
6127168S4.5.e.verif18TODO: Verification 180
6137190S4.5.f.verif19TODO: Verification 190
6147229S4.6EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Two Hex Characters` hash-trimming function `Ω_1`.ics50Implementation code exists covering a portion of code paths considered substantial
6157233S4.6EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Four Hex Characters` hash-trimming function `Ω_2`.ics50Implementation code exists covering a portion of code paths considered substantial
6167237S4.6EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Letter-Digit` hash-trimming function `Ω_3`.ics50Implementation code exists covering a portion of code paths considered substantial
6177241S4.6EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Digit-Letter` hash-trimming function `Ω_4`.ics50Implementation code exists covering a portion of code paths considered substantial
6187245S4.6EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 0-255` hash-trimming function `Ω_5`.ics50Implementation code exists covering a portion of code paths considered substantial
6197249S4.6EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 1-256` hash-trimming function `Ω_6`.ics50Implementation code exists covering a portion of code paths considered substantial
6207253S4.6EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 100-355` hash-trimming function `Ω_7`.ics50Implementation code exists covering a portion of code paths considered substantial
6217257S4.6EGRS PreencryptedBallots feature allows the ElectionManifest to specify the use of the `Number: 101-356` hash-trimming function `Ω_8`.ics50Implementation code exists covering a portion of code paths considered substantial
6227261S4.6EGRS ElectionManifest PreencryptedBallots feature allows to specify the use of and the complete configuration for an API-user-supplied hash-trimming function.ics50Implementation code exists covering a portion of code paths considered substantial
6237266S4.6EGRS PreencryptedBallots feature can produce short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.nyi4Not yet implemented
6247270S4.6EGRS PreencryptedBallots feature can produce short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.nyi4Not yet implemented
6257274S4.6EGRS PreencryptedBallots feature can produce short codes using the `Letter-Digit` hash-trimming function `Ω_3`.nyi4Not yet implemented
6267278S4.6EGRS PreencryptedBallots feature can produce short codes using the `Digit-Letter` hash-trimming function `Ω_4`.nyi4Not yet implemented
6277282S4.6EGRS PreencryptedBallots feature can produce short codes using the `Number: 0-255` hash-trimming function `Ω_5`.nyi4Not yet implemented
6287286S4.6EGRS PreencryptedBallots feature can produce short codes using the `Number: 1-256` hash-trimming function `Ω_6`.nyi4Not yet implemented
6297290S4.6EGRS PreencryptedBallots feature can produce short codes using the `Number: 100-355` hash-trimming function `Ω_7`.nyi4Not yet implemented
6307294S4.6EGRS PreencryptedBallots feature can produce short codes using the `Number: 101-356` hash-trimming function `Ω_8`.nyi4Not yet implemented
6317298S4.6EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for producing short codes.nyi4Not yet implemented
6327303S4.6EGRS PreencryptedBallots feature can consume short codes using the `Two Hex Characters` hash-trimming function `Ω_1`.nyi4Not yet implemented
6337307S4.6EGRS PreencryptedBallots feature can consume short codes using the `Four Hex Characters` hash-trimming function `Ω_2`.nyi4Not yet implemented
6347311S4.6EGRS PreencryptedBallots feature can consume short codes using the `Letter-Digit` hash-trimming function `Ω_3`.nyi4Not yet implemented
6357315S4.6EGRS PreencryptedBallots feature can consume short codes using the `Digit-Letter` hash-trimming function `Ω_4`.nyi4Not yet implemented
6367319S4.6EGRS PreencryptedBallots feature can consume short codes using the `Number: 0-255` hash-trimming function `Ω_5`.nyi4Not yet implemented
6377323S4.6EGRS PreencryptedBallots feature can consume short codes using the `Number: 1-256` hash-trimming function `Ω_6`.nyi4Not yet implemented
6387327S4.6EGRS PreencryptedBallots feature can consume short codes using the `Number: 100-355` hash-trimming function `Ω_7`.nyi4Not yet implemented
6397331S4.6EGRS PreencryptedBallots feature can consume short codes using the `Number: 101-356` hash-trimming function `Ω_8`.nyi4Not yet implemented
6407335S4.6EGRS PreencryptedBallots feature allows an API user to provide a custom hash-trimming function for consuming short codes.nyi4Not yet implemented
6417360S5EGRI uses `HMAC` as the basis for the function `H`.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6427402S5.1EGRI converts between nonegative integers and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 122 - 123.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6437459S5.1.1EGRI converts between nonegative integers mod `p` and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 124 - 126.b.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6447508S5.1.2EGRI converts between nonegative integers mod `q` and fixed-size byte arrays using the big endian convention as specified in EG DS v2.1.0 eq. 127 - 129.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6457536S5.1.3EGRI converts between nonnegative integers less than 2^31 and 4-byte fixed-size arrays using the big endian convention.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6467560S5.1.4EGRI hashes fixed-length strings as their minimal UTF-8 encoding.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6477565S5.1.4EGRI hashes varible-length strings as their minimal UTF-8 encoding prefixed by their length in bytes as a sequence of 4 bytes using big endian convention.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6487588S5.1.5EGRI can read and write files as contiguous sequences of bytes.nfd2Needs further discussion
6497593S5.1.5EGRI encodes byte lengths as 4-byte big-endian values.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6507598S5.1.5EGRI accepts byte lengths less than `2^31`.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6517603S5.1.5EGRI does not produce byte lengths greater than `2^31 - 1`.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6527644S5.2EGRI implements the function `H` using HMAC and SHA-2-256 as specified in EG DS v2.1.0 eq. 130.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6537675S5.3EGRI hashes multiple inputs using an unambiguous separation as specified in the relevant section in EG DS v2.1.0utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6547716S5.4EGRI implements the function `H_q` as specified in EG DS v2.1.0 eq. 131 - 132.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6557721S5.4Iff Fixed Parameter `q` begins with 248 or more consecutive leading `1` valued bits, such as is the case with `q` from the standard parameter set, the function `H_q` can be simplified to `H`.utsp75Unit tests exercising a portion of code paths considered sufficient are passing
6567745S5.5This is expected to be a fully-redundant section, but we could double-check itutsp75Unit tests exercising a portion of code paths considered sufficient are passing
6577786S5.5.1This is expected to be a fully-redundant section, but we could double-check itace3Applicable to implementation code, but more directly covered elsewhere
6587852S5.5.2This is expected to be a fully-redundant section, but we could double-check itace3Applicable to implementation code, but more directly covered elsewhere
6597989S5.5.3This is expected to be a fully-redundant section, but we could double-check itace3Applicable to implementation code, but more directly covered elsewhere
6608043S5.5.4This is expected to be a fully-redundant section, but we could double-check itace3Applicable to implementation code, but more directly covered elsewhere
6618120S5.5.5This is expected to be a fully-redundant section, but we could double-check itace3Applicable to implementation code, but more directly covered elsewhere
6628185S6.1.bTODOutsp75Unit tests exercising a portion of code paths considered sufficient are passing
6638208S6.1.cEGRI should compute moduluar multiplication on large integers without generating unnecessarily large intermediate valuesutsp75Unit tests exercising a portion of code paths considered sufficient are passing
6648232S6.1.dEGRI should compute moduluar exponentiation on large integers without generating intermediate values having more digits than there are particles in the universeutsp75Unit tests exercising a portion of code paths considered sufficient are passing
6658302S6.2.1.verif1TODO: Verification 1ace3Applicable to implementation code, but more directly covered elsewhere
6668352S6.2.2.a.verif2TODO: Verification 2ace3Applicable to implementation code, but more directly covered elsewhere
6678394S6.2.2.b.verif3TODO: Verification 3ace3Applicable to implementation code, but more directly covered elsewhere
6688432S6.2.3.verif4TODO: Verification 4ace3Applicable to implementation code, but more directly covered elsewhere
6698473S6.2.4.a.verif5TODO: Verification 5ace3Applicable to implementation code, but more directly covered elsewhere
6708518S6.2.4.b.verif6TODO: Verification 6ace3Applicable to implementation code, but more directly covered elsewhere
6718565S6.2.4.c.verif7TODO: Verification 7ace3Applicable to implementation code, but more directly covered elsewhere
6728615S6.2.4.d.verif8TODO: Verification 8ace3Applicable to implementation code, but more directly covered elsewhere
6738653S6.2.5.a.verif9TODO: Verification 9ace3Applicable to implementation code, but more directly covered elsewhere
6748701S6.2.5.b.verif10TODO: Verification 10ace3Applicable to implementation code, but more directly covered elsewhere
6758726S6.2.5.c.verif11TODO: Verification 11ace3Applicable to implementation code, but more directly covered elsewhere
6768772S6.2.6.a.verif12Verification of Correct Contest Data Decryptionace3Applicable to implementation code, but more directly covered elsewhere
6778816S6.2.7.a.verif13TODO: Verification 13ace3Applicable to implementation code, but more directly covered elsewhere
6788852S6.2.7.b.verif14TODO: Verification 14ace3Applicable to implementation code, but more directly covered elsewhere
6798892S6.2.8.a.verif15TODO: Verification 15ace3Applicable to implementation code, but more directly covered elsewhere
6808947S6.2.8.b.verif16TODO: Verification 16ace3Applicable to implementation code, but more directly covered elsewhere
6818987S6.2.8.c.verif17TODO: Verification 17ace3Applicable to implementation code, but more directly covered elsewhere
6829034S6.2.8.d.verif18TODO: Verification 18ace3Applicable to implementation code, but more directly covered elsewhere
6839059S6.2.8.e.verif19TODO: Verification 19ace3Applicable to implementation code, but more directly covered elsewhere
+
+

[Reload]   index.html +
+ + diff --git a/other/spec-todo/html/index.html b/other/spec-todo/html/index.html new file mode 100644 index 0000000..5133c26 --- /dev/null +++ b/other/spec-todo/html/index.html @@ -0,0 +1,53 @@ + + + + + + + EGDS 2.1.0 Functional requirements and EGRI statuses + + + +

EGDS 2.1.0

+

Functional requirements and EGRI statuses [plain: xreqs.html] +

Other items [plain: xtodos.html] + + diff --git a/other/spec-todo/html/other_items.html b/other/spec-todo/html/other_items.html new file mode 100644 index 0000000..cec3089 --- /dev/null +++ b/other/spec-todo/html/other_items.html @@ -0,0 +1,218 @@ + + + + + + + Other Items + + + +
+

[Reload]   index.html +
+

Other Items

+

[plain: xtodos.html] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
query row ndoc line nsectionstatustext
119S0todoFix references to EGDS in the source code to always include section and page number
220S0todoFix references to EGDS in the source docs to always include section and page number
322S0todoSet panic behavior to abort. See https://gitlab.torproject.org/legacy/trac/-/issues/27199
423S0todoRemove eg/src/example*.rs files, migrate to resource_producer
524S0todoMigrate away from 'anyhow' in library code
627S0todoQ|UG left pad as necessary to ensure length is correct
728S0todoMAYB|serialize format wrapped in version
830S0todocrate feature for guardian secret operations.
932S0todoensure SecretCoefficient is serialized in a fixed-length format
1034S0todorewrite build-docs script in sh
1136S0todoBuild on Linux32
1237S0todoTest on Linux32
1338S0todoBuild on Linux64
1439S0todoTest on Linux64
1540S0todoBuild on Win64
1641S0todoTest on Win64
1742S0todoTest library components for Wasm32
1844S0tododocs/general: style sheet for markdown, ideally match API docs
1946S0todoIf an overvote occurs, the overvote must be captured, encrypted, and never decrypted.
2048S0tododocs/specs/serialization: data formats section
2149S0tododocs/specs/serialization: standards and references section
2250S0tododocs/specs/serialization: election manifest section
2351S0tododocs/specs/serialization: election record section
2452S0tododocs/specs/serialization: vendor data section
2554S0tododocs/api: reference NIST CDF where types clearly correspond. E.g., BallotStyle https://github.com/usnistgov/ElectionResultsReporting/blob/nist-pages/index.md#17_0_2_4_78e0236_1389366224561_797289_2360
2656S0tododocs/implementation guide/Requirements for election systems vendors: complete
2757S0tododocs/implementation guide/Requirements for verifier app authors: complete
2858S0tododocs/implementation guide/roles: consider splitting into separate pages: complete
2959S0tododocs/implementation guide/roles/Election Administrator: complete
3060S0tododocs/implementation guide/roles/Election Guardians: complete
3161S0tododocs/implementation guide/roles/Voters: complete
3262S0tododocs/implementation guide/roles/Political parties and voter-interest organizations: complete
3363S0tododocs/implementation guide/roles/Journalists and other media: complete
3464S0tododocs/implementation guide/Hardware requirements/Gurardian secret key storage: complete
3565S0tododocs/implementation guide/Hardware requirements/Gurardian secret key operations: complete
3666S0tododocs/implementation guide/step-by-step/Advance preparation: complete
3767S0tododocs/implementation guide/step-by-step/Key ceremony: complete
3868S0tododocs/implementation guide/step-by-step/Tally ceremony: complete
3969S0tododocs/implementation guide/step-by-step/Publishing: complete
4070S0tododocs/implementation guide/step-by-step/Verification: complete
4171S0tododocs/implementation guide/step-by-step/Reporting: complete
4273S0tododocs/api: use correct logo
4374S0tododocs/api: complete
4476S0tododocs: complete, #![warn(missing_docs)]
4578S0tododocs: upload docs to github pages (see compliance notes)
4680S0todosecurity review: ensure that no file leaks info through filesize
4782S0tododistinguish between PartySelection, BallotMeasureSelection, CandidateSelection
4883S0todoBallotDefinition doc for write-in option
4984S0todowould be nice to support a PartySelection type vote, rather than rely on the vendor to give us the correct selections explicitly
5086S0todoa trait for types that have pub fn validate(&Self, &ElectionParameters)
5188S0tododocs: more investigation into using rust modules to build documentation along with api, observe how cargo_crev does it with the include_str! macro: https://github.com/crev-dev/cargo-crev/blob/master/cargo-crev/src/doc/mod.rs
5289S0tododocs: more investigation into using mdbook for all project documentation https://github.com/rust-lang/rust/issues/66249
5390S0tododocs: cargo-external-doc is nice but doesn't support virtual manifests https://github.com/Geal/cargo-external-doc
5492S0todoVaryingParameters (n, k) This isn't really an 'index' (ordinal), it's a cardinal number. Maybe we need a general purpose ranged number type.
5594S0todoexe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%)
5695S0todoexe: common parameter: manifest file
5796S0todoexe: common parameter: others
5898S0todopersist/file: create artifact directories if they don't exist. q: what about permissions on guardian secret directories?
59100S0todoperisist: encryption or password protection for guaridan secret key files
60101S0todoutil: read guardian secret key, print info, suppressing secrets
61102S0todoutil: read guardian public key, print info
62104S0todopersist: define standard election directory layout - look at other implementers and users
63105S0tododesign: key file represents its kind: guardian, ..., ?
64107S0todoConsider structures defined in JSON schema https://github.com/usnistgov/ElectionResultsReporting/blob/version2/NIST_V2_election_results_reporting.json
65109S0todoif electionguard.exe fails to read or write a file, check the path to see if it has a leading ~. If so, print a good error message.
66111S0todotest on 32-bit target such as x86 or wasm32
67112S0todotest on big-endian target such as powerpc64-unknown-linux-gnu, s390x-unknown-linux-gnu, riscv64gc-unknown-linux-gnu, or loongarch64-unknown-linux-gnu
68114S0todoelectionguard-test script: incorporate ballots
69115S0todoelectionguard-test script: incorporate tally
70119S0donemove types for numbers mod p and mod q back into 'eg' lib so they can be known at compile time
71120S0donechange obtain_resource_production_result_from_cache_downcast
72121S0doneelectionguard.exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%)
73122S0doneelectionguard.exe: seed: write random seed to artifact file
74123S0doneelectionguard.exe manifest: write ElectionManifest to pretty json file
75124S0doneelectionguard.exe manifest: write ElectionManifest to canonical bytes file
76125S0doneelectionguard.exe parameters: write ElectionParameters to json file
77126S0doneH_V, H_P, H_M, and H_B updated for 2.0 calculation
78127S0doneGenerate joint election public key
79128S0doneExtended base hash H_E
80129S0doneelectionguard-test script: implementation in cmd started
81130S0doneelectionguard-test script: implementation in cmd exercises all (current) functionality
82131S0doneBigUint values (mod p or q) now left-padded as necessary to avoid leaking value via serialized file size
83132S0doneHash values now serialized with 'H(upper hex)' format to match spec
84133S0doneexe: Csprng now seeded with more entropy from the operating system RNG
85134S0doneElection-varying parameters (n and k) now checked for validity
86135S0doneSerialization of BigUints now uses base64 encoding
87136S0doneRename guardian private key to secret key
88137S0doneelectionguard.exe: generate guardian secret key
89138S0doneelectionguard.exe: write guardian secret key
90139S0doneelectionguard.exe: derive guardian public key from secret key
91140S0doneelectionguard.exe: write guardian public key
92141S0doneGuardian i uses 1-based indexing
93142S0donecompute H_E extended base hash
94143S0donecompute joint election public key
95144S0doneelectionguard.exe: write joint election public key to json file
96145S0donestandardize on 'validate' instead of 'verify' when checking deserialized structures
97146S0doneinstead of from_json and to_json implement from_stdioread and to_stdiowrite
98147S0doneevery struct that has Self::from_stdioread*() should prefer Self::from_stdioread_validated() and have a self.validate()
99148S0doneelectionguard.exe: write H_E extended base hash to json file
100149S0doneconvert many uses of if !() { bail!() } to ensure!()
101150S0doneGenerate data structure docs from the Reference Implementation in Rust
102151S0doneeg: New constrained numeric type for indices. Convert n, k, and other indices to this type.
103152S0donebuild-docs script: initial implementation in cmd
104153S0doneevaluate scripting language 'nu' https://www.nushell.sh/
105154S0doneelectionguard-test script: begin rewrite in nu
106155S0donedoc/LICENSE: checked
107156S0donedoc/SECURITY.md: complete
108157S0donedocs/general: begin writing
109158S0donedocs/api: begin writing
110159S0doneRemove link to internal site
111160S0doneVaryingParameters: enum BallotChaining { Prohibited, Allowed, Required, }
112161S0doneremove old EG 1.54 constants
113162S0doneexe: Under artifacts dir, first level dirs are specific to secrecy requirements
114163S0doneBallot define data type
115164S0doneget fixeduint stuff out of bunna branch
116165S0doneMerge code from Anunay
117166S0donedoc/SUPPORT.md: complete
118167S0donedoc/README.md: complete
119168S0donedoc/CODE_OF_CONDUCT.md: complete
120169S0donedoc/BUILDING.md: complete
121170S0doneComplete all planned code reorganization/renaming
122171S0donedocs/implementation guide/References: complete
123172S0donea trait for fn to_canonical_json()
124173S0donemany to_stdiowrite() methods have common code that could be factored into a common function
125174S0donea trait for types that have to_stdiowrite()
126175S0donea trait for types that have to_stdiowrite() and perhaps _pretty() and _canonical() variants
127177S0doneserialize bignums only all as uppercase hex
128187S0todoWhat is a Preencrypted Ballot called after Voter Selections have been applied?
1291404S3.a.gtodoTODO: Do we need separate Administrative (Public|Secret) Key pairs for encryption and signing operations?
1301405S3.a.gtodoTODO: How is it configured which specific other data is to be encrypted with which of the Joint Ballot Data Encryption Public Key and/or Administrative Public Key?
1312456S3.1.3ntodoIf the effective Contest Selection Limit is > 1, could someone evade the Option Selection Limit by both selecting and writing-in the same candidate? Similarly if multiple write-in fields are allowed, couldn't they write-in the same choice multiple times?
1322642S3.2.ctodo(Ref: S3.a.a.b) "EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)"
1332915S3.2.2.atodoeq8 pg22 refers to "the secret key for guardian $G_i$" "the public key for guardian $G_i$" Are these keys ever really used as such in v2.1.0 ?
1343488S3.2.2.otodoEGRI supports a Guardian `l` in complaining to the Election Administrator and all other Guardians. TODO: In practice this likely implies providing a set of data TBD.
1355215S3.5.b.verif9todoTODO: Verification 9
1365340S3.6.1todoTODO: Presumably this ("enables a Guardian to review the set of Ciphertexts marked for decryption") requirement applies to any use of the Guardian Ballot Data Encryption Secret Key as well? -> xreq S3.6.1 EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Ballot Data Encryption Secret Key.
1375341S3.6.1todoDoes EGRI need to determine whether write-in fields may actually be relevant to an outcome, or just always decrypt all write-in fields with every Tally?
1385342S3.6.1todoPresumably, many Ballots will not have any write-in fields actually written-in. Does each Ballot record whether it has a write-in field written-in in a way that avoids the need to decrypt the ballot nonce during the Tally if write-in values are needed?
1395780S3.6.6.atodo"[write-in] data may need to be decrypted if the tallies record a significant number
1406362S3.7todoThe Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: the partial decryption of the Ballot Nonce
1416363S3.7todoThe Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: for every Contest, for every Option Field, the partial decryption
1426405S3.7ThetodoElection Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the partial decryption from each available Guardian?
1436587S4.1todopg. 58 "in a contest where a voter is allowed to make multiple selections, multiple pre-encryption vectors can be combined to form a single contest encryption vector with multiple encryptions of one that precisely matches the standard \EG format."
1446588S4.1todopg. 58 QUESTION: How are they "combined"? E.g., are these multiple encryptions combined such that there is still a single vector `Psi_{i,m}` for Option `i`, or are there additional Option vectors? Note: [S4.1.4 pg. 63] says "For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected", which seems to imply they are multiplied.
1456589S4.1todopg. 58 QUESTION: What about when the effective Option Selection Limit is greater than `1`? Do all possible assignments within the effective (Option and Contest) Selection Limits require distinct Selection Vectors and Selection Hashes? E.g. say a Contest has two options and the Contest and Option Selection Limits are all `5`. How is the selection vector constructed which allows the voter to apply a value of `3` to Option 1 and `2` to Option 2?
1466590S4.1todopg. 58 QUESTION: How to these "multiple pre-encryption vectors" map to "the j-th selection vector" and "the k-th encryption" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?
1476639S4.1.1todopg. 58 QUESTION: S4.1 pg. 57 designates the "null form" explicitly as `Psi_{0,m}`, but (eq. 114 pg. 58) refers to them as `psi_{m+l}` where `1 <= l <= L`. How to these multiple pre-encryption vectors map to "the j-th selection vector" and "the k-th encryption" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?
1486755S4.1.4todopg. 59 EGRS says "A pre-encrypted ballot’s hash will typically be printed directly on the ballot. Ideally, two copies of the ballot hash will be included on each ballot with one remaining permanently with the ballot and the other in an immediately adjacent location on a removable tab. These removable ballot codes are intended to be retained by voters and can be used as an identifier to allow voters to look up their (encrypted) ballots in the election record and confirm that they include the proper short codes as described below."
1496996S4.4todopg. 62 refers to "every pre-encrypted ballot listed in the election record as uncast". QUESTION: Does this imply that every `PreencryptedBallot` generated is recorded in the Election Record?
1506997S4.4todopg. 63 states that "For each uncast ballot, the ballot nonce for that ballot is published in the encryption record." and [S4.5 pg. 64] refers to "every pre-encrypted ballot listed in the election record as uncast". QUESTION: Does this imply that there is a point during the tally process at which generated-but-not-cast `PreencryptedBallots` are no longer accepted? If there are multiple Tallys taken, then it seems really important that this only happen at the final Tally. Someone who submitted a PreencryptedBallot that was somehow delayed in the mail probably does not want their Voter Selections made public.
1519095S7todoTODO: Consider any special features which may be needed for RLAs
+
+

[Reload]   index.html +
+ + diff --git a/other/spec-todo/html/xreqs.html b/other/spec-todo/html/xreqs.html new file mode 100644 index 0000000..eb0705a --- /dev/null +++ b/other/spec-todo/html/xreqs.html @@ -0,0 +1,211 @@ + + + + + + + xreqs.html + + + +

xreqs

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
query row ndoc line nsectionstatustext
119S0todoFix references to EGDS in the source code to always include section and page number
220S0todoFix references to EGDS in the source docs to always include section and page number
322S0todoSet panic behavior to abort. See https://gitlab.torproject.org/legacy/trac/-/issues/27199
423S0todoRemove eg/src/example*.rs files, migrate to resource_producer
524S0todoMigrate away from 'anyhow' in library code
627S0todoQ|UG left pad as necessary to ensure length is correct
728S0todoMAYB|serialize format wrapped in version
830S0todocrate feature for guardian secret operations.
932S0todoensure SecretCoefficient is serialized in a fixed-length format
1034S0todorewrite build-docs script in sh
1136S0todoBuild on Linux32
1237S0todoTest on Linux32
1338S0todoBuild on Linux64
1439S0todoTest on Linux64
1540S0todoBuild on Win64
1641S0todoTest on Win64
1742S0todoTest library components for Wasm32
1844S0tododocs/general: style sheet for markdown, ideally match API docs
1946S0todoIf an overvote occurs, the overvote must be captured, encrypted, and never decrypted.
2048S0tododocs/specs/serialization: data formats section
2149S0tododocs/specs/serialization: standards and references section
2250S0tododocs/specs/serialization: election manifest section
2351S0tododocs/specs/serialization: election record section
2452S0tododocs/specs/serialization: vendor data section
2554S0tododocs/api: reference NIST CDF where types clearly correspond. E.g., BallotStyle https://github.com/usnistgov/ElectionResultsReporting/blob/nist-pages/index.md#17_0_2_4_78e0236_1389366224561_797289_2360
2656S0tododocs/implementation guide/Requirements for election systems vendors: complete
2757S0tododocs/implementation guide/Requirements for verifier app authors: complete
2858S0tododocs/implementation guide/roles: consider splitting into separate pages: complete
2959S0tododocs/implementation guide/roles/Election Administrator: complete
3060S0tododocs/implementation guide/roles/Election Guardians: complete
3161S0tododocs/implementation guide/roles/Voters: complete
3262S0tododocs/implementation guide/roles/Political parties and voter-interest organizations: complete
3363S0tododocs/implementation guide/roles/Journalists and other media: complete
3464S0tododocs/implementation guide/Hardware requirements/Gurardian secret key storage: complete
3565S0tododocs/implementation guide/Hardware requirements/Gurardian secret key operations: complete
3666S0tododocs/implementation guide/step-by-step/Advance preparation: complete
3767S0tododocs/implementation guide/step-by-step/Key ceremony: complete
3868S0tododocs/implementation guide/step-by-step/Tally ceremony: complete
3969S0tododocs/implementation guide/step-by-step/Publishing: complete
4070S0tododocs/implementation guide/step-by-step/Verification: complete
4171S0tododocs/implementation guide/step-by-step/Reporting: complete
4273S0tododocs/api: use correct logo
4374S0tododocs/api: complete
4476S0tododocs: complete, #![warn(missing_docs)]
4578S0tododocs: upload docs to github pages (see compliance notes)
4680S0todosecurity review: ensure that no file leaks info through filesize
4782S0tododistinguish between PartySelection, BallotMeasureSelection, CandidateSelection
4883S0todoBallotDefinition doc for write-in option
4984S0todowould be nice to support a PartySelection type vote, rather than rely on the vendor to give us the correct selections explicitly
5086S0todoa trait for types that have pub fn validate(&Self, &ElectionParameters)
5188S0tododocs: more investigation into using rust modules to build documentation along with api, observe how cargo_crev does it with the include_str! macro: https://github.com/crev-dev/cargo-crev/blob/master/cargo-crev/src/doc/mod.rs
5289S0tododocs: more investigation into using mdbook for all project documentation https://github.com/rust-lang/rust/issues/66249
5390S0tododocs: cargo-external-doc is nice but doesn't support virtual manifests https://github.com/Geal/cargo-external-doc
5492S0todoVaryingParameters (n, k) This isn't really an 'index' (ordinal), it's a cardinal number. Maybe we need a general purpose ranged number type.
5594S0todoexe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%)
5695S0todoexe: common parameter: manifest file
5796S0todoexe: common parameter: others
5898S0todopersist/file: create artifact directories if they don't exist. q: what about permissions on guardian secret directories?
59100S0todoperisist: encryption or password protection for guaridan secret key files
60101S0todoutil: read guardian secret key, print info, suppressing secrets
61102S0todoutil: read guardian public key, print info
62104S0todopersist: define standard election directory layout - look at other implementers and users
63105S0tododesign: key file represents its kind: guardian, ..., ?
64107S0todoConsider structures defined in JSON schema https://github.com/usnistgov/ElectionResultsReporting/blob/version2/NIST_V2_election_results_reporting.json
65109S0todoif electionguard.exe fails to read or write a file, check the path to see if it has a leading ~. If so, print a good error message.
66111S0todotest on 32-bit target such as x86 or wasm32
67112S0todotest on big-endian target such as powerpc64-unknown-linux-gnu, s390x-unknown-linux-gnu, riscv64gc-unknown-linux-gnu, or loongarch64-unknown-linux-gnu
68114S0todoelectionguard-test script: incorporate ballots
69115S0todoelectionguard-test script: incorporate tally
70119S0donemove types for numbers mod p and mod q back into 'eg' lib so they can be known at compile time
71120S0donechange obtain_resource_production_result_from_cache_downcast
72121S0doneelectionguard.exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%)
73122S0doneelectionguard.exe: seed: write random seed to artifact file
74123S0doneelectionguard.exe manifest: write ElectionManifest to pretty json file
75124S0doneelectionguard.exe manifest: write ElectionManifest to canonical bytes file
76125S0doneelectionguard.exe parameters: write ElectionParameters to json file
77126S0doneH_V, H_P, H_M, and H_B updated for 2.0 calculation
78127S0doneGenerate joint election public key
79128S0doneExtended base hash H_E
80129S0doneelectionguard-test script: implementation in cmd started
81130S0doneelectionguard-test script: implementation in cmd exercises all (current) functionality
82131S0doneBigUint values (mod p or q) now left-padded as necessary to avoid leaking value via serialized file size
83132S0doneHash values now serialized with 'H(upper hex)' format to match spec
84133S0doneexe: Csprng now seeded with more entropy from the operating system RNG
85134S0doneElection-varying parameters (n and k) now checked for validity
86135S0doneSerialization of BigUints now uses base64 encoding
87136S0doneRename guardian private key to secret key
88137S0doneelectionguard.exe: generate guardian secret key
89138S0doneelectionguard.exe: write guardian secret key
90139S0doneelectionguard.exe: derive guardian public key from secret key
91140S0doneelectionguard.exe: write guardian public key
92141S0doneGuardian i uses 1-based indexing
93142S0donecompute H_E extended base hash
94143S0donecompute joint election public key
95144S0doneelectionguard.exe: write joint election public key to json file
96145S0donestandardize on 'validate' instead of 'verify' when checking deserialized structures
97146S0doneinstead of from_json and to_json implement from_stdioread and to_stdiowrite
98147S0doneevery struct that has Self::from_stdioread*() should prefer Self::from_stdioread_validated() and have a self.validate()
99148S0doneelectionguard.exe: write H_E extended base hash to json file
100149S0doneconvert many uses of if !() { bail!() } to ensure!()
101150S0doneGenerate data structure docs from the Reference Implementation in Rust
102151S0doneeg: New constrained numeric type for indices. Convert n, k, and other indices to this type.
103152S0donebuild-docs script: initial implementation in cmd
104153S0doneevaluate scripting language 'nu' https://www.nushell.sh/
105154S0doneelectionguard-test script: begin rewrite in nu
106155S0donedoc/LICENSE: checked
107156S0donedoc/SECURITY.md: complete
108157S0donedocs/general: begin writing
109158S0donedocs/api: begin writing
110159S0doneRemove link to internal site
111160S0doneVaryingParameters: enum BallotChaining { Prohibited, Allowed, Required, }
112161S0doneremove old EG 1.54 constants
113162S0doneexe: Under artifacts dir, first level dirs are specific to secrecy requirements
114163S0doneBallot define data type
115164S0doneget fixeduint stuff out of bunna branch
116165S0doneMerge code from Anunay
117166S0donedoc/SUPPORT.md: complete
118167S0donedoc/README.md: complete
119168S0donedoc/CODE_OF_CONDUCT.md: complete
120169S0donedoc/BUILDING.md: complete
121170S0doneComplete all planned code reorganization/renaming
122171S0donedocs/implementation guide/References: complete
123172S0donea trait for fn to_canonical_json()
124173S0donemany to_stdiowrite() methods have common code that could be factored into a common function
125174S0donea trait for types that have to_stdiowrite()
126175S0donea trait for types that have to_stdiowrite() and perhaps _pretty() and _canonical() variants
127177S0doneserialize bignums only all as uppercase hex
128187S0todoWhat is a Preencrypted Ballot called after Voter Selections have been applied?
1291404S3.a.gtodoTODO: Do we need separate Administrative (Public|Secret) Key pairs for encryption and signing operations?
1301405S3.a.gtodoTODO: How is it configured which specific other data is to be encrypted with which of the Joint Ballot Data Encryption Public Key and/or Administrative Public Key?
1312456S3.1.3ntodoIf the effective Contest Selection Limit is > 1, could someone evade the Option Selection Limit by both selecting and writing-in the same candidate? Similarly if multiple write-in fields are allowed, couldn't they write-in the same choice multiple times?
1322642S3.2.ctodo(Ref: S3.a.a.b) "EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)"
1332915S3.2.2.atodoeq8 pg22 refers to "the secret key for guardian $G_i$" "the public key for guardian $G_i$" Are these keys ever really used as such in v2.1.0 ?
1343488S3.2.2.otodoEGRI supports a Guardian `l` in complaining to the Election Administrator and all other Guardians. TODO: In practice this likely implies providing a set of data TBD.
1355215S3.5.b.verif9todoTODO: Verification 9
1365340S3.6.1todoTODO: Presumably this ("enables a Guardian to review the set of Ciphertexts marked for decryption") requirement applies to any use of the Guardian Ballot Data Encryption Secret Key as well? -> xreq S3.6.1 EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Ballot Data Encryption Secret Key.
1375341S3.6.1todoDoes EGRI need to determine whether write-in fields may actually be relevant to an outcome, or just always decrypt all write-in fields with every Tally?
1385342S3.6.1todoPresumably, many Ballots will not have any write-in fields actually written-in. Does each Ballot record whether it has a write-in field written-in in a way that avoids the need to decrypt the ballot nonce during the Tally if write-in values are needed?
1395780S3.6.6.atodo"[write-in] data may need to be decrypted if the tallies record a significant number
1406362S3.7todoThe Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: the partial decryption of the Ballot Nonce
1416363S3.7todoThe Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: for every Contest, for every Option Field, the partial decryption
1426405S3.7ThetodoElection Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the partial decryption from each available Guardian?
1436587S4.1todopg. 58 "in a contest where a voter is allowed to make multiple selections, multiple pre-encryption vectors can be combined to form a single contest encryption vector with multiple encryptions of one that precisely matches the standard \EG format."
1446588S4.1todopg. 58 QUESTION: How are they "combined"? E.g., are these multiple encryptions combined such that there is still a single vector `Psi_{i,m}` for Option `i`, or are there additional Option vectors? Note: [S4.1.4 pg. 63] says "For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected", which seems to imply they are multiplied.
1456589S4.1todopg. 58 QUESTION: What about when the effective Option Selection Limit is greater than `1`? Do all possible assignments within the effective (Option and Contest) Selection Limits require distinct Selection Vectors and Selection Hashes? E.g. say a Contest has two options and the Contest and Option Selection Limits are all `5`. How is the selection vector constructed which allows the voter to apply a value of `3` to Option 1 and `2` to Option 2?
1466590S4.1todopg. 58 QUESTION: How to these "multiple pre-encryption vectors" map to "the j-th selection vector" and "the k-th encryption" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?
1476639S4.1.1todopg. 58 QUESTION: S4.1 pg. 57 designates the "null form" explicitly as `Psi_{0,m}`, but (eq. 114 pg. 58) refers to them as `psi_{m+l}` where `1 <= l <= L`. How to these multiple pre-encryption vectors map to "the j-th selection vector" and "the k-th encryption" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?
1486755S4.1.4todopg. 59 EGRS says "A pre-encrypted ballot’s hash will typically be printed directly on the ballot. Ideally, two copies of the ballot hash will be included on each ballot with one remaining permanently with the ballot and the other in an immediately adjacent location on a removable tab. These removable ballot codes are intended to be retained by voters and can be used as an identifier to allow voters to look up their (encrypted) ballots in the election record and confirm that they include the proper short codes as described below."
1496996S4.4todopg. 62 refers to "every pre-encrypted ballot listed in the election record as uncast". QUESTION: Does this imply that every `PreencryptedBallot` generated is recorded in the Election Record?
1506997S4.4todopg. 63 states that "For each uncast ballot, the ballot nonce for that ballot is published in the encryption record." and [S4.5 pg. 64] refers to "every pre-encrypted ballot listed in the election record as uncast". QUESTION: Does this imply that there is a point during the tally process at which generated-but-not-cast `PreencryptedBallots` are no longer accepted? If there are multiple Tallys taken, then it seems really important that this only happen at the final Tally. Someone who submitted a PreencryptedBallot that was somehow delayed in the mail probably does not want their Voter Selections made public.
1519095S7todoTODO: Consider any special features which may be needed for RLAs
+ + diff --git a/other/spec-todo/html/xtodos.html b/other/spec-todo/html/xtodos.html new file mode 100644 index 0000000..1aef6aa --- /dev/null +++ b/other/spec-todo/html/xtodos.html @@ -0,0 +1,211 @@ + + + + + + + xtodos.html + + + +

xtodos

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
query row ndoc line nsectionstatustext
119S0todoFix references to EGDS in the source code to always include section and page number
220S0todoFix references to EGDS in the source docs to always include section and page number
322S0todoSet panic behavior to abort. See https://gitlab.torproject.org/legacy/trac/-/issues/27199
423S0todoRemove eg/src/example*.rs files, migrate to resource_producer
524S0todoMigrate away from 'anyhow' in library code
627S0todoQ|UG left pad as necessary to ensure length is correct
728S0todoMAYB|serialize format wrapped in version
830S0todocrate feature for guardian secret operations.
932S0todoensure SecretCoefficient is serialized in a fixed-length format
1034S0todorewrite build-docs script in sh
1136S0todoBuild on Linux32
1237S0todoTest on Linux32
1338S0todoBuild on Linux64
1439S0todoTest on Linux64
1540S0todoBuild on Win64
1641S0todoTest on Win64
1742S0todoTest library components for Wasm32
1844S0tododocs/general: style sheet for markdown, ideally match API docs
1946S0todoIf an overvote occurs, the overvote must be captured, encrypted, and never decrypted.
2048S0tododocs/specs/serialization: data formats section
2149S0tododocs/specs/serialization: standards and references section
2250S0tododocs/specs/serialization: election manifest section
2351S0tododocs/specs/serialization: election record section
2452S0tododocs/specs/serialization: vendor data section
2554S0tododocs/api: reference NIST CDF where types clearly correspond. E.g., BallotStyle https://github.com/usnistgov/ElectionResultsReporting/blob/nist-pages/index.md#17_0_2_4_78e0236_1389366224561_797289_2360
2656S0tododocs/implementation guide/Requirements for election systems vendors: complete
2757S0tododocs/implementation guide/Requirements for verifier app authors: complete
2858S0tododocs/implementation guide/roles: consider splitting into separate pages: complete
2959S0tododocs/implementation guide/roles/Election Administrator: complete
3060S0tododocs/implementation guide/roles/Election Guardians: complete
3161S0tododocs/implementation guide/roles/Voters: complete
3262S0tododocs/implementation guide/roles/Political parties and voter-interest organizations: complete
3363S0tododocs/implementation guide/roles/Journalists and other media: complete
3464S0tododocs/implementation guide/Hardware requirements/Gurardian secret key storage: complete
3565S0tododocs/implementation guide/Hardware requirements/Gurardian secret key operations: complete
3666S0tododocs/implementation guide/step-by-step/Advance preparation: complete
3767S0tododocs/implementation guide/step-by-step/Key ceremony: complete
3868S0tododocs/implementation guide/step-by-step/Tally ceremony: complete
3969S0tododocs/implementation guide/step-by-step/Publishing: complete
4070S0tododocs/implementation guide/step-by-step/Verification: complete
4171S0tododocs/implementation guide/step-by-step/Reporting: complete
4273S0tododocs/api: use correct logo
4374S0tododocs/api: complete
4476S0tododocs: complete, #![warn(missing_docs)]
4578S0tododocs: upload docs to github pages (see compliance notes)
4680S0todosecurity review: ensure that no file leaks info through filesize
4782S0tododistinguish between PartySelection, BallotMeasureSelection, CandidateSelection
4883S0todoBallotDefinition doc for write-in option
4984S0todowould be nice to support a PartySelection type vote, rather than rely on the vendor to give us the correct selections explicitly
5086S0todoa trait for types that have pub fn validate(&Self, &ElectionParameters)
5188S0tododocs: more investigation into using rust modules to build documentation along with api, observe how cargo_crev does it with the include_str! macro: https://github.com/crev-dev/cargo-crev/blob/master/cargo-crev/src/doc/mod.rs
5289S0tododocs: more investigation into using mdbook for all project documentation https://github.com/rust-lang/rust/issues/66249
5390S0tododocs: cargo-external-doc is nice but doesn't support virtual manifests https://github.com/Geal/cargo-external-doc
5492S0todoVaryingParameters (n, k) This isn't really an 'index' (ordinal), it's a cardinal number. Maybe we need a general purpose ranged number type.
5594S0todoexe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%)
5695S0todoexe: common parameter: manifest file
5796S0todoexe: common parameter: others
5898S0todopersist/file: create artifact directories if they don't exist. q: what about permissions on guardian secret directories?
59100S0todoperisist: encryption or password protection for guaridan secret key files
60101S0todoutil: read guardian secret key, print info, suppressing secrets
61102S0todoutil: read guardian public key, print info
62104S0todopersist: define standard election directory layout - look at other implementers and users
63105S0tododesign: key file represents its kind: guardian, ..., ?
64107S0todoConsider structures defined in JSON schema https://github.com/usnistgov/ElectionResultsReporting/blob/version2/NIST_V2_election_results_reporting.json
65109S0todoif electionguard.exe fails to read or write a file, check the path to see if it has a leading ~. If so, print a good error message.
66111S0todotest on 32-bit target such as x86 or wasm32
67112S0todotest on big-endian target such as powerpc64-unknown-linux-gnu, s390x-unknown-linux-gnu, riscv64gc-unknown-linux-gnu, or loongarch64-unknown-linux-gnu
68114S0todoelectionguard-test script: incorporate ballots
69115S0todoelectionguard-test script: incorporate tally
70119S0donemove types for numbers mod p and mod q back into 'eg' lib so they can be known at compile time
71120S0donechange obtain_resource_production_result_from_cache_downcast
72121S0doneelectionguard.exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%)
73122S0doneelectionguard.exe: seed: write random seed to artifact file
74123S0doneelectionguard.exe manifest: write ElectionManifest to pretty json file
75124S0doneelectionguard.exe manifest: write ElectionManifest to canonical bytes file
76125S0doneelectionguard.exe parameters: write ElectionParameters to json file
77126S0doneH_V, H_P, H_M, and H_B updated for 2.0 calculation
78127S0doneGenerate joint election public key
79128S0doneExtended base hash H_E
80129S0doneelectionguard-test script: implementation in cmd started
81130S0doneelectionguard-test script: implementation in cmd exercises all (current) functionality
82131S0doneBigUint values (mod p or q) now left-padded as necessary to avoid leaking value via serialized file size
83132S0doneHash values now serialized with 'H(upper hex)' format to match spec
84133S0doneexe: Csprng now seeded with more entropy from the operating system RNG
85134S0doneElection-varying parameters (n and k) now checked for validity
86135S0doneSerialization of BigUints now uses base64 encoding
87136S0doneRename guardian private key to secret key
88137S0doneelectionguard.exe: generate guardian secret key
89138S0doneelectionguard.exe: write guardian secret key
90139S0doneelectionguard.exe: derive guardian public key from secret key
91140S0doneelectionguard.exe: write guardian public key
92141S0doneGuardian i uses 1-based indexing
93142S0donecompute H_E extended base hash
94143S0donecompute joint election public key
95144S0doneelectionguard.exe: write joint election public key to json file
96145S0donestandardize on 'validate' instead of 'verify' when checking deserialized structures
97146S0doneinstead of from_json and to_json implement from_stdioread and to_stdiowrite
98147S0doneevery struct that has Self::from_stdioread*() should prefer Self::from_stdioread_validated() and have a self.validate()
99148S0doneelectionguard.exe: write H_E extended base hash to json file
100149S0doneconvert many uses of if !() { bail!() } to ensure!()
101150S0doneGenerate data structure docs from the Reference Implementation in Rust
102151S0doneeg: New constrained numeric type for indices. Convert n, k, and other indices to this type.
103152S0donebuild-docs script: initial implementation in cmd
104153S0doneevaluate scripting language 'nu' https://www.nushell.sh/
105154S0doneelectionguard-test script: begin rewrite in nu
106155S0donedoc/LICENSE: checked
107156S0donedoc/SECURITY.md: complete
108157S0donedocs/general: begin writing
109158S0donedocs/api: begin writing
110159S0doneRemove link to internal site
111160S0doneVaryingParameters: enum BallotChaining { Prohibited, Allowed, Required, }
112161S0doneremove old EG 1.54 constants
113162S0doneexe: Under artifacts dir, first level dirs are specific to secrecy requirements
114163S0doneBallot define data type
115164S0doneget fixeduint stuff out of bunna branch
116165S0doneMerge code from Anunay
117166S0donedoc/SUPPORT.md: complete
118167S0donedoc/README.md: complete
119168S0donedoc/CODE_OF_CONDUCT.md: complete
120169S0donedoc/BUILDING.md: complete
121170S0doneComplete all planned code reorganization/renaming
122171S0donedocs/implementation guide/References: complete
123172S0donea trait for fn to_canonical_json()
124173S0donemany to_stdiowrite() methods have common code that could be factored into a common function
125174S0donea trait for types that have to_stdiowrite()
126175S0donea trait for types that have to_stdiowrite() and perhaps _pretty() and _canonical() variants
127177S0doneserialize bignums only all as uppercase hex
128187S0todoWhat is a Preencrypted Ballot called after Voter Selections have been applied?
1291404S3.a.gtodoTODO: Do we need separate Administrative (Public|Secret) Key pairs for encryption and signing operations?
1301405S3.a.gtodoTODO: How is it configured which specific other data is to be encrypted with which of the Joint Ballot Data Encryption Public Key and/or Administrative Public Key?
1312456S3.1.3ntodoIf the effective Contest Selection Limit is > 1, could someone evade the Option Selection Limit by both selecting and writing-in the same candidate? Similarly if multiple write-in fields are allowed, couldn't they write-in the same choice multiple times?
1322642S3.2.ctodo(Ref: S3.a.a.b) "EGRI enables the same set of Guardians to re-use their keys from previous elections (TODO: Don't n, k, p, q, g, etc have to remain the same?)"
1332915S3.2.2.atodoeq8 pg22 refers to "the secret key for guardian $G_i$" "the public key for guardian $G_i$" Are these keys ever really used as such in v2.1.0 ?
1343488S3.2.2.otodoEGRI supports a Guardian `l` in complaining to the Election Administrator and all other Guardians. TODO: In practice this likely implies providing a set of data TBD.
1355215S3.5.b.verif9todoTODO: Verification 9
1365340S3.6.1todoTODO: Presumably this ("enables a Guardian to review the set of Ciphertexts marked for decryption") requirement applies to any use of the Guardian Ballot Data Encryption Secret Key as well? -> xreq S3.6.1 EGRS enables a Guardian to review the set of Ciphertexts marked for decryption prior to use of their Guardian Ballot Data Encryption Secret Key.
1375341S3.6.1todoDoes EGRI need to determine whether write-in fields may actually be relevant to an outcome, or just always decrypt all write-in fields with every Tally?
1385342S3.6.1todoPresumably, many Ballots will not have any write-in fields actually written-in. Does each Ballot record whether it has a write-in field written-in in a way that avoids the need to decrypt the ballot nonce during the Tally if write-in values are needed?
1395780S3.6.6.atodo"[write-in] data may need to be decrypted if the tallies record a significant number
1406362S3.7todoThe Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: the partial decryption of the Ballot Nonce
1416363S3.7todoThe Election Record records for every Ballot Decryption Operation: for every participating Guardian: for every Ballot in the `Challenged` state: for every Contest, for every Option Field, the partial decryption
1426405S3.7ThetodoElection Record records for every Tally: for every Contest: for every (Option or Additional) homomorphically-tallied Data Field: the partial decryption from each available Guardian?
1436587S4.1todopg. 58 "in a contest where a voter is allowed to make multiple selections, multiple pre-encryption vectors can be combined to form a single contest encryption vector with multiple encryptions of one that precisely matches the standard \EG format."
1446588S4.1todopg. 58 QUESTION: How are they "combined"? E.g., are these multiple encryptions combined such that there is still a single vector `Psi_{i,m}` for Option `i`, or are there additional Option vectors? Note: [S4.1.4 pg. 63] says "For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected", which seems to imply they are multiplied.
1456589S4.1todopg. 58 QUESTION: What about when the effective Option Selection Limit is greater than `1`? Do all possible assignments within the effective (Option and Contest) Selection Limits require distinct Selection Vectors and Selection Hashes? E.g. say a Contest has two options and the Contest and Option Selection Limits are all `5`. How is the selection vector constructed which allows the voter to apply a value of `3` to Option 1 and `2` to Option 2?
1466590S4.1todopg. 58 QUESTION: How to these "multiple pre-encryption vectors" map to "the j-th selection vector" and "the k-th encryption" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?
1476639S4.1.1todopg. 58 QUESTION: S4.1 pg. 57 designates the "null form" explicitly as `Psi_{0,m}`, but (eq. 114 pg. 58) refers to them as `psi_{m+l}` where `1 <= l <= L`. How to these multiple pre-encryption vectors map to "the j-th selection vector" and "the k-th encryption" referred to in [S4.2.1 eq. 121 pg. 61] to derive xi_{i,j,k}?
1486755S4.1.4todopg. 59 EGRS says "A pre-encrypted ballot’s hash will typically be printed directly on the ballot. Ideally, two copies of the ballot hash will be included on each ballot with one remaining permanently with the ballot and the other in an immediately adjacent location on a removable tab. These removable ballot codes are intended to be retained by voters and can be used as an identifier to allow voters to look up their (encrypted) ballots in the election record and confirm that they include the proper short codes as described below."
1496996S4.4todopg. 62 refers to "every pre-encrypted ballot listed in the election record as uncast". QUESTION: Does this imply that every `PreencryptedBallot` generated is recorded in the Election Record?
1506997S4.4todopg. 63 states that "For each uncast ballot, the ballot nonce for that ballot is published in the encryption record." and [S4.5 pg. 64] refers to "every pre-encrypted ballot listed in the election record as uncast". QUESTION: Does this imply that there is a point during the tally process at which generated-but-not-cast `PreencryptedBallots` are no longer accepted? If there are multiple Tallys taken, then it seems really important that this only happen at the final Tally. Someone who submitted a PreencryptedBallot that was somehow delayed in the mail probably does not want their Voter Selections made public.
1519095S7todoTODO: Consider any special features which may be needed for RLAs
+ + diff --git a/other/spec-todo/jj-query-xreqs-stats b/other/spec-todo/jj-query-xreqs-stats new file mode 100644 index 0000000..f2dbb7a --- /dev/null +++ b/other/spec-todo/jj-query-xreqs-stats @@ -0,0 +1,90 @@ +#!/usr/bin/sh + +set -o errexit +set -x +this_script_fullpath=$(readlink -f -- "$0") +this_script=$(basename -- "$this_script_fullpath") +this_script_dir=$(dirname -- "$this_script_fullpath") +set +x +set +o errexit + +verbose=0 + +do_exit() { + exit_status=$( printf '%d' "${1:-fail}" 2>>/dev/null ) + if [ $? != 0 ]; then exit_status=127; fi + + if [ $exit_status != 0 ]; then + printf '%s: Exiting with status: %d\n' "$this_script" "$exit_status" | cat -t >&2 + elif [ 0 -lt "$verbose" ]; then + printf '%s: Exiting with status: %d\n' "$this_script" "$exit_status" | cat -t + fi + exit $exit_status +} + +sqlite3_db_file=${1:-} +if [ ! -f "$sqlite3_db_file" ]; then + printf 'No db file at: %s\n' "$sqlite3_db_file" >&2 + do_exit 91 +fi + +yyyymmdd=$(date +%Y-%m-%d) +printf 'Date is: %s\n' "$yyyymmdd" + +csv_out_file="${sqlite3_db_file}.totals.${yyyymmdd}.csv" +if [ -f "$csv_out_file" ]; then + printf 'Removing existing out file at: %s\n' "$csv_out_file" + (set +x; rm "$csv_out_file") + if [ $? != 0 ]; then + printf 'Couldnt rm existing out file at: %s\n' "$csv_out_file" >&2 + do_exit 92 + fi +fi + +printf 'Writing: %s\n' "$csv_out_file" +(set -x; umask 077; touch "$csv_out_file") +if [ $? != 0 ]; then + printf 'Couldnt create out file at: %s\n' "$csv_out_file" >&2 + do_exit 93 +fi + +sqlite3 "$sqlite3_db_file" >>"$csv_out_file" <>/dev/null ) + if [ $? != 0 ]; then exit_status=127; fi + + if [ $exit_status != 0 ]; then + printf '%s: Exiting with status: %d\n' "$this_script" "$exit_status" | cat -t >&2 + elif [ 0 -lt "$verbose" ]; then + printf '%s: Exiting with status: %d\n' "$this_script" "$exit_status" | cat -t + fi + exit $exit_status +} + +act=1 +srcdst_file='' +db_file='' +sql_dump_file='' +html_dir='' +watch=0 +while [ 0 -lt $# ]; do + arg="${1:-}" + case "$arg" in + -v | --verbose) + verbose=$(( verbose + 1 )) + ;; + -n | --no-act) + act=0 + ;; + --watch) + watch=1 + ;; + --db-file=*) + db_file=${arg#*=} + ;; + --sql-dump-file=*) + sql_dump_file=${arg#*=} + ;; + --html-dir=*) + html_dir=${arg#*=} + ;; + --) + shift + break + ;; + --help) + do_help >&1 2>&2 + do_exit 0 + ;; + -?*) + printf '%s: Unknown option: %s\n' "$this_script" "$arg" >&2 + do_exit 3 + ;; + *) + if [ -n "${srcdst_file:-}" ]; then + printf '%s: Error: Ony one non-option arg allowed (the src-dest file)\n' "$this_script" "$arg" >&2 + do_exit 4 + fi + srcdst_file=$arg + ;; + esac + shift +done + +if [ "$act" = 0 ] && [ "$verbose" = 0 ] ; then verbose=1; fi + +printf '===================================== %s ======================================================\n' $this_script | cat -t >&2 + +if [ 0 -lt "$verbose" ]; then + ( + printf '%s: started: %s\n\n' "$this_script" "$this_script_fullpath" + printf 'pwd: %s' "$PWD" + printf 'this_script_fullpath: %s\n' "$this_script_fullpath" + printf 'this_script: %s\n' "$this_script" + printf 'this_script_dir: %s\n' "$this_script_dir" + printf 'this_script_rel_dir: %s\n' "$this_script_rel_dir" + + ) | cat -t +fi + +export TIME='do_exit status: %x\nelapsed real: %e s\nmax resident: %M KiB\npage faults: %R' +#time_exe=$(which time) +time_exe= + +echo #-------------- munge-reqs crate dir + +crate_dir=$this_script_rel_dir + +if ! [ -d "$crate_dir" ]; then + printf '%s: Error: munge-reqs crate dir doesn'"'"'t exist: %s\n' "$this_script" "$crate_dir" | cat -t >&2 + do_exit 71 +fi + +crate_dir=$(realpath --relative-to="$PWD" "$crate_dir") + +echo #-------------- srcdst_file + +if [ -z "$srcdst_file" ]; then + printf '%s: Error: Please specify [srcdst_file] as the non-option arg\n' "$this_script" | cat -t >&2 + do_exit 72 +fi + +srcdst_file=$(realpath --relative-to="$PWD" "$srcdst_file") + +if [ 0 -lt "$verbose" ]; then + printf 'srcdst_file (rel to %s): %s\n' "$PWD" "$srcdst_file" | cat -t +fi + +if ! [ -f "$srcdst_file" ]; then + printf '%s: Error: [srcdst_file] doesn'"'"'t exist: %s\n' "$this_script" "$srcdst_file" | cat -t >&2 + do_exit 73 +fi + +if [ 0 -lt "$verbose" ]; then + printf 'srcdst_file: %s\n' "$srcdst_file" | cat -t + (set -x; ls -al "$srcdst_file") | cat -t +fi + +echo #-------------- db_file + +if [ -z "$db_file" ]; then + db_file="${srcdst_file}.db" +fi + +if [ 0 -lt "$verbose" ]; then + printf 'db_file (rel to %s): %s\n' "$PWD" "$db_file" | cat -t + (set -x; ls -al "$db_file") 2>&1 | cat -t +fi + +echo #-------------- sql_dump_file + +if [ -z "$sql_dump_file" ]; then + sql_dump_file="${db_file}.sql" +fi + +if [ 0 -lt "$verbose" ]; then + printf 'sql_dump_file (rel to %s): %s\n' "$PWD" "$sql_dump_file" | cat -t + (set -x; ls -al "$sql_dump_file") 2>&1 | cat -t +fi + +echo #-------------- html_dir + +if [ -z "$html_dir" ]; then + html_dir=$(dirname -- "$db_file")/html +fi + +html_dir=$(realpath --relative-to="$PWD" "$html_dir") + +if [ 0 -lt "$verbose" ]; then + printf 'html_dir (rel to %s): %s\n' "$PWD" "$html_dir" | cat -t + (set -x; ls -ald "$html_dir") 2>&1 | cat -t +fi + +if [ -d "$html_dir" ]; then + printf 'html_dir exists: %s\n' "$html_dir" + (set -x; ls -ald "$html_dir") 2>&1 | cat -t + if [ 0 -lt "$act" ]; then + (set -x; rm -rf "$html_dir") + fi +fi + +if ! [ -d "$html_dir" ]; then + if [ 0 -lt "$act" ]; then + printf 'html_dir does not exist, creating: %s\n' "$html_dir" | cat -t + (set -x; umask 077; mkdir "$html_dir") + (set -x; ls -ald "$html_dir") 2>&1 | cat -t + else + printf 'html_dir does not exist (not creating due to -n): %s\n' "$html_dir" | cat -t + fi +fi + +echo #-------------- + +if [ 0 -lt "$verbose" ]; then + ( + printf 'pwd: %s\n' "$PWD" + printf 'crate_dir: %s\n' "$crate_dir" + printf 'srcdst_file: %s\n' "$srcdst_file" + printf 'db_file: %s\n' "$db_file" + printf 'sql_dump_file: %s\n' "$sql_dump_file" + printf 'html_dir: %s\n' "$html_dir" + ) | cat -t +fi +echo + +( + this_script_file2=./$(realpath --relative-to="$crate_dir" "$this_script_fullpath") + srcdst_file2=$(realpath --relative-to="$crate_dir" "$srcdst_file") + db_file2=$(realpath --relative-to="$crate_dir" "$db_file") + sql_dump_file2=$(realpath --relative-to="$crate_dir" "$sql_dump_file") + db_file2=$(realpath --relative-to="$crate_dir" "$db_file") + html_dir2=$(realpath --relative-to="$crate_dir" "$html_dir") + + prev_pwd="$PWD" + + printf '+ cd %s\n' "$crate_dir" + cd "$crate_dir" + if ! [ $? = 0 ]; then + printf '%s: Error: could not cd to crate dir: %s\n' "$this_script" "$crate_dir" | cat -t >&2 + do_exit 74 + fi + + if [ 0 -lt "$verbose" ]; then + ( + printf 'pwd: %s\n' "$PWD" + printf 'this_script_file: %s\n' "$this_script_file2" + printf 'db_file: %s\n' "$db_file2" + printf 'sql_dump_file: %s\n' "$sql_dump_file2" + printf 'html_dir: %s\n' "$html_dir2" + printf 'srcdst_file: %s\n' "$srcdst_file2" + ) | cat -t + fi + echo + + if [ 0 -lt "$watch" ]; then + + cargo_toml=Cargo.toml + + clear='-c' + clear='' + + poll='' + poll='--poll' + + restart='--no-restart' + + # cargo watch --why -w ../TODO-2.1.txt -w Cargo.toml -w ./munge-reqs -w src/ -- ./munge-reqs ../TODO-2.1.txt ../TODO-2.1.txt.db + + v='' + no_act='-n' + if [ 0 -lt "$act" ]; then + no_act='' + if [ 0 -lt "$verbose" ]; then + v='-v' + fi + fi + + echo + echo + ( set +x + cargo watch \ + $clear $restart \ + --why \ + $poll \ + -w "$this_script_file2" \ + -w "$srcdst_file2" \ + -w Cargo.toml \ + -w src/ \ + -- "$this_script_file2" $v $no_act \ + "--db-file=$db_file2" \ + "--sql-dump-file=$sql_dump_file2" \ + "--html-dir=$html_dir2" \ + "$srcdst_file2" + ) + + else # not watch + + #(set -x; "$time_exe" cargo run --release -- --db "$db_file" --html-dir "$html_dir" "$srcdst_file") + (set -x; time cargo run --release -- --db "$db_file2" --html-dir "$html_dir2" "$srcdst_file2") + + fi +) + +if ! [ 0 -lt "$act" ]; then + + printf '[no-act mode]\n' + +else + # src_dst_dir=$(dirname -- "$srcdst_file") + # echo sha256sum "$srcdst_file" "${src_dst_dir}/*.bak.txt" + # sha256sum "$srcdst_file" "${src_dst_dir}"/*.bak.txt + + sqlite3 "$db_file" .dump >"$sql_dump_file" + + if false; then + sqlite3 "$db_file" .dump \ + | grep -v "INSERT INTO lines VALUES" \ + | grep -v "INSERT INTO xreqs VALUES" \ + | grep -v "INSERT INTO xdones VALUES" \ + | grep -v "INSERT INTO sections VALUES" \ + | grep -v "INSERT INTO xtodos VALUES" + fi + + printf 'qty lines: ' + sqlite3 "$db_file" <<'EOSQL' +select count(*) from lines +EOSQL + + printf 'qty xreqs: ' + sqlite3 "$db_file" <<'EOSQL' +select count(*) from xreqs +EOSQL + + printf 'qty xdones: ' + sqlite3 "$db_file" <<'EOSQL' +select count(*) from xdones +EOSQL + + printf 'qty sections: ' + sqlite3 "$db_file" <<'EOSQL' +select count(*) from sections +EOSQL + + printf 'qty xtodos: ' + sqlite3 "$db_file" <<'EOSQL' +select count(*) from xtodos +EOSQL + + csv_out_file="${db_file}.lines.csv" + [ -f "$csv_out_file" ] && rm "$csv_out_file" + printf 'Writing: %s\n' "$csv_out_file" | cat -t + sqlite3 "$db_file" >"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <"$csv_out_file" <, + + /// html_dir + #[argh(option)] + pub html_dir: Option, + + /// filename + #[argh(positional)] + pub file: PathBuf, +} diff --git a/other/spec-todo/munge-reqs/src/db.rs b/other/spec-todo/munge-reqs/src/db.rs new file mode 100644 index 0000000..13ee9fe --- /dev/null +++ b/other/spec-todo/munge-reqs/src/db.rs @@ -0,0 +1,145 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![allow(clippy::unwrap_used)] +#![deny(elided_lifetimes_in_paths)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::type_complexity)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::let_and_return)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + borrow::{ + Cow, + //Borrow, + }, + cell::OnceCell, + //collections::{BTreeSet, BTreeMap}, + //collections::{HashSet, HashMap}, + ffi::OsString, + fs::File, + //hash::{BuildHasher, Hash, Hasher}, + io::{BufRead, BufReader, BufWriter, Cursor, Write}, + //iter::zip, + //marker::PhantomData, + sync::LazyLock, + path::{Path, PathBuf}, + process::ExitCode, + //rc::Rc, + //str::FromStr, + //sync::{, + //Arc, + //OnceLock, + //}, +}; + +use anyhow::{Context, Result, anyhow, bail, ensure}; +use argh::FromArgs; +//use either::Either; +//use futures_lite::future::{self, FutureExt}; +//use hashbrown::HashMap; +//use rand::{distr::Uniform, Rng, RngCore}; +use regex::{Captures, Regex}; +use rusqlite::{Connection, Result as RusqliteResult, backup::StepResult, params}; +use serde_json::{Value as JsonValue, json}; +//use serde::{Deserialize, Serialize}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +//use tracing::{debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, warn}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::{ + clargs::*, + //db::*, + files::*, + html_writer::*, +}; + +//=================================================================================================| + +pub(crate) fn sqlite_open_flags() -> rusqlite::OpenFlags { + use rusqlite::OpenFlags; + OpenFlags::SQLITE_OPEN_READ_WRITE + | OpenFlags::SQLITE_OPEN_CREATE + | OpenFlags::SQLITE_OPEN_NO_MUTEX + | OpenFlags::SQLITE_OPEN_EXRESCODE + | OpenFlags::SQLITE_OPEN_PRIVATE_CACHE +} + +//-------------------------------------------------------------------------------------------------| + +pub(crate) fn create_in_memory_db() -> Result { + let open_flags = sqlite_open_flags(); + println!("Opening in-memory db"); + let db = Connection::open_in_memory_with_flags(open_flags).context("opening in-memory")?; + db.execute_batch(include_str!("init.sql"))?; + Ok(db) +} + + +//-------------------------------------------------------------------------------------------------| + +pub(crate) fn save_db_to_file(db: &Connection, clargs: &Clargs, db_path: &Path) -> Result<()> { + let Some(db_path) = &clargs.db else { + println!("No db file specified - not saving."); + return Ok(()); + }; + + println!("Saving db to file: {}", db_path.display()); + { + let mut db_to = Connection::open_with_flags(db_path, sqlite_open_flags())?; + + { + use rusqlite::backup::Backup; + let mut backup = Backup::new(db, &mut db_to)?; + + while backup.step(-1)? != StepResult::Done {} + } + + db_to + .close() + .map_err(|(db_to, error)| error) + .context("Closing db file")?; + } + println!("Saved db to file: {}", db_path.display()); + + Ok(()) +} + +//-------------------------------------------------------------------------------------------------| diff --git a/other/spec-todo/munge-reqs/src/files.rs b/other/spec-todo/munge-reqs/src/files.rs new file mode 100644 index 0000000..66a4134 --- /dev/null +++ b/other/spec-todo/munge-reqs/src/files.rs @@ -0,0 +1,234 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![allow(clippy::unwrap_used)] +#![deny(elided_lifetimes_in_paths)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::type_complexity)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::let_and_return)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + //borrow::{, + //Cow, + //Borrow, + //}, + //cell::RefCell, + //collections::{BTreeSet, BTreeMap}, + //collections::{HashSet, HashMap}, + ffi::OsString, + //hash::{BuildHasher, Hash, Hasher}, + //io::{BufRead, Cursor}, + //iter::zip, + //marker::PhantomData, + path::{Path, PathBuf}, + //process::ExitCode, + //rc::Rc, + //str::FromStr, + //sync::{, + //Arc, + //OnceLock, + //}, +}; + +use anyhow::{Context, Result, anyhow, bail, ensure}; +//use either::Either; +//use futures_lite::future::{self, FutureExt}; +//use hashbrown::HashMap; +//use rand::{distr::Uniform, Rng, RngCore}; +//use serde::{Deserialize, Serialize}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +//use tracing::{debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, warn}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::{ + clargs::*, + db::*, + //files::*, + html_writer::*, +}; + +//=================================================================================================| + +pub(crate) fn hash_file>(path: P, opt_name_for_msg: Option<&str>) -> Result<[u8; 32]> { + use sha3::{Digest, Sha3_256}; + let mut hasher = Sha3_256::new(); + + if let Some(name_for_msg) = opt_name_for_msg { + let path_quoted = path.as_ref().to_string_lossy().into_owned(); + let path_quoted = shell_words::quote(&path_quoted); + println!("For {name_for_msg} {path_quoted}:"); + }; + { + let mut file = std::fs::File::open(&path).with_context(|| { + let path_quoted = path.as_ref().to_string_lossy().into_owned(); + let path_quoted = shell_words::quote(&path_quoted); + if let Some(name_for_msg) = opt_name_for_msg { + format!("Reading {name_for_msg} {path_quoted}") + } else { + format!("Reading input file {path_quoted}") + } + })?; + let _qty_bytes = std::io::copy(&mut file, &mut hasher)?; + //print!(" {_qty_bytes} hashed "); + } + let hash_value: [u8; 32] = hasher.finalize().into(); + + if opt_name_for_msg.is_some() { + let mut s = [0u8; 64]; + faster_hex::hex_encode_upper(&hash_value, &mut s).unwrap(); + let s = std::str::from_utf8(&s).unwrap(); + println!(" SHA-3-256: {s}"); + } + Ok(hash_value) +} + +pub(crate) fn back_up_and_replace_file(read_lines_path: &Path, write_lines_path: &Path) -> Result<()> { + //println!("Backing up file: {}", read_lines_path.display()); + let read_lines_hash = hash_file(read_lines_path, Some("input file"))?; + let write_lines_hash = hash_file(write_lines_path, Some("output file"))?; + + if read_lines_hash == write_lines_hash { + println!("File has not changed"); + fs_rm_file(write_lines_path)?; + return Ok(()); + } + + let opt_old_ext = read_lines_path.extension(); + + let mut opt_prev_path: Option = None; + for n in (0usize..=5).rev() { + //println!("======== n{n}"); + let n_path = { + let mut n_path = PathBuf::from(read_lines_path); + if 0 < n { + // Remove `old_ext`, if it exists + if let Some(old_ext) = opt_old_ext { + //println!("n{n} old_ext(1): {old_ext:?}"); + n_path.set_extension(""); + //println!("n{n} n_path after set_extension(1): {}", n_path.display()); + } + + // Append `new_ext` + let mut new_ext = OsString::from(format!("backup.{n}.bak")); + n_path = path_filename_append_ext(n_path, new_ext)?; + //println!("n{n} n_path after path_filename_append_ext(2): {}", n_path.display()); + + // Put back `old_ext`, if it existed + if let Some(old_ext) = opt_old_ext { + //println!("n{n} old_ext(2): {old_ext:?}"); + n_path = path_filename_append_ext(n_path, old_ext)?; + //println!("n{n} n_path after path_filename_append_ext(3): {}", n_path.display()); + } + } + n_path + }; + //println!("n{n} opt_prev_path: {opt_prev_path:?}, n_path: {n_path:?}"); + + if let Some(to_path) = &opt_prev_path { + let from_path = &n_path; + if from_path.try_exists()? { + //if n == 0 { + // fs_cp(from_path, to_path)?; + //} else { + fs_mv(from_path, to_path)?; + //} + } + } + opt_prev_path = Some(n_path); + } + + // Move/rename the output file input file to overwrite the input file. + fs_mv(write_lines_path, read_lines_path)?; + + Ok(()) +} + +pub(crate) fn path_filename_append_ext, E: AsRef>( + path_in: P, + ext: E, +) -> Result { + let mut pb = path_in.as_ref().to_path_buf(); + let mut filename = pb + .file_name() + .context("expecting file name")? + .to_os_string(); + filename.push("."); + filename.push(ext); + pb.set_file_name(filename); + Ok(pb) +} + +pub(crate) fn fs_cp, Q: AsRef>(from_path: P, to_path: Q) -> Result<()> { + fs_cp_or_mv_(true, from_path, to_path) +} + +pub(crate) fn fs_mv, Q: AsRef>(from_path: P, to_path: Q) -> Result<()> { + fs_cp_or_mv_(false, from_path, to_path) +} + +pub(crate) fn fs_cp_or_mv_, Q: AsRef>( + cp_not_mv: bool, + from_path: P, + to_path: Q, +) -> Result<()> { + let action = ["mv", "cp"][cp_not_mv as usize]; + let from_path_quoted = from_path.as_ref().to_string_lossy().into_owned(); + let from_path_quoted = shell_words::quote(&from_path_quoted); + let to_path_quoted = to_path.as_ref().to_string_lossy().into_owned(); + let to_path_quoted = shell_words::quote(&to_path_quoted); + let s = format!("{action} {from_path_quoted} {to_path_quoted}"); + println!("{s}"); + if cp_not_mv { + std::fs::copy(from_path, to_path).context(s)?; + } else { + std::fs::rename(from_path, to_path).context(s)?; + } + Ok(()) +} + +pub(crate) fn fs_rm_file>(rm_path: P) -> Result<()> { + let rm_path_quoted = rm_path.as_ref().to_string_lossy().into_owned(); + let rm_path_quoted = shell_words::quote(&rm_path_quoted); + let s = format!("rm {rm_path_quoted}"); + println!("{s}"); + std::fs::remove_file(rm_path).context(s)?; + Ok(()) +} diff --git a/other/spec-todo/munge-reqs/src/html_writer.rs b/other/spec-todo/munge-reqs/src/html_writer.rs new file mode 100644 index 0000000..861d44d --- /dev/null +++ b/other/spec-todo/munge-reqs/src/html_writer.rs @@ -0,0 +1,515 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![deny(elided_lifetimes_in_paths)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::type_complexity)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::let_and_return)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + borrow::{ + Cow, + //Borrow, + }, + //cell::RefCell, + collections::BTreeMap, //{BTreeSet, }, + //collections::{HashSet, HashMap}, + ffi::OsString, + fs::File, + //hash::{BuildHasher, Hash, Hasher}, + io::{ //BufRead, Cursor + BufWriter + }, + //iter::zip, + //marker::PhantomData, + path::{Path, PathBuf}, + //rc::Rc, + //str::FromStr, + //sync::{, + //Arc, + //OnceLock, + //}, +}; + +use anyhow::{Context, Result, anyhow, bail, ensure}; +use rusqlite::Connection; +//use either::Either; +//use futures_lite::future::{self, FutureExt}; +//use hashbrown::HashMap; +//use rand::{distr::Uniform, Rng, RngCore}; +//use serde::{Deserialize, Serialize}; +use serde_json::{Value as JsonValue, json}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +//use tracing::{debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, warn}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use minijinja::{Environment, Value as MjValue, context}; + +use crate::{ + clargs::*, + db::*, + files::*, + //html_writer::*, +}; + +//=================================================================================================| + +type Cowstaticstr = Cow<'static, str>; + +type CtxKey = Cowstaticstr; +type CtxValue = MjValue; +type Ctx = BTreeMap; + +pub(crate) struct HtmlWriter<'c> { + clargs: &'c Clargs, + html_dir_path: &'c PathBuf, + env: Environment<'static>, + ctx: Ctx, +} + +static TEMPLATE_COMMON_CSS_NAME: &str = "common.css"; +#[rustfmt::skip] +static TEMPLATE_COMMON_CSS_CONTENT: &str = +r#" :root { + color-scheme: light dark; + --light-bg: white; + --light-color: black; + --light-link-color: blue; + --light-table-border-color: black; + --light-table-header-color: black; + --dark-bg: #353535; + --dark-color: white; + --dark-link-color: #d2991d; + --dark-table-border-color: #666666; + --dark-table-header-color: #dddddd; + } + * { + background-color: light-dark(var(--light-bg), var(--dark-bg)); + color: light-dark(var(--light-color), var(--dark-color)); + } + html { + line-height: 1.15; /* from github.com/necolas/normalize.css */ + -webkit-text-size-adjust: 100%; /* from github.com/necolas/normalize.css */ + } + a { + background-color: transparent; + color: light-dark(var(--light-link-color), var(--dark-link-color)); } + table { + border-collapse: collapse; + border: 2px solid light-dark(var(--light-table-border-color), var(--dark-table-border-color)); } + td, th { + border: 1px solid light-dark(var(--light-table-border-color), var(--dark-table-border-color)); + padding: 1px 1px; } + th { + color: light-dark(var(--light-table-header-color), var(--dark-table-header-color)); + text-align: center; + } + td { + /* text-align: center; */ + } +"#; + +static TEMPLATE_HTML_BLOCK_RELOAD_NAME: &str = "block_reload.html"; +#[rustfmt::skip] +static TEMPLATE_HTML_BLOCK_RELOAD_CONTENT: &str = +r#"
+

[Reload]   index.html +
+"#; + +static TEMPLATE_HTML_HEADER_NAME: &str = "header.html"; +#[rustfmt::skip] +static TEMPLATE_HTML_HEADER_CONTENT: &str = +r#" + + + + + {{ title }} + + + +"#; + +static TEMPLATE_HTML_FOOTER_NAME: &str = "footer.html"; +#[rustfmt::skip] +static TEMPLATE_HTML_FOOTER_CONTENT: &str = +r#" + +"#; + +impl<'c> HtmlWriter<'c> { + pub fn new(clargs: &'c Clargs) -> Result> { + let Some(html_dir_path) = &clargs.html_dir else { + println!("No HTML dir specified."); + return Ok(None); + }; + println!("HTML dir: {}", html_dir_path.display()); + + //let html_dir_path = html_dir_path.to_path_buf(); + + let html_dir_exists = html_dir_path + .try_exists() + .with_context(|| format!("when finding HTML dir: {}", html_dir_path.display()))?; + ensure!( + html_dir_exists, + "HTML dir does not exist: {}", + html_dir_path.display() + ); + + let metadata = std::fs::metadata(html_dir_path) + .with_context(|| format!("when checking HTML dir: {}", html_dir_path.display()))?; + ensure!( + metadata.is_dir(), + "HTML dir is not a directory: {}", + html_dir_path.display() + ); + + let mut env = Environment::new(); + env.set_keep_trailing_newline(true); + env.set_trim_blocks(true); + env.set_lstrip_blocks(true); + + env.add_template(TEMPLATE_COMMON_CSS_NAME, TEMPLATE_COMMON_CSS_CONTENT)?; + env.add_template(TEMPLATE_HTML_BLOCK_RELOAD_NAME, TEMPLATE_HTML_BLOCK_RELOAD_CONTENT)?; + env.add_template(TEMPLATE_HTML_HEADER_NAME, TEMPLATE_HTML_HEADER_CONTENT)?; + env.add_template(TEMPLATE_HTML_FOOTER_NAME, TEMPLATE_HTML_FOOTER_CONTENT)?; + + let self_ = Self { + clargs, + html_dir_path, + env, + ctx: Ctx::new(), + }; + Ok(Some(self_)) + } + + fn ctx_set(&mut self, k: K, v: V) -> Option + where + K: Into, + V: Into, + { + let k = k.into(); + let v = v.into(); + self.ctx.insert(k, v) + } + + fn query_xreqs_statuses(&mut self, db: &Connection) -> Result<()> { + static SQL: &str = r#" + SELECT xr.line_n, xr.section, xr.xtext, xr.status_code, xr.status_note, st.ordinal, st.xtext + FROM xreqs xr + LEFT OUTER JOIN xreq_statuses st ON xr.status_code = st.status_code + ORDER BY line_n, section ASC;"#; + let mut stmt = db.prepare(SQL)?; + let mut row_n = 0_usize; + let mapped_rows_iter = stmt.query_map([], |row| { + row_n += 1; + let row: Ctx = [ + (Cowstaticstr::from("row_n"), MjValue::from(row_n)), + ("line_n".into(), row.get::<_, usize>(0)?.into()), + ("section".into(), row.get::<_, String>(1)?.into()), + ("text".into(), row.get::<_, String>(2)?.into()), + ( + "status_code".into(), + row.get::<_, Option>(3)?.unwrap_or_default().into(), + ), + ( + "status_note".into(), + row.get::<_, Option>(4)?.unwrap_or_default().into(), + ), + ( + "status_ordinal".into(), + row.get::<_, Option>(5)?.unwrap_or_default().into(), + ), + ( + "status_text".into(), + row.get::<_, Option>(6)?.unwrap_or_default().into(), + ), + ] + .into_iter() + .collect(); + Ok(row) + })?; + let rows = mapped_rows_iter + .collect::, _>>() + .context("querying xreqs")?; + + self.ctx.insert("xreqs_rows".into(), rows.into()); + Ok(()) + } + + fn query_xtodos_dones(&mut self, db: &Connection) -> Result<()> { + static SQL: &str = r#" + SELECT line_n, section, 'todo' as status, xtext FROM xtodos + UNION ALL + SELECT line_n, section, 'done' as status, xtext FROM xdones + ORDER BY line_n, section ASC;"#; + let mut stmt = db.prepare(SQL)?; + let mut row_n = 0_usize; + let mapped_rows_iter = stmt.query_map([], |row| { + row_n += 1; + let line_n: usize = row.get(0)?; + let section: String = row.get(1)?; + let status: String = row.get(2)?; + let text: String = row.get(3)?; + let row: Ctx = [ + (Cowstaticstr::from("row_n"), MjValue::from(row_n)), + ("line_n".into(), line_n.into()), + ("section".into(), section.into()), + ("status".into(), status.into()), + ("text".into(), text.into()), + ] + .into_iter() + .collect(); + Ok(row) + })?; + let rows = mapped_rows_iter + .collect::, _>>() + .context("querying xtodos and xdones")?; + self.ctx.insert("xtodos_rows".into(), rows.into()); + Ok(()) + } + + pub fn write_html(&mut self, db: &Connection) -> Result<()> { + self.query_xreqs_statuses(db)?; + self.query_xtodos_dones(db)?; + self.write_index_html()?; + self.write_functional_reqs_html()?; + self.write_xreqs_html()?; + self.write_other_items_html()?; + self.write_xtodos_html()?; + Ok(()) + } + + pub fn write_template_to_file(&mut self, file_path: &PathBuf, template_name: &str, template_src: &str) -> Result { + let mut file_writer = { + let mut f = File::create(file_path) + .with_context(|| format!("creating file: {}", file_path.display()))?; + BufWriter::new(f) + }; + + let canonical_file_path = std::fs::canonicalize(file_path)?; + let url = format!("file://{}", canonical_file_path.display()); + println!("url: {url}"); + + + let tmpl = self.env.template_from_named_str(template_name, template_src)?; + //println!("{}", tmpl.render(&ctx)?); + tmpl.render_to_write(&self.ctx, file_writer)?; + + Ok(url) + } +} + +impl<'c> HtmlWriter<'c> { + pub fn write_index_html(&mut self) -> Result { + static INDEX_HTML_TITLE: &str = "EGDS 2.1.0 Functional requirements and EGRI statuses"; + static INDEX_HTML_FILENAME: &str = "index.html"; + + #[rustfmt::skip] + static INDEX_HTML_CONTENT: &str = r#" +{% include 'header.html' %} +

EGDS 2.1.0

+

Functional requirements and EGRI statuses [plain: xreqs.html] +

Other items [plain: xtodos.html] +{% include 'footer.html' %} +"#; + + let file_path: PathBuf = self.html_dir_path.join(INDEX_HTML_FILENAME); + + self.ctx_set("title", INDEX_HTML_TITLE); + self.write_template_to_file(&file_path, INDEX_HTML_FILENAME, INDEX_HTML_CONTENT) + } + + pub fn write_functional_reqs_html(&mut self) -> Result { + static TITLE: &str = "functional_reqs.html"; + static FILENAME: &str = "functional_reqs.html"; + + #[rustfmt::skip] + static CONTENT: &str = r#" +{% include 'header.html' %} + {% include 'block_reload.html' %} +

EGDS v2.1.0 Functional Requirements with EGRI Statuses

+

[plain: xreqs.html] + + + + + + + + + + +{% for row in xreqs_rows %} + {# + #}{# + #}{# + #}{# + #}{# + #}{# + #}{# + #}{# + #} +{% endfor %} +
query row ndoc line nsectiontextstatus codestatus ordinalstatus text
{{ row.row_n }}{{ row.line_n }}{{ row.section }}{{ row.text }}{{ row.status_code }}{{ row.status_ordinal }}{{ row.status_text }}
+ {% include 'block_reload.html' %} +{% include 'footer.html' %} +"#; + + let file_path: PathBuf = self.html_dir_path.join(FILENAME); + + self.ctx_set("title", TITLE); + self.write_template_to_file(&file_path, FILENAME, CONTENT) + } + + pub fn write_xreqs_html(&mut self) -> Result { + static TITLE: &str = "xreqs.html"; + static FILENAME: &str = "xreqs.html"; + + #[rustfmt::skip] + static CONTENT: &str = r#" +{% include 'header.html' %} +

xreqs

+ + + + + + + + +{% for row in xtodos_rows %} + {# + #}{# + #}{# + #}{# + #}{# + #}{# + #} +{% endfor %} +
query row ndoc line nsectionstatustext
{{ row.row_n }}{{ row.line_n }}{{ row.section }}{{ row.status }}{{ row.text }}
+{% include 'footer.html' %} +"#; + + let file_path: PathBuf = self.html_dir_path.join(FILENAME); + + self.ctx_set("title", TITLE); + self.write_template_to_file(&file_path, FILENAME, CONTENT) + } + + pub fn write_other_items_html(&mut self) -> Result { + static TITLE: &str = "Other Items"; + static FILENAME: &str = "other_items.html"; + + #[rustfmt::skip] + static CONTENT: &str = r#" +{% include 'header.html' %} + {% include 'block_reload.html' %} +

Other Items

+

[plain: xtodos.html] + + + + + + + + +{% for row in xtodos_rows %} + {# + #}{# + #}{# + #}{# + #}{# + #}{# + #} +{% endfor %} +
query row ndoc line nsectionstatustext
{{ row.row_n }}{{ row.line_n }}{{ row.section }}{{ row.status }}{{ row.text }}
+ {% include 'block_reload.html' %} +{% include 'footer.html' %} +"#; + + let file_path: PathBuf = self.html_dir_path.join(FILENAME); + + self.ctx_set("title", TITLE); + self.write_template_to_file(&file_path, FILENAME, CONTENT) + } + + pub fn write_xtodos_html(&mut self) -> Result { + static TITLE: &str = "xtodos.html"; + static FILENAME: &str = "xtodos.html"; + + #[rustfmt::skip] + static CONTENT: &str = r#" +{% include 'header.html' %} +

xtodos

+ + + + + + + + +{% for row in xtodos_rows %} + {# + #}{# + #}{# + #}{# + #}{# + #}{# + #} +{% endfor %} +
query row ndoc line nsectionstatustext
{{ row.row_n }}{{ row.line_n }}{{ row.section }}{{ row.status }}{{ row.text }}
+{% include 'footer.html' %} +"#; + + let file_path: PathBuf = self.html_dir_path.join(FILENAME); + + self.ctx_set("title", TITLE); + self.write_template_to_file(&file_path, FILENAME, CONTENT) + } + +} diff --git a/other/spec-todo/munge-reqs/src/init.sql b/other/spec-todo/munge-reqs/src/init.sql new file mode 100644 index 0000000..22e4059 --- /dev/null +++ b/other/spec-todo/munge-reqs/src/init.sql @@ -0,0 +1,81 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +PRAGMA foreign_keys=OFF; +BEGIN TRANSACTION; + +CREATE TABLE lines ( + line_n integer primary key, + line_in_range text, + xtext text not null ); + +CREATE TABLE sections ( + line_n integer primary key, + section text not null, + title text not null ); + +CREATE TABLE pages ( + line_n integer primary key, + page_n integer ); + +CREATE TABLE xnotes ( + line_n integer primary key, + section text not null, + xtext text not null ); + +CREATE TABLE xreq_statuses ( + status_code text primary key, + ordinal integer not null, + xtext text not null ); +INSERT INTO xreq_statuses VALUES('na', 1, 'Not directly applicable to implementation code'); +INSERT INTO xreq_statuses VALUES('nfd', 2, 'Needs further discussion'); +INSERT INTO xreq_statuses VALUES('ace', 3, 'Applicable to implementation code, but more directly covered elsewhere'); +INSERT INTO xreq_statuses VALUES('nyi', 4, 'Not yet implemented'); +INSERT INTO xreq_statuses VALUES('ics', 50, 'Implementation code exists covering a portion of code paths considered substantial'); +INSERT INTO xreq_statuses VALUES('ute', 55, 'At least one unit test exercising a relevant code path exists'); +INSERT INTO xreq_statuses VALUES('utep', 60, 'At least one unit test exercising a relevant code path is passing'); +INSERT INTO xreq_statuses VALUES('uts', 65, 'Unit tests exercising a portion of code paths considered sufficient exist'); +INSERT INTO xreq_statuses VALUES('utsp', 75, 'Unit tests exercising a portion of code paths considered sufficient are passing'); +INSERT INTO xreq_statuses VALUES('its', 80, 'An integration test exercising a portion of code paths considered sufficient exists'); +INSERT INTO xreq_statuses VALUES('itsp', 100, 'An integration test exercising a portion of code paths considered sufficient is passing'); + +CREATE TABLE xreqs ( + line_n integer primary key, + section text not null, + xtext text not null, + status_code text, + status_note text, + FOREIGN KEY(status_code) REFERENCES xreq_statuses(status_code) ); + +CREATE TABLE xtodos ( + line_n integer primary key, + section text not null, + xtext text not null ); + +CREATE TABLE xdones ( + line_n integer primary key, + section text not null, + xtext text not null ); + +COMMIT; diff --git a/other/spec-todo/munge-reqs/src/main.rs b/other/spec-todo/munge-reqs/src/main.rs new file mode 100644 index 0000000..20fffc2 --- /dev/null +++ b/other/spec-todo/munge-reqs/src/main.rs @@ -0,0 +1,709 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![allow(clippy::unwrap_used)] +#![deny(elided_lifetimes_in_paths)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::type_complexity)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::let_and_return)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +mod clargs; +mod db; +mod files; +mod html_writer; + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + borrow::{ + Cow, + //Borrow, + }, + cell::OnceCell, + //collections::{BTreeSet, BTreeMap}, + //collections::{HashSet, HashMap}, + ffi::OsString, + fs::File, + //hash::{BuildHasher, Hash, Hasher}, + io::{BufRead, BufReader, BufWriter, Cursor, Write}, + //iter::zip, + //marker::PhantomData, + sync::LazyLock, + path::{Path, PathBuf}, + process::ExitCode, + //rc::Rc, + //str::FromStr, + //sync::{, + //Arc, + //OnceLock, + //}, +}; + +use anyhow::{Context, Result, anyhow, bail, ensure}; +//use either::Either; +//use futures_lite::future::{self, FutureExt}; +//use hashbrown::HashMap; +//use rand::{distr::Uniform, Rng, RngCore}; +use regex::{Captures, Regex}; +use rusqlite::{Connection, Result as RusqliteResult, backup::StepResult, params}; +use serde_json::{Value as JsonValue, json}; +//use serde::{Deserialize, Serialize}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +//use tracing::{debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, warn}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::{clargs::*, db::*, files::*, html_writer::*}; + +//=================================================================================================| + +fn main() -> ExitCode { + match main2() { + Ok(_) => { + println!("Done."); + ExitCode::SUCCESS + } + Err(e) => { + eprintln!("Error: {e:#?}"); + ExitCode::FAILURE + } + } +} + +fn main2() -> Result<()> { + let clargs: Clargs = argh::from_env(); + + println!("File to process: {}", clargs.file.display()); + + if let Some(db_path) = &clargs.db { + println!("File for db: {}", db_path.display()); + } else { + println!("No db file specified."); + } + + let db = create_in_memory_db()?; + + let mut opt_html_writer = HtmlWriter::new(&clargs)?; + + let result = fallible_stuff(&db, &clargs, &mut opt_html_writer); + + if let Some(db_path) = &clargs.db { + let _ = save_db_to_file(&db, &clargs, db_path) + .inspect_err(|e| eprintln!("Couldn't save db to file: {e}")); + } else { + println!("No db file specified - not saving."); + return Ok(()); + } + + result.map(|_| ()) +} + +fn fallible_stuff( + db: &Connection, + clargs: &Clargs, + opt_html_writer: &mut Option>, +) -> Result<()> { + let read_lines_path = clargs.file.clone(); + + let write_lines_path = { + let opt_extension = read_lines_path.extension(); + let mut write_lines_path = PathBuf::clone(&read_lines_path); + let mut new_extension: OsString = OsString::from("out"); + if let Some(extension) = read_lines_path.extension() { + new_extension.push("."); + new_extension.push(extension); + } + write_lines_path.set_extension(new_extension); + write_lines_path + }; + + println!("Reading lines from: {}", read_lines_path.display()); + println!("Writing lines to: {}", write_lines_path.display()); + + { + let mut line_pxr = LinePxr::new(db, &read_lines_path, &write_lines_path)?; + line_pxr.do_lines()?; + + println!("{} lines in", line_pxr.line_in_n); + println!("{} lines out", line_pxr.line_out_n); + + // Drop `line_pxr` to flush the output file. + } + + // Everything seems to have worked, overwrite the starting file. + back_up_and_replace_file(&read_lines_path, &write_lines_path)?; + + if let Some(html_writer) = opt_html_writer { + html_writer.write_html(db)?; + } + + Ok(()) +} + +// These values (other than `None`) need to match the prefix, lowercased without the trailing 'j' +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum Multiline { + None, + Xreqj, +} +impl Multiline { + pub fn json_introducer(&self) -> String { + format!("{self}j") + } +} +impl std::fmt::Display for Multiline { + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut s = format!("{self:?}"); + let mut s = s.to_lowercase(); + if s.ends_with('j') { + s.truncate(s.len() - 1); + } + std::fmt::Display::fmt(&s, f) + } +} + +struct LinePxr<'c> { + db: &'c Connection, + read_lines_path: PathBuf, + opt_lines: Option>>, + line_in_n: usize, + line_in_first_n: usize, + line_in: String, + line_out_n: usize, + write_path: PathBuf, + bufwriter: BufWriter, + cursor_string: String, + multiline: Multiline, +} +impl<'c> LinePxr<'c> { + fn new(db: &'c Connection, read_lines_path: &Path, write_lines_path: &Path) -> Result { + let f_read_lines = File::open(read_lines_path.clone()) + .with_context(|| format!("Opening for reading: {}", read_lines_path.display()))?; + let bufreader = BufReader::new(f_read_lines); + let lines = bufreader.lines(); + + let f_write_lines = File::create(write_lines_path.clone()) + .with_context(|| format!("Creating for writing: {}", write_lines_path.display()))?; + let mut write_lines = BufWriter::new(f_write_lines); + + Ok(Self { + db, + read_lines_path: read_lines_path.into(), + opt_lines: Some(lines), + line_in_n: 0, + line_in_first_n: 0, + line_in: String::new(), + line_out_n: 1, + write_path: write_lines_path.into(), + bufwriter: write_lines, + cursor_string: String::new(), + multiline: Multiline::None, + }) + } + + fn do_lines(&mut self) -> Result<()> { + let lines_in = self.opt_lines.take().context("lines gone")?; + + for result_line_in in lines_in { + match result_line_in { + Ok(line_in) => { + self.line_in_n += 1; + self.line_in = line_in.clone(); + self.do_line(line_in)?; + } + Err(std_io_error) => { + println!("line {:4}: Error: {std_io_error}", self.line_in_n); + return Err(std_io_error).with_context(|| format!("line {}", self.line_in_n)); + } + } + } + + if !self.cursor_string.is_empty() { + bail!( + "Early end-of-file from construct started at line {}", + self.line_in_first_n + ); + } + + Ok(()) + } + + fn do_line(&mut self, line_in: String) -> Result<()> { + let result_lines_out = if self.cursor_string.is_empty() { + self.line_in_first_n = self.line_in_n; + self.do_line_initial(line_in) + } else { + self.do_line_subsequent(line_in) + }; + + let line_in_range = format!("{}..={}", self.line_in_first_n, self.line_in_n); + + let lines_out = result_lines_out.with_context(|| format!("line {line_in_range}"))?; + + for line_out in lines_out { + self.db + .execute( + "insert into lines (line_n, line_in_range, xtext) values (?1, ?2, ?3)", + (&self.line_out_n, &line_in_range, &line_out), + ) + .context("db insert into lines")?; + + writeln!(self.bufwriter, "{line_out}")?; + + self.line_out_n += 1; + } + + Ok(()) + } + + fn do_line_initial(&mut self, line_in: String) -> Result> { + { + // (cap 1) ( cap 2 ) + // % xxxx --- S3.1.3 Election Parameters and the Election Manifest - Indices + static S: &str = r"^\s*%+\s*(?i)[x]{4,}(?-i)\s*[-]+\s*(S\S+)\s*(.+?)\s*$"; + static RX: LazyLock = LazyLock::new(|| Regex::new(S).unwrap()); + if let Some(captures) = RX.captures(&line_in) { + return self.do_line_xxxxsection_possibly_final(captures); + } + } + { + // % xxxx 8BCgdRhOK46nVaOVT77AHU4WF56ov1Noq9sGk/P+lNbDXFH92hkXYINjCdtkvUMj4h7VcBCS4c/T + static S: &str = r"^\s*%+\s*(?i)[x]{4,}(?-i)\s?(.*?)$"; + static RX: LazyLock = LazyLock::new(|| Regex::new(S).unwrap()); + if let Some(captures) = RX.captures(&line_in) { + return self.do_line_xxxx_possibly_final(captures); + } + } + { + // % xnote S2.f This section generates no unique or specific requirements + static S: &str = r"^\s*%+\s*(?i)xnote(?-i)\s+(S\S+)\s+(.+?)\s*$"; + static RX: LazyLock = LazyLock::new(|| Regex::new(S).unwrap()); + if let Some(captures) = RX.captures(&line_in) { + return self.do_line_xnote_possibly_final(captures); + } + } + { + // % xpage 60 ----------------------------------------------------------------------- + static S: &str = r"^(?i)\s*%+\s*xpage\s+-*\s*(?:pg[.]?\s*)?(\d+)\s*-*\s*(?-i)$"; + static RX: LazyLock = LazyLock::new(|| Regex::new(S).unwrap()); + if let Some(captures) = RX.captures(&line_in) { + return self.do_line_xpage_possibly_final(captures); + } + } + { + // % xreq S3.a.c.b EGRI ensures that... + // % xreq2j S3.a.c.b EGRI ensures that... + static S: &str = + r"^\s*%+\s*(?i)xreq(?(?:2j)?)(?-i)\s+(?
S\S+)\s+(?.+?)\s*$"; + static RX: LazyLock = LazyLock::new(|| Regex::new(S).unwrap()); + if let Some(captures) = RX.captures(&line_in) { + //eprintln!("captures: {captures:?}"); + return self.do_line_xreq_possibly_final(captures); + } + } + { + // % xreqj { + // "section": "S1.d", + // "text": "EGRI provides a \"detailed implementation specification\" and/or qualifies as a \"well-documented ElectionGuard implementation\"", + // "sc": "ace" } + static S: &str = r"^\s*%+\s*(?i)xreqj(?-i)\s+(.+?)$"; + static RX: LazyLock = LazyLock::new(|| Regex::new(S).unwrap()); + if let Some(captures) = RX.captures(&line_in) { + return self.do_line_xreqj_initial(captures); + } + } + { + // % xtodo S0 Some descriptive text + static S: &str = r"^\s*%+\s*(?i)xtodo(?-i)\s+(S\S+)\s+(.+?)\s*$"; + static RX: LazyLock = LazyLock::new(|| Regex::new(S).unwrap()); + if let Some(captures) = RX.captures(&line_in) { + return self.do_line_xtodo_possibly_final(captures); + } + } + { + // % xdone S0 Extended base hash H_E + static S: &str = r"^\s*%+\s*(?i)xdone(?-i)\s+(S\S+)\s+(.+?)\s*$"; + static RX: LazyLock = LazyLock::new(|| Regex::new(S).unwrap()); + if let Some(captures) = RX.captures(&line_in) { + return self.do_line_xdone_possibly_final(captures); + } + } + Ok(vec![line_in]) + } + + fn do_line_subsequent(&mut self, line_in: String) -> Result> { + //eprintln!("do_subsequent_line: {line_in}"); + + self.cursor_string.push_str(&line_in); + self.cursor_string.push('\n'); + + match self.multiline { + Multiline::None => { + bail!("unexpected subsequent line {}", self.line_in); + } + Multiline::Xreqj => self.do_line_possibly_final(), + } + } + + fn do_line_xxxxsection_possibly_final<'s>( + &mut self, + captures: Captures<'s>, + ) -> Result> { + let section = captures.get(1).context("capture xreq section")?.as_str(); + let title = captures.get(2).context("capture xreq text")?.as_str(); + + ensure!( + !section.is_empty(), + "line {} xxxxsection section is empty", + self.line_in_n + ); + ensure!( + !title.is_empty(), + "line {} xxxxsection title is empty", + self.line_in_n + ); + + self.db + .execute( + "insert into sections (line_n, section, title) values (?1, ?2, ?3)", + (&self.line_out_n, section, title), + ) + .with_context(|| { + format!( + "insert sections {}, {section:?}, {title:?}", + &self.line_out_n + ) + })?; + + let mut s = format!("% xxxx ---- {section} {title}"); + s.truncate(s.as_str().trim_end().len()); + + let v_lines_out = vec![s]; + Ok(v_lines_out) + } + + fn do_line_xxxx_possibly_final<'s>(&mut self, captures: Captures<'s>) -> Result> { + let data = captures.get(1).context("capture xxxx data")?.as_str(); + + let mut s = format!("% xxxx {data}"); + s.truncate(s.as_str().trim_end().len()); + + let v_lines_out = vec![s]; + Ok(v_lines_out) + } + + fn do_line_xnote_possibly_final<'s>(&mut self, captures: Captures<'s>) -> Result> { + let section = captures.get(1).context("capture xnote section")?.as_str(); + let xtext = captures.get(2).context("capture xnote text")?.as_str(); + + if section.is_empty() { + return Ok(vec!["% xnote".into()]); + } + + self.db + .execute( + "insert into xnotes (line_n, section, xtext) values (?1, ?2, ?3)", + (&self.line_out_n, section, xtext), + ) + .with_context(|| format!("insert xnotes {}, {section}, {{xtext}}", &self.line_out_n))?; + + let mut s = format!("% xnote {section} {xtext}"); + s.truncate(s.as_str().trim_end().len()); + + let v_lines_out = vec![s]; + Ok(v_lines_out) + } + + fn do_line_xpage_possibly_final<'s>(&mut self, captures: Captures<'s>) -> Result> { + let page_n = captures.get(1).context("capture page number")?.as_str(); + + self.db + .execute( + "insert into pages (line_n, page_n) values (?1, ?2)", + (&self.line_out_n, page_n), + ) + .with_context(|| format!("insert pages {}, {page_n}", &self.line_out_n))?; + + let mut s = format!("% xpage ---------------------------------------- pg. {page_n} -----------------------------------------"); + let v_lines_out = vec![s]; + Ok(v_lines_out) + } + + fn do_line_xreq_possibly_final<'s>(&mut self, captures: Captures<'s>) -> Result> { + let section = captures + .name("section") + .context("capture xreq section")? + .as_str(); + let xtext = captures + .name("xtext") + .context("capture xreq text")? + .as_str(); + let to_j = captures + .name("to_j") + .map(|s| !s.as_str().is_empty()) + .unwrap_or_default(); + + ensure!(!section.is_empty(), "
should not be empty"); + ensure!(!xtext.is_empty(), " should not be empty"); + + let mut jv = json!({ + "section": section, + "text": xtext, + }); + + self.handle_xreq(&mut jv, to_j) + } + + #[allow(clippy::collapsible_if)] + #[rustfmt::skip] + fn handle_xreq(&mut self, jv: &mut JsonValue, as_json: bool) -> Result> { + let section = jv + .get("section") + .context("missing 'section'")? + .as_str() + .context("expecting 'section' to be a string")?; + let xtext = jv + .get("text") + .context("missing 'text'")? + .as_str() + .context("expecting 'text' to be a string")?; + let opt_status_code = jv + .get("sc") + .and_then(|sc| sc.as_str().map(|s| s.to_owned())); + let opt_status_note: Option = jv + .get("status_note") + .and_then(|sc| sc.as_str().map(|s| s.to_owned())); + + //eprintln!("handle_xreq as_json: {as_json}"); + //eprintln!("handle_xreq section: {section}"); + //eprintln!("handle_xreq xtext: {xtext}"); + + self.db + .execute( + "insert into xreqs (line_n, section, xtext, status_code, status_note) values (?1, ?2, ?3, ?4, ?5)", + (&self.line_out_n, §ion, &xtext, &opt_status_code, &opt_status_note), + ) + .with_context(|| format!("insert xreqs {}, {section}, {{xtext}}", &self.line_out_n))?; + + let plain_introducer = "xreq"; + if as_json { + if jv.get("sc").is_none() { + jv["sc"] = json!(""); + } + fn ensure_str_field(jv: &mut JsonValue, field: &str) { + if jv.get(field).is_none() { + jv[field] = json!(""); + } + } + + if let Some(status_code) = &opt_status_code { + match status_code.as_str() { + "nfd" => { ensure_str_field(jv, "status_note"); } + "ute" => { ensure_str_field(jv, "ute"); } + "utep" => { ensure_str_field(jv, "utep"); } + "uts" => { ensure_str_field(jv, "uts"); } + "utsp" => { ensure_str_field(jv, "utsp"); } + "its" => { ensure_str_field(jv, "its"); } + "itsp" => { ensure_str_field(jv, "itsp"); } + _ => {} + } + } + + self.json_v_lines(jv, plain_introducer, as_json) + } else { + let mut s = format!("% {plain_introducer} {section} {xtext}"); + s.truncate(s.as_str().trim_end().len()); + Ok(vec![s]) + } + } + + fn json_v_lines( + &mut self, + jv: &JsonValue, + plain_introducer: &str, + as_json: bool, + ) -> Result> { + let pretty = serde_json::to_string_pretty(&jv)?; + //eprintln!("line {} {plain_introducer}j value: {pretty}", self.line_in_n); + let v_lines_out: Vec = Cursor::new(pretty).lines().map(|r| r.unwrap()).collect(); + let mut v_lines_out = merge_json_lines(v_lines_out)?; + //eprintln!( + // "line {} {plain_introducer}j value: {v_lines_out:?}", + // self.line_in_n + //); + let mut_line0 = v_lines_out.get_mut(0).context("pretty line 0")?; + *mut_line0 = format!("% {plain_introducer}j {mut_line0}"); + Ok(v_lines_out) + } + + fn do_line_xtodo_possibly_final<'s>(&mut self, captures: Captures<'s>) -> Result> { + let section = captures.get(1).context("capture xtodo section")?.as_str(); + let xtext = captures.get(2).context("capture xtodo text")?.as_str(); + + if !section.is_empty() && !xtext.is_empty() { + self.db + .execute( + "insert into xtodos (line_n, section, xtext) values (?1, ?2, ?3)", + (&self.line_out_n, section, xtext), + ) + .with_context(|| { + format!("insert xreqs {}, {section}, {{xtext}}", &self.line_out_n) + })?; + } + + let mut s = format!("% xtodo {section} {xtext}"); + s.truncate(s.as_str().trim_end().len()); + + let v_lines_out = vec![s]; + Ok(v_lines_out) + } + + fn do_line_xdone_possibly_final<'s>(&mut self, captures: Captures<'s>) -> Result> { + let section = captures.get(1).context("capture xdone section")?.as_str(); + let xtext = captures.get(2).context("capture xdone text")?.as_str(); + + if !section.is_empty() && !xtext.is_empty() { + self.db + .execute( + "insert into xdones (line_n, section, xtext) values (?1, ?2, ?3)", + (&self.line_out_n, section, xtext), + ) + .with_context(|| { + format!("insert xdones {}, {section}, {{xtext}}", &self.line_out_n) + })?; + } + + let mut s = format!("% xdone {section} {xtext}"); + s.truncate(s.as_str().trim_end().len()); + + let v_lines_out = vec![s]; + Ok(v_lines_out) + } + + fn do_line_xreqj_initial<'s>(&mut self, captures: Captures<'s>) -> Result> { + let xtext = captures.get(1).context("capture xreqj text")?.as_str(); + + self.multiline = Multiline::Xreqj; + self.cursor_string.push_str(xtext); + self.cursor_string.push('\n'); + + self.do_line_possibly_final() + } + + fn do_line_possibly_final(&mut self) -> Result> { + let ml = self.multiline; + let json_introducer = ml.json_introducer(); + let mut result_json_value = { + let mut deserializer = serde_json::Deserializer::from_str(&self.cursor_string); + use serde::de::Deserialize; + JsonValue::deserialize(&mut deserializer) + }; + match result_json_value { + Ok(mut jv) => { + self.multiline = Multiline::None; + self.cursor_string.clear(); + match ml { + Multiline::Xreqj => self.handle_xreq(&mut jv, true), + _ => { + bail!("Expecting a different Multiline value, got {ml:?}"); + } + } + } + Err(serde_deserializer_error) => match serde_deserializer_error.classify() { + serde_json::error::Category::Eof => Ok(vec![]), + category => { + let s = format!( + "line {} {json_introducer} serde_deserializer_error: {category:?} {serde_deserializer_error}", + self.line_in_n + ); + Err(serde_deserializer_error).context(s) + } + }, + } + } +} + +fn merge_json_lines(mut lines: Vec) -> Result> { + const MAX_LINE: usize = 64; + + let mut pass_n = 0; + let mut prev_qty_lines = usize::MAX; + while lines.len() != prev_qty_lines { + pass_n += 1; + prev_qty_lines = lines.len(); + + let mut line_ix = 1; + while line_ix < lines.len() { + let (l, r) = lines.split_at_mut(line_ix); + let prev_line = &mut l[line_ix - 1]; + let cur_line = &r[0]; + + let mut no_merge_with_prev = false; + + if !no_merge_with_prev { + static RE_FORBID_MERGE_ONTO_CHARS: LazyLock = + LazyLock::new(|| Regex::new(r#"[{\[]\s*$"#).unwrap()); + no_merge_with_prev |= RE_FORBID_MERGE_ONTO_CHARS.is_match(prev_line); + } + + if !no_merge_with_prev { + static RE_FORBID_MERGE_FROM_CHARS: LazyLock = + LazyLock::new(|| Regex::new(r#"[:]"#).unwrap()); + no_merge_with_prev |= RE_FORBID_MERGE_FROM_CHARS.is_match(cur_line); + } + + let cur_line = cur_line.trim(); + + if !no_merge_with_prev { + let resulting_len = prev_line.len() + 1 + cur_line.len(); + no_merge_with_prev |= MAX_LINE < resulting_len; + } + + if !no_merge_with_prev { + prev_line.push(' '); + prev_line.push_str(cur_line); + lines.remove(line_ix); + } else { + line_ix += 1; + } + } + } + + Ok(lines) +} diff --git a/other/spec-todo/spec-mapping/TODO-2.0.txt b/other/spec-todo/spec-mapping/TODO-2.0.txt new file mode 100644 index 0000000..ed74cd0 --- /dev/null +++ b/other/spec-todo/spec-mapping/TODO-2.0.txt @@ -0,0 +1,2617 @@ +====vvvv====vvvv====vvvv====vvvv====vvvv==== DONE ====vvvv====vvvv====vvvv====vvvv====vvvv==== + +electionguard.exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%) +electionguard.exe: seed: write random seed to artifact file +electionguard.exe manifest: write ElectionManifest to pretty json file +electionguard.exe manifest: write ElectionManifest to canonical bytes file +electionguard.exe parameters: write ElectionParameters to json file +H_V, H_P, H_M, and H_B updated for 2.0 calculation +Generate joint election public key +Extended base hash H_E +electionguard-test script: implementation in cmd started +electionguard-test script: implementation in cmd exercises all (current) functionality +BigUint values (mod p or q) now left-padded as necessary to avoid leaking value via serialized file size +Hash values now serialized with 'H(upper hex)' format to match spec +exe: Csprng now seeded with more entropy from the operating system RNG +Election-varying parameters (n and k) now checked for validity +Serialization of BigUints now uses base64 encoding +Rename guardian private key to secret key +electionguard.exe: generate guardian secret key +electionguard.exe: write guardian secret key +electionguard.exe: derive guardian public key from secret key +electionguard.exe: write guardian public key +Guardian i uses 1-based indexing +compute H_E extended base hash +compute joint election public key +electionguard.exe: write joint election public key to json file +standardize on 'validate' instead of 'verify' when checking deserialized structures +instead of from_json and to_json implement from_stdioread and to_stdiowrite +every struct that has Self::from_stdioread*() should prefer Self::from_stdioread_validated() and have a self.validate() +electionguard.exe: write H_E extended base hash to json file +convert many uses of if !() { bail!() } to ensure!() +Generate data structure docs from the Reference Implementation in Rust +eg: New constrained numeric type for indices. Convert n, k, and other indices to this type. +build-docs script: initial implementation in cmd +evaluate scripting language 'nu' https://www.nushell.sh/ +electionguard-test script: begin rewrite in nu +doc/LICENSE: checked +doc/SECURITY.md: complete +docs/general: begin writing +docs/api: begin writing +Remove link to internal site +VaryingParameters: enum BallotChaining { Prohibited, Allowed, Required, } +remove old EG 1.54 constants +exe: Under artifacts dir, first level dirs are specific to secrecy requirements +Ballot define data type +get fixeduint stuff out of bunna branch +Merge code from Anunay +doc/SUPPORT.md: complete +doc/README.md: complete +doc/CODE_OF_CONDUCT.md: complete +doc/BUILDING.md: complete +Complete all planned code reorganization/renaming +docs/implementation guide/References: complete +a trait for fn to_canonical_json() +many to_stdiowrite() methods have common code that could be factored into a common function +a trait for types that have to_stdiowrite() +a trait for types that have to_stdiowrite() and perhaps _pretty() and _canonical() variants + +====^^^^====^^^^====^^^^====^^^^====^^^^==== DONE merged to main ====^^^^====^^^^====^^^^====^^^^====^^^^==== + +Add .errorviz-version to .gitignore +incorporate the latex of egds2.0 into todo.txt +add todo.txt to repo +serialize bignums only all as uppercase hex +serialize bignums includes base e.g., "base16:f81e2b3..." + +====^^^^====^^^^====^^^^====^^^^====^^^^==== locally committed ====^^^^====^^^^====^^^^====^^^^====^^^^==== + +====vvvv====vvvv====vvvv====vvvv====vvvv==== TODO general ====vvvv====vvvv====vvvv====vvvv====vvvv==== + +UG left pad as necessary to ensure length is correct +serialize format wrapped in version + +ensure SecretCoefficient is serialized in a fixed-length format + +Election parameters: add a type field initally set to 'HomomorphicTallying' + +[Security: out of scope] TOCTOU bug with --insecure-deterministic flag electionguard.exe main.rs. Should be fixed, but in practice if that filesystem is writable by a malicious party then you likely have much, much bigger concerns. + +build-docs script: finish rewrite in nu + +electionguard-test script: incorporate ballots +electionguard-test script: incorporate tally + +Build on Linux64 + +Test on Linux64 + +new electionguard-testdata.exe tool example_election_manifest +new electionguard-testdata.exe tool generates test data, example_election_manifest +new electionguard-testdata.exe tool does validate-standard-parameters + +docs/general: style sheet for markdown, ideally match API docs + +If an overvote occurs, the overvote must be captured, encrypted, and never decrypted. + +docs/specs/serialization: data formats section +docs/specs/serialization: standards and references section +docs/specs/serialization: election manifest section +docs/specs/serialization: election record section +docs/specs/serialization: vendor data section + +docs/implementation guide/Requirements for election systems vendors: complete +docs/implementation guide/Requirements for verifier app authors: complete +docs/implementation guide/roles: consider splitting into separate pages: complete +docs/implementation guide/roles/Election Administrator: complete +docs/implementation guide/roles/Election Guardians: complete +docs/implementation guide/roles/Voters: complete +docs/implementation guide/roles/Political parties and voter-interest organizations: complete +docs/implementation guide/roles/Journalists and other media: complete +docs/implementation guide/Hardware requirements/Gurardian secret key storage: complete +docs/implementation guide/Hardware requirements/Gurardian secret key operations: complete +docs/implementation guide/step-by-step/Advance preparation: complete +docs/implementation guide/step-by-step/Key ceremony: complete +docs/implementation guide/step-by-step/Tally ceremony: complete +docs/implementation guide/step-by-step/Publishing: complete +docs/implementation guide/step-by-step/Verification: complete +docs/implementation guide/step-by-step/Reporting: complete + +docs/api: use correct logo +docs/api: complete + +docs: complete, #![warn(missing_docs)] + +doc: upload docs to github pages (see compliance notes) + +security review: ensure that no file leaks info through filesize + +====----====----====----====----==== milestone: "alpha" release ====----====----====----====----==== + +alpha testing + +====----====----====----====----==== milestone: "beta" release ====----====----====----====----==== + +beta testing + +#![warn(missing_doc_code_examples)] +#![warn(missing_debug_implementations)] +#![warn(missing_copy_implementations)] +#![warn(unused_doc_comments)] + +distinguish between PartySelection, BallotMeasureSelection, CandidateSelection + +open issues for any remaining TODO items for 1.0 + +====----====----====----====----==== milestone: "1.0" release ====----====----====----====----==== + +BallotDefinition doc for write-in option + +would be nice to support a PartySelection type vote, rather than rely on the vendor to give us the correct selections explicitly + +a trait for types that have pub fn validate(&Self) +a trait for types that have pub fn validate(&Self, &ElectionParameters) + +docs: more investigation into using rust modules to build documentation along with api, observe how cargo_crev does it with the include_str! macro: https://github.com/crev-dev/cargo-crev/blob/master/cargo-crev/src/doc/mod.rs +docs: more investigation into using mdbook for all project documentation https://github.com/rust-lang/rust/issues/66249 +docs: cargo-external-doc is nice but doesn't support virtual manifests https://github.com/Geal/cargo-external-doc + +VaryingParameters (n, k) This isn't really an 'index' (ordinal), it's a cardinal number. Maybe we need a general purpose ranged number type. + +exe: common parameter: election filesystem directory to look for files (%ELECTIONGUARD_ARTIFACTS_DIR%) +exe: common parameter: manifest file +exe: common parameter: others + +exe: create artifact directories if they don't exist. q: what about permissions on guardian secret directories? + +exe: read guardian secret key, print info, suppressing secrets in secret key +exe: read guardian public key, print info + +Proof of valid decryption + +design: document +design: define standard election directory layout - look at other implementers and users +design: key file represents its kind: guardian, ..., ? + +Consider structures defined in JSON schema https://github.com/usnistgov/ElectionResultsReporting/blob/version2/NIST_V2_election_results_reporting.json + +if electionguard.exe fails to read or write a file, check the path to see if it has a leading ~. If so, print a good error message. + +test on 32-bit target such as x86 or wasm32 +test on big-endian target such as powerpc64-unknown-linux-gnu, s390x-unknown-linux-gnu, riscv64gc-unknown-linux-gnu, or loongarch64-unknown-linux-gnu + +docs/api: reference NIST CDF where types clearly correspond. E.g., BallotStyle https://github.com/usnistgov/ElectionResultsReporting/blob/nist-pages/index.md#17_0_2_4_78e0236_1389366224561_797289_2360 + +====vvvv====vvvv====vvvv====vvvv====vvvv==== TODO EG 2.0 Design Specification text ====vvvv====vvvv====vvvv====vvvv====vvvv==== + +Abbreviations: + +RI Reference Implementation +OI Other implementation + + +%%%%%%%%%%%%% The plan here is to chop up the specification text and extract statements that can be translate into test cases + +\pagebreak + +\tableofcontents + +\pagebreak + +-------- Section 1 + +\section{Introduction} +This document describes the cryptographic details of the design of \EG which can be used in conjunction with many new and existing voting systems to enable both end-to-end (E2E) verifiability and privacy-enhanced risk-limiting audits (RLAs). \EG is not a complete election system. It instead provides components that are designed to be flexible and to promote innovation by election officials and system developers. When properly used, it can promote voter confidence by empowering voters to independently verify the accuracy of election results. + +---- S1a +\subsubsection*{End-to-end (E2E) verifiability} +An E2E-verifiable election provides artifacts which allow voters to confirm that their votes have been accurately recorded and counted. Specifically, an election is End-to-end (E2E) verifiable if two properties are achieved. +\begin{enumerate} +\item Individual voters can verify that their votes have been accurately recorded. +\item Voters and observers can verify that all recorded votes have been accurately counted. +\end{enumerate} +An E2E-verifiable tally can be used as the primary tally in an election or as a verifiable secondary tally alongside traditional methods. \EG is compatible with in-person voting -- either using an electronic ballot-marking device or an optical scanner capable of reading hand-marked or machine-marked ballots, with voting by mail, and even with Internet voting\footnote{ +Note that there are many challenges to responsible Internet voting that are mitigated but not fully solved by E2E-verifiability. The 2015 U.S. Vote Foundation report at https://www.usvotefoundation.org/E2E-VIV details many of these issues, and the 2018 National Academies report at https://nap.nationalacademies.org/catalog/25120/securing-the-vote-protecting-american-democracy includes a section on Internet voting (pp. 101--105). +}. + +---- S1b +\subsubsection*{Risk-limiting audits (RLAs)} +RLAs offer election administrators efficient methods to validate reported election tallies against physical ballot records. There are several varieties of RLAs, but the most efficient and practical are \emph{ballot-comparison audits} in which electronic \emph{cast-vote records} (CVRs) are individually compared against physical ballots. + +The challenge with ballot-comparison audits is that public release of the full set of CVRs can compromise voter privacy while an audit without public disclosure of CVRs offers no basis for public confidence in the outcome. \EG can bridge this gap by enabling public disclosure of encrypted ballots that can be matched directly to physical ballots selected for auditing and can also be proven to match the reported tallies. + +---- S1c +\subsubsection*{About this specification} +This specification can be used by expert reviewers to evaluate the details of the \EG process and by independent parties to write \EG implementations. This document is not intended to be a fully detailed implementation specification. It does not specify serialization and data structures for the election record or mappings between the notation used here and the corresponding data types and components of a specific implementation. However, this document, together with a detailed implementation specification or a well-documented \EG implementation, can be used by independent parties to write \EG verifiers to confirm the consistency of election artifacts with announced election results. + +S1 Individual voters can verify that their votes have been accurately recorded. +S1 Voters and observers can verify that all recorded votes have been accurately counted. +S1 An E2E-verifiable tally can be used as the primary tally in an election sor as a verifiable secondary tally alongside traditional methods. +S1 ElectionGuard is compatible with in-person voting using an electronic ballot-marking device +S1 ElectionGuard is compatible with in-person voting using an optical scanner capable of reading hand-marked ballots +S1 ElectionGuard is compatible with in-person voting using an optical scanner capable of reading machine-marked ballots +S1 ElectionGuard is compatible with with voting by mail +S1 ElectionGuard is compatible with with Internet voting +S1 ElectionGuard can enable public disclosure of encrypted ballots that can sbe matched directly to physical ballots selected for auditing +S1 ElectionGuard can enable public disclosure of encrypted ballots that can be proven to match the reported tallies. + + +-------- Section 2 + +%pagebreak +---- S2a +\section{Overview} + +This section gives a very brief and high-level overview of the \EG system's functionality and how it can be used during an election or in a post-election audit such as an RLA. + +To use \EG in an election, a set of \emph{guardians} is enlisted to serve as trustees who manage cryptographic keys. The members of a canvassing board can serve as guardians. Prior to the commencement of voting or auditing, the guardians work together to form a public encryption key that will be used to encrypt individual ballots. + +After the conclusion of voting or auditing, a \emph{quorum} of guardians is necessary to produce the artifacts required to enable public verification of the tally. + +S2a A quorum of guardians can produce the artifacts required to enable public verification of the tally. +S2a A quorum of guardians is necessary to produce the artifacts required to enable public verification of the tally. + +---- S2b +\subsubsection*{Key generation} +Prior to the start of voting (for an E2E-verifiable election) or auditing (for an RLA), the election guardians participate in a process wherein they generate public keys to be used in the election. Each guardian generates its own public-secret key pair. These public keys will be combined to form a single public key with which votes are encrypted. They are also used individually by guardians to exchange information about their secret keys so that the election record can be produced after voting or auditing is complete -- even if not all guardians are available at that time. + +The key generation ceremony begins with each guardian publishing its public keys together with proofs of knowledge of the associated secret keys. Once all public keys are published, each guardian uses each other guardian's public key to encrypt shares of its own secret keys. Finally, each guardian decrypts the shares it receives from other guardians and checks them for consistency. If the received shares verify, the receiving guardian announces its completion. If any shares fail to verify, the receiving guardian challenges the sender. In this case, the sender is obliged to reveal the shares it sent. If all challenged guardians release their challenged shares and these shares all verify, the ceremony concludes and the election proceeds. If a challenged guardian fails to produce key shares that verify, that guardian is removed and the key generation ceremony restarts with a replacement guardian. + +S2b Each guardian generates its own public-secret key pair. +S2b The guardian public keys can be combined to form a single public key +S2b The single public key can encrypt votes. +S2b The guardian public keys can be used individually by guardians to exchange information about their secret keys enabling the election record tob be produced after voting or auditing is complete +S2b The guardian public keys can be used individually by guardians to exchange information about their secret keys enabling the election record tob be produced after voting or auditing is complete even if not all guardians are available at that time +S2b Each guardian can publish their public keys together with proofs of knbowledge of the associated secret keys. +S2b Each guardian can use each other guardian's public key to encrypt shares ofb its own secret keys +S2b Each guardian can decrypt the shares it receives from other guardians +S2b Each guardian can check the decrypted shares for consistency +S2b A guardian sender can reveal the shares it sent +S2b If all challenged guardians release their challenged shares and these shbares all verify, the ceremony can conclude and the election proceed. +S2b A guardian can be removed if they are challenged and fail to produce key shbares that verify +S2b the key generation ceremony can be restarted with a replacement guardian. + +---- S2c +\subsubsection*{Ballot encryption} +In most uses, the election system makes a single call to the \EG API after each voter completes the process of making selections or with each ballot to be encrypted for an RLA. \EG will encrypt the selections made by the voter and return a confirmation code which the system should give to the voter.\footnote{The confirmation code is not necessary for RLA usage.} + +This is the only point where an existing election system must interface with \EG. In most uses of \EG, voters will have an opportunity to challenge their encrypted ballots and view their decryptions to ensure that the encryptions are correct.\footnote{A ballot that has been decrypted should be regarded as a test ballot and should not be included in an election tally. After a ballot is challenged, the voter should have an opportunity to cast a fresh ballot. In an E2E-verifiable election, a decypted ballot should never be cast. However, in an RLA, some anonymized cast ballots may ultimately be challenged and decrypted.} +In certain vote-by-mail scenarios and when \EG is used within an RLA, cast-vote records can be provided in batch without any interface between the voting equipment and \EG. + +The encrypted ballots are published along with non-interactive zero-knowledge (NIZK) proofs of their well-formedness. The proofs assert that an encrypted ballot is well-formed, which means that it is a legitimate ballot and adheres to the limits imposed on selection options and contests. For example, they prove that a selection did not receive more votes than allowed and that no more than the allowed number of votes were received across the selection options in each contest. The encryption method used herein has a homomorphic property which allows the encrypted ballots to be combined into a single aggregate ballot which consists of encryptions of the election tallies. + +S2c The election system can make a single call to the ElectionGuard API after each voter completes the process of making selections +S2c The election system can make a single call to the ElectionGuard API with each ballot to be encrypted for an RLA. +S2c ElectionGuard will encrypt the selections made by the voter and return a confirmation code for the system to give to the voter after they complete the process of making selections. (not relevant to RLA scenario) +S2c No other point at which an existing election system must interface with EG is required. +S2c Voters will have an opportunity to challenge their encrypted ballots and view their decryptions to ensure that the encryptions are correct. +S2c A decypted ballot should never be cast, can EG somehow ensure this? +S2c In an RLA, some anonymized cast ballots may be challenged and decrypted. +S2c In certain vote-by-mail scenarios, cast-vote records can be provided in batch without any interface between the voting equipment and ElectionGuard. +S2c When ElectionGuard is used within an RLA, cast-vote records can be provided in batch without any interface between the voting equipment and ElectionGuard +S2c Encrypted ballots can be published along with non-interactive zero-knowledge (NIZK) proofs of their well-formedness. +S2c The NIZK proofs assert that an encrypted ballot is a legitimate ballot +S2c The NIZK proofs assert that an encrypted ballot adheres to the limits S2c imposed on selection options +S2c The NIZK proofs assert that an encrypted ballot adheres to the limits imposed on contests +S2c The NIZK proofs prove that a selection did not receive more votes than allowed +S2c The NIZK proofs prove that no more than the allowed number of votes were received across the selection options in each contest. +S2c The encryption method allows the encrypted ballots to be combined into a single aggregate ballot +S2c The single aggregate ballot into which the encryption method combined the encrypted ballots consists of encryptions of the election tallies. + +---- S2d +\subsubsection*{Verifiable decryption} +In the final step, election guardians independently use a share of the secret decryption key, which each guardian computes from the previously shared secret key fragments, to jointly decrypt the election tallies and generate associated verification data. It is not necessary for all guardians to be available to complete this step. If some guardians are missing, a quorum of guardians is sufficient to complete decryption and generate the verification data. + +S2d Each guardian can compute from the previously shared secret key fragments a share of the secret decryption key +S2d Guardians can independently use their share of the secret decryption key to jointly decrypt the election tallies +S2d Guardians can independently use their share of the secret decryption key to generate associated verification data +S2d Only a quorum of guardians is sufficient to complete decryption +S2d Only a quorum of guardians is sufficient to generate the verification data + +---- S2e +\subsubsection*{Independent verification} +Observers can use this open specification and/or accompanying materials to write independent \emph{election verifiers} that can confirm the well-formedness of each encrypted ballot, the correct aggregation of these ballots, and the accurate decryption of election tallies. + +The verification of an election consists of various verification steps that can be separated into three groupings. + +S2e An observer can use this open specification and/or accompanying materials to write independent election verifiers that can confirm the well-formedness of each encrypted ballot +S2e An observer can use this open specification and/or accompanying materials to write independent election verifiers that can the correct aggregation of these ballots +S2e An observer can use this open specification and/or accompanying materials to write independent election verifiers that can the accurate decryption of election tallies. + +\paragraph{Verification of key generation.} +The first group of verification steps pertain to the key generation process. Under normal circumstances, guardians will engage in a key generation ceremony and produce an election public key without controversy, and there is no need for independent verification of this process. However, if one or more parties to the key generation ceremony complain about the process, the associated independent verification steps can help to resolve conflicts. + +These steps should therefore be considered to be optional. The integrity of an election record can be fully verified by independent parties without any of the key verification steps. However, a ``premium'' verifier may wish to include these steps to verify the correctness of guardian actions during the key generation ceremony. + +The key generation verification steps are highlighted in orange boxes. + +\paragraph{Verification of ballot correctness.} +As will be described in detail below, there are instances in both the E2E-verifiability application and the RLA application where it is desirable to verifiably decrypt individual ballots. In the former application, this allows voters to confirm that their selections have been correctly recorded within an encrypted ballot, and in the latter application, this allows encrypted electronic ballots that have been selected for audit to be compared with their associated paper ballots. + +The steps required to verify the correct decryption of a ballot are grouped together to allow independent verifiers to be easily constructed whose sole purpose is to allow voters or observers to directly verify correct decryption of individual ballots. + +The ballot correctness verification steps are highlighted in blue boxes. + +\paragraph{Verification of election record.} +The remaining verification steps apply to the election as a whole. These steps enable independent verification that every ballot in an election record is well-formed, that cast ballots have been correctly aggregated homorphically, and that these aggregated values have been correctly decrypted to produce election tallies. + +It is critical that a complete verification of an election record also includes verification of the correct decryption of any individual ballots that have been decrypted as part of the process. A complete election verifier must therefore incorporate an individual ballot generator as described above. + +The election record verification steps are highlighted in green boxes. + +---- S2f +\subsubsection*{Using this specification} +The principal purposes of this document are to specify the functionality of the \EG toolkit and to provide details necessary for independent parties to write election verifiers that consume the artifacts produced by the toolkit. + + +\pagebreak + +-------- Section 3 + +---- S3a +\section{Components}\label{sec:EGcomponents} +This section describes the four principal components of \EG. +\begin{enumerate} + +\item \emph{Parameter Requirements:} These are properties required of parameters to securely and efficiently instantiate the cryptographic components of ElectionGuard. These \emph{cryptographic parameters} are fixed ahead of every election and the same parameters can be used across multiple elections. A specific, recommended set of standard parameters is provided. In addition, there are properties required of the \emph{election parameters} that define the election contests, selectable options, and ballot styles, among other information. +\item \emph{Key Generation:} Prior to each individual election, guardians must generate individual public-secret key pairs and exchange shares of secret keys to enable completion of an election even if some guardians become unavailable. Although it is preferred to generate new keys for each election, it is permissible to use the same keys for multiple elections so long as the set of guardians remains the same. A complete new set of keys must be generated if even a single guardian is replaced. + +S3a2 Guardians can generate individual public-secret key pairs +S3a2 Guardians can exchange shares of secret keys +S3a2 If the set of guardians and parameters k and n remain the same, they can re-use the same keys for multiple elections +S3a2 A complete new set of keys must be generated if k is changed +S3a2 A complete new set of keys must be generated if n is changed +S3a2 A complete new set of keys must be generated if even a single guardian is replaced +S3a2 TODO is this true?: A complete new set of keys must be generated if any of p, q, r, g is changed + +\item \emph{Ballot Encryption:} While encrypting the contents of a ballot is a relatively simple operation, most of the work of \EG is the process of creating externally-verifiable artifacts to prove that each encrypted ballot is well-formed (i.e., its decryption is a legitimate ballot without overvotes or improper values). +\item \emph{Verifiable Decryption:} At the conclusion of each election, guardians use their secret key shares to jointly produce election tallies together with verifiable artifacts that prove that the tallies are correct. +\end{enumerate} + +S3a3 Guardians can use their secret key shares to jointly produce election tallies together with verifiable artifacts that prove that the tallies are correct. + +---- S3b +\subsubsection*{Notation} +In the remainder of this specification, the following notation will be used. +\begin{itemize} + \item $\Z = \{\dots,-3,-2,-1,0,1,2,3,\dots\}$ is the set of integers. + \item $\Z_n = \{0,1,2,\dots,n-1\}$ is the ring of integers modulo $n$. + \item $\Z_n^*$ is the multiplicative subgroup of $\Z_n$ that consists of all invertible elements modulo $n$. When $p$ is a prime, $\Z_p^* = \{1,2,3,\dots,p-1\}$. + \item $\Z_p^r$ is the set of $r$-th-residues in $\Z_p^*$. Formally, $\Z_p^r = \{y\in\Z_p^* \mbox{ for which there exists } x\in\Z_p^* \mbox{ such that } y=x^r \bmod p\}$. When $p$ is a prime for which $p-1=qr$ with $q$ a prime that is not a divisor of the integer $r$, then $\Z_p^r$ is an order-$q$ cyclic subgroup of $\Z_p^*$ and for each $y\in\Z_p^*$, $y\in\Z_p^r$ if and only if $y^q \bmod p = 1$. + \item $x\equiv_n y$ is the predicate that is true if and only if $x \bmod n=y \bmod n$. + \item The function $\HMAC(\ ,\ )$ shall be used to denote the \HMAC-\SHA keyed Hash Message Authentication Code (as defined in NIST PUB FIPS 198-1\footnote{NIST (2008) The Keyed-Hash Message Authentication Code (HMAC). In: FIPS 198-1. \url{https://csrc.nist.gov/publications/detail/fips/198/1/final}}) instantiated with \SHA (as defined in NIST PUB FIPS 180-4\footnote{NIST (2015) Secure Hash Standard (SHS). In: FIPS 180-4. \url{https://csrc.nist.gov/publications/detail/fips/180/4/final}}). $\HMAC$ takes as input a key $k$ and a message $m$ of arbitrary length and returns a bit string $\HMAC(k,m)$ of length 256 bits. + \item The \EG hash function $H(\ ,\ )$ is instantiated with \HMAC and thus has two inputs, both given as byte arrays. The first input to $H$ is used to bind hash values to a specific election. The second is a byte array of arbitrary length and consists of domain separation bytes and the data being hashed. $H$ outputs a digest of 256 bits, which is interpreted as a byte array of length 32. The detailed specification for $H$ is given in Section~\ref{sec:hashing} below. + \item The symbol $\oplus$ denotes bitwise XOR. + \item The symbol $\parallel$ denotes concatenation. + \item In general, the variable pairs $(\alpha,\beta)$, $(a,b)$, and $(A,B)$ will be used to denote encryptions. Specifically, $(\alpha,\beta)$ will be used to designate encryptions of votes (usually an encryption of a zero or a one), $(A,B)$ will be used to denote aggregations of encryptions -- which may be encryptions of larger values, and $(a,b)$ will be used to denote encryption commitments used to prove properties of other encryptions. +\end{itemize} + +---- S3c +\subsubsection*{Encryption of votes} +Encryption of votes in \EG is performed using a public key encryption method suggested by Devillez, Pereira, and Peters,\footnote{Devillez H., Pereira, O., Peters, T. (2022) \emph{How to Verifiably Encrypt Many Bits for an Election?} in ESORICS 2022 Lecture Notes in Computer Science, vol 13555. Springer, Berlin, Heidelberg. \url{https://link.springer.com/chapter/10.1007/978-3-031-17146-8_32}} which is a variant exponential form of the ElGamal cryptosystem,\footnote{ElGamal T. (1985) \emph{A Public Key Cryptosystem and a Signature Scheme Based on Discrete Logarithms}. In: Blakley G.R., Chaum D. (eds) Advances in Cryptology. CRYPTO 1984. Lecture Notes in Computer Science, vol 196. Springer, Berlin, Heidelberg. \url{https://link.springer.com/content/pdf/10.1007/3-540-39568-7_2.pdf}} which is, in turn, a static form of the widely-used Diffie-Hellman(-Merkle) key exchange\footnote{Diffie W., Hellman, M. (1976) \emph{New Directions in Cryptography} IEEE Transactions on Information Theory, vol 22}. This encryption method is called \emph{DPP vote encryption} or simply \emph{vote encryption} in this document and rests on precisely the same security basis as Diffie-Hellman(-Merkle) key exchange---which is used to protect the confidentiality of the vast majority of Internet traffic. + +S3c Encryption of votes in ElectionGuard is performed using the DPP vote encryption method of Devillez, Pereira, and Peters (2022) + +Primes $p$ and $q$ are publicly fixed such that $q$ is not a divisor of $r=(p-1)/q$. A generator $g$ of the order $q$ subgroup $\Z_p^r$ is also fixed. (Any $g=x^r \bmod p$ for which $x\in\Z_p^*$ suffices so long as $g\neq 1$.) + +S3c P is fixed +S3c P is prime +S3c Q is fixed +S3c Q is prime +S3c Q is is not a divisor of r=(p-1)/q +S3c G is fixed. +S3c G is a generator $g$ of the order $q$ subgroup $\Z_p^r$ + +A public-secret key pair can be chosen by selecting a random $s\in\Z_q$ as a secret key and publishing $K=g^s \bmod p$ as the corresponding public key\footnote{As will be seen below, the actual public key used to encrypt votes will be a combination of separately generated public keys. So, no entity will ever be in possession of a secret key that can be used to decrypt votes.}. + +S3c No entity is ever in possession of a secret key that can be used to decrypt votes + +A message $m\in\Z_p^r$ is then encrypted by selecting a random nonce $\xi\in\Z_q$ and forming the pair $(\alpha, \beta) = (g^\xi \bmod p, K^m\cdot K^\xi \bmod p) = (g^\xi \bmod p, K^{m+\xi} \bmod p)$. An encryption $(\alpha, \beta)$ can be decrypted by the holder of the secret $s$ as $\beta/\alpha^s\bmod p = K^m \bmod p$ because +\begin{equation} +\frac{\beta}{\alpha^s} \equiv_p \frac{K^m\cdot K^\xi}{(g^\xi)^s} \equiv_p \frac{K^m\cdot (g^s)^\xi}{(g^\xi)^s} \equiv_p \frac{K^m\cdot g^{\xi s}}{g^{\xi s}} \equiv_p K^m. +\end{equation} + +S3c A message $m\in\Z_p^r$ can be encrypted by selecting a random nonce $\xi\in\Z_q$ and forming the pair $(\alpha, \beta) = (g^\xi \bmod p, K^m\cdot K^\xi \bmod p) = (g^\xi \bmod p, K^{m+\xi} \bmod p)$ +S3c An encryption $(\alpha, \beta)$ can be decrypted by the holder of the secret $s$ as $\beta/\alpha^s\bmod p = K^m \bmod p$ + +The value of $m$ can be computed from $K^m \bmod p$ as long as the message $m$ is limited to a small, known set of options.\footnote{The simplest way to compute $m$ from $K^m \bmod p$ is an exhaustive search through possible values of $m$. Alternatively, a table pairing each possible value of $K^m \bmod p$ with $m$ can be pre-computed. A final option which can accommodate a larger space of possible values for $m$ is to use Shanks's baby-step giant-step method as described in the 1971 paper \emph{Class Number, a Theory of Factorization and Genera}, Proceedings of Symposium in Pure Mathematics, Vol. 20, American Mathematical Society, Providence, 1971, pp. 415-440.} + +The value of $K^m$ and therefore $m$ can also be computed from the encryption nonce $\xi$, namely via $\beta/K^\xi \equiv_p K^m$. While the secret key $s$ allows decryption of any ciphertext encrypted to the public key $K$, the encryption nonce only allows decryption of the specific ciphertext it was used to generate. The encryption nonce must therefore be securely protected. Release of an encryption nonce can, when appropriate, serve as a fast and convenient method of verifiable decryption. + +Usually, only two possible messages are encrypted in this way by \EG. An encryption of one is used to indicate that an option is selected, and an encryption of zero is used to indicate that an option is not selected. For some voting methods such as cumulative voting, Borda count, and a variety of cardinal voting methods like score voting and STAR-voting, it can be necessary to encrypt other small, positive integers. + +S3c The value of $K^m$ can be computed from the encryption nonce $\xi$ +S3c The value of $m$ can be computed from the encryption nonce $\xi$ +S3c An encryption of one is used to indicate that an option is selected +S3c An encryption of zero is used to indicate that an option is not selected + +---- S3d +\subsubsection*{Homomorphic properties} +A fundamental quality of the DPP vote encryption is its additively homomorphic property. If two messages $m_1$ and $m_2$ are respectively encrypted as $(A_1,B_1 ) = (g^{\xi_1} \bmod p , K^{m_1+\xi_1} \bmod p)$ and $(A_2, B_2) = (g^{\xi_2}\bmod p, K^{m_2+\xi_2} \bmod p)$, then the component-wise product +\begin{equation} + (A,B) = ( A_1 A_2 \bmod p, B_1 B_2 \bmod p) = (g^{\xi_1+\xi_2} \bmod p, K^{(m_1+m_2) + (\xi_1+\xi_2)} \bmod p) +\end{equation} +is an encryption of the sum $m_1+m_2$. (There is an implicit assumption here that $(m_1+m_2) < q$ which is easily satisfied when $m_1$ and $m_2$ are both small. If $(\xi_1+\xi_2)\geq q$, $(\xi_1+\xi_2) \bmod q$ may be substituted without changing the equation since $g^q\equiv_p 1$.) + +This additively homomorphic property is used in two important ways in \EG. First, all of the encryptions of a single option across ballots can be multiplied to form an encryption of the sum of the individual values. Since the individual values are one (or some other integer for certain voting methods) on ballots that select that option and zero otherwise, the sum is the tally of votes for that option and the product of the individual encryptions is an encryption of the tally. + +S3d All the encryptions of a single option across ballots can be multiplied to form an encryption of the sum of the individual values +S3d An encryption of some other integer is used for certain voting methods +S3d The product of the encryptions of a single option across ballots is an encryption of the tally + +The other use is to sum all of the selections made in a single contest on a single ballot. In the simplest case, after demonstrating that each option is an encryption of either zero or one, the product of the encryptions indicates the number of options that are encryptions of one, and this can be used to show that no more ones than permitted are among the encrypted options -- i.e., that no more options were selected than permitted. When larger integers are allowed, i.e. an option can receive multiple votes or weighted votes, the product of the ciphertexts then encrypts the total number of votes or the sum of weights and is used in the same way to ensure only the permitted number of votes or permitted sum of weights were used. + +S3d Every option can be demonstrated to be an encryption of either zero or one +S3d Every option can be demonstrated to be an encryption of some other integer for certain voting methods +S3d The product of the encrypted options of a single ballot can be used to show that no more options were selected than permitted +S3d When an option can receive multiple or weighted votes, the product of the encrypted options of a single ballot can be used to show that only the permitted number of votes or permitted sum of weights were used + +However, as will be described below, it is possible for a holder of a nonce $\xi$ to prove to a third party that a pair $(\alpha, \beta)$ is an encryption of $m$ without revealing the nonce $\xi$ and without access to the secret $s$. + +S3d A holder of a nonce $\xi$ can prove to a third party that a pair $(\alpha, \beta)$ is an encryption of $m$ without access to the secret $s$ or revealing the nonce $\xi$ + +---- S3e +\subsubsection*{Non-interactive zero-knowledge (NIZK) proofs} +\EG provides numerous proofs about encryption keys, encrypted ballots, and election tallies using the following four techniques. +\begin{enumerate} +\item A Schnorr proof\footnote{Schnorr C.P. (1990) Efficient Identification and Signatures for Smart Cards. In: Brassard G. (eds) Advances in Cryptology — CRYPTO' 89 Proceedings. CRYPTO 1989. Lecture Notes in Computer Science, vol 435. Springer, New York, NY. \url{https://link.springer.com/content/pdf/10.1007\%2F0-387-34805-0_22.pdf}} allows the holder of a secret key $s$ to interactively prove possession of $s$ without revealing $s$. + +S3e1 The holder of a secret key $s$ can interactively prove possession of $s$ without revealing $s$ using a Schnorr proof. + +\item A Chaum-Pedersen proof\footnote{Chaum D., Pedersen T.P. (1993) \emph{Wallet Databases with Observers}. In: Brickell E.F. (eds) Advances in Cryptology — CRYPTO' 92. CRYPTO 1992. Lecture Notes in Computer Science, vol 740. Springer, Berlin, Heidelberg. \url{https://link.springer.com/content/pdf/10.1007\%2F3-540-48071-4_7.pdf}} allows an encryption to be interactively proven to decrypt to a particular value without revealing the nonce used for encryption or the secret decryption key $s$. (This proof can be constructed with access to either the nonce used for encryption or the secret decryption key.) + +S3e2 An encryption can be interactively proven to decrypt to a particular value without revealing the nonce used for encryption or the secret decryption key $s$, with access to the nonce used for encryption with a Chaum-Pedersen proof. +S3e2 An encryption can be interactively proven to decrypt to a particular value without revealing the nonce used for encryption or the secret decryption key $s$, with access to $s$ using a Chaum-Pedersen proof. + +\item The Cramer-Damgård-Schoenmakers technique\footnote{Cramer R., Damgård I., Schoenmakers B. (1994) \emph{Proofs of Partial Knowledge and Simplified Design of Witness Hiding Protocols}. In: Desmedt Y.G. (eds) Advances in Cryptology — CRYPTO' 94. CRYPTO 1994. Lecture Notes in Computer Science, vol 839. Springer, Berlin, Heidelberg. \url{https://link.springer.com/content/pdf/10.1007\%2F3-540-48658-5_19.pdf}} enables a disjunction to be interactively proven without revealing which disjunct is true. + +S3e3 A disjunction can be interactively proven without revealing which disjunct is true using the Cramer-Damgård-Schoenmakers technique. + +\item The Fiat-Shamir heuristic\footnote{Fiat A., Shamir A. (1987) \emph{How To Prove Yourself: Practical Solutions to Identification and Signature Problems}. In: Odlyzko A.M. (eds) Advances in Cryptology — CRYPTO' 86. CRYPTO 1986. Lecture Notes in Computer Science, vol 263. Springer, Berlin, Heidelberg. \url{https://link.springer.com/content/pdf/10.1007\%2F3-540-47721-7_12.pdf}} allows interactive proofs to be converted into non-interactive proofs. + +S3e4 An interactive proof can be converted into a non-interactive proof using the Fiat-Shamir heuristic. + +\end{enumerate} +Using a combination of the above techniques, it is possible for \EG to demonstrate that keys are properly chosen, that ballots are well-formed, and that decryptions match claimed values.\footnote{For all proof variants, \EG uses a compact representation that omits the commitments. Proofs consist of the challenge and response values only. When verifying proofs, the verification equations can be used to recompute the commitments and check their correctness via the challenge hash computation.} + +S3e ElectionGuard can demonstrate that keys are properly chosen. +S3e ElectionGuard can demonstrate that ballots are well-formed. +S3e ElectionGuard can demonstrate that decryptions match claimed values. + +---- S3f +\subsubsection*{Threshold encryption} +Threshold encryption is used for encryption of ballots and other data. This form of encryption makes it very easy to combine individual guardian public keys into a single public key. It also offers a homomorphic property that allows individual encrypted votes to be combined to form encrypted tallies. + +S3f Threshold encryption is used for encryption of ballots and other data. +S3f It is very easy to combine individual guardian public keys into a single public key. +S3f Threshold encryption offers a homomorphic property that allows individual encrypted votes to be combined to form encrypted tallies. + +The guardians of an election will each generate a public-secret key pair. The public keys will then be combined (as described in the following section) into a single election public key which is used to encrypt all selections made by voters in the election. + +S3f Every guardian can generate a public-secret key pair. +S3f The guardians' public keys can be combined into a single election public key. +S3f The single election public key can be used to encrypt all selections made by voters in the election. + +At the conclusion of the election, each guardian will compute a verifiable partial decryption of each tally. These partial decryptions will then be combined to form full verifiable decryptions of the election tallies. + +S3f A guardian can compute a verifiable partial decryption of every tally. +S3f Verifiable partial tally decryptions can be combined to form full verifiable decryptions of the election tallies. + +To accommodate the possibility that one or more of the guardians will not be available at the conclusion of the election to form their partial decryptions, the guardians will cryptographically share\footnote{Shamir A. (1979) \emph{How to Share a Secret}. Communications of the ACM, vol 22.} their secret keys amongst each other during key generation in a manner to be detailed in Section~\ref{sec:keygen}. Each guardian can then compute a share of the secret decryption key, which they use to form the partial decryptions. A pre-determined threshold quorum value ($k$) out of the ($n$) guardians will be necessary to produce a full decryption. + +S3f During key generation, a guardian can cryptographically share their secret keys with another guardian per Section~\ref{sec:keygen}. +S3f A guardian, having received secret keys from other guardians, can compute a share of the secret decryption key +S3f A guardian, having computed a share of the secret decryption key, can +form a partial decryption. + +---- S3g +\subsubsection*{Encryption of other data} +\EG provides means to encrypt data other than votes, which are selections usually encoded as zero or one that need to be homomorphically aggregated. Such data may include the cryptographic shares of a guardian's secret key that are sent to the other guardians and are encrypted to the receiving guardians' public keys, write-in information that needs to be attached to an encrypted selection and is encrypted to the election public key, or other auxiliary data attached to an encrypted ballot, either encrypted to the election public key or to an administrative public key. The non-vote data do not need to be homomorphically encrypted and can use a more standard form of public-key encryption removing the data size restrictions imposed by the vote encryption. \EG encrypts such data with hashed ElGamal encryption, which deploys a key derivation function (\KDF) to generate a key stream that is then XORed with the data. To implement the \KDF and to provide a message authentication code (\MAC), encryption makes use of the keyed Hash Message Authentication Code \HMAC. In \EG, \HMAC is instantiated as \HMAC-\SHA with the hash function \SHA. + +S3g EGRI provides a non-homomorphic encryption method known as "hashed ElGamal" for encrypting data other than votes +S3g EGRI provides a way to use the appropriate private key to decrypt data encrypted with hashedElGamal +S3g data encrypted with hashedElGamal cannot be decrypted without the private key +S3g hashed ElGamal encryption does not have the data size restrictions imposed by the vote encryption +S3g hashed ElGamal encryption uses a KDF based on HMAC-SHA-2-256 to generate a key stream XORed with the data +S3g EGRI allows to encrypt the cryptographic shares of a guardian's secret key to the receiving guardians' public keys +S3g EGRI allows to encrypt to the election public key write-in information to be attached to an encrypted selection +S3g EGRI allows to attach to an encrypted selection write-in information that is encrypted to the election public key +S3g EGRI allows to attach to an encrypted ballot other auxiliary data without size restrictions encrypted with hashed-ElGamal to the election public key +S3g EGRI allows to attach to an encrypted ballot other auxiliary data without size restrictions encrypted with hashed-ElGamal to an administrative public key + +---- S3.1 +\subsection{Parameter requirements} +\EG uses integers to instantiate the encryption rather than elliptic curves in order to make construction of election verifiers as simple as possible without requiring special tools and dependencies. The encryption scheme used to encrypt votes is defined by a prime $p$ and a prime $q$ which divides $(p-1)$. We use $r = (p-1)/q$ to denote the cofactor of $q$, and a generator $g$ of the order $q$ subgroup $\Z_p^r$ is fixed. We also require $r/2$ to be prime. The specific values for a 4096-bit prime $p$ and a 256-bit prime $q$ which divides $(p-1)$ and a generator $g$ that define all standard baseline parameters are given below. + +S3.1 EGRI uses integer-based encryption rather than elliptic curves +S3.1 EGRI does not use elliptic curves +S3.1 The encryption scheme used to encrypt votes is defined by primes p and q. +S3.1 q divides p - 1 +S3.1 r = (p-1)/q is fixed. We also require $r/2$ to be prim +S3.1 r is a generator g of the order q subgroup Z_p^r +S3.1 subgroup Z_p^r is of order q +S3.1 r/2 is prime +S3.1 The value of p matches the specific value given in EG 2.0 DS section 3.1 +S3.1 The value of q matches the specific value given in EG 2.0 DS section 3.1 +S3.1 The value of r matches the specific value given in EG 2.0 DS section 3.1 +S3.1 The value of g matches the specific value given in EG 2.0 DS section 3.1 + +---- S3.1.1 +\subsubsection{Standard baseline cryptographic parameters}\label{sec:parameters} +Standard parameters for \EG begin with the largest 256-bit prime $q = 2^{256}-189$. The (big endian) hexadecimal representation of $q$ is as follows. +\begin{equation} +q = \mathtt{0xFFFFFFFF \ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFF43} +\end{equation} +The modulus $p$ is then set to be a 4096-bit prime with the following properties. +\begin{enumerate} + \item The first 256 bits of $p$ are all ones. + \item The last 256 bits of $p$ are all ones. + \item $p-1$ is a multiple of $q$. + \item $(p-1)/2q$ is also prime. +\end{enumerate} +The middle 3584 bits of $p$ are chosen by starting with the first 3584 bits of the constant $\mathrm{ln}(2)$ (the natural logarithm of $2$).\footnote{See \url{https://oeis.org/A068426} for the integer sequence consisting of the bits of $\mathrm{ln}(2)$.} After pre-pending and appending 256 ones, $p$ is determined by finding the smallest prime larger than this value that satisfies the above properties. + +This works out to $p = 2^{4096}-2^{3840}+2^{256} (\lfloor2^{3584} \mathrm{ln}(2)\rfloor + \delta) + (2^{256} - 1)$ where the value of $\delta$ is given by +{\small +$$\delta=287975203778583638958138611533602521491887169409704874643524560756486080635197037903.\footnote{Discovering this value $\delta$ required enumerating roughly 2.49 million values satisfying the first three of the above properties to find the first one for which both $p$ and $(p-1)/2q$ are both prime.} +$$ +} +The hexadecimal representation of $p$ is as follows. +{\allowdisplaybreaks +\begin{align*} + p \ = \ \mathtt{0x} + & \mathtt{FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF}\\ + & \mathtt{B17217F7\ D1CF79AB\ C9E3B398\ 03F2F6AF\ 40F34326\ 7298B62D\ 8A0D175B\ 8BAAFA2B}\\ + & \mathtt{E7B87620\ 6DEBAC98\ 559552FB\ 4AFA1B10\ ED2EAE35\ C1382144\ 27573B29\ 1169B825}\\ + & \mathtt{3E96CA16\ 224AE8C5\ 1ACBDA11\ 317C387E\ B9EA9BC3\ B136603B\ 256FA0EC\ 7657F74B}\\ + & \mathtt{72CE87B1\ 9D6548CA\ F5DFA6BD\ 38303248\ 655FA187\ 2F20E3A2\ DA2D97C5\ 0F3FD5C6}\\ + & \mathtt{07F4CA11\ FB5BFB90\ 610D30F8\ 8FE551A2\ EE569D6D\ FC1EFA15\ 7D2E23DE\ 1400B396}\\ + & \mathtt{17460775\ DB8990E5\ C943E732\ B479CD33\ CCCC4E65\ 9393514C\ 4C1A1E0B\ D1D6095D}\\ + & \mathtt{25669B33\ 3564A337\ 6A9C7F8A\ 5E148E82\ 074DB601\ 5CFE7AA3\ 0C480A54\ 17350D2C}\\ + & \mathtt{955D5179\ B1E17B9D\ AE313CDB\ 6C606CB1\ 078F735D\ 1B2DB31B\ 5F50B518\ 5064C18B}\\ + & \mathtt{4D162DB3\ B365853D\ 7598A195\ 1AE273EE\ 5570B6C6\ 8F969834\ 96D4E6D3\ 30AF889B}\\ + & \mathtt{44A02554\ 731CDC8E\ A17293D1\ 228A4EF9\ 8D6F5177\ FBCF0755\ 268A5C1F\ 9538B982}\\ + & \mathtt{61AFFD44\ 6B1CA3CF\ 5E9222B8\ 8C66D3C5\ 422183ED\ C9942109\ 0BBB16FA\ F3D949F2}\\ + & \mathtt{36E02B20\ CEE886B9\ 05C128D5\ 3D0BD2F9\ 62136319\ 6AF50302\ 0060E499\ 08391A0C}\\ + & \mathtt{57339BA2\ BEBA7D05\ 2AC5B61C\ C4E9207C\ EF2F0CE2\ D7373958\ D7622658\ 90445744}\\ + & \mathtt{FB5F2DA4\ B7510058\ 92D35689\ 0DEFE9CA\ D9B9D4B7\ 13E06162\ A2D8FDD0\ DF2FD608}\\ + & \mathtt{FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF} +\end{align*} +The hexadecimal representation of the cofactor $r = (p-1)/q$ is as follows. +\begin{align*} + r \ = \ \mathtt{0x01}\ + & \mathtt{00000000\ 00000000\ 00000000\ 00000000\ 00000000\ 00000000\ 00000000\ 000000BC}\\ + & \mathtt{B17217F7\ D1CF79AB\ C9E3B398\ 03F2F6AF\ 40F34326\ 7298B62D\ 8A0D175B\ 8BAB857A}\\ + & \mathtt{E8F42816\ 5418806C\ 62B0EA36\ 355A3A73\ E0C74198\ 5BF6A0E3\ 130179BF\ 2F0B43E3}\\ + & \mathtt{3AD86292\ 3861B8C9\ F768C416\ 9519600B\ AD06093F\ 964B27E0\ 2D868312\ 31A9160D}\\ + & \mathtt{E48F4DA5\ 3D8AB5E6\ 9E386B69\ 4BEC1AE7\ 22D47579\ 249D5424\ 767C5C33\ B9151E07}\\ + & \mathtt{C5C11D10\ 6AC446D3\ 30B47DB5\ 9D352E47\ A53157DE\ 04461900\ F6FE360D\ B897DF53}\\ + & \mathtt{16D87C94\ AE71DAD0\ BE84B647\ C4BCF818\ C23A2D4E\ BB53C702\ A5C8062D\ 19F5E9B5}\\ + & \mathtt{033A94F7\ FF732F54\ 12971286\ 9D97B8C9\ 6C412921\ A9D86797\ 70F499A0\ 41C297CF}\\ + & \mathtt{F79D4C91\ 49EB6CAF\ 67B9EA3D\ C563D965\ F3AAD137\ 7FF22DE9\ C3E62068\ DD0ED615}\\ + & \mathtt{1C37B4F7\ 4634C2BD\ 09DA912F\ D599F433\ 3A8D2CC0\ 05627DCA\ 37BAD43E\ 64A39631}\\ + & \mathtt{19C0BFE3\ 4810A21E\ E7CFC421\ D53398CB\ C7A95B3B\ F585E5A0\ 4B790E2F\ E1FE9BC2}\\ + & \mathtt{64FDA810\ 9F6454A0\ 82F5EFB2\ F37EA237\ AA29DF32\ 0D6EA860\ C41A9054\ CCD24876}\\ + & \mathtt{C6253F66\ 7BFB0139\ B5531FF3\ 01899612\ 02FD2B0D\ 55A75272\ C7FD7334\ 3F7899BC}\\ + & \mathtt{A0B36A4C\ 470A64A0\ 09244C84\ E77CEBC9\ 2417D5BB\ 13BF1816\ 7D8033EB\ 6C4DD787}\\ + & \mathtt{9FD4A7F5\ 29FD4A7F\ 529FD4A7\ F529FD4A\ 7F529FD4\ A7F529FD\ 4A7F529F\ D4A7F52A} +\end{align*} +Finally, the generator $g$ is chosen to be $g = 2^r \bmod p$ and has the following hexadecimal representation. +\begin{align*} + g \ = \ \mathtt{0x} + & \mathtt{36036FED\ 214F3B50\ DC566D3A\ 312FE413\ 1FEE1C2B\ CE6D02EA\ 39B477AC\ 05F7F885}\\ + & \mathtt{F38CFE77\ A7E45ACF\ 4029114C\ 4D7A9BFE\ 058BF2F9\ 95D2479D\ 3DDA618F\ FD910D3C}\\ + & \mathtt{4236AB2C\ FDD783A5\ 016F7465\ CF59BBF4\ 5D24A22F\ 130F2D04\ FE93B2D5\ 8BB9C1D1}\\ + & \mathtt{D27FC9A1\ 7D2AF49A\ 779F3FFB\ DCA22900\ C14202EE\ 6C996160\ 34BE35CB\ CDD3E7BB}\\ + & \mathtt{7996ADFE\ 534B63CC\ A41E21FF\ 5DC778EB\ B1B86C53\ BFBE9998\ 7D7AEA07\ 56237FB4}\\ + & \mathtt{0922139F\ 90A62F2A\ A8D9AD34\ DFF799E3\ 3C857A64\ 68D001AC\ F3B681DB\ 87DC4242}\\ + & \mathtt{755E2AC5\ A5027DB8\ 1984F033\ C4D17837\ 1F273DBB\ 4FCEA1E6\ 28C23E52\ 759BC776}\\ + & \mathtt{5728035C\ EA26B44C\ 49A65666\ 889820A4\ 5C33DD37\ EA4A1D00\ CB62305C\ D541BE1E}\\ + & \mathtt{8A92685A\ 07012B1A\ 20A746C3\ 591A2DB3\ 815000D2\ AACCFE43\ DC49E828\ C1ED7387}\\ + & \mathtt{466AFD8E\ 4BF19355\ 93B2A442\ EEC271C5\ 0AD39F73\ 3797A1EA\ 11802A25\ 57916534}\\ + & \mathtt{662A6B7E\ 9A9E449A\ 24C8CFF8\ 09E79A4D\ 806EB681\ 119330E6\ C57985E3\ 9B200B48}\\ + & \mathtt{93639FDF\ DEA49F76\ AD1ACD99\ 7EBA1365\ 7541E79E\ C57437E5\ 04EDA9DD\ 01106151}\\ + & \mathtt{6C643FB3\ 0D6D58AF\ CCD28B73\ FEDA29EC\ 12B01A5E\ B86399A5\ 93A9D5F4\ 50DE39CB}\\ + & \mathtt{92962C5E\ C6925348\ DB54D128\ FD99C14B\ 457F883E\ C20112A7\ 5A6A0581\ D3D80A3B}\\ + & \mathtt{4EF09EC8\ 6F9552FF\ DA1653F1\ 33AA2534\ 983A6F31\ B0EE4697\ 935A6B1E\ A2F75B85}\\ + & \mathtt{E7EBA151\ BA486094\ D68722B0\ 54633FEC\ 51CA3F29\ B31E77E3\ 17B178B6\ B9D8AE0F} +\end{align*} +} + +Alternative parameter sets are possible and may be allowed in future versions of \EG\footnotemark. + +\footnotetext{If alternative parameters are allowed, election verifiers must confirm that $p$, $q$, $r$, and $g$ are such that both $p$ and $q$ are prime (this may be done probabilistically using the Miller-Rabin algorithm), that $p-1 = qr$ is satisfied, that $q$ is not a divisor of $r$, that $1 (confirmed|failed)] (\prod_{j=0}^{k-1}(K_{i,j})^{l^j} mod p) +S3.2.2e [4/4] publicly reports i, l, and ('confirmed' or 'failed'). +S3.2.2e Every guardian G_i, in response to G_l reporting not receiving a suitable value P_i(l), can publish the P_i(l) and nonce xi_{i,l} they used to compute the key share to G_l +S3.2.2e [1/4] RI provides a utility to determine whether the published P_i(l) and xi_{i,l} match both the published encryption and the above equation. +S3.2.2e [2/4] This utility may also advise to +S3.2.2e [3/4] (if not matching) evict guardian G_i from the election on suspicion of malfeasance and restart the key generation process again with a different guardian, or +S3.2.2e [4/4] (if matching) absovle guardian G_i of suspicion of malfeasance and continue the key ceremony, optionally with suspicion on guardian G_l for having made a false claim of malfeasance. +S3.2.2e Every guardian G_i computes its share of the implicit secret key, P(i) = (sum_{j = 1}^n P_j(i)) mod q = (P_1(i) + P_2(i) + ... + P_n(i) mod q +S3.2.2e Every guardian G_i saves for later use its share of the implicit secret key, P(i) +S3.2.2e ***changed*** Every guardian G_i saves for later use its random integer secret s_i +S3.2.2e Every guardian G_i discards s_i at the end of the key generation ceremony + +---- S3.2.3 +\subsubsection{Extended base hash}\label{sec:extendedhash} +Once the baseline parameters have been produced and confirmed, the base hash $\HH_B$ is hashed with the election public key $K$ to form an extended base hash +\begin{equation}\label{eq:extbasehash} + \HH_E = H(\HH_B; \mathtt{0x12}, K) +\end{equation} +that will form the basis of subsequent hash computations. + +\EGverif{\veriftitleExtendedBaseHashValidation}{\label{verif:extendedBaseHash} +\veriftextExtendedBaseHashValidation} + +S3.2.3 The extended base hash is computed as H_E = H(H_B; 0x12, K) (reminder: K is election public key) + +---- S3.3 +\subsection{Ballot Encryption}\label{sec:ballotencryption} + +Although there are some exceptions, an \EG ballot is typically comprised entirely of encryptions of one (indicating selection made) and zero (indicating selection not made). To enable homomorphic addition (for tallying), these values are exponentiated during vote encryption. + +---- S3.3.1 +\subsubsection{Selection encryption}\label{sec:selectionencryption} +To encrypt a ballot entry $\sigma$, a random value $\xi$ is selected such that $0\leq \xi0$ is then computed as above via +$\HH(B_j)=H(\HH_E; \mathtt{0x24}, \chi_1, \chi_2, \ldots, \chi_{m_B}, \B_{\mathrm{aux},j})$ and the additional byte array +\begin{equation} + \B_{\mathrm{aux},j} = \HH_{j-1}\parallel \B_{\mathrm{aux},0} +\end{equation} +now must contain the confirmation code $\HH_{j-1}$ of the previous ballot $B_{j-1}$ (or the initialization code $\HH_0$ when computing $\HH_1 = \HH(B_1)$) and the voting device information by including $\B_{\mathrm{aux},0}$. +If $\B_{\mathrm{aux},j}$ is allowed to contain more than one prior confirmation code, a tree of hash dependencies can be formed. The auxiliary byte array $\B_{\mathrm{aux},j}$ should be stored in the encrypted ballot. + +The chain should be closed at the end of an election by forming and publishing +\begin{equation}\label{eq:hashchainclose} +\overline{\HH} = H(\HH_E; \mathtt{0x24}, \overline\B_{\mathrm{aux}}), +\end{equation} +using $\overline\B_{\mathrm{aux}} = \HH(B_\ell)\parallel \B_{\mathrm{aux},0} \parallel \bytes(\Str{CLOSE}, 5)$, where $\HH(\B_\ell)$ is the final confirmation code in the chain. The benefit of a chain is that it makes it more difficult for a malicious insider to selectively delete ballots and confirmation codes after an election without detection. + +\EGverif{\veriftitleValidationOfTrackingCodes}{\label{verif:trackingcodes} +\veriftextValidationOfTrackingCodes} + +\paragraph{Ballot casting or challenging.} +Once in possession of a confirmation code (\emph{and never before}), a voter is afforded an option to either cast the associated ballot or challenge it and restart the ballot preparation process. The precise mechanism for voters to make these selections may vary depending upon the instantiation, but this choice would ordinarily be made immediately after a voter is presented with the confirmation code, and the status of the ballot would be undetermined until the decision is made. It is possible, for instance, for a voter to make the decision directly on the voting device, or a voter may instead be afforded an option to deposit the ballot in a receptacle or to take it to a poll worker to be challenged. For vote-by-mail scenarios, a voter can be sent (hashes of) two complete sets of encryptions for each selectable option and can effect a ballot challenge implicitly by choosing which encryptions to return. + +---- S3.5 +\subsection{Ballot Aggregation}\label{sec:ballotaggregation} +At the conclusion of voting, all of the ballot encryptions are published in the election record together with the proofs that the ballots are well-formed. Additionally, all of the encryptions of each option are homomorphically combined to form an encryption of the sum of the values that were individually encrypted. The encryptions $(\alpha_i, \beta_i)$ of each individual option are combined by forming the product +\begin{equation} + (A,B)=\left(\prod_i\alpha_i \bmod p,\ \prod_i\beta_i \bmod p\right). +\end{equation} +This aggregate encryption $(A,B)$, which represents an encryption of the tally of that option, is published in the election record for each option. + +\EGverif{\veriftitleCorrectnessOfBallotAggregation}{\label{verif:aggregation} +\veriftextCorrectnessOfBallotAggregation} + + +---- S3.6 +\subsection{Verifiable Decryption}\label{sec:verifiable_decrypt} +To decrypt an aggregate encryption $(A,B)$ (or an individual encryption such as one on a challenged ballot), guardians work together in a protocol, where each guardian produces a partial decryption and contributes to an accumulated Chaum-Pederson proof of correct decryption. + +As long as at least $k$ guardians are present for decryption, the partial decryptions produced by the guardians are used to compute the value +\begin{equation} +M = A^s \bmod p, +\end{equation} +without ever computing the secret key $s$. This value is then used to obtain +\begin{equation} +T = B\cdot M^{-1} \bmod p. +\end{equation} +This $T$ has the property that $T=K^t \bmod p$ where $t$ is the tally of the associated option. + +In general, computation of this tally value $t$ is computationally intractable. However, in this application, $t$ is relatively small---usually bounded by the number of votes cast. This tally value $t$ can be determined from $T$ by exhaustive search, by precomputing a table of all possible $T$ values in the allowable range and then performing a single look-up, or by a combination in which some exponentiations are precomputed and a small search is used to find the value of $t$ (e.g., a partial table consisting of $K^{100} \bmod p$, $K^{200} \bmod p$, $K^{300} \bmod p$, \dots is precomputed and the value $T$ is repeatedly divided (or multiplied) by $K$ until a value is found that is in the partial table). The value $t$ is published in the election record, and verifiers should check both that $T = K^t \bmod p$ and that $B = T\cdot M \bmod p$. + +---- S3.6.1 +\subsubsection{Partial decryption by available guardians}\label{sec:decrypt_missing} + +During the key generation process (as described in Section~\ref{sec:keygendetails}), each guardian $G_i$ receives from each other guardian $G_j$ a share $P_j(i)$ of $G_j$'s secret key $s_j$ ($1\leq i,j \leq n$ and $i \neq j$). Guardian $G_i$ also computes its own share $P_i(i)$ and is therefore in possession of all shares $P_j(i)$ for $1 \leq j \leq n$. Either at that time or at a later time but before $G_i$ participates in any joint decryption process, $G_i$ computes +\begin{equation} +P(i) = \left(\sum_{j = 1}^n P_j(i)\right) \bmod q = \left(P_1(i) + P_2(i) + \dots + P_n(i)\right) \bmod q. +\end{equation} +The value $P(i)$ is $G_i$'s share of the implicit secret key $s = (s_1 + s_2 + \dots + s_n) \bmod q$.\footnote{Guardian $G_i$'s secret key $s_i$ is the constant coefficient of the polynomial $P_i(x)$ and therefore the implicit secret key $s = (s_1 + s_2 + \dots + s_n) \bmod q$ is the constant coefficient of the polynomial $P(x) = (P_1(x) + P_2(x) + \dots + P_n(x)) \bmod q$. The collection of the $P(i)$ are the shares for a $k$-out-of-$n$ sharing of the secret key $s$. It is important to note that this secret key $s$ is never actually computed; instead each guardian uses its share of the implicit secret key $s$ to form a share of any decryption that needs to be performed.} + +Each guardian $G_i$ available for decryption uses the share $P(i)$ to compute the partial decryption +\begin{equation} +M_{i} = A^{P(i)} \bmod p +\end{equation} +that contributes to computing the value $M = A^s \bmod p$. + +---- S3.6.2 +\subsubsection{Combination of partial decryptions.} +The partial decryptions are combined to obtain $M$ with the help of the Lagrange coefficients that correspond to the available guardians. Let $U \subseteq \{1,2,\dots,n\}$ be the set of indices such that $\{G_i : i\in U\}$ is the set of available guardians. For decryption to succeed, a quorum of at least $k$ guardians is needed, i.e. $|U| = h \geq k$. All available guardians participate in the reconstruction even if $h>k$, i.e., there are more guardians available than strictly necessary for having a quorum. + +The Lagrange coefficients corresponding to $U$ are computed as +\begin{equation} +w_i = \left(\prod_{\ell\in(U\setminus\{i\})} \frac{\ell}{\ell-i}\right) \bmod q. +\end{equation} +These coefficients are used to combine the $M_i$ for $i\in U$ provided by the available guardians to obtain +\begin{equation}\label{eqn:threshold_decrypt} + M = \prod_{i\in U}(M_{i})^{w_i} \bmod p. +\end{equation} + +\EGnote{The decryption $M = A^s \bmod p$ can be computed as shown in Equation~\eqref{eqn:threshold_decrypt} because $s = (\sum_{i\in U} w_i P(i)) \bmod q$. Likewise, a missing secret $s_j$ could be computed directly as $s_j = (\sum_{i\in U} w_i P_j(i)) \bmod q$. +However, it is preferable to not release the secret $s$ (or any of the missing secrets) and instead only release the partial decryptions that the secret would have produced. This prevents the secret from being used for additional decryptions without the cooperation of at least $k$ guardians.\vspace{2pt}\\ +As an example, consider an election with five guardians and a threshold of three. If two guardians are missing at the time of decryption, the remaining three can perform any required decryptions as described in the text above. If, instead, they take the shortcut of simply reconstructing and then using the two missing secrets, then any of the three could, at a later time, use these missing secrets together with its own secret to perform additional decryptions without the cooperation of any other guardian.} + +---- S3.6.3 +\subsubsection{Proof of correctness}\label{sec:verifiable_decrypt_proof} +The available guardians work together to produce a Chaum-Pedersen proof that $M$ was computed correctly, which means that $M= A^s \bmod p$. The proof is computed as follows. + +\paragraph{NIZK Proof:} The available guardians jointly prove that they have shared knowledge of $s\in \Z_q$ such that $M = A^{s} \bmod p$ and $K = g^{s} \bmod p$. + +\noindent Each available guardian $G_i$, $i \in U$, selects a random value $u_i$ in $\Z_q$ and commits to the pair +\begin{equation} +(a_i,b_i) = (g^{u_i} \bmod p,\ A^{u_i} \bmod p). +\end{equation} +The $a_i$ and $b_i$ obtained from each guardian are used to compute the accumulated commitments as +\begin{equation} +a = \left(\prod_{i\in U} a_i\right) \bmod p,\ b = \left(\prod_{i\in U} b_i\right) \bmod p. +\end{equation} +This means $a = g^u \bmod p$ and $b = A^u \bmod p$, where $u = (\sum_{i \in U} u_i) \bmod q$ and $u$ is not computed explicitly. + +The ciphertext $(A,B)$, the commitments $(a,b)$, and the combined value $M$ are then hashed together with the extended base hash value $\HH_E$ to form a challenge +\begin{equation}\label{eq:nizk_c_dec} +c = H(\HH_E;\mathtt{0x30},K,A,B,a,b,M). +\end{equation} +The challenge $c$ is adjusted by the $i$-th Lagrange coefficient to produce a challenge $c_i$ for available guardian $G_i$ ($i\in U$) as +\begin{equation} +c_i = (c\cdot w_i) \bmod q. +\end{equation} +Next, each available guardian $G_i$ responds to the challenge $c_i$ with +\begin{equation} +v_i = (u_i-c_i P(i)) \bmod q. +\end{equation} +All individual responses $v_i$ are verified by recomputing the commitments from the responses, the individual challenge values $c_i$, the guardians' commitments $K_{j,m}$ to the coefficients $a_{j,m}$ of their secret sharing polynomials $P_j$ (see \ref{sec:keygendetails}), the ciphertext value $A$ and the partial decryptions $M_i$ as +\begin{align} + a_i' & = g^{v_i} \left(\prod_{j=1}^n\prod_{m=0}^{k-1}K_{j,m}^{i^m}\right)^{c_i} \bmod p,\\ + b_i' & = A^{v_i} M_i^{c_i} \bmod p +\end{align} +and checking that $a_i' = a_i$ and $b_i' = b_i$. If these equations are all satisfied, an accumulated response +\begin{equation} +v = \left(\sum_{i \in U} v_i\right) \bmod q +\end{equation} +is computed and the decrypted value $T = B\cdot M^{-1} \bmod p$ is published along with the proof $(c,v)$ in the election record. Note that $v = (u - c\cdot s) \bmod q$. + +\EGverif{\veriftitleCorrectnessOfDecryptions}{\label{verif:decryption} +\veriftextCorrectnessOfDecryptions} + +\paragraph{Tally verification.} +The final step is to verify the tallies themselves. + +\EGverif{\veriftitleValidationOfCorrectDecryptionOfTallies}{\label{verif:tallies} +\veriftextValidationOfCorrectDecryptionOfTallies} + +---- S3.6.4 +\subsubsection{Decryption of contest data}\label{sec:decrypt_contest_data} +For each contest, an encrypted ballot contains a ciphertext $C_E = (C_0, C_1, C_2)$ encrypting contest data such as overvote, undervote, and null vote information and write-in text fields. The ciphertext has been generated as described in Section \ref{sec:encrypt_ext_data}. Such data may need to be decrypted if the tallies record a significant number of votes in write-in selections. Also, all contest data fields on each challenged ballot are decrypted. + +Decryption can be done by a quorum of guardians similarly to the decryption of tallies explained above. Each available guardian $G_i$, $i \in U$, computes a partial decryption +\begin{equation} + m_i = C_0^{P(i)} \bmod p +\end{equation} +using its precomputed share $P(i) = \sum_{j=1}^n P_j(i) \bmod q$. The partial decryptions are combined using the Lagrange coefficients for the set $U$ of available guardians to obtain +\begin{equation} +\beta = \left(\prod_{i\in U} m_i^{w_i}\right) \bmod p +\end{equation} +Again, the available guardians work together to produce and publish the following proof. + +\paragraph{NIZK Proof:} The available guardians jointly prove that they have shared knowledge of $s\in \Z_q$ such that $\beta = C_0^{s} \bmod p$ and $K = g^{s} \bmod p$. + +\noindent This proof is exactly the same as the one in Section~\ref{sec:verifiable_decrypt_proof}, where the ciphertext $(A,B)$ is replaced by the contest data ciphertext $(C_0, C_1, C_2)$ as follows. +Guardian $G_i$ selects a random value $u_i$ in $\Z_q$ and commits to the pair +\begin{equation} +(a_i,b_i) = (g^{u_i} \bmod p,\ C_0^{u_i} \bmod p). +\end{equation} +The values $a_i$ and $b_i$ are accumulated into +\begin{equation} + a = \left(\prod_{i\in U} a_i\right) \bmod p,\ b = \left(\prod_{i\in U} b_i\right) \bmod p + \end{equation} +and the joint challenge value $c$ is obtained as +\begin{equation}\label{eq:nizk_c_dec_cont} +c = H(\HH_E;\mathtt{0x31},K,C_0,C_1,C_2,a,b,\beta). +\end{equation} +Each available guardian $G_i$ responds to its challenge $c_i = (c\cdot w_i) \bmod q$ with +\begin{equation} +v_i = (u_i-c_i P(i)) \bmod q. +\end{equation} +As above, all proof parts $v_i$ are verified by recomputing commitments +\begin{align} + a_i' & = g^{v_i} \left(\prod_{j=1}^n\prod_{m=0}^{k-1}K_{j,m}^{i^m}\right)^{c_i} \bmod p,\\ + b_i' & = C_0^{v_i} m_i^{c_i} \bmod p. +\end{align} +If $a_i' = a_i$ and $b_i' = b_i$, an accumulated response +\begin{equation} +v = \left(\sum_{i \in U} v_i\right) \bmod q +\end{equation} +is computed and the decryption value $\beta$ is published along with the proof $(c,v)$. + +Then decryption proceeds by computing the key $k = H(\HH_E; \mathtt{0x22}, K, C_0, \beta)$ and the MAC key +\begin{equation} + k_0 = \HMAC(k,\bytes(0,4)\parallel\mathtt{Label}\parallel\mathtt{0x00}\parallel\mathtt{Context}\parallel \bytes((b_D+1)\cdot 256,4)) +\end{equation} +with $\mathtt{Label} = \bytes(\mathtt{``data\_enc\_keys"}, 13)$ and $\mathtt{Context} = \bytes(\mathtt{``contest\_data"}, 12)\parallel \bytes(\indc(\Lambda), 4)$. +When it is the case that $C_2 = \HMAC(k_0, C_0\parallel C_1)$, the encryption keys $k_1, k_2, \dots, k_{b_D}$ are computed as +\begin{equation} + k_i = \HMAC(k,\bytes(i,4)\parallel\mathtt{Label}\parallel\mathtt{0x00}\parallel\mathtt{Context}\parallel \bytes((b_D+1)\cdot 256,4)) +\end{equation} +and the byte array $D$ representing the contest data string is decrypted by parsing $C_1$ in 32-byte blocks as +\begin{equation} +C_1 = C_{1,1} \parallel C_{1,2} \parallel \dots \parallel C_{1,b_D} +\end{equation} +and obtaining +\begin{equation} + D = C_{1,1}\oplus k_1 \parallel C_{1,2}\oplus k_2 \parallel \dots \parallel C_{1,b_D}\oplus k_{b_D}. +\end{equation} +The byte array $D$ can now be parsed to reveal the captured overvote, undervote, and null vote data and write-in text fields. + + +\EGverif{\veriftitleCorrectnessOfDecryptionsOfContestData}{\label{verif:extdecryption} +\veriftextCorrectnessOfDecryptionsOfContestData} + + +---- S3.6.5 +\subsubsection{Decryption of challenged ballots}\label{sec:decrypt_challenged} +Each and every challenged ballot must be verifiably decrypted. \EG supports two methods of decrypting challenged ballots. The first is for the guardians to use their secret keys in precisely the way they do to decrypt election tallies. The second is to simply publish the primary nonce used to encrypt each challenged ballot. Whether these nonces are available depends on how \EG is used. + +\paragraph{Standard decryption with guardian keys.} +Every ballot challenged in an election is individually verifiably decrypted in exactly the same way that the aggregate ballot of tallies is decrypted. Each individual selection encryption $(\alpha, \beta)$ in each contest on the challenged ballot is jointly decrypted by the available guardians as shown at the beginning of Section~\ref{sec:verifiable_decrypt} with $(A,B)$ replaced by $(\alpha, \beta)$. This includes the generation of the appropriate NIZK proofs to prove correctness of these decryptions $M$. The corresponding encrypted selection is then decrypted by combining the partial decryptions as shown above for tallies by computing +\begin{equation} +S = \beta\cdot M^{-1}\bmod p. +\end{equation} +This $S$ encrypts a vote $\sigma$ as $S = K^\sigma \bmod p$ from which $\sigma$ can be determined as described at the beginning of Section~\ref{sec:verifiable_decrypt}.\footnote{When, as is usually the case, $\sigma$ is known to be in the set $\{0,1\}$, the decryption can be completed simply by checking whether $S = 1$ or $S = K$.} + +In addition, all contest data fields on a challenged ballot are decrypted as shown in the previous Subsection~\ref{sec:decrypt_contest_data}. + +An election verifier must confirm the correct decryption of each challenged ballot using the same process that was used to confirm the election tallies. In particular, this means that Verification~\ref{verif:decryption} must be confirmed. +In order to assign unique verification steps for challenged ballots, the relevant verification steps for tallies are adjusted and listed here again. + +In addition to all selection and contest data decryptions, election verifiers should confirm that a ballot is well-formed. Overall, casual observers should be able to simply view the decryptions and confirm that they match their expectations. + +\EGverifBallot{\veriftitleCorrectnessOfDecryptionsChallenged}{\label{verif:challengedDecrypt} +\veriftextCorrectnessOfDecryptionsChallenged} + +\EGverifBallot{\veriftitleValidationOfCorrectDecryptionOfChallengedBallots}{\label{verif:challengedballots} +\veriftextValidationOfCorrectDecryptionOfChallengedBallots} + +Finally, the following verification steps are carried out for each challenged ballot to confirm the correct decryption of contest data. + +\EGverifBallot{\veriftitleCorrectnessOfDecryptionsOfContestDataChallenged}{\label{verif:challengedDecryptContest} +\veriftextCorrectnessOfDecryptionsOfContestDataChallenged} + +\paragraph{Verifying decryption with nonces.} +When the primary nonce $\xi_B$ that was used to encrypt a ballot $B$ is available, a verifiable decryption can be produced by simply publishing this nonce. This can drastically reduce the amount of computation required of both guardians and verifiers. + +There are several scenarios in which this nonce may be available. For instance, if a ballot is challenged immediately after encryption, the device that performed the encryption may be able to provide the nonce as soon as the challenge is issued. Alternatively, the device that encrypted the ballot might encrypt the ballot's primary nonce with the election key or with another key and provide this with the encrypted ballot. + +Publishing a ballot's primary nonce in an election record enables a decryption to be confirmed by simply repeating the encryption and checking for a match. The same steps could also be taken by a voter equipped with a suitable application immediately upon receiving this nonce to verify a challenge ballot without having to wait for the election record to be published.\footnote{It is likely that a voter would only have access to a ballot's confirmation code --- not its full encryption --- if a verification is to be performed prior to the publication of the election record. Thus, the voter's application would need to regenerate the encrypted ballot from the primary nonce and the voter's selections and then recompute the confirmation code in order to verify its accuracy.} + +Specifically, with possession of a primary ballot nonce $\xi_B$ and the selections made on the ballot, a verification application can repeat the vote encryption process as described in \S\ref{sec:ballotencryption} as follows. +\begin{itemize} + \item It derives the selection encryption nonce for each contest and each selection in the contest. The nonce for the $i$-th contest, which has label $\Lambda_i$, and the $j$-th selection in the contest, which has label $\lambda_j$ is $\xi_{i,j}$ and is derived by a hash computation as shown in Equation~\eqref{eq:noncegen} in Section~\ref{sec:noncegen}. + \item Using the encryption nonces $\xi_{i,j}$, it recomputes the selection encryptions as shown in Equation~\eqref{eqn:encryptvote} of Section~\ref{sec:selectionencryption}. + \item Finally, it recomputes the confirmation code $\HH(B)$ as described in Section~\ref{sec:confirmationcodes} via its contest hashes. +\end{itemize} +If the resulting confirmation code matches the confirmation code provided to the voter, then this is verification that the confirmation code corresponds to a ballot with votes for the indicated selections. + +Note that confirmation codes {\em do not} include any of the zero-knowledge proofs of ballot correctness. So a verifier does not need to reproduce these zero-knowledge proofs. Only the actual encryptions of the selections must be regenerated and confirmed. + +---- S3.7 +\subsection{The Election Record} +The record of an election should be a full accounting of all of the election artifacts. Specifically, it should contain the following. +\begin{itemize} + \item Information sufficient to uniquely identify and describe the election, such as date, location, election type, etc. (not otherwise included in the election manifest). + \item The election manifest file. + \item The baseline parameters: + \begin{itemize} + \item primes $p$ and $q$ and integer $r$ such that $p=qr+1$ and $r$ is not a multiple of $q$, + \item a generator $g$ of the order $q$ multiplicative subgroup $\Z_p^*$, + \item the number $n$ of election guardians, + \item the quorum threshold $k$ of guardians required to complete verification. + \end{itemize} + \item The parameter base hash $\HH_P$ computed from the parameters. + \item The base hash value $\HH_B$ computed from the above. + \item The commitments from each election guardian to each of their polynomial coefficients. + \item The proofs from each guardian of possession of each of the associated coefficients. + \item The election public key. + \item The extended base hash value $\HH_E$ computed from the above. + \item Every encrypted ballot prepared in the election (whether cast or challenged): + \begin{itemize} + \item All of the encrypted selections on each ballot, + \item the proofs that each such value is an encryption of either zero or one, + \item the selection limit for each contest, + \item the proof that the number of selections made matches the selection limit, + \item the ballot style, + \item the device information for the device that encrypted the ballot, + \item the date and time of the ballot encryption, + \item the confirmation code produced for the ballot. + \end{itemize} + \item The decryption of each challenged ballot: + \begin{itemize} + \item The selections made on the ballot, + \item the plaintext representation of the selections, + \item proofs of each decryption. + \end{itemize} + \item Tallies of each option in an election: + \begin{itemize} + \item The encrypted tally of each option, + \item full decryptions of each encrypted tally, + \item plaintext representations of each tally, + \item proofs of correct decryption of each tally. + \end{itemize} + \item Ordered lists of the ballots encrypted by each device. +\end{itemize} +An election record should be digitally signed by election administrators together with the date of the signature. The entire election record and its digital signature should be published and made available for full download by any interested individuals. Tools should also be provided for easy look up of confirmation codes by voters. + +The exact organizational structure of the election record will be specified in a separate document. + + + + +\pagebreak + +-------- Section 4 + +\section{Pre-Encrypted Ballots}\label{sec:pre-encrypted} + +In typical use, \EG ballots are encrypted after the voter selections are known. However, there are several common scenarios in which it is desirable to perform the encryption before the selections of a voter are known. These include Vote-by-Mail, pre-printed ballots for use in precincts with central count or minimal in-precinct equipment, and back-ups for precincts which ordinarily perform encryption on demand. + +With pre-encrypted ballots, each possible selection on a ballot is individually encrypted in advance; and the selections made by the voter indicate which encryptions are used. + +\EG requires two applications to support pre-encrypted ballots: a \emph{ballot encrypting tool} to provide data to enable printing of blank ballots and a companion \emph{ballot recording tool} to receive information about selections made on pre-printed ballots and produce data compatible with the \EG election record. + +---- S4.1 +\subsection{Format of Pre-Encrypted Ballots} +Each selectable option within each contest is associated with a vector $\Psi$ of encryptions $E_j$ --- with one encryption for each selectable option within the contest. Selectable options in a contest have a unique order determined by their option indices in increasing order. Let $i$ be the position of the selectable option in this ordering (not necessarily identical to its option index). +In its \emph{normal} form and for a contest with $m$ selection options, this vector +\begin{equation}\label{eq:encvector} +\Psi_{i,m}=\langle E_1,E_2,\ldots,E_m\rangle +\end{equation} +includes an encryption $E_i=(\alpha_i,\beta_i) = \Enc(1; \xi_i)$ of one in the vector position $i$ associated with the selection made and encryptions $E_j=(\alpha_j,\beta_j) = \Enc(0, \xi_j)$ of zero in every other position $1\leq j \leq m$, $j\neq i$, where the $\xi_j$ are (pseudo-)random encryption nonces. For example, the vector $\Psi_{2,4}$ associated with selecting the second option in a contest with a total of $m=4$ selection options is $\Psi_{2,4} = \langle \Enc(0; \xi_1), \Enc(1; \xi_2), \Enc(0; \xi_3), \Enc(0; \xi_4)\rangle$. This corresponds precisely to the standard \EG encryption of a vote for the same option. There is also a \emph{null} form $\Psi_{0,m} = \langle \Enc(0; \xi_1), \Enc(0; \xi_2), \dots, \Enc(0; \xi_m)\rangle$ which is the same form except that all values are encryptions of zero. + +The principal difference between a pre-encrypted ballot and a standard \EG ballot is that while standard \EG has a single vector of encryptions for each contest, here we have a vector of encryptions for each selectable option in each contest (generally including the possibility of an undervote in which no selections are made). Another difference is that while a standard \EG contest encryption can contain multiple selections, each vector of pre-encryptions represents at most one selection. However, in a contest where a voter is allowed to make multiple selections, multiple pre-encryption vectors can be combined to form a single contest encryption vector with multiple encryptions of one that precisely matches the standard \EG format. + +---- S4.1.1 +\subsubsection{Selection hash}\label{sec:selectionhash_pre} +Each pre-encrypted vector of a pre-encrypted ballot is hashed using the \EG hash function $H$ (specified in detail in Section~\ref{sec:hashing}) to form a \emph{selection hash}. For all selectable options, i.e. for each option at position $i$ in the contest with $1\leq i \leq m$, the hash value $\psi_i$ of the selection vector $\Psi_{i,m} = \langle E_1,E_2,\ldots,E_m\rangle$ is computed as +\begin{equation}\label{eq:selectionhash_pre} +\psi_i = H(\HH_E; \mathtt{0x40}, K, \alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m), +\end{equation} +where $E_i = (\alpha_i, \beta_i)$ is an encryption of one and $E_j = (\alpha_j, \beta_j)$ is an encryption of zero for $j \neq i$. + +In a contest with a selection limit of $L$, an additional $L$ null vectors are hashed to obtain +\begin{equation}\label{eq:nullhash_pre} +\psi_{m+\ell}=H(\HH_E; \mathtt{0x40}, K, \alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m), +\end{equation} +where all $E_i = (\alpha_i, \beta_i)$ are encyptions of zero and $1\leq \ell \leq L$. + +---- S4.1.2 +\subsubsection{Contest hash}\label{sec:contesthash_pre} +% Copyright (C) Microsoft Corporation. All rights reserved. +All of the selection hashes within each contest will ultimately be hashed {\em in sorted order} to form the \emph{contest hash} of that contest. Each contest on a ballot has a unique position determined by the position of its contest index in the list of contest indices in increasing order. The contest hash for the $l$-th contest (with label $\Lambda_l$) on the ballot is computed as +\begin{equation}\label{eq:contesthash_pre} +\chi_l = H(\HH_E; \mathtt{0x41}, \indc(\Lambda_l), K,\psi_{\sigma(1)},\psi_{\sigma(2)},\ldots,\psi_{\sigma(m+L)}), +\end{equation} +where $\sigma$ is a permutation that represents the sorting of the selection hashes. This means that contests are \emph{not} hashed in the order given by the contest indices, but instead $\sigma(i)<\sigma(j)$ implies that $\psi_{\sigma(i)}<\psi_{\sigma(j)}$ (when hash values are interpreted as integers in big endian byte order). +The sorting is required so that the order of the selection hashes $\psi_1,\psi_2,\ldots,\psi_m$ does not reveal the contents of the encryptions that are used to generate the hashes. + +---- S4.1.3 +\subsubsection{Confirmation code}\label{sec:confirmationcode_pre} +While contest hashes for pre-encrypted ballots are computed from selection hashes, which differs from the standard scenario for \EG described in Section~\ref{sec:confirmationcodes}, the computation of confirmation codes aligns with the previous case. The \emph{confirmation code} $\HH(B)$ of a pre-encrypted ballot $B$ is generated as the hash of all the contest hashes on the ballot in sequential order. If there are $m_B$ contests on the ballot (in sequential order specified by their contest indices in the election manifest file), its confirmation code is computed as +\begin{equation}\label{eq:ballothash_pre} +\HH(B)=H(\HH_E; \mathtt{0x42}, \chi_1,\chi_2,\ldots, \chi_{m_B}, \B_{\mathrm{aux}}). +\end{equation} +Use of the auxiliary input byte array $\B_{\mathrm{aux}}$ is the same as described in Sections~\ref{sec:confirmationcode} and \ref{sec:ballotchaining} to enable the input of specific information of the device generating pre-encrypted ballots. + +A ballot's hash will be printed directly on the ballot. +Ideally, two copies of the ballot hash will be included on each ballot with one remaining permanently with the ballot and the other in an immediately adjacent location on a removable tab. These removable ballot codes are intended to be retained by voters and can be used as an identifier to allow voters to look up their (encrypted) ballots in the election record and confirm that they include the proper \emph{short codes} as described below. + + +---- S4.1.4 +\subsubsection{Short Codes}\label{sec:shortcodes_pre} +In any instantiation of pre-encrypted ballots, an additional \emph{hash trimming function} $\Omega$ must be provided. The hash trimming function takes as its input a selection hash, and its output is a \emph{short code}. As an example, $\Omega$ could produce the last byte of its input in a specified form. + For instance, a short code representation could be a pair of hex characters, a pair of letters from a 16-letter alphabet, a letter followed by a digit, or a three-digit number. The size of a short code does not need to be a single byte, but this is a convenient choice. Different vendors or jurisdictions might choose to distinguish themselves by using their own preferred short code formats, so the details of the short code format are intentionally left open. However, $\Omega$ must be completely specified in the election manifest so that a verifier can match its functionality. + +The hash trimming function $\Omega$ associates each selection on a ballot with a short code. \emph{The short codes on a ballot need not be unique.} However, it is required that the short codes within a contest be unique. If there is a collision of short codes within a contest, the randomness should be changed to generate a new ballot. When a pre-encrypted ballot is presented to a voter, the short codes associated with each selection should be displayed beside the selection. If the ballot is cast by a voter, the short codes associated with selections made by the voter will be published as part of the election record. + +\paragraph{Undervotes.} +In addition to the short codes for each possible selection, short codes are provided to indicate undervotes. (It may not be necessary to print undervote short codes on ballots.) A short code for a null vote is generated from a vector of encryptions of zero. In a contest in which the voter may select only one option there will be a single pre-encrypted null vote and associated short code to indicate that the voter did not make a selection in that contest. In general, the number of null votes and short codes for a contest should match the selection limit of the contest. So, for example, a contest in which a voter may make three selections should have three null short codes. Since the short codes corresponding to the selections made by each voter will be published in the election record, the use of null short codes allows the election record to not reveal undervotes. + +---- S4.2 +\subsection{The Ballot Encrypting Tool} +The encrypting tool for pre-encrypted ballots takes as input parameters +\begin{itemize} + \item the election manifest, + \item a ballot style index, + \item and an optional nonce encryption key (this may be the election encryption key). +\end{itemize} +The tool produces the following outputs -- which can be used to construct a single pre-encrypted ballot. (Note that it will likely be desirable to construct a wrapper that can be called to produce data for a specified number of pre-encrypted ballots.) +\begin{itemize} + \item An (optional) encryption of the primary nonce used to encrypt the ballot, + \item a selection hash value for each possible selection in the ballot style, + \item for each contest, additional null hash values corresponding in number to the contest selection limit, + \item a contest hash for each contest computed from the sorted list of selection hashes and null hashes for that contest, + \item and a confirmation code consisting of a hash of all of the contest hashes on the ballot. +\end{itemize} + +The encrypting tool operates as follows. + +First, it generates a 256-bit primary nonce $\xi$ for the ballot and, if a nonce encryption key is provided, encrypts this primary nonce with the nonce encryption key provided. + +Next, for each contest on the indicated ballot style, an encryption vector is produced for each selection within the contest (see Equation~\eqref{eq:encvector}). This encryption vector is deterministically derived from the primary nonce $\xi$ and consists of an encryption of one in the position corresponding to the selection and an encryption of zero in all other positions in the contest (see Equation~\eqref{eq:selectionhash_pre}). Additionally, one or more null vectors consisting entirely of encryptions of zeros are produced (again deterministically from the primary nonce) as in Equation~\eqref{eq:nullhash_pre}. The number of null vectors should match the selection limit $L$ of the contest. The selection hashes and null hashes (computed as described in Section~\ref{sec:selectionhash_pre}) are then sorted numerically and hashed together (in sorted order) to produce the contest hash as shown in Equation~\eqref{eq:contesthash_pre} in Section~\ref{sec:contesthash_pre}. + +Finally, the contest hashes are themselves hashed sequentially to form the ballot’s confirmation code according to Equation~\eqref{eq:ballothash_pre} in Section~\ref{sec:confirmationcode_pre}. + +---- S4.2.1 +\subsubsection{Deterministic nonce derivation}\label{sec:noncegen_pre} +The process of deterministic encryption is guided by a primary nonce $\xi$. It is assumed that each contest has a label $\Lambda$ and that within each contest each possible selection has a label $\lambda$. The nonce used within the $i^\text{th}$ contest and within that the $j^\text{th}$ selection vector to form the $k^\text{th}$ encryption is +\begin{equation}\label{eq:noncegen_pre} + \xi_{i,j,k}=H(\HH_E; \mathtt{0x43},\xi,\indc(\Lambda_i),\indo(\lambda_j),\indo(\lambda_k)). +\end{equation} +Note that the nonce $\xi_{i,j,k}$ will be used to encrypt a one whenever $j=k$ and a zero whenever $j \neq k$. Note also that some of the selection labels will represent null votes. If labels for null votes are not included within the manifest file, the labels ``null1'', ``null2'', ... should be used within each contest as needed. + +The output of the encryption tool includes the (optionally encrypted) primary nonce, the selection hash corresponding to each selection on the ballot (including nulls), the contest hashes, and the confirmation code. + + +---- S4.2.2 +\subsubsection{Using the Ballot Encrypting Tool} +It is the responsibility of the entity that calls the encryption tool to produce short codes from selection hashes. This must be done in a deterministic, repeatable fashion using a hash trimming function $\Omega$. As suggested above, one option is to use a human-friendly representation of the final byte (or bytes) of each selection hash. Another would be to use an ordinal integer to indicate where in the sorted order of selection hashes within each contest each selection falls. (For example, if a contest has four possible selections, the integers 1, 2, 3, 4, 5 could be placed beside each selection – including “none” to indicate the sorted position of each of the five selection hashes.) This flexibility allows vendors to distinguish themselves with different ballot presentations and for vendors and jurisdictions to choose presentations that best accommodate their voters. + +Unless the short codes in a particular instantiation are quite long, it is likely that there will be occasional collisions of short codes within a contest. It is the responsibility of the entity that calls the ballot encryption tool to ensure that no ballot is printed with a short code that is repeated within a contest. If a collision is found, the caller simply discards this ballot data and calls the ballot encryption tool again. The caller may also choose to discard ballot data if a short code is repeated anywhere within a ballot or if two short codes -- within a contest or across a ballot – meet some definition of similarity. The caller is free to discard data and obtain a new ballot pre-encryption as often as it likes. (Note that a malicious encryption wrapper could bias the printed ballots by, for instance, only using encryptions in which a particular selection’s hash is always numerically first within a contest. However, a malicious wrapper already knows the associations between the selection hashes, the short codes, and the actual selections, so it is not clear how a malicious wrapper could benefit from creating a bias.) + +---- S4.3 +\subsection{The Ballot Recording Tool} +The ballot recording tool receives an election manifest, an identifier for a ballot style, the decrypted primary nonce $\xi$ and, for a cast ballot, all the selections made by the voter. The recording tool uses the primary nonce $\xi$ to regenerate all of the encryptions on the ballot. For a cast ballot, the tool then isolates the pre-encryptions corresponding to the selections made by the voter and, using the encryption nonces derived from the primary nonce, generates proofs of ballot-correctness as in standard \EG section~\ref{sec:proofsballotcorrectness}. + +Note that if a contest selection limit is greater than one, the recording tool homomorphically combines the selected pre-encryption vectors corresponding to the selections made to produce a single vector of encrypted selections. The selected pre-encryption vectors are combined by componentwise multiplication (modulo $p$), and the derived encryption nonces are added (modulo $q$) to create suitable nonces for this combined pre-encryption vector. These derived nonces will be necessary to form zero-knowledge proofs that the associated encryption vectors are well-formed. + +For each uncast (implicitly or explicitly challenged) ballot, the recording tool returns the primary nonce that enables the encryptions to be opened and checked. + +---- S4.3.1 +\subsubsection{Using the Recording Tool} +A wrapper for the recording tool takes one or more encrypted nonces and obtains the decryption(s) by interacting with guardians, an administrator, or a local database and then calls the recording tool with each decrypted nonce and ballot style – and for a cast ballot, the voter selections. + +If the ballot is a cast ballot, the wrapper then uses the hash trimming function $\Omega$ to compute the short codes for the selections made by the voter and posts in the election record the full set of selection hashes generated from \emph{all} pre-encryption vectors (whether or not selected by the voter), the full pre-encryption vectors corresponding to the voter selections, the proofs that these selected pre-encryption vectors are well-formed, and the short codes for the selections made by the voter. + +For an uncast ballot, the wrapper computes the short codes for all possible selections and posts in the election record the full set of pre-encryption vectors, selection hashes, and short codes for each possible selection. + +---- S4.4 +\subsection{The Election Record} +Selection vectors generated from pre-encrypted ballots are indistinguishable from those produced by standard \EG. However, the election record for each pre-encrypted ballot includes a significant amount of additional information. Specifically, for each cast ballot, the election record should contain +\begin{itemize} + \item the standard \EG encrypted ballot data consisting of selection vectors for each contest together with all the standard associated zero-knowledge proofs that the ballot is well-formed, + \item the selection hashes for every option on the ballot (including null options) – sorted numerically within each contest, and + \item the short codes and pre-encryption selection vectors associated with all selectable options (including null options) on the ballot made by the voter. +\end{itemize} +Note that in a contest with a selection limit of one, the selection vector will be identical to one of the pre-encryption selection vectors. However, when a contest has a selection limit greater than one, the resulting selection vector will be a product of multiple pre-encryption selection vectors. + +For each uncast ballot, the primary nonce for that ballot is published in the encryption record. + +While the basic pre-encrypted ballots are identical to standard \EG ballots, the confirmation codes are computed differently. Unlike standard \EG ballots, pre-encrypted ballot confirmation codes are computed before any selections are made. The confirmation codes on pre-encrypted ballots are computed from the full set of pre-encryptions. This is the same whether the pre-encrypted ballot is cast or spoiled. + +---- S4.4.1 +\subsubsection{Election Record Presentation} +The presentation of data in the election record should be cognizant of the fact that there are two very different uses that may be made of this record. Individual voters will want to look up their own cast and uncast ballots and to easily review that they match their expectations. Election verifiers will want to verify the cryptographic artifacts associated with individual cast and uncast ballots and check their consistency as well as the consistency of the reported tallies. + +It should therefore be possible for voters to see, in as clean a presentation as is feasible, the short codes associated with selections made on their cast ballots and the short codes associated with all possible selections on uncast ballots. The presentation of uncast ballots should match, as closely as feasible, the appearance of the physical uncast ballot as this presentation would facilitate comparison between the two. + +For election verifiers, all of the cryptographic artifacts should be made available for verification. Verifiers should not only confirm the consistency of this additional data but also that this additional data is consistent with the cleaner data views made available to individual voters. + +---- S4.5 +\subsection{Verification of Pre-Encrypted Ballots} +Every step of verification that applies to traditional \EG ballots also applies to pre-encrypted ballots – with the exception of the process for computing confirmation codes. However, there are some additional verification steps that must be applied to pre-encrypted ballots. Specifically, the following verifications should be done for every pre-encrypted cast ballot contained in the election record. +\begin{itemize} + \item The ballot confirmation code correctly matches the hash of all contest hashes on the ballot (listed sequentially). + \item Each contest hash correctly matches the hash of all selection hashes (including null selection hashes) within that contest (sorted within each contest). + \item All short codes shown to voters are correctly computed from selection hashes in the election record which are, in turn, correctly computed from the pre-encryption vectors published in the election record. + \item For contests with selection limit greater than 1, the selection vectors published in the election record match the product of the pre-encryptions associated with the short codes listed as selected. +\end{itemize} + +The following verifications should be done for every pre-encrypted ballot listed in the election record as uncast. +\begin{itemize} + \item The ballot confirmation code correctly matches the hash of all contest hashes on the ballot (listed sequentially). + \item Each contest hash correctly matches the hash of all selection hashes (including null selection hashes) within that contest (sorted within each contest). + \item All short codes on the ballot are correctly computed from the selection hashes in the election record which are, in turn, correctly computed from the pre-encryption vectors published in the election record. + \item The decryptions of all pre-encryptions correspond to the plaintext values indicated in the election manifest. +\end{itemize} + +\EGverif{\veriftitlePreEncryptedBallotsCorrectnessOfSelectionEncryptions}{\label{verif:PreEncryptedBallotsCorrectnessOfSelectionEncryptions} +\veriftextPreEncryptedBallotsCorrectnessOfSelectionEncryptions} + +\EGverif{\veriftitlePreEncryptedBallotsAdherenceToVoteLimits}{\label{verif:PreEncryptedBallotsAdherenceToVoteLimits} +\veriftextPreEncryptedBallotsAdherenceToVoteLimits} + +\EGverif{\veriftitlePreEncryptedValidationOfTrackingCodes}{\label{verif:PreEncryptedValidationOfTrackingCodes} +\veriftextPreEncryptedValidationOfTrackingCodes} + +\EGverifBallot{\veriftitlePreEncryptedValidationOfShortCodes}{\label{verif:PreEncryptedValidationOfShortCodes} +\veriftextPreEncryptedValidationOfShortCodes} + + +---- S4.6 +\subsection{Hash-Trimming Functions} +To allow vendors and jurisdictions to present distinct formats to their voters, the details of the hash-trimming function that produces short codes from full-sized hashes are not explicitly provided. However, to facilitate verification, a variety of possible hash-trimming functions are pre-specified here. + +\begin{itemize} +\item \hbox to 3in{\bf Two Hex Characters\hfil} $\Omega_1(x)=$ final byte of $x$ expressed as two hexadecimal characters +\item \hbox to 3in{\bf Four Hex Characters\hfil} $\Omega_2(x)=$ final two bytes of $x$ expressed as four hexadecimal characters +\item \hbox to 3in{\bf Letter-Digit\hfil} $\Omega_3(x)=$ final byte of $x$ expressed as a letter followed by a digit with $0,1,\ldots,255$ mapping to A0,A1,...,A9,B0,B1,...B9,...,Z0,Z1,...,Z5 +\item \hbox to 3in{\bf Digit-Letter\hfil} $\Omega_4(x)=$ final byte of $x$ expressed as a digit followed by a letter with $0,1,\ldots,255$ mapping to 0A,0B,...,0Z,1A,1B,...1Z,...,9A,9B,...,9V +\item \hbox to 3in{\bf Number: 0-255\hfil} $\Omega_5(x)=$ final byte of $x$ expressed as a number with $0,1,\ldots,255$ mapping to $0,1,\ldots,255$ using the identity function +\item \hbox to 3in{\bf Number: 1-256\hfil} $\Omega_6(x)=$ final byte of $x$ expressed as a number with $0,1,\ldots,255$ mapping to $1,2,\ldots,256$ by adding 1 +\item \hbox to 3in{\bf Number: 100-355\hfil} $\Omega_7(x)=$ final byte of $x$ expressed as a number with $0,1,\ldots,255$ mapping to $100,101,\ldots,355$ by adding 100 +\item \hbox to 3in{\bf Number: 101-356\hfil} $\Omega_8(x)=$ final byte of $x$ expressed as a number with $0,1,\ldots,255$ mapping to $101,102,\ldots,355$ by adding 101 +\end{itemize} + +\pagebreak + + +-------- Section 5 + +\section{Hash Computation}\label{sec:hashing} +The function $H$ that is used throughout \EG is instantiated based on the hashed message authentication code \HMAC. This section defines how to evaluate $H$. It first defines how inputs are represented as byte arrays and then how these inputs are used to compute hash values. + +S5.1 +\subsection{Input data representation}\label{sec:hashinputdata} + +All inputs to the function $H$ are byte arrays. A \emph{byte} is a non-negative integer less than $2^8$, i.e. an integer in the set $\Bcal = \{0,1,\dots,255\}$. Its binary form consists of at most $8$ bits. Its hexadecimal form consists of at most two hexadecimal characters. Here, the leading 0 characters are written out such that a byte always has exactly two hexadecimal characters. Therefore, $\Bcal$ is represented as $\{\mathtt{0x00}, \mathtt{0x01}, \mathtt{0x02},\dots \mathtt{0xFE}, \mathtt{0xFF}\}$. + +A \emph{byte array} $\mathrm{B}$ of length $m$ is an array of $m$ bytes $\mathrm{b}_0, \mathrm{b}_1, \dots, \mathrm{b}_m \in \Bcal$. It is represented by the concatenation\footnote{The symbol $\parallel$ simply denotes concatenation and does not mean that this symbol is inserted as a separator into the array in any way.} of the byte values as $\mathrm{B} = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_m$. A byte array of length $m$ consists of $8m$ bits. For $0 \leq i < 8m$, the $i$-th bit of the byte array $\mathrm{B}$ is the $(i \bmod 8)$-th bit of the byte $\mathrm{b}_{\lfloor i/8 \rfloor}$.\footnote{Here, $\lfloor\cdot\rfloor$ denotes the floor function, which means that the result is obtained by rounding down. For a real number $x$, $\lfloor x \rfloor$ is the largest integer that is not larger than $x$.} The set of all byte arrays of length exactly $m$ is denoted by $\Bcal^m$ and the set of all byte arrays of any length is denoted by $\Bcal^*$. + +Any byte array $\Bcal$ of length $m$ represents a non-negative (i.e. unsigned) integer less than $2^{8m}$ by interpreting the bytes of $\Bcal$ as the digits of the representation in base $2^8$ with the most significant bytes to the left, i.e. in big endian format. For example, the byte array $\Bcal = \mathtt{0x1F}\parallel \mathtt{0xFF}$ of length $2$ represents the integer $\mathtt{0x1FFF}$ in hexadecimal form, which corresponds to the integer $2^{13}-1 = 8191$. In general, let $\mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{m-1}$ be a byte array of length $m$, the integer +\begin{equation} + \mathrm{b}_0\cdot 2^{(m-1)\cdot 8} + \mathrm{b}_1\cdot 2^{(m-2)\cdot 8} + \dots + \mathrm{b}_{m-1} +\end{equation} +is a non-negative integer less than $2^{8m}$. Vice versa, if $0\leq a < 2^{8m}$, then the byte array of length $m$ representing $a$ is given as +\begin{equation} + \bytes(a, m) = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{m-1},\mbox{ where } \mathrm{b}_i = \lfloor a/ 2^{8(m-1-i)} \rfloor \bmod 2^8. +\end{equation} +In this document and if not specified otherwise, a byte array and big endian non-negative integers are used synonymously. However, byte arrays representing integer data types have a fixed length and must be padded with $\mathtt{0x00}$ bytes to the left. The byte array $\mathtt{0x00}\parallel\mathtt{0x1F}\parallel \mathtt{0xFF}$ represents the same integer as $\Bcal$, but has length $3$ instead of $2$. For the integer data types used in \EG, this is laid out in detail in the following sections. + + +S5.1.1 +\subsubsection{Integers modulo the large prime $p$} +Most inputs to $H$ like public keys, vote encryptions and commitments for NIZK proofs consist of big integers modulo the large prime $p$, i.e. integers in the set $\Z_p = \{0,1,\dots, p-1\}$. Any such element is represented in big endian format by a fixed size byte array of length exactly $l_p$, the length of the byte array representing $p$. If $p$ has 4096 bits like the prime given in the standard parameters in Section~\ref{sec:parameters}, this length is exactly 512. The conversion from an integer to a byte array works explicitly as follows. For $a \in \Z_p$, the byte array $\bytes(a, l_p)$ of length $l_p$ representing $a$ is defined as +\begin{equation} + \bytes(a, l_p) = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{l_p-1}, +\end{equation} +where +\begin{equation} + \mathrm{b}_{i} = \lfloor a/ 2^{8(l_p-1-i)} \rfloor \bmod 2^8 \in \Bcal \mbox{ for } i \in \{0, 1, \dots, l_p-1\}. +\end{equation} +The bytes $\mathrm{b}_i$ are the coefficients of $a$ when it is written in base $2^8$, i.e. +\begin{equation} +a = \sum_{i=0}^{l_p-1} \mathrm{b}_{i} 2^{8(l_p-1-i)} = \mathrm{b}_0\cdot 2^{(l_p-1)\cdot 8} + \mathrm{b}_1\cdot 2^{(l_p-2)\cdot 8} + \dots + \mathrm{b}_{l_p-1}. +\end{equation} +Any byte array $\bytes(a, l_p)$ that represents an integer $a$ modulo $p$ always has length $l_p$. +% +This means that, for the standard parameters, where $l_p = 512$, we have $\bytes(a, 512) = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{511}$, where +\begin{equation*} + \mathrm{b}_{i} = \lfloor a/ 2^{8(511-i)} \rfloor \bmod 2^8 \in \Bcal \mbox{ for } i \in \{0, 1, \dots, 511\}. +\end{equation*} +For example, $\bytes(0, 512) = \mathtt{0x0000\dots 000000000000}$ is a byte array of 512 00-bytes, $\bytes(15, 512) = \mathtt{0x0000\dots 00000000000F}$, $\bytes(8572345, 512)= \texttt{0x0000\dots 00000082CDB9}$, and $\bytes(p-1, 512)$ is the array shown in Section~\ref{sec:parameters} for $p$, but ending with \texttt{\dots FFFFFFFFFFFFFE}. + +S5.1.2 +\subsubsection{Integers modulo the small prime $q$}\label{sec:intmodq} +Other inputs are integers modulo the smaller prime $q$, such as response values in NIZK proofs and encryption nonces. They are integers in the set $\Z_q = \{0,1,\dots,q-1\}$ and are represented by a fixed size byte array of length exactly $l_q$ in big endian format. For the standard parameters, $l_q=32$. The conversion from integer to byte array works as above. If $a\in \Z_q$, the byte array $\bytes(a,32)$ representing $a$ is defined as +\begin{equation} + \bytes(a,32) = \mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{31}, +\end{equation} +where +\begin{equation} + \mathrm{b}_{i} = \lfloor a/ 2^{8(31-i)} \rfloor \bmod 2^8 \in \Bcal \mbox{ for } i \in \{0, 1, \dots, 31\}. +\end{equation} +Again, the bytes are coefficients of $a$ in its $2^8$-adic form, namely +\begin{equation} + a = \sum_{i=0}^{31} \mathrm{b}_{i} 2^{8(31-i)} = \mathrm{b}_0\cdot 2^{31\cdot 8} + \mathrm{b}_1\cdot 2^{30\cdot 8} + \dots + \mathrm{b}_{31}. +\end{equation} +All byte arrays that represent integers modulo q have length 32. For example, +\begin{align*} + \bytes(0,32) & = \mathtt{0x0000000000000000000000000000000000000000000000000000000000000000},\\ + \bytes(16,32) & = \mathtt{0x0000000000000000000000000000000000000000000000000000000000000010},\\ + \bytes(q-1,32) & = \mathtt{0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42}. +\end{align*} + +S5.1.3 +\subsubsection{Small integers} +Other integers such as \emph{indices} are much smaller and are encoded as fixed length byte arrays in big endian format in the same way, but have much smaller lengths. In \EG, all such small integers are assumed to be smaller than $2^{31}$. They can therefore be encoded with $4$ bytes, i.e. with $32$ bits and the most significant bit set to $0$.\footnote{Setting the most significant bit to $0$ is done in consideration of languages and runtimes that do not have full support for unsigned integers.} + +The number of guardians $n$ and the quorum threshold value $k$ are examples of such small integers that require even less than 4 bytes for all reasonable use cases, i.e. they are represented as $\bytes(n, 4)$ and $\bytes(k, 4)$. For example, $\bytes(5,4) = \mathtt{0x00000005}$ and $\bytes(3,4) = \mathtt{0x00000003}$. + +S5.1.4 +\subsubsection{Strings}\label{sec:stringencoding} +When an input to the function $H$ is a string $s$, it is encoded as a byte array $\bytes(s, \len(s))$ using UTF-8 encoding. Here, $\len(s)$ is the length of the UTF-8 encoding of the string $s$ in bytes. For example, the string ``ElectionGuard" is encoded as $\bytes(\mathrm{``ElectionGuard"}, 13) = \mathtt{0x456C656374696F6E4775617264}$. +Some string inputs may be of variable length that cannot be specified in advance by this document. Therefore all encodings of strings that are input to the \EG hash function $H$ start with a $4$-byte encoding in big endian format of the byte length of the string encoding followed by the encoding itself.\footnote{The maximal length of strings that can be encoded for input to $H$ is therefore $2^{32}$ bytes.} For example, if the string ``ElectionGuard" is an input to $H$, the input byte array is $\bytes(13, 4)\parallel \bytes(\mathrm{``ElectionGuard"}, 13) = \mathtt{0x0000000D456C656374696F6E4775617264}$. + +S5.1.5 +\subsubsection{Files} +When an input to the function $H$ is a file \texttt{file}, it is parsed as input entirely, meaning that all bytes of the file are parsed to $H$ as a byte array $\bytes(\mathtt{file}, \len(\mathtt{file}))$ that is a concatenation of all the bytes of the file in order. As above for strings, file lengths are not specified in this document and file inputs to $H$ start with a $4$-byte encoding\footnote{Input files to $H$ are restricted to a length of $2^{32}$ bytes.} in big endian format of the file byte length $\len(\mathtt{file})$ followed by the file bytes, i.e. $\bytes(\len(\mathtt{file}), 4)\parallel \bytes(\mathtt{file}, \len(\mathtt{file}))$. + +S5.2 +\subsection{Hash function} +The hash function $H$ used in \EG is \HMAC-\SHA, i.e. \HMAC\footnote{NIST (2008) The Keyed-Hash Message Authentication Code (HMAC). In: FIPS 198-1. \url{https://csrc.nist.gov/publications/detail/fips/198/1/final}} instantiated with \SHA\footnote{NIST (2015) Secure Hash Standard (SHS). In: FIPS 180-4. \url{https://csrc.nist.gov/publications/detail/fips/180/4/final}}. Therefore, $H$ takes two byte arrays as inputs. + +The first input corresponds to the key in \HMAC. The \HMACSHA specification allows arbitrarily long keys, but preprocesses the key to make it exactly 64 bytes (512 bits) long, which is the block size for the input to the \SHA hash function. If the given key is smaller, \HMACSHA pads it with \texttt{00} bytes at the end, if it is longer, \HMACSHA hashes it first with \SHA and then pads it to 64 bytes. In \EG, all inputs that are used as the \HMAC key, i.e. all inputs to the first argument of $H$ have a fixed length of exactly 32 bytes. + +The second input can have arbitrary length and is only restricted by the maximal input length for \SHA and \HMAC. Hence we view the function $H$ formally as follows (understanding that \HMAC implementations pad the $32$-byte keys to exactly $64$ bytes by appending $32$ $\mathtt{0x00}$ bytes): + +\begin{equation} + H: \Bcal^{32}\times \Bcal^* \rightarrow \Bcal^{32},\ H(\mathrm{B}_0; \mathrm{B}_1) \mapsto \HMACSHA(\mathrm{B}_0,\mathrm{B}_1). +\end{equation} + +\EG uses \HMAC not as a keyed hash function with a secret key or a message authentication code, but instead uses it as a general purpose hash function to implement a random oracle.\footnote{Dodis Y., Ristenpart T., Steinberger J., Tessaro S. (2012) \emph{To Hash or Not to Hash Again? (In)differentiability Results for $H^2$ and \HMAC.} This paper shows that \HMAC used as a general purpose hash function with keys of a fixed length shorter than $d-1$, where $d$ is the block length in bits of the underlying hash function is indifferentiable from a random oracle.} + +The first input is used to bind hash values to a specific election by including the parameter base hash $\HH_P$, the election base hash $\HH_B$ or the extended base hash $\HH_E$. The second input consists of domain separation tags for different use cases and the actual data that is being hashed. + +S5.3 +\subsection{Hashing multiple inputs} +\EG often requires that multiple input elements are hashed together. In fact, the second input to the function $H$ specified in the previous section always consists of multiple parts. The input byte array $B_1$ is then simply the concatenation of the byte arrays that represent the multiple input elements as specified in Section~\ref{sec:hashinputdata} in the order specified in each case. As all byte arrays that represent input elements have a fixed length, there is no need for a separator byte or character. + +The notation in this document lists the multiple input elements in order separated by commas. The first input element forming the byte array $B_0$ and the list of elements forming the second byte array $B_1$ are separated by a semicolon. +To illustrate the notation, consider, for example, the challenge value for the proof of correctness in ballot encryption. It is computed as $H(\HH_E;\mathtt{0x21},K,\alpha,\beta,a_0,b_0,a_1,b_1)$ (see Equation~\eqref{eq:encproof0_challenge}). This notation means that \HMACSHA is called as $\HMACSHA(\mathrm{B}_0, \mathrm{B}_1)$ with the byte arrays\footnote{The symbol $\parallel$ does not represent a separator symbol and is simply used to denote concatenation like for byte arrays as mentioned above.} +$$\mathrm{B}_0 = \HH_E,\ +\mathrm{B}_1 = \mathtt{0x21}\parallel \bytes(K,l_p)\parallel \bytes(\alpha,l_p)\parallel\bytes(\beta,l_p)\parallel\bytes(a_0,l_p)\parallel\bytes(b_0,l_p)\parallel\bytes(a_1,l_p)\parallel\bytes(b_1,l_p)$$ +as inputs. +The first argument is simply the extended base hash $\HH_E$. The second argument is the byte array that is the result of concatenating the domain separator byte $\mathtt{0x21}$ that identifies the use case and the byte arrays representing the public key $K$, the vote encryption components $\alpha$ and $\beta$ and the commitments to the Chaum-Pedersen proofs $a_0$, $b_0$, $a_1$, and $b_1$ in that order. + +S5.4 +\subsection{Hash function outputs} +The output of \SHA and therefore $H$ is a 256-bit string, which can be interpreted as a byte array of $32$ bytes. As such, outputs of $H$ are directly used as inputs to $H$ again without any modifications. + +When generating NIZK proofs, hash function outputs are involved in computations modulo $q$. Therefore they need to be interpreted as integers. The byte array $\mathrm{b}_0 \parallel \mathrm{b}_1 \parallel \dots \parallel \mathrm{b}_{31}$ is interpreted as a big endian base-$2^8$ representation of an integer $a$, which means that +\begin{equation} + a = \sum_{i=0}^{31} \mathrm{b}_{i} 2^{8(31-i)} = \mathrm{b}_0\cdot 2^{31\cdot 8} + \mathrm{b}_1\cdot 2^{30\cdot 8} + \dots + \mathrm{b}_{31}. +\end{equation} +Therefore, hash function outputs correspond to integers in the interval $[0,2^{256}-1]$. Note that, although this byte representation is identical to the byte representation of integers modulo $q$ as described above, the integers corresponding to hash function outputs do not have to be smaller than $q= 2^{256} - 189$. However, in practice, it is highly unlikely that an integer in the interval $[2^{256}-189, 2^{256}-1]$ will occur. When picking a 256-bit integer uniformly at random, the probability that it is not smaller than $q$ is negligibly small, namely $189/2^{256} < 2^{-248}$. + +Although hash function outputs are used in computations modulo $q$, they are not reduced modulo $q$ by default. Each hash function output, such as a challenge value $c$ in the computation of a NIZK proof, remains a byte array of length $32$ that can be interpreted as an integer in $[0,2^{256}-1]$. Only during the computation of response values does a reduction modulo $q$ occur. + +S5.5 +\subsection{Domain separation} +The tables below list every use of the hash function $H$ in this specification together with the specific domain separation input for the second argument. They explicitly provide the byte arrays $\mathrm{B}_0$ and $\mathrm{B}_1$ that are passed to \HMAC as inputs to evaluate $\HMACSHA(\mathrm{B}_0,\mathrm{B}_1)$ together with the length $\len(\B_1)$ of $\B_1$ in bytes (note that always $\len(\B_0)=32$). The tables use the standard parameters such that $l_p = 512$ and $l_q = 32$. For other parameters, the values $l_p$ and $l_q$ can be different. + +S5.5.1 +\subsubsection{Parameter base, manifest and base hashes} +\begin{center} + \begin{tabularx}{\textwidth}{Xr} + \toprule + Computation of the parameter base hash $\HH_P$ & \S\ref{sec:parameterhash}\\ + $\HH_P = H(\mathtt{ver};\mathtt{0x00}, p, q, g)$ & \eqref{eq:parameterhash}\\ + $\mathrm{B}_0 = \mathtt{ver} = \mathtt{0x322E302E30} \parallel \bytes(0,27)$\\ + $\mathrm{B}_1 = \mathtt{0x00}\parallel \bytes(p,512)\parallel\bytes(q,32)\parallel \bytes(g,512)$\\ + $\len(\B_1) = 1057$\\ + \midrule + Computation of the manifest hash $\HH_M$ & \S\ref{sec:manifest}\\ + $\HH_M = H(\HH_P;\mathtt{0x01}, \mathtt{manifest})$ & \eqref{eq:manifesthash}\\ + $B_0 = \HH_P$ & \\ + $B_1 = \mathtt{0x01} \parallel \bytes(\len(\mathtt{manifest}), 4) \parallel \bytes(\mathtt{manifest}, \len(\mathtt{manifest}))$ & \\ + $\len(B_1) = 5 + \len(\mathtt{manifest})$ & \\ + \midrule + Computation of the base hash $\HH_B$ & \S\ref{sec:basehash}\\ + $\HH_B = H(\HH_P; \mathtt{0x02}, \HH_M, n, k)$ & \eqref{eq:basehash}\\ + $\mathrm{B}_0 = \HH_P$\\ + $\mathrm{B}_1 = \mathtt{0x02} \parallel \HH_M\parallel \bytes(n,4) \parallel \bytes(k,4)$& \\ + $\len(\B_1) = 41$\\ + \bottomrule + \end{tabularx} +\end{center} + +S5.5.2 +\subsubsection{Key generation and extended base hash} +\begin{center} + %%%%%%%%%%%%%%%%%%%%%% + \begin{tabularx}{\textwidth}{Xr} + \toprule + NIZK proof of knowledge of polynomial coefficients in key generation & \S\ref{sec:keygendetails}\\ + $c_{i,j} = H(\HH_P; \mathtt{0x10}, i, j, K_{i,j},h_{i,j})$ & \eqref{eq:hash_nizk_keygen}\\ + $\mathrm{B}_0 = \HH_P$\\ + $\mathrm{B}_1 = \mathtt{0x10}\parallel\bytes(i, 4)\parallel\bytes(j, 4)\parallel \bytes(K_{i,j},512)\parallel\bytes(h_{i,j},512)$ \\ + $\len(\B_1) = 1033$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Encryption of key shares in key generation & \S\ref{sec:keygendetails}\\ + $k_{i,\ell} = H(\HH_P; \mathtt{0x11}, i, \ell, K_\ell, \alpha_{i,\ell}, \beta_{i,\ell})$ & \eqref{eq:hash_shareenc}\\ + $\mathrm{B}_0 = \HH_P$\\ + $\mathrm{B}_1 = \mathtt{0x11}\parallel\bytes(i,4)\parallel\bytes(\ell,4)\parallel\bytes(K_\ell, 512)\parallel\bytes(\alpha_{i,\ell},512)\parallel\bytes(\beta_{i,\ell},512)$ \\ + $\len(\B_1) = 1545$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of the extended base hash $\HH_E$ & \S\ref{sec:keygendetails}\\ + $\HH_E = H(\HH_B; \mathtt{0x12}, K)$ & \eqref{eq:extbasehash}\\ + $\mathrm{B}_0 = \HH_B$\\ + $\mathrm{B}_1 = \mathtt{0x12} \parallel \bytes(K,512)$ \\ + $\len(\B_1) = 513$\\ + \bottomrule + \end{tabularx} +\end{center} + +S5.5.3 +\subsubsection{Ballot encryption and confirmation codes} +\enlargethispage{1cm} +\begin{center} + %%%%%%%%%%%%%%%%%%%%%% + \begin{tabularx}{\textwidth}{Xr} + \toprule + Computation of encryption nonces & \$\ref{sec:noncegen}\\ + $\xi_{i,j} = H(\HH_E; \mathtt{0x20},\xi_B,\indc(\Lambda_i),\indo(\lambda_j))$ & \eqref{eq:noncegen}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x20} \parallel \xi_B \parallel \bytes(\indc(\Lambda_i), 4) \parallel \bytes(\indo(\lambda_j),4) $ \\ + $\len(\B_1) = 41$\\ + Computation of encryption nonces for contest data & \S\ref{sec:encrypt_ext_data}\\ + $\xi = H(\HH_E; \mathtt{0x20}, \xi_B, \indc(\Lambda), \mathtt{``contest\_data"})$ & \eqref{eq:noncegen_contestdata}\\ + $\B_0 = \HH_E$\\ + \multicolumn{2}{l}{ + $\B_1 = \mathtt{0x20} \parallel \xi_B \parallel \bytes(\indc(\Lambda), 4) \parallel \mathtt{0x0000000C636F6E746573745F64617461}$}\\ + $\len(\B_1) = 53$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Challenge computation for 0/1-range proofs & \S\ref{sec:proofsballotcorrectness}\\ + $c = H(\HH_E;\mathtt{0x21},K,\alpha,\beta,a_0,b_0,a_1,b_1)$ & \eqref{eq:encproof0_challenge}, \eqref{eq:encproof1_challenge}\\ + $\mathrm{B}_0 = \HH_E$\\ + \multicolumn{2}{l}{ + $\mathrm{B}_1 = \mathtt{0x21} \parallel \bytes(K,512)\parallel\bytes(\alpha,512)\parallel\bytes(\beta,512)\parallel\bytes(a_0,512)\parallel\bytes(b_0,512)\parallel\bytes(a_1,512)\parallel\bytes(b_1,512)$}\\ + $\len(\B_1) = 3585$\\ + Challenge computation for $L$-range proofs & \S\ref{sec:selectionlimit}\\ + $c = H(\HH_E;\mathtt{0x21},K,\bar\alpha,\bar\beta,a_0,b_0,a_1,b_1,\dots,a_L,b_L)$ & \eqref{eq:nizk_c_selection_limit}\\ + $\mathrm{B}_0 = \HH_E$\\ + \multicolumn{2}{l}{ + $\mathrm{B}_1 = \mathtt{0x21} \parallel \bytes(K,512)\parallel\bytes(\alpha,512)\parallel\bytes(\beta,512)\parallel\bytes(a_0,512)\parallel\bytes(b_0,512)\parallel\dots\parallel\bytes(a_L,512)\parallel\bytes(b_L,512)$}\\ + $\len(\B_1) = 1+(2L+5)\cdot 512$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Key derivation for contest data encryption & \S\ref{sec:encrypt_ext_data}\\ + $k = H(\HH_E; \mathtt{0x22}, K, \alpha, \beta)$ & \eqref{eq:k_enc_contest}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x22} \parallel \bytes(K,512)\parallel\bytes(\alpha,512)\parallel\bytes(\beta,512)$\\ + $\len(\B_1) = 1537$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of contest hashes & \S\ref{sec:contesthash}\\ + $\chi_l = H(\HH_E; \mathtt{0x23}, \indc(\Lambda_l), K, \alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m)$ & \eqref{eq:contesthash}\\ + $\mathrm{B}_0 = \HH_E$\\ + \multicolumn{2}{l}{ + $\mathrm{B}_1 = \mathtt{0x23} \parallel \bytes(\indc(\Lambda_l), 4) \parallel \bytes(K,512)\parallel\bytes(\alpha_1,512)\parallel\bytes(\beta_1,512)\parallel\bytes(\alpha_2,512)\parallel\ldots$}\\ + \quad\qquad$\parallel\bytes(\alpha_m,512)\parallel\bytes(\beta_m,512)$ & \\ + $\len(\B_1) = 5 + (2m+1)\cdot 512$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of confirmation codes & \S\ref{sec:confirmationcode}\\ + $\HH(B) = H(\HH_E; \mathtt{0x24}, \chi_1, \chi_2, \ldots, \chi_{m_B}, \B_{\mathrm{aux}})$ & \eqref{eq:confirmationcode}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x24} \parallel \chi_1\parallel \chi_2\parallel \ldots\parallel \chi_{m_B}\parallel \bytes(\len(\B_{\mathrm{aux}}), 4) \parallel \B_{\mathrm{aux}}$\\ + $\len(\B_1) = 5 + m_B\cdot 32 + \len(\B_{\mathrm{aux}})$\\ + Hash chain initialization for ballot chaining & \\ + $\HH_0 = H(\HH_E;\mathtt{0x24},\B_{\mathrm{aux},0})$ & \eqref{eq:hashchain0}\\ + $\B_1 = \mathtt{0x24}\parallel \bytes(\len(\B_{\mathrm{aux},0}), 4)\parallel \B_{\mathrm{aux},0}$, $\len(\B_1) = 5 + \len(\B_{\mathrm{aux},0})$ \\ + Hash chain closing for ballot chaining & \\ + $\overline{\HH} = H(\HH_E; \mathtt{0x24}, \overline\B_{\mathrm{aux}})$ & \eqref{eq:hashchainclose}\\ + \multicolumn{2}{l}{ + $\B_1 = \mathtt{0x24}\parallel\overline\B_{\mathrm{aux}} = \mathtt{0x24}\parallel\HH(B_\ell)\parallel \bytes(\len(\B_{\mathrm{aux},0}), 4)\parallel \B_{\mathrm{aux},0} \parallel \mathtt{0x00000005434C4F5345}$}\\ + $\len(\B_1) = 46 + \len(\B_{\mathrm{aux},0})$ & \\ + \bottomrule + \end{tabularx} + %%%%%%%%%%%%%%%%%%%%%% +\end{center} + +S5.5.4 +\subsubsection{Verifiable decryption} +\begin{center} +\begin{tabularx}{\textwidth}{Xr} + \toprule + Challenge computation for proofs of correct decryption & \S\ref{sec:verifiable_decrypt_proof}\\ + $c = H(\HH_E;\mathtt{0x30},K,A,B,a,b,M)$ & \eqref{eq:nizk_c_dec}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x30} \parallel \bytes(K,512)\parallel\bytes(A,512)\parallel\bytes(B,512)\parallel\bytes(a,512)\parallel\bytes(b,512)\parallel\bytes(M,512)$\\ + $\len(\B_1) = 3073$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Challenge computation for proofs of correct decryption of contest data & \S\ref{sec:decrypt_contest_data}\\ + $c = H(\HH_E;\mathtt{0x31},K,C_0,C_1,C_2,a,b,\beta)$ & \eqref{eq:nizk_c_dec_cont}\\ + $\mathrm{B}_0 = \HH_E$\\ + \multicolumn{2}{l}{$\mathrm{B}_1 = \mathtt{0x31} \parallel \bytes(K,512)\parallel\bytes(C_0,512)\parallel\bytes(C_1,512)\parallel\bytes(C_2,512)\parallel\bytes(a,512)\parallel\bytes(b,512)\parallel\bytes(\beta,512)$}\\ + $\len(\B_1) = 3585$ & \\ + \bottomrule +\end{tabularx} +\end{center} + +S5.5.5 +\subsubsection{Pre-encrypted ballots} +\begin{center} + %%%%%%%%%%%%%%%%%%%%%% + \begin{tabularx}{\textwidth}{Xr} + \toprule + Computation of selection hashes for pre-encrypted ballots & \S\ref{sec:selectionhash_pre}\\ + $\psi_i = H(\HH_E; \mathtt{0x40}, K,\alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m)$ & \eqref{eq:selectionhash_pre}\\ + $\psi_{m+\ell}=H(\HH_E; \mathtt{0x40}, K,\alpha_1, \beta_1, \alpha_2, \beta_2\ldots,\alpha_m, \beta_m)$ & \eqref{eq:nullhash_pre}\\ + $\mathrm{B}_0 = \HH_E$\\ + \multicolumn{2}{l}{$\mathrm{B}_1 = \mathtt{0x40} \parallel \bytes(K,512)\parallel\bytes(\alpha_1,512)\parallel\bytes(\beta_1,512)\parallel\bytes(\alpha_2,512)\parallel\ldots\parallel\bytes(\alpha_m,512)\parallel\bytes(\beta_m,512)$}\\ + $\len(\B_1) = 1 + (2m+1)\cdot 512$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of contest hashes for pre-encrypted ballots & \S\ref{sec:contesthash_pre}\\ + $\chi_l = H(\HH_E; \mathtt{0x41}, \indc(\Lambda_l), K,\psi_{\sigma(1)},\psi_{\sigma(2)},\ldots,\psi_{\sigma(m+L)})$ & \eqref{eq:contesthash_pre}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x41} \parallel \bytes(\indc(\Lambda_l), 4) \parallel \bytes(K,512)\parallel \psi_{\sigma(1)}\parallel\psi_{\sigma(2)}\parallel\ldots\parallel\psi_{\sigma(m+L)}$\\ + $\len(\B_1) = 517 + (m+L)\cdot 32$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of ballot hashes for pre-encrypted ballots & \S\ref{sec:confirmationcode_pre}\\ + $\HH(B)=H(\HH_E; \mathtt{0x42}, K,\chi_1,\chi_2,\ldots, \chi_{m_B})$ & \eqref{eq:ballothash_pre}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x42} \parallel \bytes(K,512)\parallel \chi_{1}\parallel\chi_{2}\parallel\ldots\parallel\chi_{m_B}$\\ + $\len(\B_1) = 513 + m_B\cdot 32$\\ + \midrule + %%%%%%%%%%%%%%%%%%%%%% + Computation of encryption nonces & \$\ref{sec:noncegen_pre}\\ + $\xi_{i,j,k}=H(\HH_E; \mathtt{0x43},\xi,\indc(\Lambda_i),\indo(\lambda_j),\indo(\lambda_k))$ & \eqref{eq:noncegen_pre}\\ + $\mathrm{B}_0 = \HH_E$\\ + $\mathrm{B}_1 = \mathtt{0x43} \parallel \xi \parallel \bytes(\indc(\Lambda_i), 4) \parallel \bytes(\indo(\lambda_j), 4) \parallel \bytes(\indo(\lambda_k), 4) $ \\ + $\len(\B_1) = 45 $\\ + \bottomrule + \end{tabularx} + \end{center} + +-------- Section 6 Verifier Construction + +\pagebreak +\section{Verifier Construction} +While it is desirable for anyone who may construct an \EG verifier to have as complete an understanding as possible of the \EG design, this section isolates the items which must be verified and maps the variables used in the specification equations herein to the labels provided in the artifacts produced in an election. + +---- S6.1 +\subsection{Implementation Details} + +---- S6.1a +There are four operations which must be performed -- all on very large integer values: modular addition, modular multiplication, modular exponentiation, and hash computations, i.e. evaluations of the hash function $H$. These operations can be performed using a programming language that provides native support, by importing tools to perform these large integer operations, or by implementing these operations from scratch. + +---- S6.1b +\subsubsection*{Modular addition} +To compute $(a+b) \bmod n$, one can compute $((a \bmod n)+(b \bmod n)) \bmod n$. However, this is rarely beneficial. If it is known that $a,b\in \Z_n$, then one can choose to avoid the division normally inherent in the modular reduction and just use $(a+b) \bmod n = a+b$ (if $a+b&2 + exit 1 + fi + + case "$1" in + --help) + do_help + exit 0 + ;; + -?*) + printf 'export-to-csv: Unknown option: %s' "$1" | cat -A >&2 + echo >&2 + exit 1 + ;; + *) + if [[ ! -z "$sqlite3_db_file" ]]; then + echo "export-to-csv: Exactly one positional arg is allowed: SQLITE3_DB_FILENAME" >&2 + do_help >&1 2>&2 + return 1 + else + sqlite3_db_file="$1" + fi + ;; + esac + shift +done + +if [ -z "$sqlite3_db_file" ]; then + echo "export-to-csv: Positional arg SQLITE3_DB_FILENAME is required" >&2 + exit 1 +elif [ ! -f "$sqlite3_db_file" ]; then + printf 'export-to-csv: File does not exist: %s' "$sqlite3_db_file" | cat -A >&2 + echo >&2 + exit 1 +fi + +printf 'export-to-csv: Reading from: %s' "$sqlite3_db_file" | cat -A >&2; echo >&2 + +sqlite3_db_file_basename=$(printf '%s' "$sqlite3_db_file") +printf -v csv_out_file '%s.mapping.csv' "$sqlite3_db_file_basename" +sqlite3_db_file_basename= + +printf 'export-to-csv: Writing to: %s' "$csv_out_file" | cat -A >&2; echo >&2 + +sqlite3 "$sqlite3_db_file" >"$csv_out_file" <>, + current_number: usize, + }, + + #[error("Capacity exceeded")] + CapacityOverflow, + + #[error( + "New rule could not be added because the rule set is full. It already contains {current_number} rules." + )] + RuleSetFull { current_number: usize }, + + #[error("Allocation failed")] + AllocationFailed, + /* + #[error("The symbols collection is already in use.")] + SymbolsAlreadyInUse, + + #[error("The symbols collection is already (mutably) in use.")] + SymbolsAlreadyMutablyInUse, + // */ +} + +/// [`Result`](std::result::Result) type with an [`ProblemError`]. +pub type Result = std::result::Result; + +impl From for Error { + /// An [`Error`] can always be made from a [`hashbrown::TryReserveError`]. + #[inline] + fn from(src: hashbrown::TryReserveError) -> Self { + use hashbrown::TryReserveError::*; + match src { + CapacityOverflow => Error::CapacityOverflow, + _ => Error::AllocationFailed, + } + } +} + +fn opt_symbol_to_space_quoted_string(opt_symbol: &Option>) -> Cow<'static, str> { + match opt_symbol { + Some(symbol) => format!(" `{}`", symbol).into(), + None => "".into(), + } +} diff --git a/other/unused-code/formulator/src/imp/domain.rs b/other/unused-code/formulator/src/imp/domain.rs new file mode 100644 index 0000000..dee143a --- /dev/null +++ b/other/unused-code/formulator/src/imp/domain.rs @@ -0,0 +1,655 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + borrow::Cow, + //cell::RefCell, + //collections::{BTreeSet, BTreeMap}, + //collections::{HashSet, HashMap}, + hash::{BuildHasher, Hash, Hasher}, + //io::{BufRead, Cursor}, + //iter::zip, + //marker::PhantomData, + //path::{Path, PathBuf}, + //sync::Arc, + //str::FromStr, + //sync::OnceLock, +}; + +//use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +//use equivalent::Equivalent; +use hashbrown::HashMap; +use indoc::indoc; +//use indexmap::set::IndexSet; +//use rand::{distr::Uniform, Rng, RngCore}; +//use serde::{Deserialize, Serialize}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +use tracing::{ + debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, + warn, +}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::{ + error::{Error, Result}, + imp::{BuildHasher_Sym, BuildHasher_Symbol, MAX_SYMREPR, SymRepr, SymSetRepr, reserve_addl}, + //Problem, Rule, RuleCost, RuleCostSum, RULE_IX_INVALID, +}; + +use util::const_minmax::const_min_u128; + +//=================================================================================================| + +/// Type of a fn that can convert a symbol to a [`string or str`](std::borrow::Cow). +pub type DynFnRefSymbolToCowstr = dyn Fn(&Symbol) -> Cow<'static, str>; + +/// Type of a Box of fn that can convert a symbol to a [`string or str`](std::borrow::Cow). +pub type BoxDynFnRefSymbolToCowstr = Box>; + +/// Type of an option of Box of fn that can convert a symbol to a [`string or str`](std::borrow::Cow). +pub type OptionBoxDynFnRefSymbolToCowstr = Option>; + +//=================================================================================================| + +pub struct Domain +where + Symbol: Eq + Hash, +{ + opt_bx_dyn_fn_symbol_to_cowstr: OptionBoxDynFnRefSymbolToCowstr, + + symbols: HashMap, + /* + refcell_symbols: RefCell>, + // */ +} + +impl Domain +where + Symbol: Eq + Hash, +{ + /// The max number of active symbols in any solving operation. + /// + /// Symbols which only appear in rules that have not yet been considered relevant may not + /// apply to this limit. + pub const MAX_ACTIVE_SYMBOLS: usize = const_min_u128(&[ + const_min_u128(&[u128::MAX - 1, MAX_SYMREPR as u128]) + 1, + usize::MAX as u128, + ]) as usize; + + /// Creates a new, empty [`Domain`]. + /// + /// - `opt_bx_dyn_fn_symbol_to_cowstr` - Function to convert a symbol to a string. Optional. + /// - `seed` - Seed data with which to initialize data structure hash function. + pub fn new( + opt_bx_dyn_fn_symbol_to_cowstr: OptionBoxDynFnRefSymbolToCowstr, + seed: H, + ) -> Self { + let [build_hasher_symbol] = + crate::imp::hasher::BuildHasher_StableSipHasher128::new_arr(seed); + + /* + let symbols = IndexSet::with_hasher(build_hasher_symbol); + + let refcell_symbols = RefCell::new(symbols); + + Domain { + opt_bx_dyn_fn_symbol_to_cowstr, + refcell_symbols, + } + // */ + let symbols = HashMap::with_hasher(build_hasher_symbol.clone()); + + Domain { + opt_bx_dyn_fn_symbol_to_cowstr, + symbols, + } + } + + /// Returns access to the [`BuildHasher`](std::hash::BuildHasher) used for hashing [`Symbol`]s. + pub fn build_hasher_symbol(&self) -> &BuildHasher_Symbol { + self.symbols.hasher() + } + + /// Hashes a [`Symbol`]. + pub fn hash_symbol(&self, symbol: &Symbol) -> u64 { + self.build_hasher_symbol().hash_one(symbol) + } + + /* + /// Returns the total number of [`Symbol`]s in this [`Domain`]. + pub fn cnt_symbols(&self) -> usize { + let opt_symbols = self.refcell_symbols.try_borrow().ok(); + + // Unwrap() is justified here because we are careful to scope borrows. + #[allow(clippy::unwrap_used)] + let symbols = opt_symbols.unwrap(); + + symbols.len() + } + // */ +} + +impl Domain +where + Symbol: Eq + Hash + Clone, +{ + /// Introduces a new [`Symbol`] into this [`Domain`] independent of any rules. + /// + /// Returns a reference to the symbol with lifetime of this [`Domain`]: + /// + /// - `Ok((true, symbol))` iff it is a new symbol to this domain + /// - `Ok((false, symbol))` if this symbol had been introduced previously. + /// - `Err(Error::SymbolSetFull)` if the symbol set is full. + pub fn symbol<'d, 'a>(&'d mut self, symbol: &'a Symbol) -> Result<(bool, &'d Symbol)> { + let (is_new, ref_d_symbol, _) = self.ensure_symbol(symbol)?; + Ok((is_new, ref_d_symbol)) + } + + /// Ensures `self.symbols` contains the `Symbol`. Returns a [`bool`] indicating newly inserted + /// and the [`symbol`]'s hash value. + pub(crate) fn ensure_symbol<'d, 's>( + &'d mut self, + symbol: &'s Symbol, + ) -> Result<(bool, &'d Symbol, u64)> { + // Ensure we have some additional capacity in advance, whether we need it or not. + reserve_addl(self.symbols.capacity(), |addl| { + self.symbols.try_reserve(addl) + })?; + + let symbol_hash_u64 = self.hash_symbol(symbol); + + let is_match = |other: &Symbol| *symbol == *other; + + // Copy these in advance because `self.symbol_to_sym.raw_entry_mut()` below borrows self mutably. + let symbols_len = self.symbols.len(); + let opt_bx_dyn_fn_symbol_to_cowstr = self + .opt_bx_dyn_fn_symbol_to_cowstr + .as_ref() + .map(|bx| bx.as_ref()); + + // self is mutably borrowed for this scope + use hashbrown::hash_map::{RawEntryBuilderMut, RawEntryMut}; + let symbols_raw_entry_mut = self + .symbols + .raw_entry_mut() + .from_hash(symbol_hash_u64, is_match); + + use hashbrown::hash_map::RawEntryMut::*; + let (is_new, ref_d_symbol): (bool, &'d Symbol) = match symbols_raw_entry_mut { + Occupied(entry) => (false, entry.into_key()), + Vacant(entry) => { + if Self::MAX_ACTIVE_SYMBOLS <= symbols_len { + let opt_symbol_str = opt_bx_dyn_fn_symbol_to_cowstr + .map(|fn_symbol_to_cow_str| fn_symbol_to_cow_str(symbol)); + let e = Error::SymbolSetFull { + opt_symbol_str, + current_number: symbols_len, + }; + return Err(e); + } else { + let symbol_ix = symbols_len; + let (ref_d_symbol, _) = + entry.insert_hashed_nocheck(symbol_hash_u64, symbol.clone(), symbol_ix); + (true, ref_d_symbol) + } + } + }; + + Ok((is_new, ref_d_symbol, symbol_hash_u64)) + } + + /* + /// Introduces a new [`Symbol`] into this [`Domain`] independent of any rules. + /// + /// This version is to be preferred when you have a `&mut self`. + /// + /// Returns : + /// + /// - `Ok((symbol, index))` with a reference to the symbol with lifetime of this [`Domain`] + /// - `Err(Error::SymbolSetFull)` if the symbol needs to be added but the set is already full. + pub fn symbol_mut<'d, 'a>(&'d mut self, symbol: &'a Symbol) -> Result<(usize, &'d Symbol)> { + let ref_d_symbols = self.refcell_symbols.get_mut(); + + Self::symbol_(&self.opt_bx_dyn_fn_symbol_to_cowstr, ref_d_symbols, symbol) + } + + /// Introduces a new [`Symbol`] into this [`Domain`] independent of any rules. + /// + /// This version is for when you only have a `&self`, but it could theoretically fail + /// if somehow there is already mutable ref. + /// + /// Returns : + /// + /// - `Ok((symbol, index))` with a reference to the symbol with lifetime of this [`Domain`] + /// - `Err(Error::SymbolsAlreadyMutablyInUse)` if there is already a mutable ref out. + /// - `Err(Error::SymbolSetFull)` if the Symbol needs to be added but the set is already full. + pub fn symbol<'d, 'a>(&'d self, symbol: &'a Symbol) -> Result<(usize, &'d Symbol)> { + let ref_d_symbols: std::cell::Ref<'d, _> = self.refcell_symbols.try_borrow() + .or(Err(Error::SymbolsAlreadyMutablyInUse))?; + + if let Some((index, ref_d_symbol)) = ref_d_symbols.get_full(symbol) { + // Symbol already exists in set + //drop(ref_d_symbols); + Ok((index, ref_d_symbol)) + } else { + // Symbol must be added to set + drop(ref_d_symbols); + + let stdcellrefmut_d_symbols: std::cell::RefMut<'d, _> = self.refcell_symbols.try_borrow_mut() + .or(Err(Error::SymbolsAlreadyInUse))?; + + Self::symbol_(&self.opt_bx_dyn_fn_symbol_to_cowstr, stdcellrefmut_d_symbols, symbol) + } + } + + fn symbol_<'d, 'a, M, S>( + opt_bx_dyn_fn_symbol_to_cowstr: &OptionBoxDynFnRefSymbolToCowstr, + std_cell_refmut_symbols: M, //std::cell::RefMut<'d, IndexSet>, + ref_a_symbol: &'a Symbol + ) -> Result<(usize, &'d Symbol)> + where + M: std::ops::Deref> + std::ops::DerefMut, + S: BuildHasher, + { + // Ensure we have some additional capacity in advance, whether we need it or not. + reserve_addl(std_cell_refmut_symbols.capacity(), |addl| { + std_cell_refmut_symbols.try_reserve(addl) + .map_err(|_| Error::AllocationFailed) + })?; + + let (index, newly_added) = std_cell_refmut_symbols.insert_full(ref_a_symbol.clone()); + + // Unwrap() is justified here because we just got this index from insertion. + #[allow(clippy::unwrap_used)] + let ref_d_symbol = std_cell_refmut_symbols.get_index(index).unwrap(); + + Ok((index, ref_d_symbol)) + + /* + let symbol_hash_u64 = self.hash_symbol(symbol); + + let is_match = |other: &Symbol| *symbol == *other; + + // Copy these in advance because `self.symbol_to_sym.raw_entry_mut()` below borrows self mutably. + let symbols_len = symbols.len(); + let opt_bx_dyn_fn_symbol_to_cowstr = self + .opt_bx_dyn_fn_symbol_to_cowstr + .as_ref() + .map(|bx| bx.as_ref()); + + // self is mutably borrowed for this scope + use hashbrown::hash_map::{RawEntryBuilderMut, RawEntryMut}; + let symbols_raw_entry_mut = self + .symbols + .raw_entry_mut() + .from_hash(symbol_hash_u64, is_match); + + use hashbrown::hash_map::RawEntryMut::*; + let (is_new, ref_d_symbol): (bool, &'d Symbol) = match symbols_raw_entry_mut { + Occupied(entry) => (false, entry.into_key()), + Vacant(entry) => { + if Self::MAX_ACTIVE_SYMBOLS <= symbols_len { + let opt_symbol_str = opt_bx_dyn_fn_symbol_to_cowstr + .map(|fn_symbol_to_cow_str| fn_symbol_to_cow_str(symbol)); + let e = Error::SymbolSetFull { + opt_symbol_str, + current_number: symbols_len, + }; + return Err(e); + } else { + let symbol_ix = symbols_len; + let (ref_d_symbol, _) = + entry.insert_hashed_nocheck(symbol_hash_u64, symbol.clone(), symbol_ix); + (true, ref_d_symbol) + } + } + }; + + Ok((is_new, ref_d_symbol, symbol_hash_u64)) + // */ + } + // */ +} + +impl Domain +where + Symbol: Eq + Hash, +{ + /// Obtains a &'d lifetime ref given an arbitrary `Symbol` ref. + /// Also returns the symbol's hash value. + /// + /// Returns `None` if the `Symbol` does not exist in the domain. + pub(crate) fn get_existing_symbol_hashvalue<'d, 's>( + &'d self, + symbol: &'s Symbol, + ) -> (Option<&'d Symbol>, u64) { + let (opt_pr_symbol_index, symbol_hash_u64) = + self.get_existing_symbol_index_hashvalue(symbol); + + let opt_symbol = opt_pr_symbol_index.map(|pr_symbol_index| pr_symbol_index.0); + + (opt_symbol, symbol_hash_u64) + } + + /// Obtains a &'d lifetime ref given an arbitrary `Symbol` ref. + /// Also returns the symbol's hash value and index. + /// + /// Returns `None` if the `Symbol` does not exist in the domain. + pub(crate) fn get_existing_symbol_index_hashvalue<'d, 's>( + &'d self, + symbol: &'s Symbol, + ) -> (Option<(&'d Symbol, usize)>, u64) { + let symbol_hash_u64 = self.hash_symbol(symbol); + + let is_match = |other: &Symbol| *symbol == *other; + + let opt_pr_symbol_index: Option<(&'d Symbol, usize)> = self + .symbols + .raw_entry() + .from_hash(symbol_hash_u64, is_match) + .map(|pr| (pr.0, *pr.1)); + + (opt_pr_symbol_index, symbol_hash_u64) + } + + /// Obtains a &'d lifetime ref given an arbitrary `Symbol` ref. + /// + /// Returns `None` if the `Symbol` does not exist in the domain. + pub(crate) fn get_existing_symbol<'d, 's>(&'d self, symbol: &'s Symbol) -> Option<&'d Symbol> { + self.get_existing_symbol_hashvalue(symbol).0 + } + + /// Returns the 0-based index value for a known symbol. + /// + /// Returns `None` if the `Symbol` does not exist in the domain. + pub fn get_existing_index(&self, symbol: &Symbol) -> Option { + let (opt_pr_symbol_index, _) = self.get_existing_symbol_index_hashvalue(symbol); + opt_pr_symbol_index.map(|pr| pr.1) + } + + /// Attempts to convert a `Symbol` to a string. + /// + /// Returns `None` if the `Symbol` does not exist in the domain. + pub fn try_symbol_to_str(&self, symbol: &Symbol) -> Option> { + self.opt_bx_dyn_fn_symbol_to_cowstr + .as_ref() + .map(|bx_fn_symbol_to_cow_str| bx_fn_symbol_to_cow_str(symbol)) + } + + /// Attempts to convert a [`Symbol`] to a string. + /// + /// If it cannot be converted to a string, a string representation of its index value, + /// prefixed with '@', is substituted. + /// + /// Returns `None` if the `Symbol` does not exist in the domain. + pub fn get_existing_symbol_string(&self, symbol: &Symbol) -> Option> { + self.try_symbol_to_str(symbol).or_else(|| { + let opt_symbol_ix = self.get_existing_index(symbol); + opt_symbol_ix.map(|symbol_ix| format!("@{symbol_ix}").into()) + }) + } +} + +impl Domain +where + Symbol: Eq + Hash + Ord, +{ + /// Returns a sorted collection of the Symbols + pub fn sorted_symbols(&self) -> Vec<&Symbol> { + let mut symbols: Vec<&Symbol> = self.symbols.keys().collect(); + symbols.sort(); + symbols + } + + /// Returns a sorted collection of the [`Symbol`]s as strings. + /// When the `Symbol` cannot be converted to a string, its index + /// value is substituted. + pub fn sorted_symbols_as_strings(&self) -> Vec> { + self.sorted_symbols() + .iter() + .map(|&symbol| { + if let Some(cowstr) = self.try_symbol_to_str(symbol) { + cowstr + } else { + // Unwrap() is justified here because we are iterating only known existing symbols. + #[allow(clippy::unwrap_used)] + self.get_existing_symbol_string(symbol).unwrap() + } + }) + .collect() + } +} + +impl std::fmt::Debug for Domain +where + Symbol: Eq + Hash + Ord, +{ + /// Format the value suitable for debugging output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_domain(f) + } +} + +impl std::fmt::Display for Domain +where + Symbol: Eq + Hash + Ord, +{ + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_domain(f) + } +} + +impl Domain +where + Symbol: Eq + Hash + Ord, +{ + fn fmt_domain(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.symbols.is_empty() { + write!(f, "Domain {{ }}") + } else { + let symbols = self.sorted_symbols(); + + let strs: Vec> = self.sorted_symbols_as_strings(); + + writeln!(f, "Domain {{")?; + + let line_prefix: &str = " "; + let line_len_max: usize = 72; + fmt_strs_comma_line(f, strs.as_slice(), line_prefix, line_len_max)?; + + write!(f, "}}") + } + } +} + +fn fmt_strs_comma_line>( + f: &mut std::fmt::Formatter<'_>, + strs: &[S], + line_prefix: &str, + line_len_max: usize, +) -> std::fmt::Result { + let line_prefix_len = line_prefix.len(); + + let mut line_len = 0; + for s in strs { + if line_len == 0 { + write!(f, "{}", line_prefix)?; + line_len = line_prefix_len; + } + + let s: &str = s.as_ref(); + let s_len = s.len(); + if line_len <= line_prefix_len { + write!(f, "{s}")?; + line_len += s_len; + } else { + let line_len_if_s_appended = line_len + 2 + s_len; + if line_len_if_s_appended < line_len_max { + write!(f, ", {s}")?; + line_len = line_len_if_s_appended; + } else { + write!(f, ",\n{line_prefix}{s}")?; + line_len = line_prefix_len + s_len; + } + } + } + + if line_len != 0 { + writeln!(f)?; + } + Ok(()) +} + +//-------------------------------------------------------------------------------------------------| + +impl serde::Serialize for Domain +where + Symbol: Eq + Hash + Ord + serde::Serialize, +{ + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::ser::Serializer, + { + use serde::ser::SerializeStruct; + + let mut state = serializer.serialize_struct("Domain", 1)?; + + let mut symbols: Vec<&Symbol> = self.symbols.keys().collect(); + symbols.sort(); + state.serialize_field("symbols", &symbols)?; + state.end() + } +} + +//=================================================================================================| + +/// Generates a random [`Domain`] having the [`String`] symbol type. +pub fn gen_random_domain( + cnt_symbols: usize, + rng: &mut R, +) -> Result> { + use static_assertions::assert_impl_all; + type Symbol = String; + + fn symbol_to_cowstr(symbol: &Symbol) -> Cow<'static, str> { + Cow::Owned(symbol.clone()) + } + assert_impl_all!(Symbol: Eq, Hash); + + let opt_bx_dyn_fn_symbol_to_cowstr: OptionBoxDynFnRefSymbolToCowstr = + Some(Box::new(symbol_to_cowstr)); + + let mut domain = crate::Domain::::new(opt_bx_dyn_fn_symbol_to_cowstr, rng.next_u64()); + + let v_names = crate::imp::gen_random_name_strings(cnt_symbols, rng); + for symbol in v_names { + domain.symbol(&symbol)?; + } + + Ok(domain) +} + +//=================================================================================================| + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod t { + use super::*; + use super::{Domain, DynFnRefSymbolToCowstr, SymRepr}; + use anyhow::{Context, Result, anyhow, bail, ensure}; + use insta::{assert_debug_snapshot, assert_json_snapshot, assert_snapshot}; + use static_assertions::assert_impl_all; + + #[test] + fn t0() { + type Symbol = char; + assert_impl_all!(Symbol: Eq, Hash); + + fn symbol_to_cowstr(ch: &char) -> Cow<'static, str> { + ch.to_string().into() + } + let opt_bx_dyn_fn_symbol_to_cowstr: OptionBoxDynFnRefSymbolToCowstr = + Some(Box::new(symbol_to_cowstr)); + + let seed = 1234; + let mut domain = Domain::::new(opt_bx_dyn_fn_symbol_to_cowstr, seed); + + // `snapshots/formulator__t__t0.snap` + assert_snapshot!(domain); + + domain.symbol(&'a').unwrap(); + + // `snapshots/formulator__t__t0-2.snap` + assert_snapshot!(domain); + + let (opt_symbol, symbol_hash_u64) = domain.get_existing_symbol_hashvalue(&'a'); + let symbol = opt_symbol.unwrap(); + + // `snapshots/formulator__t__t0-3.snap` + assert_snapshot!(format!( + "domain: {domain},\nsymbol: {symbol},\nsymbol_hash_u64: {symbol_hash_u64:#018x}" + )); + } + + #[test] + fn t1() { + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; + + let mut rng = XorShiftRng::seed_from_u64(1234); + let rng = &mut rng; + + const CNT_SYMBOLS: usize = 50; + + let domain = gen_random_domain(CNT_SYMBOLS, rng).unwrap(); + + // `snapshots/formulator__t__t1.snap` + assert_snapshot!(domain); + + // `snapshots/formulator__t__t1-2.snap` + assert_json_snapshot!(domain); + } +} diff --git a/other/unused-code/formulator/src/imp/hasher.rs b/other/unused-code/formulator/src/imp/hasher.rs new file mode 100644 index 0000000..1323f93 --- /dev/null +++ b/other/unused-code/formulator/src/imp/hasher.rs @@ -0,0 +1,226 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] + +use std::hash::{BuildHasher, Hash, Hasher}; + +use static_assertions::const_assert; + +use rustc_stable_hash::{ + ExtendedHasher, FromStableHash, StableHasher, + hashers::{SipHasher128, SipHasher128Hash, StableSipHasher128}, +}; + +//=================================================================================================| + +/// `rustc_stable_hash::hashers::StableSipHasher128` is non-randomized and indepent of the host +/// endianness. +pub type DefaultHasher = StableSipHasher128; + +/// `rustc_stable_hash::hashers::StableSipHasher128` is non-randomized and indepent of the host +/// endianness. +pub type DefaultBuildHasher = BuildHasher_StableSipHasher128; + +//=================================================================================================| + +static CUSTOMIZATION_VALUES: [u64; 2] = [ + // Well-known constant 'pi' in BCD (binary coded decimal) format. + 0x_3141_5926_5358_9793_u64, + // Well-known constant 'e' in BCD (binary coded decimal) format. + 0x_2718_2818_2845_9045_u64, +]; + +#[derive(Clone, Default)] +pub struct BuildHasher_StableSipHasher128(StableSipHasher128); + +impl BuildHasher_StableSipHasher128 { + /// Generate an array of [`BuildHasher_StableSipHasher128`]s. + /// + /// Each will be seeded differently incorporating the supplied hash seed data and + /// its individual sequence number. + /// + /// So parameter `N` can be changed without changing the previous sequences. + pub(crate) fn new_arr(seed_data: H) -> [Self; N] { + // A hasher initialized with some custom state and the caller-supplied seed. + let mut ssh128 = StableSipHasher128::with_hasher(SipHasher128::new_with_keys( + CUSTOMIZATION_VALUES[0], + CUSTOMIZATION_VALUES[1], + )); + seed_data.hash(&mut ssh128); + let ssh128 = &ssh128; + + // Customize the hasher for ix each 0..`N` + std::array::from_fn(|ix1| { + const_assert!(size_of::() <= size_of::()); + let ix1 = ix1 as u64; + + let mut ssh128 = ssh128.clone(); + for &cv_a in CUSTOMIZATION_VALUES.iter() { + let u = cv_a.wrapping_mul(ix1); + for m in [59_u64, 61] { + let r = u % m; + for &cv_b in CUSTOMIZATION_VALUES.iter() { + ssh128.write_u64(cv_b.rotate_left(r as u32)); + } + } + } + + ssh128.write_u64(ix1); + + Self(ssh128) + }) + } + + /// Generate a single [`BuildHasher_StableSipHasher128`]. + pub fn new(seed_data: H) -> Self { + let [self_] = Self::new_arr(seed_data); + self_ + } + + /// Simply clones the internal [`StableSipHasher128`]. + pub fn build(&self) -> StableSipHasher128 { + self.0.clone() + } +} + +impl std::hash::BuildHasher for BuildHasher_StableSipHasher128 { + type Hasher = StableSipHasher128; + + /// Simply clones the internal [`StableSipHasher128`]. + fn build_hasher(&self) -> Self::Hasher { + self.build() + } +} + +impl std::fmt::Display for BuildHasher_StableSipHasher128 { + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let u: u64 = Hasher::finish(&self.0); + write!(f, "BuildHasher_StableSipHasher128 {{ finish: {u:#018x} }}") + } +} + +impl std::fmt::Debug for BuildHasher_StableSipHasher128 { + /// Format the value suitable for debugging output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +impl serde::Serialize for BuildHasher_StableSipHasher128 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + use serde::ser::{Error, SerializeStruct}; + + let mut state = serializer.serialize_struct("BuildHasher_StableSipHasher128", 1)?; + + let u: u64 = Hasher::finish(&self.0); + state.serialize_field("finish", &format!("{u:#018x}"))?; + + state.end() + } +} + +impl std::hash::Hash for BuildHasher_StableSipHasher128 { + #[inline] + fn hash(&self, state: &mut H) { + // Use the hash of two arbitrary customization values generated by this hasher instance, + // as the hash input. + static ARBITRARY_CUSTOMIZATION_VALUES: [u64; 2] = [ + CUSTOMIZATION_VALUES[0].rotate_left(13) ^ CUSTOMIZATION_VALUES[1].rotate_left(22), + CUSTOMIZATION_VALUES[0].rotate_left(5) ^ CUSTOMIZATION_VALUES[1].rotate_left(8), + ]; + + self.hash_one(ARBITRARY_CUSTOMIZATION_VALUES[0]).hash(state); + self.hash_one(ARBITRARY_CUSTOMIZATION_VALUES[1]).hash(state); + } +} + +//=================================================================================================| + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod t { + use super::*; + use anyhow::{Context, Result, anyhow, bail, ensure}; + use insta::{assert_debug_snapshot, assert_snapshot}; + use itertools::Itertools; + + #[test] + fn t0() { + const CNT_HASHERS: usize = 15; + + let bhxrs: [BuildHasher_StableSipHasher128; CNT_HASHERS] = + BuildHasher_StableSipHasher128::new_arr(0_u64); + + assert_debug_snapshot!(bhxrs); + + let hxrs: [StableSipHasher128; CNT_HASHERS] = bhxrs + .iter() + .map(|bhxr| bhxr.build()) + .collect_array() + .unwrap(); + + let hvs: [[u64; 4]; CNT_HASHERS] = hxrs + .iter() + .map(|hxr| { + (0_u64..) + .map(|ix| { + let mut s = hxr.clone(); + s.write_u64(0); + Hasher::finish(&s) + }) + .next_array() + .unwrap() + }) + .collect_array() + .unwrap(); + + let mut s = String::new(); + s.push_str("Hash values: ["); + for a in hvs { + s.push_str(&format!( + "\n [ {:#018x}, {:#018x}, {:#018x}, {:#018x} ],", + a[0], a[1], a[2], a[3] + )); + } + s.push_str("\n]\n"); + assert_snapshot!(s); + + // Check that each hasher produces a unique hash value. + for i in 1..CNT_HASHERS { + for j in 0..i { + let hv_i = hvs[i]; + let hv_j = hvs[j]; + assert_ne!(hv_i, hv_j) + } + } + } +} diff --git a/other/unused-code/formulator/src/imp/mod.rs b/other/unused-code/formulator/src/imp/mod.rs new file mode 100644 index 0000000..9811e89 --- /dev/null +++ b/other/unused-code/formulator/src/imp/mod.rs @@ -0,0 +1,165 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::expect_used)] // This is `cfg(test)` code +#![allow(clippy::manual_assert)] // This is `cfg(test)` code +#![allow(clippy::new_without_default)] // This is `cfg(test)` code +#![allow(clippy::panic)] // This is `cfg(test)` code +#![allow(clippy::unwrap_used)] // This is `cfg(test)` code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +//=================================================================================================| + +pub(crate) mod domain; +pub(crate) mod hasher; +pub(crate) mod problem; +pub(crate) mod rule; +pub(crate) mod solution; +//pub(crate) mod sym; +//pub(crate) mod sym_set; + +//=================================================================================================| + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + borrow::Cow, + //cell::RefCell, + //collections::{BTreeSet, BTreeMap}, + //collections::{HashSet, HashMap}, + hash::{BuildHasher, Hash, Hasher}, + //io::{BufRead, Cursor}, + //iter::zip, + //marker::PhantomData, + //path::{Path, PathBuf}, + //sync::Arc, + //str::FromStr, + //sync::OnceLock, +}; + +//use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +//use log::{debug, error, info, trace, warn}; +//use rand::{distr::Uniform, Rng, RngCore}; +use tinyset::setu32::SetU32; + +use util::const_minmax::const_min_u128; + +//=================================================================================================| + +// The internal representation type for a symbol as a zero-based ordinal. +type SymRepr = u32; + +/// The max value of a SymRepr +pub(crate) static MAX_SYMREPR: SymRepr = + const_min_u128(&[usize::MAX as u128 - 1, SymRepr::MAX as u128]) as SymRepr; + +// The internal representation type for set of symbols. +type SymSetRepr = SetU32; + +//=================================================================================================| + +/// [`BuildHasher`] for [`Symbol`]. +pub(crate) type BuildHasher_Symbol = crate::DefaultBuildHasher; + +/// [`BuildHasher`] for [`Sym`]. +pub(crate) type BuildHasher_Sym = crate::DefaultBuildHasher; + +//=================================================================================================| + +/// Calls `f` with the recommended amount to call [`try_reserve`] with. +pub(crate) fn reserve_addl(cur: usize, f: F) -> crate::error::Result<()> +where + E: Into, + F: FnOnce(usize) -> std::result::Result<(), E>, +{ + const CAPACITY_MULTIPLE: usize = 64; + let wanted = (cur + 1).next_multiple_of(CAPACITY_MULTIPLE); + let addl = wanted - cur; + if 0 < addl { + f(addl).map_err(Into::into) + } else { + Ok(()) + } +} + +//=================================================================================================| + +pub(crate) fn gen_random_name_strings( + cnt: usize, + rng: &mut R, +) -> Vec { + use hashbrown::HashMap; + use rand_distr::{Distribution, Exp, Uniform}; + + type Symbol = String; + + fn symbol_to_cowstr(symbol: &Symbol) -> Cow<'static, str> { + Cow::Owned(symbol.clone()) + } + + let opt_bx_dyn_fn_symbol_to_cowstr: Option<&crate::DynFnRefSymbolToCowstr> = + Some(&symbol_to_cowstr); + + let exp = Exp::new(0.5).unwrap(); + + // Unwrap() is justified here because this is a const input. + #[allow(clippy::unwrap_used)] + let distr_char = Uniform::try_from('a'..='z').unwrap(); + + let hash_builder = crate::DefaultBuildHasher::new(rng.next_u64()); + + let mut h: HashMap = HashMap::with_capacity_and_hasher(cnt * 2, hash_builder); + let mut v = vec![]; + let mut cnt_iter = 25_u32; + while v.len() < cnt && cnt_iter < (1u32 << 30) { + cnt_iter += 1; + debug_assert_ne!(cnt_iter, 1u32 << 30); + + let exp_multiplier: f64 = (cnt_iter as f64).log(26.0); + let f: f64 = exp.sample(rng) * exp_multiplier; + let string_len = 1_usize + f.round() as usize; + + let mut s = String::new(); + while s.len() < string_len { + let ch = distr_char.sample(rng); + s.push(ch); + } + + if h.insert(s.clone(), ()).is_none() { + v.push(s); + } + } + + v +} diff --git a/other/unused-code/formulator/src/imp/problem.rs b/other/unused-code/formulator/src/imp/problem.rs new file mode 100644 index 0000000..d873631 --- /dev/null +++ b/other/unused-code/formulator/src/imp/problem.rs @@ -0,0 +1,281 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(clippy::absurd_extreme_comparisons)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + borrow::Cow, + //cell::RefCell, + //collections::{BTreeSet, BTreeMap}, + //collections::{HashSet, HashMap}, + //io::{BufRead, Cursor}, + //iter::zip, + hash::{BuildHasher, Hash, Hasher}, + //marker::PhantomData, + //path::{Path, PathBuf}, + //sync::Arc, + //str::FromStr, + //sync::OnceLock, +}; + +//use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +use hashbrown::HashMap; +use indoc::indoc; +//use rand::{distr::Uniform, Rng, RngCore}; +//use serde::{Deserialize, Serialize}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +//use tracing::{debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, warn}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::{ + Domain, DynFnRefSymbolToCowstr, Rule, RuleCost, RuleCostSum, + error::{Error, Result}, + imp::{BuildHasher_Sym, BuildHasher_Symbol, MAX_SYMREPR, SymRepr, SymSetRepr, reserve_addl}, +}; + +//=================================================================================================| + +#[derive(Clone)] +pub struct Problem<'d, Symbol> +where + Symbol: Eq + Hash + Ord, +{ + pub(crate) domain: &'d Domain, + + pub(crate) starting_set: HashMap, + pub(crate) finishing_set: HashMap, + + pub(crate) rules: Vec>, +} + +impl<'d, Symbol> Problem<'d, Symbol> +where + Symbol: Eq + Hash + Clone + Ord, +{ + pub fn new( + domain: &'d Domain, + starting_set_iter: SS_II, + finishing_set_iter: FS_II, + ) -> Self + where + SS_II: IntoIterator, + SS_II_Symbol: std::borrow::Borrow, + FS_II: IntoIterator, + FS_II_Symbol: std::borrow::Borrow, + { + let build_hasher_symbol = domain.build_hasher_symbol().clone(); + + let mut starting_set = HashMap::with_hasher(build_hasher_symbol.clone()); + for s in starting_set_iter { + starting_set.insert(s.borrow().clone(), ()); + } + + let mut finishing_set = HashMap::with_hasher(build_hasher_symbol); + for s in finishing_set_iter { + finishing_set.insert(s.borrow().clone(), ()); + } + + Self { + domain, + starting_set, + finishing_set, + rules: Vec::new(), + } + } +} + +impl<'d, Symbol> Problem<'d, Symbol> +where + Symbol: Eq + Hash + Ord, +{ + /// Returns access to the [`Domain`]. + pub(crate) fn domain(&self) -> &'d Domain { + self.domain + } + + /// Adds a new rule to the Domain. Also adds all Symbols mentioned by the rule. + /// + /// Returns the index of the new rule. + pub fn add_rule(&mut self, mut rule: Rule<'d, Symbol>) -> Result { + let rule_ix = self.rules.len(); + + //? TODO If another rule which is at least as powerful with no less cost + // does not exist already, return the existing rule? + + if rule_ix >= crate::RULES_CNT_MAX { + return Err(Error::RuleSetFull { + current_number: self.rules.len(), + }); + } + + rule.ix = rule_ix; + self.rules.push(rule); + + Ok(rule_ix) + } +} + +impl<'d, Symbol> std::fmt::Debug for Problem<'d, Symbol> +where + Symbol: Eq + Hash + Ord + std::fmt::Debug, +{ + /// Format the value suitable for debugging output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + indoc! {" + Problem {{ + starting_set: {:?}, + finishing_set: {:?}, + rules: [ + "}, + self.starting_set, self.finishing_set + )?; + + for (ix, r) in self.rules.iter().enumerate() { + writeln!(f, " {ix:4}: {r:?}")?; + } + + write!( + f, + indoc! {" + ], + }}"} + ) + } +} + +impl<'d, Symbol> std::fmt::Display for Problem<'d, Symbol> +where + Symbol: Eq + Hash + Ord + std::fmt::Debug, +{ + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } +} + +/* +impl arbitrary::Arbitrary<'_> for Problem { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + let mut starting_set: SymSet = u.arbitrary()?; + let mut finishing_set: SymSet = u.arbitrary()?; + + // Most of the time we want the starting set at least as large as the finishing set. + if u.ratio(9, 10)? && starting_set.len() < finishing_set.len() { + (starting_set, finishing_set) = (finishing_set, starting_set); + } + + // Most of the time we want some rules + let mut rules: Vec = Vec::new(); + if u.ratio(19, 20)? { + let mut n: usize = 1; + for _ in 0..4 { + n += u.arbitrary::()?.leading_ones() as usize; + } + while rules.len() < n && !u.is_empty() { + let rule: Rule = u.arbitrary()?; + let rule_exists = rules + .iter() + .any(|r2| r2.requires == rule.requires && r2.produces == rule.produces); + if !rule_exists { + rules.push(rule); + } + } + } + + // Ensure at least 1/4 of the rules include only elements from the starting set + for rule in rules.iter_mut() { + if !starting_set.is_empty() && u.ratio(1, 4)? { + rule.requires &= &starting_set; + } + } + + // Ensure at least 1/4 of the rules produce at least one element of the finishing set + for rule in rules.iter_mut() { + if !finishing_set.is_empty() && u.ratio(1, 4)? { + let sym = u.choose_iter(finishing_set.iter())?; + rule.produces |= sym; + } + } + + Ok(Problem { + starting_set, + finishing_set, + rules, + }) + } +} + +impl proptest::arbitrary::Arbitrary for Problem { + type Parameters = (); + type Strategy = proptest_arbitrary_interop::ArbStrategy; + + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + proptest_arbitrary_interop::arb() + } +} +// */ + +//=================================================================================================| + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod t { + //use anyhow::{anyhow, bail, ensure, Context, Result}; + //?use insta::assert_ron_snapshot; + use proptest::prelude::*; + + use super::*; + + /* + // [`Problem`] tests + proptest! { + #[test] + fn sym_arbitrary(_problem: Problem) { + //prop_assert!(); + } + } + // */ +} diff --git a/other/unused-code/formulator/src/imp/rule.rs b/other/unused-code/formulator/src/imp/rule.rs new file mode 100644 index 0000000..fcd9132 --- /dev/null +++ b/other/unused-code/formulator/src/imp/rule.rs @@ -0,0 +1,418 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(clippy::absurd_extreme_comparisons)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +use std::{ + //borrow::Cow, + //cell::RefCell, + //collections::{BTreeSet, BTreeMap}, + //collections::{HashSet, HashMap}, + hash::{BuildHasher, Hash, Hasher}, + iter::FusedIterator, + //io::{BufRead, Cursor}, + //iter::zip, + //marker::PhantomData, + //path::{Path, PathBuf}, + //sync::Arc, + //str::FromStr, + //sync::OnceLock, +}; + +//use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +use hashbrown::HashMap; +//use rand::{distr::Uniform, Rng, RngCore}; +//use serde::{Deserialize, Serialize}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +//use tracing::{debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, warn}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use util::const_minmax::const_min_u128; + +use crate::{ + Domain, DynFnRefSymbolToCowstr, Problem, + error::{Error, Result}, + imp::{BuildHasher_Sym, BuildHasher_Symbol, MAX_SYMREPR, SymRepr, SymSetRepr, reserve_addl}, +}; + +//=================================================================================================| + +/// Type representing the cost of a rule. +pub type RuleCost = i32; +/* +pub const fn rule_cost_invalid_value() -> RuleCost { RuleCost::MIN } + +pub const fn rule_cost_valid_min() -> RuleCost { rule_cost_invalid_value() + 1 } + +pub const fn rule_cost_valid_max() -> RuleCost { RuleCost::MAX } + +pub const fn valid_rule_cost_range() -> std::ops::RangeInclusive { + rule_cost_valid_min() ..= rule_cost_valid_max() +} + +pub const fn rule_cost_is_valid(rule_cost: RuleCost) -> bool { + rule_cost != rule_cost_invalid_value() +} +// */ + +/// Type representing a sum of many rule costs. +pub type RuleCostSum = i64; + +/// The maximum number of rules. +/// +/// Note that we limit to [`i32::MAX`] here so we can use `i64` for a sum of costs without worrying +/// about overflow. +pub const RULES_CNT_MAX: usize = const_min_u128(&[i32::MAX as u128, usize::MAX as u128]) as usize; + +/// The largest possible rule index. +pub const RULE_IX_MAX: usize = RULES_CNT_MAX - 1; + +/// An invalid rule index. +pub const RULE_IX_INVALID: usize = RULES_CNT_MAX; + +//=================================================================================================| + +/// A rule in the domain. +#[derive(Clone)] +pub struct Rule<'d, Symbol> +where + Symbol: Eq + Hash, +{ + pub(crate) domain: &'d Domain, + pub(crate) ix: usize, + pub(crate) requires: HashMap<&'d Symbol, (), BuildHasher_Symbol>, + pub(crate) produces: HashMap<&'d Symbol, (), BuildHasher_Symbol>, + pub(crate) cost: RuleCost, +} + +impl<'d, Symbol> Rule<'d, Symbol> +where + Symbol: Eq + Hash + Clone + Ord, +{ + /// Creates a new rule, adding its [`Symbol`]s to this [`Domain`] as necessary. + /// + /// The new rule will have its index set to [`RULE_IX_INVALID`]. + pub fn new_rule_and_symbols<'a, 'b, RS_II, RS_II_AsRef_Symbol, PS_II, PS_II_AsRef_Symbol>( + domain: &'d mut Domain, + requires_iter: RS_II, + produces_iter: PS_II, + cost: RuleCost, + ) -> Result> + where + RS_II: IntoIterator + Clone, + RS_II_AsRef_Symbol: std::convert::AsRef + 'a, + PS_II: IntoIterator + Clone, + PS_II_AsRef_Symbol: std::convert::AsRef + 'b, + { + let build_hasher_symbol = domain.build_hasher_symbol().clone(); + + for s in requires_iter.clone() { + let s: &Symbol = s.as_ref(); + domain.ensure_symbol(s)?; // keep no reference in this loop + } + + for s in produces_iter.clone() { + let s: &Symbol = s.as_ref(); + domain.ensure_symbol(s)?; // keep no reference in this loop + } + + Self::new_rule(domain, requires_iter, produces_iter, cost) + } + + /// Creates a new rule, where all its [`Symbol`]s are already present in the [`Domain`]. + /// + /// The new rule will have its index set to [`RULE_IX_INVALID`]. + pub fn new_rule<'a, 'b, RS_II, RS_II_AsRef_Symbol, PS_II, PS_II_AsRef_Symbol>( + domain: &'d Domain, + requires_iter: RS_II, + produces_iter: PS_II, + cost: RuleCost, + ) -> Result> + where + RS_II: IntoIterator + Clone, + RS_II_AsRef_Symbol: std::convert::AsRef + 'a, + PS_II: IntoIterator + Clone, + PS_II_AsRef_Symbol: std::convert::AsRef + 'b, + { + let mut requires = HashMap::with_hasher(domain.build_hasher_symbol().clone()); + for s in requires_iter { + // Unwrap() is justified here because we just called `ensure_symbol()` on each one. + #[allow(clippy::unwrap_used)] + let s: &'d Symbol = domain.get_existing_symbol(s.as_ref()).unwrap(); + requires.insert(s, ()); + } + + let mut produces = HashMap::with_hasher(domain.build_hasher_symbol().clone()); + for s in produces_iter { + // Unwrap() is justified here because we just called `ensure_symbol()` on each one. + #[allow(clippy::unwrap_used)] + let s: &'d Symbol = domain.get_existing_symbol(s.as_ref()).unwrap(); + produces.insert(s, ()); + } + + let rule = Self { + domain, + ix: RULE_IX_INVALID, + requires, + produces, + cost, + }; + + Ok(rule) + } +} + +impl<'d, Symbol> Rule<'d, Symbol> +where + Symbol: Eq + Hash, +{ + /// Returns rule's Domain. + pub fn domain(&self) -> &'d Domain { + self.domain + } + + /// Returns rule index, if it is valid. + pub fn ix(&self) -> Option { + let ix = self.ix; + (ix != RULE_IX_INVALID).then_some(ix) + } + + /// Returns iterator over the [`Symbols`] this rule `requires`. + pub fn requires(&self) -> impl ExactSizeIterator + FusedIterator { + self.requires.keys().copied() + } + + /// Returns iterator over the [`Symbols`] this rule `produces`. + pub fn produces(&self) -> impl ExactSizeIterator + FusedIterator { + self.produces.keys().copied() + } + + /// Returns the rule's [`cost`](RuleCost). + pub fn cost(&self) -> RuleCost { + self.cost + } + + /* + /// Returns access to the [`BuildHasher`](std::hash::BuildHasher) used for hashing [`Symbol`]s. + pub fn build_hasher_symbol(&self) -> &BuildHasher_Symbol { + self.requires.hasher() + } + + /// Hashes a [`Symbol`]. + pub fn hash_symbol(&self, symbol: &Symbol) -> u64 { + self.build_hasher_symbol().hash_one(symbol) + } + // */ +} + +impl<'d, Symbol> PartialEq for Rule<'d, Symbol> +where + Symbol: Eq + Hash + Ord + 'd, +{ + #[inline] + fn eq(&self, rhs: &Self) -> bool { + self.ix == rhs.ix + && self.requires == rhs.requires + && self.produces == rhs.produces + && self.cost == rhs.cost + } +} + +impl<'d, Symbol> Eq for Rule<'d, Symbol> where Symbol: Eq + Hash + Ord + 'd {} + +impl<'d, Symbol> std::fmt::Debug for Rule<'d, Symbol> +where + Symbol: Eq + Hash + Ord + std::fmt::Debug + 'd, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut ds = f.debug_struct("Rule"); + ds.field("ix", &self.ix); + ds.field("requires", &self.requires); + ds.field("produces", &self.produces); + ds.field("cost", &self.cost); + ds.finish() + } +} + +impl<'d, Symbol> std::fmt::Display for Rule<'d, Symbol> +where + Symbol: Eq + Hash + Ord + std::fmt::Display + 'd, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut ds = f.debug_struct("Rule"); + ds.field("ix", &self.ix); + + /* + let v_symbol_to_string = |symbols: &Vec| -> String { + let mut s = "[".to_string(); + for (&symbol, _) in self.requires.iter() { + s.push_str(&format!("\n {symbol}")); + } + s.push_str("\n]"); + s + }; + // */ + + let hm_symbol_to_string = |symbols: &HashMap<&Symbol, (), _>| -> String { + let mut s = "[".to_string(); + for (&symbol, _) in self.requires.iter() { + s.push_str(&format!("\n {symbol}")); + } + s.push_str("\n]"); + s + }; + + ds.field("requires", &hm_symbol_to_string(&self.requires)); + ds.field("produces", &hm_symbol_to_string(&self.produces)); + ds.field("cost", &format_args!("{}", self.cost)); + ds.finish() + } +} + +/* +pub(crate) struct HashSetSymbolWrapper<'a, Symbol, BuildHasher_Symbol>( + pub(crate) &'a HashMap +); +impl <'a, Symbol, BuildHasher_Symbol> HashSetSymbolWrapper<'a, Symbol, BuildHasher_Symbol> { + +} +impl serde::Serialize for HashSetSymbolWrapper<'_, Symbol, BuildHasher_Symbol>{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + use serde::ser::{ + Error, + SerializeSeq, + }; + let mut state = serializer.serialize_seq(Some(self.0.len()))?; + for elem in self { + state.serialize_element(elem)?; + } + state.end() + } +} +// */ + +//=================================================================================================| + +/* +#[derive(Clone, Debug, PartialEq, Eq //, Hash +)] +pub(crate) struct Rule_Sym { + id: usize, + requires: HashMap, + produces: HashMap, + cost: RuleCost, +} +pub struct RuleKey +where + Symbol: Eq + Hash + Ord, +{ + SetU32, +} +#[derive(Clone//, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash +)] +pub struct Rules { + rules: HashMap, +} +// + +impl Rule { + pub fn new(requires: &str, produces: &str, cost: i64) -> Result { + +impl std::fmt::Display for Rule { + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } +} + +impl arbitrary::Arbitrary<'_> for Rule { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + let mut requires: SymSet = u.arbitrary()?; + let mut produces: SymSet = u.arbitrary()?; + + // Most of the time we want the [`produces`] set at least as large as the [`requires`] set. + if u.ratio(9, 10)? && produces.len() < requires.len() { + (produces, requires) = (requires, produces); + } + + Ok(Rule { + requires, + produces, + cost: u.int_in_range::(1..=1_000_000)?, + }) + } +} + +impl proptest::arbitrary::Arbitrary for Rule { + type Parameters = (); + type Strategy = proptest_arbitrary_interop::ArbStrategy; + + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + proptest_arbitrary_interop::arb() + } +} +// */ + +//=================================================================================================| + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod t { + //?use anyhow::{anyhow, bail, ensure, Context, Result}; + //?use insta::assert_ron_snapshot; + use super::*; + + /* + use proptest::prelude::*; + proptest! { + //#![proptest_config(ProptestConfig::with_cases(10))] + #[test] + fn rule(rule: Rule) { + let s = rule.to_string(); + } + } + // */ +} diff --git a/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-2.snap b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-2.snap new file mode 100644 index 0000000..7b7b44a --- /dev/null +++ b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-2.snap @@ -0,0 +1,7 @@ +--- +source: formulator/src/imp/domain.rs +expression: domain +--- +Domain { + a +} diff --git a/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-3.snap b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-3.snap new file mode 100644 index 0000000..ffd0eb0 --- /dev/null +++ b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-3.snap @@ -0,0 +1,9 @@ +--- +source: formulator/src/imp/domain.rs +expression: "format!(\"domain: {domain},\\nsymbol: {symbol},\\nsymbol_hash_u64: {symbol_hash_u64:#018x}\")" +--- +domain: Domain { + a +}, +symbol: a, +symbol_hash_u64: 0x02be3baf0fa40b0d diff --git a/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-4.snap b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-4.snap new file mode 100644 index 0000000..ecd9d41 --- /dev/null +++ b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0-4.snap @@ -0,0 +1,21 @@ +--- +source: formulator/src/imp/domain.rs +expression: domain +--- +Domain { + symbols: { + 'a', + 'b', + 'c', + }, + symbol_to_sym: { + 'a' -> 0, + }, + sym_to_symbol: { + 0 -> 'a', + }, + rules: [ + 0: Rule { ix: 0, requires: ['a'], produces: ['b'], cost: 10 }, + 1: Rule { ix: 1, requires: ['b'], produces: ['c'], cost: 20 }, + ], +} diff --git a/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0.snap b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0.snap new file mode 100644 index 0000000..2147b32 --- /dev/null +++ b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t0.snap @@ -0,0 +1,5 @@ +--- +source: formulator/src/imp/domain.rs +expression: domain +--- +Domain { } diff --git a/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t1-2.snap b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t1-2.snap new file mode 100644 index 0000000..37f6d7d --- /dev/null +++ b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t1-2.snap @@ -0,0 +1,58 @@ +--- +source: formulator/src/imp/domain.rs +expression: domain +--- +{ + "symbols": [ + "aadf", + "as", + "btmgm", + "bw", + "cenh", + "dr", + "e", + "eiamp", + "eqhiojyxtn", + "erdy", + "fo", + "fvjh", + "gkevvvs", + "gp", + "guv", + "gxf", + "h", + "i", + "iljblwuaemtszsvn", + "imka", + "itrkr", + "iztifnijcqj", + "jfpvxaocg", + "jh", + "kfuw", + "klv", + "lai", + "llb", + "md", + "mj", + "my", + "n", + "net", + "oag", + "ogu", + "pth", + "q", + "qj", + "qrvry", + "tzug", + "ufyeb", + "v", + "vhpih", + "vll", + "vvoej", + "x", + "xhnic", + "xmk", + "xsk", + "z" + ] +} diff --git a/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t1.snap b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t1.snap new file mode 100644 index 0000000..0c0bc7f --- /dev/null +++ b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__domain__t__t1.snap @@ -0,0 +1,11 @@ +--- +source: formulator/src/imp/domain.rs +expression: domain +--- +Domain { + aadf, as, btmgm, bw, cenh, dr, e, eiamp, eqhiojyxtn, erdy, fo, fvjh, + gkevvvs, gp, guv, gxf, h, i, iljblwuaemtszsvn, imka, itrkr, + iztifnijcqj, jfpvxaocg, jh, kfuw, klv, lai, llb, md, mj, my, n, net, + oag, ogu, pth, q, qj, qrvry, tzug, ufyeb, v, vhpih, vll, vvoej, x, + xhnic, xmk, xsk, z +} diff --git a/other/unused-code/formulator/src/imp/snapshots/formulator__imp__hasher__t__t0-2.snap b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__hasher__t__t0-2.snap new file mode 100644 index 0000000..dfa5573 --- /dev/null +++ b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__hasher__t__t0-2.snap @@ -0,0 +1,21 @@ +--- +source: formulator/src/imp/hasher.rs +expression: s +--- +Hash values: [ + [ 0x5be76e5a07d86663, 0x5be76e5a07d86663, 0x5be76e5a07d86663, 0x5be76e5a07d86663 ], + [ 0xae4c00af7fb28e9c, 0xae4c00af7fb28e9c, 0xae4c00af7fb28e9c, 0xae4c00af7fb28e9c ], + [ 0xffcc7264fa855787, 0xffcc7264fa855787, 0xffcc7264fa855787, 0xffcc7264fa855787 ], + [ 0x56d73a7ab029dd36, 0x56d73a7ab029dd36, 0x56d73a7ab029dd36, 0x56d73a7ab029dd36 ], + [ 0xbbe163b5f5c0b78f, 0xbbe163b5f5c0b78f, 0xbbe163b5f5c0b78f, 0xbbe163b5f5c0b78f ], + [ 0x60d9576bcd3a7776, 0x60d9576bcd3a7776, 0x60d9576bcd3a7776, 0x60d9576bcd3a7776 ], + [ 0x256ded8e330a158c, 0x256ded8e330a158c, 0x256ded8e330a158c, 0x256ded8e330a158c ], + [ 0xe598c70d03a01f73, 0xe598c70d03a01f73, 0xe598c70d03a01f73, 0xe598c70d03a01f73 ], + [ 0x21f0b164a9e28884, 0x21f0b164a9e28884, 0x21f0b164a9e28884, 0x21f0b164a9e28884 ], + [ 0x8a1f5a12e093ecf8, 0x8a1f5a12e093ecf8, 0x8a1f5a12e093ecf8, 0x8a1f5a12e093ecf8 ], + [ 0x05f5ded8bd57dd11, 0x05f5ded8bd57dd11, 0x05f5ded8bd57dd11, 0x05f5ded8bd57dd11 ], + [ 0x723c7ea7144dcde0, 0x723c7ea7144dcde0, 0x723c7ea7144dcde0, 0x723c7ea7144dcde0 ], + [ 0x8a1265e1b9f503f9, 0x8a1265e1b9f503f9, 0x8a1265e1b9f503f9, 0x8a1265e1b9f503f9 ], + [ 0xd6f4083ff1c00663, 0xd6f4083ff1c00663, 0xd6f4083ff1c00663, 0xd6f4083ff1c00663 ], + [ 0xb8783f70c018c27f, 0xb8783f70c018c27f, 0xb8783f70c018c27f, 0xb8783f70c018c27f ], +] diff --git a/other/unused-code/formulator/src/imp/snapshots/formulator__imp__hasher__t__t0.snap b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__hasher__t__t0.snap new file mode 100644 index 0000000..c1ef546 --- /dev/null +++ b/other/unused-code/formulator/src/imp/snapshots/formulator__imp__hasher__t__t0.snap @@ -0,0 +1,21 @@ +--- +source: formulator/src/imp/hasher.rs +expression: bhxrs +--- +[ + BuildHasher_StableSipHasher128 { finish: 0xbe5074ddd1fadb52 }, + BuildHasher_StableSipHasher128 { finish: 0xf836886f9787673e }, + BuildHasher_StableSipHasher128 { finish: 0xb52407fb434d60d2 }, + BuildHasher_StableSipHasher128 { finish: 0xe215048853a288f0 }, + BuildHasher_StableSipHasher128 { finish: 0x37a9ed24f7586ac8 }, + BuildHasher_StableSipHasher128 { finish: 0x0f3f011aa629c3d9 }, + BuildHasher_StableSipHasher128 { finish: 0x96f7a891b8264d6d }, + BuildHasher_StableSipHasher128 { finish: 0x407e938cc82b60a7 }, + BuildHasher_StableSipHasher128 { finish: 0x70b5a7ab749b8e49 }, + BuildHasher_StableSipHasher128 { finish: 0xcf175d66006da462 }, + BuildHasher_StableSipHasher128 { finish: 0x2630d9663b269a8a }, + BuildHasher_StableSipHasher128 { finish: 0x81ce8ef043b745df }, + BuildHasher_StableSipHasher128 { finish: 0xcbc645d902eed406 }, + BuildHasher_StableSipHasher128 { finish: 0x1be2911d5c793bd4 }, + BuildHasher_StableSipHasher128 { finish: 0x39983bffd62bcfaf }, +] diff --git a/other/unused-code/formulator/src/imp/solution.rs b/other/unused-code/formulator/src/imp/solution.rs new file mode 100644 index 0000000..8ec2413 --- /dev/null +++ b/other/unused-code/formulator/src/imp/solution.rs @@ -0,0 +1,487 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(clippy::absurd_extreme_comparisons)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + borrow::Cow, + //cell::RefCell, + //collections::{BTreeSet, BTreeMap}, + //collections::{HashSet, HashMap}, + //io::{BufRead, Cursor}, + //iter::zip, + hash::{BuildHasher, Hash, Hasher}, + //marker::PhantomData, + //path::{Path, PathBuf}, + //sync::Arc, + //str::FromStr, + //sync::OnceLock, +}; + +//use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +use hashbrown::HashMap; +use indoc::indoc; +//use rand::{distr::Uniform, Rng, RngCore}; +//use serde::{Deserialize, Serialize}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +//use tracing::{debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, warn}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::{ + Domain, DynFnRefSymbolToCowstr, Problem, Rule, RuleCost, RuleCostSum, + error::{Error, Result}, + imp::{BuildHasher_Sym, BuildHasher_Symbol, MAX_SYMREPR, SymRepr, SymSetRepr, reserve_addl}, +}; + +//=================================================================================================| + +#[derive(Clone)] +pub struct Solution<'d: 'p, 'p, Symbol> +where + Symbol: Eq + Hash + Ord, +{ + domain: &'d Domain, + problem: &'p Problem<'d, Symbol>, + + build_hasher_sym: BuildHasher_Sym, + + symbol_to_sym: HashMap, + sym_to_symbol: HashMap, +} + +impl<'d: 'p, 'p, Symbol> Solution<'d, 'p, Symbol> +where + Symbol: Eq + Hash + Ord, +{ + /// Creates a new [`Solution`], or at least space for solving. + /// + /// - `problem` - The problem, references the [`Domain`]. + /// - `seed` - Seed data with which to initialize data structure hash function. + pub fn new(problem: &'p Problem<'d, Symbol>, seed: H) -> Self { + let domain: &'d Domain = problem.domain(); + + let build_hasher_symbol = domain.build_hasher_symbol().clone(); + + let [build_hasher_sym] = crate::imp::hasher::BuildHasher_StableSipHasher128::new_arr(seed); + + let symbol_to_sym = HashMap::with_hasher(build_hasher_symbol); + let sym_to_symbol = HashMap::with_hasher(build_hasher_sym.clone()); + + Self { + domain, + problem, + build_hasher_sym, + symbol_to_sym, + sym_to_symbol, + } + } + + /// Hashes a [`SymRepr`]. + pub(crate) fn hash_sym(&self, sym: SymRepr) -> u64 { + self.build_hasher_sym.hash_one(sym) + } + + /// Attempts to convert a [`T: TryInto`](std::convert::TryInto) to a [`SymRepr`]. + pub fn sym_try_from(src: T) -> Option + where + T: TryInto, + { + #[allow(clippy::absurd_extreme_comparisons)] + >::try_into(src) + .ok() + .filter(|&sym| sym <= MAX_SYMREPR) + } + + /// Returns the total number of syms that have been mapped from Symbols. + pub fn cnt_syms(&self) -> usize { + let cnt = self.symbol_to_sym.len(); + debug_assert_eq!(cnt, self.sym_to_symbol.len()); + cnt + } + + /* + pub fn successors(&self, ss_from: SymSet) -> impl Iterator + use<> { + let ss_from2 = &ss_from; + let mut filter_map_rule = &mut move |rule: &Rule| { + let ss_from3 = ss_from2; + let ss_rule_requires_and_ss_from = rule.requires.clone() & ss_from3; + (&rule.requires == &ss_rule_requires_and_ss_from) + .then(|| (ss_from.clone() | &rule.produces, rule.cost)) + }; + self.rules.iter().filter_map(filter_map_rule) + } + // */ +} + +impl<'d: 'p, 'p, Symbol> Solution<'d, 'p, Symbol> +where + Symbol: Eq + Hash + Ord + Clone, +{ + /// Gets the [`SymRepr`] for a [`Symbol`], or assigns a new one if it does not exist. + /// + /// You probably don't want to call this. Let the system manage the creation of syms. + pub fn get_or_assign_sym_repr<'s>(&mut self, symbol: &'s Symbol) -> Result { + use hashbrown::hash_map::{ + RawEntryBuilderMut, + RawEntryMut::{self, *}, + }; + + // insert into symbols + let (opt_symbol_entry, symbol_hash_u64): (Option<&'d Symbol>, u64) = + self.domain.get_existing_symbol_hashvalue(symbol); + + // Ensure we have some additional capacity in advance, whether we need it or not. + reserve_addl(self.symbol_to_sym.capacity(), |addl| { + self.symbol_to_sym.try_reserve(addl) + })?; + reserve_addl(self.sym_to_symbol.capacity(), |addl| { + self.sym_to_symbol.try_reserve(addl) + })?; + + // Insert or lookup into `symbol_to_sym`. + let (is_new_sym, sym) = { + let is_symbol_match = |other: &Symbol| *symbol == *other; + + let symbol_to_sym_len = self.symbol_to_sym.len(); + + let symbol_to_sym_raw_entry_mut = self + .symbol_to_sym + .raw_entry_mut() + .from_hash(symbol_hash_u64, is_symbol_match); + + match symbol_to_sym_raw_entry_mut { + Occupied(entry) => (false, *entry.get()), + Vacant(entry) => { + // pick the next sym value + let Some(sym) = Self::sym_try_from(symbol_to_sym_len) else { + let opt_symbol_str = self.domain.try_symbol_to_str(symbol); + return Err(Error::SymbolSetFull { + opt_symbol_str, + current_number: self.cnt_syms(), + }); + }; + + entry.insert_hashed_nocheck(symbol_hash_u64, symbol.clone(), sym); + + (true, sym) + } + } + }; + + // Insert into `sym_to_symbol`. + if is_new_sym { + let sym_hash_u64 = self.hash_sym(sym); + + let is_sym_match = |other: &SymRepr| sym == *other; + + let sym_to_symbol_raw_entry_mut = self + .sym_to_symbol + .raw_entry_mut() + .from_hash(sym_hash_u64, is_sym_match); + + match sym_to_symbol_raw_entry_mut { + Occupied(entry) => { + debug_assert!(entry.get() == symbol); + } + Vacant(entry) => { + entry.insert_hashed_nocheck(sym_hash_u64, sym, symbol.clone()); + } + } + } else { + debug_assert!(self.sym_to_symbol.contains_key(&sym)); + } + + Ok(sym) + } +} + +impl<'d, 'p, Symbol> std::fmt::Debug for Solution<'d, 'p, Symbol> +where + Symbol: Eq + Hash + Ord, +{ + /// Format the value suitable for debugging output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /* + writeln!(f, " }},\n symbol_to_sym: {{")?; + + for (symbol, sym) in self.symbol_to_sym.iter() { + writeln!(f, " {symbol:?} -> {sym:?},")?; + } + + writeln!(f, " }},\n sym_to_symbol: {{")?; + + for (sym, symbol) in self.sym_to_symbol.iter() { + writeln!(f, " {sym:?} -> {symbol:?},")?; + } + + writeln!(f, " }},\n rules: [")?; + + for (ix, r) in self.rules.iter().enumerate() { + writeln!(f, " {ix:4}: {r:?},")?; + } + write!(f, " ],\n}}") + // */ + + write!(f, "Solution") //? TODO + } +} + +impl<'d, 'p, Symbol> std::fmt::Display for Solution<'d, 'p, Symbol> +where + Symbol: Eq + Hash + Ord, +{ + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Solution") //? TODO + } +} + +/* +impl proptest::arbitrary::Arbitrary for Solution { + type Parameters = (); + type Strategy = proptest_arbitrary_interop::ArbStrategy; + + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + proptest_arbitrary_interop::arb() + } +} +// */ + +//=================================================================================================| + +/* +pub fn find_solutions(pr: &Problem) -> impl IntoIterator { + + let starting_set = pr.starting_set.clone(); + let pr_finishing_set = pr.finishing_set.clone(); + + let mut success = |ss: &SymSet| ss.contains_all_of(&pr_finishing_set); + + /* + let mut heuristic = |_ss_from: &SymSet| { + // "must not be greater than the real cost, or a wrong shortest path may be returned" + 0 + }; + + let opt_v_c = pathfinding::directed::fringe::fringe( + &start, + successors, + heuristic, + success); + // */ + + pub struct SuccessorsIter<'a> { + ss_from: SymSet, + rules: &'a Vec, + next_rule_ix: usize, + } + + impl SuccessorsIter<'_> { + pub fn new<'a>(rules: &'a Vec, ss_from: SymSet) -> SuccessorsIter<'a> { + SuccessorsIter { + rules, + ss_from, + next_rule_ix: 0, + } + } + } + + impl<'a> Iterator for SuccessorsIter<'a> { + type Item = (SymSet, i64); + + fn next(&mut self) -> Option { + while self.next_rule_ix < self.rules.len() { + let this_rule_ix = self.next_rule_ix; + self.next_rule_ix += 1; + + let this_rule = &self.rules[this_rule_ix]; + if self.ss_from.contains_all_of(&this_rule.requires) { + let node = self.ss_from.clone() | &this_rule.produces; + let cost: i64 = this_rule.cost; + //println!("{} -> {} for {}", &this_rule.requires, &node, cost); + return Some((node, cost)); + } + } + None + } + } + + let successors = |ss_from: &SymSet| { + SuccessorsIter::new(&pr.rules, ss_from.clone()) + }; + + use pathfinding::directed::dijkstra::dijkstra; + let opt_v_c = dijkstra(&starting_set, successors, success); + + let mut opt_solution: Option = None; + + if let Some((path, cost)) = opt_v_c { + let so = Solution { path, cost }; + let _ = opt_solution.insert(so); + } + + opt_solution.into_iter() +} +// */ + +//=================================================================================================| + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(unknown_lints, clippy::literal_string_with_formatting_args)] +mod t { + use proptest::prelude::*; + + use super::*; + /* + proptest! { + //#![proptest_config(ProptestConfig::with_cases(10))] + #[test] + fn t_find_solutions(pr: Problem) { + //println!("vvvvvvvvvvvvvvvvvvvvvvv proptest find_solutions vvvvvvvvvvvvvvvvvvvvvvv"); + //println!("Problem: {pr:?}"); + let _v_so: Vec = find_solutions(&pr).into_iter().collect::>(); + //if !_v_so.is_empty() { + // eprintln_problem_solutions(&pr, v_so.as_slice()); + //} + //println!("^^^^^^^^^^^^^^^^^^^^^^^ proptest find_solutions ^^^^^^^^^^^^^^^^^^^^^^^"); + } + } + + #[test] + #[rustfmt::skip] + fn t() { + let a = [ + ( "{}", "{a}", Some( 10 )), + ( "{a}", "{a,b}", Some( 100 )), + ( "{}", "{a,b}", Some( 110 )), + ( "{a,b}", "{a,b}", Some( 0 )), + ( "{a,b}", "{a}", Some( 0 )), + ( "{a,b}", "{b}", Some( 0 )), + ( "{a,b}", "{}", Some( 0 )), + ( "{}", "{a,b,c}", Some( 1110 )), + ( "{}", "{b,c}", Some( 1110 )), + ( "{a,b,c}", "{}", Some( 0 )), + ( "{a,b,c}", "{z}", None ), + ( "{c}", "{a,b,c}", Some( 10 )), + ( "{}", "{d}", Some( 11110 )), + ( "{d}", "{e}", Some( 10 )), + ( "{d}", "{f}", Some( 20 )), + ( "{d}", "{g}", Some( 30 )), + ( "{d}", "{h}", Some( 40 )), + ( "{d}", "{i}", Some( 50 )), + ( "{d}", "{j}", Some( 11 )), // d -> abcd -> abcdj + ]; + for (ss, fs, opt_c) in a { + test_from_to(ss, fs, opt_c); + } + } + + #[rustfmt::skip] + fn test_from_to(starting_set: &str, finishing_set: &str, opt_expected_cost: Option) { + let pr = Problem { + starting_set: starting_set.parse().unwrap(), + finishing_set: finishing_set.parse().unwrap(), + rules: [ + // requires, produces, cost + Rule::new( "{}", "{a}", 10 ), + Rule::new( "{a}", "{b}", 100 ), + Rule::new( "{a,b}", "{c}", 1000 ), + Rule::new( "{c}", "{a,b,c}", 10 ), + Rule::new( "{c}", "{b,d}", 10000 ), + Rule::new( "{d}", "{a,b,c}", 1 ), + Rule::new( "{d}", "{e}", 10 ), + Rule::new( "{e}", "{f}", 10 ), + Rule::new( "{f}", "{g}", 10 ), + Rule::new( "{g}", "{h}", 10 ), + Rule::new( "{h}", "{i}", 10 ), + Rule::new( "{i}", "{j}", 10 ), + Rule::new( "{a}", "{j}", 10 ), + ].into_iter() + .map(Result::unwrap) + .collect(), + }; + + let v_so: Vec = find_solutions(&pr).into_iter().collect::>(); + + let mut success = false; + if let Some(expected_cost) = opt_expected_cost { + if v_so.len() == 1 { + let so = &v_so[0]; + success = so.cost == expected_cost; + } + } else { + success = v_so.is_empty(); + } + + if !success { + println!(); + eprintln_problem_solutions(&pr, v_so.as_slice()); + assert!(success); + } + //else { println!(""); eprintln_problem_solutions(&pr, v_so.as_slice()); } + } + + fn eprintln_problem_solutions(pr: &Problem, solutions: &[Solution]) { + println!("vvvvvvvvvvvvvvvvvvvvvvv problem, solutions vvvvvvvvvvvvvvvvvvvvvvv"); + println!("Problem: {pr:?}"); + + if solutions.is_empty() { + println!("Found {} solutions.", solutions.len()); + } else { + if solutions.len() == 1 { + println!("Found {} solution:", solutions.len()); + } else { + println!("Found {} solutions:", solutions.len()); + } + for solution in solutions { + for line in format!("{solution:#?}").lines() { + println!(" {line}"); + } + } + } + println!("^^^^^^^^^^^^^^^^^^^^^^^ problem, solutions ^^^^^^^^^^^^^^^^^^^^^^^"); + } + // */ +} diff --git a/other/unused-code/formulator/src/imp/sym.rs b/other/unused-code/formulator/src/imp/sym.rs new file mode 100644 index 0000000..380b16e --- /dev/null +++ b/other/unused-code/formulator/src/imp/sym.rs @@ -0,0 +1,347 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +use std::ops::RangeInclusive; + +use static_assertions::{assert_cfg, assert_impl_all, assert_obj_safe, const_assert}; + +use crate::{ + error::{Error, Result}, + imp::sym_set::SymSet, +}; + +//=================================================================================================| + +/// The representation type for [`Sym`] as a zero-based ordinal. +pub type Sym_ReprType = u16; + +/// A symbol. Currently limited to one or two lowercase letters `[a-z]{1,2}`. +#[derive(Copy, Clone, Hash, PartialEq, Ord, Eq, PartialOrd)] +pub struct Sym(Sym_ReprType); + +impl Sym { + /// The total number of values. + pub(crate) const CNT_TOTAL_VALUES: usize = 27 * 26; + + /// The minimum zero-based ordinal. + pub(crate) const ZBO_MIN: Sym_ReprType = 0; + + /// The maximum zero-based ordinal. + pub(crate) const ZBO_MAX: Sym_ReprType = (Self::CNT_TOTAL_VALUES - 1) as Sym_ReprType; + + /// The inclusive range of the zero-based ordinal. + pub(crate) const ZBO_RANGEINCLUSIVE: RangeInclusive = + Self::ZBO_MIN..=Self::ZBO_MAX; + + /// The inclusive range of the zero-based ordinal. + pub(crate) fn is_valid_zbo(val: T) -> bool + where + T: TryInto, + { + #[allow(clippy::manual_range_contains, clippy::absurd_extreme_comparisons)] + if let Ok(repr) = TryInto::::try_into(val) { + Self::ZBO_MIN <= repr && repr <= Self::ZBO_MAX + } else { + false + } + } + + pub(crate) const fn zbo_rangeinclusive() -> RangeInclusive { + Self::ZBO_RANGEINCLUSIVE + } + + //pub const fn cnt_total_values() -> usize { + // Self::CNT_TOTAL_VALUES + //} + + //pub(crate) fn all_zbos() -> impl Iterator { + // Self::ZBO_RANGEINCLUSIVE + //} + + pub fn all_values() -> impl Iterator { + Self::ZBO_RANGEINCLUSIVE.map(Self::from_zbo_be_careful) + } + + #[allow(clippy::unwrap_used)] + pub(crate) fn zbo_to_string_unwrap(zbo: Sym_ReprType) -> String { + Self::zbo_tryinto_string(zbo).unwrap() + } + + //#[allow(clippy::manual_range_contains)] + fn zbo_tryinto_string(zbo: Sym_ReprType) -> Option { + match zbo { + a @ 0..26 => { + let mut ach: [char; 1] = [intou32_to_radix26_char_be_careful(a)]; + Some(ach.iter().collect()) + } + a @ 26..=Self::ZBO_MAX => { + let (b, c) = (a / 26, a % 26); + assert!((1..=26).contains(&b)); // first char of multiple is radix 27 + let b = b - 1; + let mut ach: [char; 2] = [ + intou32_to_radix26_char_be_careful(b), + intou32_to_radix26_char_be_careful(c), + ]; + Some(ach.iter().collect()) + } + _ => None, + } + } + + #[allow(clippy::absurd_extreme_comparisons)] + pub(crate) const fn from_zbo_unwrap(repr: Sym_ReprType) -> Self { + assert!(Self::ZBO_MIN <= repr && repr <= Self::ZBO_MAX); + Self(repr) + } + + #[allow(clippy::absurd_extreme_comparisons)] + pub(crate) const fn from_zbo_be_careful(zbo: Sym_ReprType) -> Self { + debug_assert!(Self::ZBO_MIN <= zbo && zbo <= Self::ZBO_MAX); + Self(zbo) + } + + #[allow(clippy::absurd_extreme_comparisons)] + const fn try_from_zbo(zbo: Sym_ReprType) -> Option { + if Self::ZBO_MIN <= zbo && zbo <= Self::ZBO_MAX { + Some(Self(zbo)) + } else { + None + } + } + + pub(crate) fn try_from_tryinto_repr_zbo(val: T) -> Option + where + T: TryInto, + { + TryInto::::try_into(val) + .ok() + .and_then(Self::try_from_zbo) + } + + #[inline] + pub(crate) const fn into_zbo(self) -> Sym_ReprType { + self.0 + } + + pub fn predecessor(&self) -> Option { + (Self::ZBO_MIN < self.0).then(|| Self::from_zbo_be_careful(self.0 - 1)) + } + + pub fn successor(&self) -> Option { + (self.0 < Self::ZBO_MAX).then(|| Self::from_zbo_be_careful(self.0 + 1)) + } +} + +const_assert!(Sym::CNT_TOTAL_VALUES as u64 - 1 <= Sym_ReprType::MAX as u64); +//-------------------------------------------------------------------------------------------------| +impl std::fmt::Debug for Sym { + /// Format the value suitable for debugging output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +impl std::fmt::Display for Sym { + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use std::fmt::Write; + f.write_str(&Self::zbo_to_string_unwrap(self.0)) + } +} +//-------------------------------------------------------------------------------------------------| +impl serde::Serialize for Sym { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::ser::Serializer, + { + self.to_string().serialize(serializer) + } +} + +impl<'de> serde::Deserialize<'de> for Sym { + fn deserialize>(deserializer: D) -> std::result::Result { + struct SymVisitor; + impl serde::de::Visitor<'_> for SymVisitor { + type Value = Sym; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("A Sym is one or two lowercase letters") + } + fn visit_str(self, value: &str) -> std::result::Result { + value.parse::() + .map_err(|_e| serde::de::Error::custom("not a valid Sym")) + } + } + deserializer.deserialize_any(SymVisitor) + } +} +//-------------------------------------------------------------------------------------------------| +impl arbitrary::Arbitrary<'_> for Sym { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + let zbo: Sym_ReprType = u.int_in_range(Self::ZBO_RANGEINCLUSIVE)?; + let ss = Self::from_zbo_be_careful(zbo); + Ok(ss) + } +} + +impl proptest::arbitrary::Arbitrary for Sym { + type Parameters = (); + /* + type Strategy = + proptest::strategy::Map, fn(char) -> Self>; + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + let prop_char_range = proptest::char::range('a', 'z'); + proptest::strategy::Strategy::prop_map(prop_char_range, |ch| Sym::unwrap_from_ascii_u8(ch as u8)) + } + // */ + type Strategy = proptest_arbitrary_interop::ArbStrategy; + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + proptest_arbitrary_interop::arb() + } +} +//-------------------------------------------------------------------------------------------------| +impl std::str::FromStr for Sym { + type Err = Error; + + /// Attempts to parse a string `s` to return a [`Sym`]. + fn from_str(s: &str) -> std::result::Result { + let mut repr: Sym_ReprType = 0; + let mut have_at_least_one_char = false; + for (digit_ix, ch) in s.chars().enumerate() { + let radix26 = (ch as u32).wrapping_sub('a' as u32); + if 26 <= radix26 { + return Err(Error::ParseSym); + } else { + have_at_least_one_char = true; + } + + // Surprising: if there are multiple digits, the first digit is actually radix-27, + // because we don't allow leading '0's, so 'a-z' represent '1-26'. + if digit_ix == 1 { + repr += 1; + } + + repr = repr.checked_mul(26).ok_or(Error::ParseSym)?; + repr = repr + .checked_add(radix26 as Sym_ReprType) + .ok_or(Error::ParseSym)?; + } + + if !have_at_least_one_char { + return Err(Error::ParseSym); + } + + Self::try_from_zbo(repr).ok_or(Error::ParseSym) + } +} +//-------------------------------------------------------------------------------------------------| +impl> std::ops::BitAnd for Sym { + type Output = SymSet; + + #[inline] + fn bitand(self, rhs: T) -> Self::Output { + SymSet::from(self) & rhs + } +} +//-------------------------------------------------------------------------------------------------| +impl> std::ops::BitOr for Sym { + type Output = SymSet; + + #[inline] + fn bitor(self, rhs: T) -> Self::Output { + SymSet::from(self) | rhs + } +} +//-------------------------------------------------------------------------------------------------| +fn tryinto_radix26_char>(u: T) -> Option { + if let Ok(u) = TryInto::::try_into(u) { + if u < 26 { + return Some(u32_to_radix26_char_be_careful(u)); + } + } + None +} +//-------------------------------------------------------------------------------------------------| +fn intou32_to_radix26_char_be_careful>(u: T) -> char { + let u: u32 = u.into(); + u32_to_radix26_char_be_careful(u) +} +//-------------------------------------------------------------------------------------------------| +fn u32_to_radix26_char_be_careful(u: u32) -> char { + debug_assert!(u < 26); + char::from(('a' as u32 + u) as u8) +} +//=================================================================================================| + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod t { + //use anyhow::{anyhow, bail, ensure, Context, Result}; + //?use insta::assert_ron_snapshot; + use proptest::prelude::*; + use proptest_arbitrary_interop::arb; + + use super::*; + + // [`Sym`] tests + proptest! { + #[test] + fn sym_valid_val(s in "[a-z]") { + let sym: Sym = s.parse().unwrap(); + prop_assert_eq!(sym.to_string(), s); + } + + #[test] + fn sym_invalid_val(s in "[^a-z]") { + prop_assert!(s.parse::().is_err()); + } + + #[test] + fn sym_arbitrary(sym: Sym) { + prop_assert!(Sym::all_values().any(|s| s == sym)); + let str2 = sym.to_string(); + let sym2: Sym = str2.parse().unwrap(); + prop_assert_eq!(sym, sym2); + let str3 = sym2.to_string(); + prop_assert_eq!(str3, str2); + } + } +} diff --git a/other/unused-code/formulator/src/imp/sym_set.rs b/other/unused-code/formulator/src/imp/sym_set.rs new file mode 100644 index 0000000..bbe3822 --- /dev/null +++ b/other/unused-code/formulator/src/imp/sym_set.rs @@ -0,0 +1,586 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +use arbitrary::unstructured::ArbitraryIter; +//use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +//use rand::{distr::Uniform, Rng, RngCore}; +use smallbitvec::SmallBitVec; +use static_assertions::{assert_cfg, assert_impl_all, assert_obj_safe, const_assert}; +//use tracing::{debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, warn}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::{ + error::{Error, Result}, + imp::sym::{Sym, Sym_ReprType}, +}; + +//=================================================================================================| + +//pub type SymSet_ReprType = u32; +//const_assert!(Sym::CNT_TOTAL_VALUES <= size_of::()*8); + +pub type SymSet_ReprType = SmallBitVec; + +#[derive(Clone, Default, Hash, PartialEq, Eq)] +pub struct SymSet(SymSet_ReprType); + +impl SymSet { + pub(crate) const SYM_ZBO_RANGEINCLUSIVE: std::ops::RangeInclusive = + Sym::ZBO_RANGEINCLUSIVE; + + /* + pub(crate) const MIN_REPR: SymSet_ReprType = 0; + pub(crate) const MAX_REPR: SymSet_ReprType = ((1u64 << Sym::CNT_TOTAL_VALUES) - 1) as SymSet_ReprType; + pub(crate) const RANGEINCLUSIVE_REPR: std::ops::RangeInclusive = Self::MIN_REPR..=Self::MAX_REPR; + // */ + + pub const fn new() -> Self { + Self(SymSet_ReprType::new()) + } + + /* + const fn from_repr_unchecked(u: SymSet_ReprType) -> Self { + debug_assert!(u <= Self::MAX_REPR); + Self(u) + } + + const fn try_from_repr(u: SymSet_ReprType) -> Option { + if u <= Self::MAX_REPR { + Some(Self(u)) + } else { + None + } + } + + fn try_from_tryinto_repr(val: T) -> Option + where + T: TryInto + ?Sized, + { + TryInto::::try_into(val) + .ok() + .and_then(Self::try_from_repr) + } + + #[inline] + pub const fn into_repr(self) -> SymSet_ReprType { + self.0 + } + // */ + + pub fn contains(&self, sym: Sym) -> bool { + self.contains_sym_by_zbo(sym.into_zbo()) + } + + pub(crate) fn contains_sym_by_zbo(&self, sym_zbo: Sym_ReprType) -> bool { + self.0.get(sym_zbo as usize).unwrap_or(false) + } + + pub fn is_empty(&self) -> bool { + self.0.all_false() + } + + pub fn len(&self) -> usize { + self.0.iter().filter(|b| *b).count() + } + + pub fn contains_all_of(&self, other: &SymSet) -> bool { + for (ix, other_b) in other.0.iter().enumerate() { + if other_b { + let this_b = self.0.get(ix).unwrap_or(false); + if other_b && !this_b { + return false; + } + } + } + true + } + + pub fn count_set(&self) -> usize { + self.0.iter().filter(|b| *b).count() + } + + #[inline] + pub fn iter(&self) -> SymSet_Iterator<'_> { + let cnt_remain = self.count_set(); + SymSet_Iterator { + sbv: &self.0, + next_ix: 0, + cnt_remain, + } + } + + fn get_at_zbo(&self, zbo: usize) -> bool { + self.0.get(zbo).unwrap_or(false) + } + + fn set_at_zbo(&mut self, zbo: usize) { + let cur_sbv_len = self.0.len(); + if cur_sbv_len <= zbo { + if cur_sbv_len < zbo { + self.0.resize(zbo, false); + } + self.0.push(true); + } else { + // We just checked the len + unsafe { self.0.set_unchecked(zbo, true) } + } + } +} +//-------------------------------------------------------------------------------------------------| + +#[allow(non_camel_case_types)] +pub struct SymSet_Iterator<'a> { + sbv: &'a SmallBitVec, + next_ix: usize, + cnt_remain: usize, +} + +impl<'a> Iterator for SymSet_Iterator<'a> { + type Item = Sym; + fn next(&mut self) -> Option { + while self.cnt_remain != 0 && self.next_ix < self.sbv.len() { + let this_ix = self.next_ix; + self.next_ix += 1; + if unsafe { self.sbv.get_unchecked(this_ix) } { + debug_assert!(0 < self.cnt_remain); + self.cnt_remain -= 1; + return Some(Sym::from_zbo_be_careful(this_ix as Sym_ReprType)); + } + } + None + } + fn size_hint(&self) -> (usize, Option) { + (self.cnt_remain, Some(self.cnt_remain)) + } +} + +impl<'a> std::iter::ExactSizeIterator for SymSet_Iterator<'a> {} +//-------------------------------------------------------------------------------------------------| +impl IntoIterator for SymSet { + type Item = Sym; + type IntoIter = SymSet_IntoIterator; + fn into_iter(self) -> SymSet_IntoIterator { + let cnt_remain = self.count_set(); + let Self(sbv) = self; + SymSet_IntoIterator { + sbv, + next_ix: 0, + cnt_remain, + } + } +} + +#[allow(non_camel_case_types)] +pub struct SymSet_IntoIterator { + sbv: SmallBitVec, + next_ix: usize, + cnt_remain: usize, +} + +impl Iterator for SymSet_IntoIterator { + type Item = Sym; + fn next(&mut self) -> Option { + while self.cnt_remain != 0 && self.next_ix < self.sbv.len() { + let this_ix = self.next_ix; + self.next_ix += 1; + if unsafe { self.sbv.get_unchecked(this_ix) } { + debug_assert!(0 < self.cnt_remain); + self.cnt_remain -= 1; + return Some(Sym::from_zbo_be_careful(this_ix as Sym_ReprType)); + } + } + None + } + fn size_hint(&self) -> (usize, Option) { + (self.cnt_remain, Some(self.cnt_remain)) + } +} + +impl std::iter::ExactSizeIterator for SymSet_IntoIterator {} +//-------------------------------------------------------------------------------------------------| +impl std::fmt::Debug for SymSet { + /// Format the value suitable for debugging output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +impl std::fmt::Display for SymSet { + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use std::fmt::Write; + f.write_char('{')?; + let mut sym_iter = self.iter(); + if let Some(sym) = sym_iter.next() { + std::fmt::Display::fmt(&sym, f)?; + //#[allow(clippy::while_let_on_iterator)] + //while let Some(sym) = sym_iter.next() + for sym in sym_iter { + f.write_char(',')?; + std::fmt::Display::fmt(&sym, f)?; + } + } + f.write_char('}') + } +} +//-------------------------------------------------------------------------------------------------| +impl serde::Serialize for SymSet { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::ser::Serializer, + { + self.to_string().serialize(serializer) + } +} + +impl<'de> serde::Deserialize<'de> for SymSet { + fn deserialize>(deserializer: D) -> std::result::Result { + struct SymVisitor; + impl serde::de::Visitor<'_> for SymVisitor { + type Value = SymSet; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("A SymSet is one or two lowercase letters") + } + fn visit_str(self, value: &str) -> std::result::Result { + value.parse::() + .map_err(|_e| serde::de::Error::custom("not a valid SymSet")) + } + } + deserializer.deserialize_any(SymVisitor) + } +} +//-------------------------------------------------------------------------------------------------| +impl arbitrary::Arbitrary<'_> for SymSet { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + let ss: SymSet = u.arbitrary_iter::()?.filter_map(|r| r.ok()).collect(); + Ok(ss) + } + + fn arbitrary_take_rest(u: arbitrary::Unstructured<'_>) -> arbitrary::Result { + let ss: SymSet = u + .arbitrary_take_rest_iter::()? + .filter_map(|r| r.ok()) + .collect(); + Ok(ss) + } + + #[inline] + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) + } +} + +impl proptest::arbitrary::Arbitrary for SymSet { + type Parameters = (); + type Strategy = proptest_arbitrary_interop::ArbStrategy; + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + proptest_arbitrary_interop::arb() + } +} +//-------------------------------------------------------------------------------------------------| +impl From for SymSet { + /// A [`SymSet`] can always be made from a [`Sym`]. + #[inline] + fn from(sym: Sym) -> Self { + let ix = sym.into_zbo() as usize; + let mut sbv = SmallBitVec::with_capacity(ix + 1); + sbv.resize(ix, false); + sbv.push(true); + Self(sbv) + } +} + +impl From<&Sym> for SymSet { + /// A [`SymSet`] can always be made from a [`&Sym`]. + #[inline] + fn from(sym: &Sym) -> Self { + Self::from(*sym) + } +} +//-------------------------------------------------------------------------------------------------| +impl std::iter::FromIterator for SymSet { + fn from_iter>(ii: II) -> Self { + ii.into_iter().fold(SymSet::new(), |ss, s| ss | s) + } +} + +impl<'a> std::iter::FromIterator<&'a Sym> for SymSet { + fn from_iter>(ii: II) -> Self { + ii.into_iter().copied().collect() + } +} +//-------------------------------------------------------------------------------------------------| +/* +// An error which can be returned when parsing a [`SymSet`]. +#[derive(Clone, Copy, Debug)] +pub struct SymSetParseError; + +impl std::fmt::Display for SymSetParseError { + /// Format the value suitable for user-facing output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } +} +// */ + +impl std::str::FromStr for SymSet { + type Err = Error; + + /// Attempts to parse a string `s` to return a [`SymSet`]. + fn from_str(s: &str) -> std::result::Result { + let mut e = false; + let opt_ss = s + .strip_prefix('{') + .and_then(|s| s.strip_suffix('}')) + .map(|s| { + //println!("symset::from_str splitting: {:?}", s); + s.split(',') + .filter_map(|s| { + //println!("symset::from_str split: {:?}", s); + if s.is_empty() { + // This happens with "{}" + None + } else { + let opt_sym = Sym::from_str(s).ok(); + e |= opt_sym.is_none(); + opt_sym + } + }) + .collect::() + }); + if e { None } else { opt_ss }.ok_or(Error::ParseSymSet) + } +} +//-------------------------------------------------------------------------------------------------| +impl std::ops::BitAndAssign for SymSet { + #[allow(clippy::suspicious_op_assign_impl)] + fn bitand_assign(&mut self, rhs: Sym) { + let mut ss = SymSet::new(); + if self.contains(rhs) { + ss |= rhs; + } + std::mem::swap(self, &mut ss); + } +} + +impl std::ops::BitAndAssign<&SymSet> for SymSet { + fn bitand_assign(&mut self, rhs: &SymSet) { + let mut rhs_iter = rhs.0.iter(); + if rhs.0.len() < self.0.len() { + self.0.truncate(rhs.0.len()); + } + let sbv_len = self.0.len(); + unsafe { + for ix in 0..sbv_len { + if self.0.get_unchecked(ix) && !rhs.0.get_unchecked(ix) { + self.0.set_unchecked(ix, false) + } + } + } + } +} + +impl std::ops::BitAnd<&SymSet> for SymSet { + type Output = SymSet; + #[inline] + fn bitand(mut self, rhs: &SymSet) -> Self::Output { + self &= rhs; + self + } +} + +impl> std::ops::BitAnd for SymSet { + type Output = SymSet; + #[inline] + fn bitand(mut self, rhs: T) -> Self::Output { + self &= &Into::::into(rhs); + self + } +} +//-------------------------------------------------------------------------------------------------| +impl std::ops::BitOrAssign for SymSet { + fn bitor_assign(&mut self, rhs: Sym) { + let rhs_zbo = rhs.into_zbo() as usize; + self.set_at_zbo(rhs_zbo) + } +} + +//impl std::ops::BitOrAssign for SymSet { +// fn bitor_assign(&mut self, rhs: SymSet) { +// std::ops::BitOrAssign::<&SymSet>::bitor_assign(self, &rhs) +// } +//} + +impl std::ops::BitOrAssign<&SymSet> for SymSet { + fn bitor_assign(&mut self, rhs: &SymSet) { + for sym in rhs.iter() { + self.bitor_assign(sym) + } + } +} + +impl std::ops::BitOr<&SymSet> for SymSet { + type Output = SymSet; + #[inline] + fn bitor(mut self, rhs: &SymSet) -> Self::Output { + self |= rhs; + self + } +} + +impl> std::ops::BitOr for SymSet { + type Output = SymSet; + #[inline] + fn bitor(mut self, rhs: T) -> Self::Output { + self |= &Into::::into(rhs); + self + } +} +//=================================================================================================| +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod t { + //use anyhow::{anyhow, bail, ensure, Context, Result}; + //?use insta::assert_ron_snapshot; + use proptest::prelude::*; + use proptest_arbitrary_interop::arb; + + use super::*; + + #[test] + fn t0() { + let str2 = "{}".to_string(); + let ss2: SymSet = str2.parse().unwrap(); + let str3 = ss2.to_string(); + assert_eq!(str3, str2); + } + + #[test] + fn t1() { + let sym_a: Sym = "a".parse().unwrap(); + let sym_b: Sym = "b".parse().unwrap(); + let sym_c: Sym = "c".parse().unwrap(); + + let ss = SymSet::new(); + assert_eq!(ss.to_string().as_str(), "{}"); + let ss_a = ss.clone() | sym_a; + let ss_b = ss.clone() | sym_b; + let ss_c = ss.clone() | sym_c; + assert_eq!(ss_a.to_string().as_str(), "{a}"); + let ss_ab = ss_a.clone() | sym_b; + let ss_ac = ss_a.clone() | sym_c; + assert_eq!(ss_ab.to_string().as_str(), "{a,b}"); + let ss_bc = sym_b | sym_c; + assert_eq!(ss_bc.to_string().as_str(), "{b,c}"); + let ss_abc = ss_a.clone() | &ss_bc; + assert_eq!(ss_abc.to_string().as_str(), "{a,b,c}"); + + let ss_ab_and_bc = ss_ab.clone() & &ss_bc; + assert_eq!(ss_ab_and_bc.to_string().as_str(), "{b}"); + + assert!(ss_a.contains_all_of(&ss)); + assert!(ss_ab.contains_all_of(&ss)); + + assert!(ss_ab.contains_all_of(&ss_a)); + assert!(ss_ab.contains_all_of(&ss_b)); + assert!(ss_ab.contains_all_of(&ss_ab)); + + assert!(ss_abc.contains_all_of(&ss_a)); + assert!(ss_abc.contains_all_of(&ss_b)); + assert!(ss_abc.contains_all_of(&ss_b)); + assert!(ss_abc.contains_all_of(&ss_ab)); + assert!(ss_abc.contains_all_of(&ss_ac)); + assert!(ss_abc.contains_all_of(&ss_bc)); + assert!(ss_abc.contains_all_of(&ss_abc)); + } + + // [`SymSet`] tests + proptest! { + #[test] + fn symset_arbitrary_sym(sym: Sym) { + let symset: SymSet = sym.into(); + } + + #[test] + fn symset_arbitrary(ss1: SymSet) { + let str1 = ss1.to_string(); + + let ss2: SymSet = str1.parse().unwrap(); + prop_assert_eq!(&ss2, &ss1); + + let str2 = ss2.to_string(); + prop_assert_eq!(str2, str1); + } + + #[test] + fn symset_bitor_op(ss1: SymSet, ss2: SymSet) { + let ss3 = ss1.clone() | &ss2; + prop_assert!(ss3.contains_all_of(&ss1)); + prop_assert!(ss3.contains_all_of(&ss2)); + } + + #[test] + fn symset_bitor_assign_op(mut ss1: SymSet, ss2: SymSet) { + let mut ss3 = ss1.clone(); + ss3 |= &ss2; + prop_assert!(ss3.contains_all_of(&ss1)); + prop_assert!(ss3.contains_all_of(&ss2)); + } + + #[test] + fn symset_bitand_op(ss1: SymSet, ss2: SymSet) { + let ss3 = ss1.clone() & &ss2; + prop_assert!(ss1.contains_all_of(&ss3)); + prop_assert!(ss2.contains_all_of(&ss3)); + } + + #[test] + fn symset_bitand_assign_op(mut ss1: SymSet, ss2: SymSet) { + let mut ss3 = ss1.clone(); + ss3 &= &ss2; + prop_assert!(ss1.contains_all_of(&ss3)); + prop_assert!(ss2.contains_all_of(&ss3)); + } + } +} diff --git a/other/unused-code/formulator/src/lib.rs b/other/unused-code/formulator/src/lib.rs new file mode 100644 index 0000000..1b0f084 --- /dev/null +++ b/other/unused-code/formulator/src/lib.rs @@ -0,0 +1,84 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::empty_line_after_doc_comments)] //? TODO: Remove temp development code +#![allow(clippy::needless_lifetimes)] //? TODO: Remove temp development code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(non_upper_case_globals)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +mod error; +pub use error::{Error, Result}; +use serde::de::IntoDeserializer; + +pub(crate) mod imp; +pub use crate::imp::{ + domain::{ + BoxDynFnRefSymbolToCowstr, Domain, DynFnRefSymbolToCowstr, OptionBoxDynFnRefSymbolToCowstr, + }, + hasher::DefaultBuildHasher, + problem::Problem, + rule::{RULE_IX_INVALID, RULE_IX_MAX, RULES_CNT_MAX, Rule, RuleCost, RuleCostSum}, +}; + +//=================================================================================================| + +#[rustfmt::skip] //? TODO: Remove temp development code +use std::{ + borrow::Cow, + cmp::Eq, + hash::{BuildHasher, Hash, Hasher}, + //os::raw, + sync::Arc + //str::FromStr, + //sync::OnceLock, +}; + +//use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +//use hashbrown::HashMap; +//use indoc::indoc; +//use rand::{distr::Uniform, Rng, RngCore}; +//use serde::{Deserialize, Serialize}; +//use static_assertions::{assert_obj_safe, assert_impl_all, assert_cfg, const_assert}; +use tracing::{ + debug, error, field::display as trace_display, info, info_span, instrument, trace, trace_span, + warn, +}; +//use zeroize::{Zeroize, ZeroizeOnDrop}; + +//=================================================================================================| diff --git a/test/Cargo.lock b/test/Cargo.lock new file mode 100644 index 0000000..ed46e28 --- /dev/null +++ b/test/Cargo.lock @@ -0,0 +1,1441 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bench-util" +version = "0.1.0" +dependencies = [ + "anyhow", + "crypto-bigint", + "eg", + "fixed-width-nonnegative", + "num-bigint", + "rand", + "rand_pcg", + "util", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diesel" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94" +dependencies = [ + "chrono", + "diesel_derives", + "libsqlite3-sys", + "serde_json", + "time", +] + +[[package]] +name = "diesel_derives" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ff2be1e7312c858b2ef974f5c7089833ae57b5311b334b30923af58e5718d8" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" +dependencies = [ + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dsl_auto_type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "eg" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "chrono", + "digest", + "fixed-width-nonnegative", + "hex-literal", + "hmac", + "itertools 0.12.1", + "lazy_static", + "num-bigint", + "num-traits", + "rand", + "serde", + "serde_json", + "sha2", + "static_assertions", + "thiserror", + "util", +] + +[[package]] +name = "eg-artifacts-dir" +version = "0.1.0" +dependencies = [ + "anyhow", + "eg", + "util", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fixed-width-nn-bench" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "crypto-bigint", + "diesel", + "dotenvy", + "eg", + "fixed-width-nn-bench-db", + "fixed-width-nonnegative", + "log", + "logging", + "num-bigint", + "sha2", +] + +[[package]] +name = "fixed-width-nn-bench-db" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "diesel", + "log", + "logging", + "vcpkg", +] + +[[package]] +name = "fixed-width-nonnegative" +version = "0.1.0" +dependencies = [ + "anyhow", + "base16ct", + "bytemuck", + "cfg-if", + "cfg_aliases", + "crypto-bigint", + "either", + "hacl-rs", + "inventory", + "log", + "num-bigint", + "num-integer", + "num-traits", + "paste", + "prettyplease", + "proc-macro2", + "quote", + "serde", + "static_assertions", + "strum", + "syn", + "zeroize", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hacl-rs" +version = "0.1.0" +source = "git+https://github.com/hacl-star/hacl-star.git?branch=afromher_rs#e83275b78cb4144254b2097fe36e6d2602f87cd7" +dependencies = [ + "krml", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "krml" +version = "0.1.0" +source = "git+https://github.com/hacl-star/hacl-star.git?branch=afromher_rs#e83275b78cb4144254b2097fe36e6d2602f87cd7" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libsqlite3-sys" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4588d65215825ee71ebff9e1c9982067833b1355d7546845ffdb3165cbd7456" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "logging" +version = "0.1.0" +dependencies = [ + "env_logger", + "log", + "os-id", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + +[[package]] +name = "os-id" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510856ec55c552d86db0d675df95c32b87f28cfe1cdc47d3eba2342c39a0a5f6" +dependencies = [ + "libc", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" + +[[package]] +name = "plotters-svg" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test-eg" +version = "0.1.0" +dependencies = [ + "anyhow", + "bench-util", + "criterion", + "crypto-bigint", + "eg", + "eg-artifacts-dir", + "fixed-width-nonnegative", + "lazy_static", + "num-bigint", + "rand", + "rand_core", + "rand_pcg", + "rayon", + "static_assertions", + "util", +] + +[[package]] +name = "test-fixed-width-nonnegative" +version = "0.1.0" +dependencies = [ + "anyhow", + "bench-util", + "cfg-if", + "criterion", + "diesel", + "dotenvy", + "fixed-width-nonnegative", + "num-traits", + "rand", + "static_assertions", + "util", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "util" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "itertools 0.12.1", + "num-bigint", + "num-integer", + "num-traits", + "rand", + "serde", + "serde_json", + "sha3", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/test/Cargo.toml b/test/Cargo.toml new file mode 100644 index 0000000..1df9817 --- /dev/null +++ b/test/Cargo.toml @@ -0,0 +1,100 @@ +[workspace] +resolver = "2" + +members = [ + "bench-util", + "fixed-width-nn-bench", + "fixed-width-nn-bench-db", + "test-eg", + "test-fixed-width-nonnegative", +] + +[workspace.dependencies] +eg = { path = "../src/eg", features = [ "bench" ] } +eg-artifacts-dir = { path = "../src/eg-artifacts-dir" } +preencrypted = { path = "../src/preencrypted" } +util = { path = "../src/util" } +logging = { path = "../src/logging" } + +bench-util = { path = "bench-util" } +fixed-width-nn-bench-db = { path = "fixed-width-nn-bench-db" } + + +anyhow = { version = "1.0.79", default-features = false, features = ["std"] } +base16ct = { version = "0.2.0", default-features = false, features = [] } +base64 = { version = "0.21.2", default-features = false, features = ["std"] } +cfg-if = { version = "1.0.0", default-features = false, features = [] } +chrono = { version = "0.4.35", default-features = false, features = [] } +criterion = { version = "0.5.1", features = ["rayon"] } +diesel = { version = "2.1.4", default-features = false, features = [ + "sqlite", + "returning_clauses_for_sqlite_3_35", + "32-column-tables", + "serde_json", + "chrono", + # "numeric", # uses bigdecimal + "without-deprecated", +] } +dotenvy = { version = "0.15.7", default-features = false, features = [] } +env_logger = { version = "0.11.2", default-features = false, features = [] } +hex-literal = { version = "0.4.1", default-features = false, features = [] } +indexmap = { version = "2.2.2", default-features = false, features = [] } # Used by serde_json +lazy_static = { version = "1.4.0", default-features = false, features = [] } +log = { version = "0.4.21", default-features = false, features = ["std"] } +num-bigint = { version = "0.4.4", default-features = false, features = ["serde", "std"] } +num-derive = { version = "0.4.2", default-features = false, features = [] } +num-traits = { version = "0.2.18", default-features = false, features = ["std"] } +num-integer = { version = "0.1.46", default-features = false, features = ["std"] } +os-id = { version = "3.0.1", default-features = false, features = [] } +paste = { version = "1.0.14", default-features = false, features = [] } +rand = { version = "0.8.5", default-features = false, features = [ ] } +rand_core = { version = "0.6.4", default-features = false, features = ["getrandom"] } +rand_pcg = { version = "0.3.1", default-features = false, features = [ ] } +rayon = "1.8.1" +serde = { version = "1.0.196", default-features = false, features = ["std", "derive"] } +serde_json = { version = "1.0.113", default-features = false, features = ["std", "preserve_order"] } +sha2 = { version = "0.10.8", default-features = false, features = [ ]} +sha3 = { version = "0.10.8", default-features = false, features = [ ]} +static_assertions = { version = "1.1.0", default-features = false } +strum = { version = "0.26.1", default-features = false, features = ["std", "derive"]} +syn = { version = "2.0.48", default-features = false, features = [] } + +[workspace.dependencies.crypto-bigint] +version = "0.5.5" +default-features = false +features = [ + # "default", # ["rand"] + # "alloc", + # "rand", + "serde", + # "extra-sizes", + # "der", + # "generic", + # "rand_core", + # "rlp", + "zeroize", +] + +[workspace.dependencies.hacl-rs] +git = "https://github.com/hacl-star/hacl-star.git" +rev = "5180845d78b2be36a34381d921f916186801585a" +# path = "../../../hacl-star/hacl-star/dist/rs" +default-features = false +features = [] + +[workspace.dependencies.fixed-width-nonnegative] +path = "../src/fixed-width-nonnegative" +default-features = false +features = [ + "crypto-bigint", + "num-bigint", + "bits-256", + "bits-4096", + "basic-array", + "basic-array-u64", + "crypto-bigint", + "hacl-rs", + "montgomery", + "num-bigint", + "zeroize" +] diff --git a/test/bench-util/Cargo.toml b/test/bench-util/Cargo.toml new file mode 100644 index 0000000..83ae0e7 --- /dev/null +++ b/test/bench-util/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "bench-util" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow.workspace = true +crypto-bigint = { workspace = true, features = [] } +eg.workspace = true +fixed-width-nonnegative = { workspace = true, features = ["num-bigint"] } +num-bigint.workspace = true +rand.workspace = true +rand_pcg.workspace = true +util.workspace = true diff --git a/test/bench-util/src/lib.rs b/test/bench-util/src/lib.rs new file mode 100644 index 0000000..c5d107e --- /dev/null +++ b/test/bench-util/src/lib.rs @@ -0,0 +1,278 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::assertions_on_constants)] +#![deny(clippy::unwrap_used)] +#![deny(clippy::expect_used)] +#![deny(clippy::panic)] +#![deny(clippy::manual_assert)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use crypto_bigint::Zero; + +pub type CheapPrng = rand_pcg::Mcg128Xsl64; // 128 bit size + +#[allow(non_snake_case)] +pub fn CheapPrng_new>(seed: &Seed) -> CheapPrng { + let mut csprng = util::csprng::Csprng::new(seed.as_ref()); + CheapPrng_from_u128(csprng.next_u128()) +} + +#[allow(non_snake_case)] +pub fn CheapPrng_from_u128(state: u128) -> CheapPrng { + CheapPrng::new(state) +} + +//=================================================================================================| + +pub type crybi_U4096 = ::crypto_bigint::U4096; +pub type crybi_DynResidueParams_4096 = ::crypto_bigint::modular::runtime_mod::DynResidueParams<{crybi_U4096::LIMBS}>; +pub type crybi_DynResidue_4096 = ::crypto_bigint::modular::runtime_mod::DynResidue<{crybi_U4096::LIMBS}>; +pub type crybi_Nonnegative_4096 = fixed_width_nonnegative::cryptobigint::Nonnegative_4096; + +pub type crybi_Word = ::crypto_bigint::Word; +pub type crybi_U256 = ::crypto_bigint::U256; + +//=================================================================================================| + +pub fn standard_parameter_p() -> &'static ::fixed_width_nonnegative::basicarray_u64::Nonnegative_4096 { + use ::std::sync::OnceLock; + static N: OnceLock<::fixed_width_nonnegative::basicarray_u64::Nonnegative_4096> = + OnceLock::new(); + N.get_or_init(|| { + let p_biguint: &'static ::num_bigint::BigUint = + ::eg::standard_parameters::STANDARD_PARAMETERS.group.modulus(); + let p_numbi: ::fixed_width_nonnegative::numbigint::Nonnegative_4096 = + p_biguint.try_into().unwrap(); + p_numbi.into() + }) +} + +pub fn standard_parameter_p_dynresidueparams() -> &'static crybi_DynResidueParams_4096 { + use ::std::sync::OnceLock; + static N: OnceLock = OnceLock::new(); + N.get_or_init(|| { + let p: crybi_Nonnegative_4096 = standard_parameter_p().into(); + let p_crybi: &crybi_U4096 = p.as_ref(); + crybi_DynResidueParams_4096::new(p_crybi) + }) +} + +pub fn standard_parameter_p_dynresidueparams_cloned() -> crybi_DynResidueParams_4096 { + standard_parameter_p_dynresidueparams().clone() +} + +pub fn standard_parameter_g() -> &'static ::fixed_width_nonnegative::basicarray_u64::Nonnegative_4096 { + use ::std::sync::OnceLock; + static N: OnceLock<::fixed_width_nonnegative::basicarray_u64::Nonnegative_4096> = + OnceLock::new(); + N.get_or_init(|| { + let p_biguint: &::num_bigint::BigUint = &::eg::standard_parameters::STANDARD_PARAMETERS.g(); + let p_numbi: ::fixed_width_nonnegative::numbigint::Nonnegative_4096 = + p_biguint.try_into().unwrap(); + p_numbi.into() + }) +} + +pub fn standard_parameter_g_dynresidue_mod_p() -> &'static crybi_DynResidue_4096 { + use ::std::sync::OnceLock; + static N: OnceLock = OnceLock::new(); + N.get_or_init(|| { + let g_crybi: crybi_Nonnegative_4096 = standard_parameter_g().into(); + let g_crybi: &crybi_U4096 = g_crybi.as_ref(); + let p_crypbi_dynresidueparams = standard_parameter_p_dynresidueparams_cloned(); + crybi_DynResidue_4096::new(&g_crybi, p_crypbi_dynresidueparams) + }) +} + +pub fn standard_parameter_q() -> &'static ::fixed_width_nonnegative::basicarray_u64::Nonnegative_256 { + use ::std::sync::OnceLock; + static N: OnceLock<::fixed_width_nonnegative::basicarray_u64::Nonnegative_256> = + OnceLock::new(); + N.get_or_init(|| { + let q_biguint: &'static ::num_bigint::BigUint = + ::eg::standard_parameters::STANDARD_PARAMETERS.group.order(); + let q_numbi: ::fixed_width_nonnegative::numbigint::Nonnegative_256 = + q_biguint.try_into().unwrap(); + q_numbi.into() + }) +} + +pub fn standard_parameter_exptable_g_p_4() -> &'static ExpTable4096 { + use ::std::sync::OnceLock; + static N: OnceLock = OnceLock::new(); + N.get_or_init(|| { + let p: crybi_Nonnegative_4096 = standard_parameter_p().into(); + let g: crybi_Nonnegative_4096 = standard_parameter_g().into(); + ExpTable4096::new( + g, + standard_parameter_p_dynresidueparams_cloned(), + 4 + ).unwrap() + }) +} + +//=================================================================================================| + +pub fn eprintln_here() -> &'static bool +{ + use ::std::sync::OnceLock; + static N: OnceLock = OnceLock::new(); + N.get_or_init(|| { + eprintln!("here"); + true + }) +} + +pub fn eprintln_there() -> &'static bool +{ + use ::std::sync::OnceLock; + static N: OnceLock = OnceLock::new(); + N.get_or_init(|| { + eprintln!("there"); + true + }) +} + +//=================================================================================================| + +#[derive(Clone)] +pub struct ExpTable4096 { + base: crybi_Nonnegative_4096, + modulus_dynresidueparams: crybi_DynResidueParams_4096, + bits_per_col: u8, + base_crybi_dynresidue: crybi_DynResidue_4096, + cols: u16, + rows: u16, + bits_mask: crybi_Word, + v: Vec, +} + +impl ExpTable4096 { + pub fn new( + base: crybi_Nonnegative_4096, + modulus_dynresidueparams: crybi_DynResidueParams_4096, + bits_per_col: usize, + ) -> Result { + ensure!((1..=15).contains(&bits_per_col)); + let bits_per_col = bits_per_col as u8; + let bits_per_col_usize: usize = bits_per_col.into(); + + let base_crybi_dynresidue = crybi_DynResidue_4096::new( + base.as_ref(), modulus_dynresidueparams); + + let exponent_bits: u16 = crybi_U256::BITS.try_into()?; + ensure!(exponent_bits != 0); + + let cols = exponent_bits.div_ceil(bits_per_col.into()); + ensure!(cols != 0); + + let rows: u16 = (1_u32 << bits_per_col).try_into()?; + ensure!(rows != 0); + let rows: u16 = rows.try_into()?; + + let mut v = Vec::with_capacity(rows as usize * cols as usize); + + let one = crybi_DynResidue_4096::new(&crybi_U4096::ONE, modulus_dynresidueparams); + + for col in 0_u16..cols { + let mut shift = 0_usize; + + v.push(one); + + for row in 1_u16..rows { + let exponent = crybi_U256::from(row) << shift; + v.push(base_crybi_dynresidue.pow(&exponent)); + } + + shift += bits_per_col_usize; + } + + let bits_mask = (crybi_Word::from(1_u8) << bits_per_col) - 1; + + Ok(ExpTable4096 { + base, + modulus_dynresidueparams, + bits_per_col, + base_crybi_dynresidue, + cols, + rows, + bits_mask, + v, + }) + } + + pub fn pow(&self, exp: crybi_U256) -> Result { + let mut exp = exp; + let mut exponent_bits: u16 = exp.bits().try_into()?; + + let bits_per_col_usize: usize = self.bits_per_col.into(); + assert!(bits_per_col_usize < std::mem::size_of::<::crypto_bigint::Word>()*8); + + let mut n = crybi_DynResidue_4096::new(&crybi_U4096::ONE, self.modulus_dynresidueparams); + + let rows: usize = self.rows.into(); + + let mut col_ix = 0_usize; + while exponent_bits != 0 { + let row_ix = (exp.as_words()[0] & self.bits_mask) as usize; + + debug_assert!((0..rows).contains(&row_ix)); + + let ix = col_ix*rows + row_ix; + debug_assert!(ix < self.v.len()); + + n *= self.v[ix]; + + exp >>= self.bits_per_col.into(); + exponent_bits = exponent_bits.saturating_sub(self.bits_per_col.into()); + col_ix += 1; + } + + Ok(n) + } + + /// Gets something from the table. This is so the compiler can't optimize stuff out. + pub fn get_something(&self, rng: &mut R) -> u64 { + use ::rand::Rng; + if self.v.len() != 0 { + let ix = rng.gen_range(0..self.v.len()); + let dynresidue = &self.v[ix]; + let words = dynresidue.as_montgomery().as_words(); + if words.len() != 0 { + let ix = rng.gen_range(0..words.len()); + return words[ix] as u64 + } + } + 0_u64 + } +} diff --git a/test/fixed-width-nn-bench-db/Cargo.toml b/test/fixed-width-nn-bench-db/Cargo.toml new file mode 100644 index 0000000..fa6c6d2 --- /dev/null +++ b/test/fixed-width-nn-bench-db/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "fixed-width-nn-bench-db" +version = "0.1.0" +edition = "2021" + +[package.metadata.vcpkg] +git = "https://github.com/microsoft/vcpkg" +branch = "master" +dependencies = [ + "sqlite3", # sqlite3 3.43.2 SQLite is a software library that implements a self-contained, serverless,... + # "sqlite3[dbstat]", # Enable the DBSTAT virtual table + # "sqlite3[fts3]", # Enable the FTS3 extension + # "sqlite3[fts4]", # Enable the FTS4 extension + # "sqlite3[fts5]", # Enable the FTS5 extension + # "sqlite3[geopoly]", # Enable geopoly functionality for sqlite3 + # "sqlite3[json1]", # Enable JSON functionality for sqlite3 + # "sqlite3[limit]", # Enable the UPDATE/DELETE LIMIT clause + "sqlite3[math]", # Enable math functions + # "sqlite3[memsys3]", # Enable MEMSYS3 + # "sqlite3[memsys5]", # Enable MEMSYS5 + # "sqlite3[omit-load-extension]", # Enable loading of external extensions + # "sqlite3[rtree]", # Enable the RTREE extension + # "sqlite3[session]", # Enable the SESSION extension + "sqlite3[tool]", # Build sqlite3 executable + # "sqlite3[zlib]", # Build sqlite3 command line tool with zlib support; has no effect on the li... +] + +[dependencies] +anyhow.workspace = true +chrono.workspace = true +diesel.workspace = true +log.workspace = true +logging.workspace = true + +[build-dependencies] +anyhow.workspace = true +vcpkg = { version = "0.2.15", default-features = false, features = []} diff --git a/test/fixed-width-nn-bench-db/build.rs b/test/fixed-width-nn-bench-db/build.rs new file mode 100644 index 0000000..c7861e8 --- /dev/null +++ b/test/fixed-width-nn-bench-db/build.rs @@ -0,0 +1,58 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] + +use anyhow::Result; + +//=================================================================================================| + +fn main() -> Result<()> { + vcpkg::find_package("sqlite3")?; + Ok(()) +} diff --git a/test/fixed-width-nn-bench-db/diesel.toml b/test/fixed-width-nn-bench-db/diesel.toml new file mode 100644 index 0000000..b363837 --- /dev/null +++ b/test/fixed-width-nn-bench-db/diesel.toml @@ -0,0 +1,4 @@ + +[print_schema] + +file = "src/db_schema.rs" diff --git a/test/fixed-width-nn-bench-db/migrations/2024-03-16-005804_create_table_exes/down.sql b/test/fixed-width-nn-bench-db/migrations/2024-03-16-005804_create_table_exes/down.sql new file mode 100644 index 0000000..8e848af --- /dev/null +++ b/test/fixed-width-nn-bench-db/migrations/2024-03-16-005804_create_table_exes/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE IF EXISTS `exes`; diff --git a/test/fixed-width-nn-bench-db/migrations/2024-03-16-005804_create_table_exes/up.sql b/test/fixed-width-nn-bench-db/migrations/2024-03-16-005804_create_table_exes/up.sql new file mode 100644 index 0000000..1667115 --- /dev/null +++ b/test/fixed-width-nn-bench-db/migrations/2024-03-16-005804_create_table_exes/up.sql @@ -0,0 +1,15 @@ +-- Your SQL goes here +CREATE TABLE exes( + id INTEGER NOT NULL PRIMARY KEY, + file_name TEXT NOT NULL, + file_path TEXT NOT NULL, + file_modified_time TEXT NOT NULL, + file_sha256_uchex TEXT NOT NULL, + file_len_bytes INTEGER NOT NULL +) STRICT; + +CREATE TABLE processes( + id INTEGER NOT NULL PRIMARY KEY, + exe_id INTEGER NOT NULL REFERENCES exes(id), + pid INTEGER NOT NULL +) STRICT; diff --git a/test/fixed-width-nn-bench-db/src/db.rs b/test/fixed-width-nn-bench-db/src/db.rs new file mode 100644 index 0000000..02fe9d4 --- /dev/null +++ b/test/fixed-width-nn-bench-db/src/db.rs @@ -0,0 +1,139 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] // Those are the best kind +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use std::ops::DerefMut; +//use std::borrow::Cow; +//use std::collections::HashSet; +//use std::convert::AsRef; +//use std::io::{BufRead, Cursor}; +//use std::mem::{size_of, size_of_val}; +//use std::path::{Path, PathBuf}; +//use std::str::FromStr; +use std::sync::{Mutex, MutexGuard, OnceLock}; + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use chrono::{DateTime, Local}; +use diesel::{connection, sql_types, table}; +use diesel::prelude::*; +use diesel::sql_types::TimestamptzSqlite; +use diesel::sqlite::SqliteConnection; + +//use either::Either; +//use log::{debug, error, info, trace, warn}; +//use proc_macro2::{Ident,Literal,TokenStream}; +//use quote::{format_ident, quote, ToTokens, TokenStreamExt}; + +//use crate::{}; +//use crate::*; + +use crate::db_models::Exe; +use crate::db_models::NewExe; + +//=================================================================================================| + +fn try_connect() -> Result { + let database_url = std::env::var("DATABASE_URL") + .context("Env var 'DATABASE_URL' is not set")?; + + SqliteConnection::establish(&database_url) + .with_context(|| format!("Error connecting to {database_url}")) +} + +#[repr(transparent)] +pub struct DbConnLock(MutexGuard<'static, Option>); + +impl std::ops::Deref for DbConnLock { + type Target = SqliteConnection; + fn deref(&self) -> &Self::Target { + // Unwrap() is justified here because we checked it before creation. + #[allow(clippy::unwrap_used)] + self.0.as_ref().unwrap() + } +} + +impl std::ops::DerefMut for DbConnLock { + fn deref_mut(&mut self) -> &mut Self::Target { + // Unwrap() is justified here because we checked it before creation. + #[allow(clippy::unwrap_used)] + self.0.as_mut().unwrap() + } +} + +pub fn db_conn_lock() -> Result { + static M: Mutex> = Mutex::new(None); + + let mut mg = M.lock() + .map_err(|err| anyhow!("DB connection Mutex poisoned: {err}"))?; + + if mg.is_none() { + let conn = try_connect()?; + *mg = Some(conn); + } + + Ok(DbConnLock(mg)) +} + +//-------------------------------------------------------------------------------------------------| + +pub fn create_exe( + conn: &mut SqliteConnection, + file_name: &str, + file_path: &str, + file_modified_time: DateTime, + file_sha256_uchex: &str, + file_len_bytes: i64, +) -> Result { + use crate::db_schema::exes; + + let new_exe = NewExe { + file_name, + file_path, + file_modified_time, + file_sha256_uchex, + file_len_bytes, + }; + + diesel::insert_into(exes::table) + .values(&new_exe) + .returning(Exe::as_returning()) + .get_result(conn) + .context("Error saving new exe") +} diff --git a/test/fixed-width-nn-bench-db/src/db_models.rs b/test/fixed-width-nn-bench-db/src/db_models.rs new file mode 100644 index 0000000..e2cf66f --- /dev/null +++ b/test/fixed-width-nn-bench-db/src/db_models.rs @@ -0,0 +1,86 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] // Those are the best kind +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +//use std::borrow::Cow; +//use std::collections::HashSet; +//use std::convert::AsRef; +//use std::io::{BufRead, Cursor}; +//use std::mem::{size_of, size_of_val}; +//use std::path::{Path, PathBuf}; +//use std::str::FromStr; +//use std::sync::OnceLock; + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use chrono::{DateTime, Local}; +use diesel::prelude::*; +use diesel::sql_types::TimestamptzSqlite; +//use log::{debug, error, info, trace, warn}; +//use proc_macro2::{Ident,Literal,TokenStream}; +//use quote::{format_ident, quote, ToTokens, TokenStreamExt}; + +//use crate::{}; +//use crate::*; + +use super::db_schema::exes; + +//=================================================================================================| + +#[derive(Debug, Queryable, Selectable)] +#[diesel(table_name = crate::db_schema::exes)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub struct Exe { + pub id: i32, + pub file_name: String, + pub file_path: String, + pub file_modified_time: DateTime, + pub file_sha256_uchex: String, + pub file_len_bytes: i64, +} + +#[derive(Insertable)] +#[diesel(table_name = crate::db_schema::exes)] +pub struct NewExe<'a> { + pub file_name: &'a str, + pub file_path: &'a str, + pub file_modified_time: DateTime, + pub file_sha256_uchex: &'a str, + pub file_len_bytes: i64, +} diff --git a/test/fixed-width-nn-bench-db/src/db_schema.rs b/test/fixed-width-nn-bench-db/src/db_schema.rs new file mode 100644 index 0000000..21c0203 --- /dev/null +++ b/test/fixed-width-nn-bench-db/src/db_schema.rs @@ -0,0 +1,27 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + exes (id) { + id -> Integer, + file_name -> Text, + file_path -> Text, + file_modified_time -> TimestamptzSqlite, + file_sha256_uchex -> Text, + file_len_bytes -> BigInt, + } +} + +diesel::table! { + processes (id) { + id -> Integer, + exe_id -> Integer, + pid -> Integer, + } +} + +diesel::joinable!(processes -> exes (exe_id)); + +diesel::allow_tables_to_appear_in_same_query!( + exes, + processes, +); diff --git a/test/fixed-width-nn-bench-db/src/lib.rs b/test/fixed-width-nn-bench-db/src/lib.rs new file mode 100644 index 0000000..da85034 --- /dev/null +++ b/test/fixed-width-nn-bench-db/src/lib.rs @@ -0,0 +1,27 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +pub mod db; +pub mod db_models; +pub mod db_schema; diff --git a/test/fixed-width-nn-bench/.env b/test/fixed-width-nn-bench/.env new file mode 100644 index 0000000..4ae54fa --- /dev/null +++ b/test/fixed-width-nn-bench/.env @@ -0,0 +1,2 @@ + +DATABASE_URL="${ELECTIONGUARD_ARTIFACTS_DIR}/fixed-width-nn-bench-test.sqlite3" diff --git a/test/fixed-width-nn-bench/Cargo.toml b/test/fixed-width-nn-bench/Cargo.toml new file mode 100644 index 0000000..5af8424 --- /dev/null +++ b/test/fixed-width-nn-bench/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "fixed-width-nn-bench" +version = "0.1.0" +edition = "2021" + + +[dependencies] +#static_assertions.workspace = true +anyhow.workspace = true +chrono.workspace = true +diesel.workspace = true +dotenvy.workspace = true +crypto-bigint.workspace = true +#cfg-if.workspace = true +eg.workspace = true +log.workspace = true +logging.workspace = true +num-bigint.workspace = true +sha2 = { workspace = true, features = ["std"] } +#rand.workspace = true +#util.workspace = true + +#bench-util.workspace = true +fixed-width-nonnegative.workspace = true +fixed-width-nn-bench-db.workspace = true + + +[features] +default = [ + "basic-array", + "crypto-bigint", + "num-bigint", + "bits-256", + "bits-4096", + "zeroize" +] + +basic-array = [ "fixed-width-nonnegative/basic-array" ] +basic-array-u8 = [ "fixed-width-nonnegative/basic-array-u8" ] +basic-array-u16 = [ "fixed-width-nonnegative/basic-array-u16" ] +basic-array-u32 = [ "fixed-width-nonnegative/basic-array-u32" ] +basic-array-u64 = [ "fixed-width-nonnegative/basic-array-u64" ] +# basic-array-u128 = [ "fixed-width-nonnegative/basic-array-u128" ] TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry +crypto-bigint = [ "fixed-width-nonnegative/crypto-bigint" ] +hacl-rs = [ "fixed-width-nonnegative/hacl-rs" ] +hacl-rs-u32 = [ "fixed-width-nonnegative/hacl-rs-u32" ] +hacl-rs-u64 = [ "fixed-width-nonnegative/hacl-rs-u64" ] +num-bigint = [ "fixed-width-nonnegative/num-bigint" ] +montgomery = [ "fixed-width-nonnegative/montgomery" ] +# bits-8 = [ "fixed-width-nonnegative/bits-8" ] TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry +# bits-16 = [ "fixed-width-nonnegative/bits-16" ] +# bits-32 = [ "fixed-width-nonnegative/bits-32" ] +# bits-64 = [ "fixed-width-nonnegative/bits-64" ] +bits-128 = [ "fixed-width-nonnegative/bits-128" ] +bits-256 = [ "fixed-width-nonnegative/bits-256" ] +bits-512 = [ "fixed-width-nonnegative/bits-512" ] +bits-1024 = [ "fixed-width-nonnegative/bits-1024" ] +bits-2048 = [ "fixed-width-nonnegative/bits-2048" ] +bits-4096 = [ "fixed-width-nonnegative/bits-4096", "fixed-width-nonnegative/bits-8192" ] +bits-8192 = [ "fixed-width-nonnegative/bits-8192" ] +zeroize = [ "fixed-width-nonnegative/zeroize" ] diff --git a/test/fixed-width-nn-bench/notes.txt b/test/fixed-width-nn-bench/notes.txt new file mode 100644 index 0000000..3df4755 --- /dev/null +++ b/test/fixed-width-nn-bench/notes.txt @@ -0,0 +1,232 @@ + +#------------- Only if not using vcpkg + +# Cd to the project +cd C:\w\snc\eg\electionguard-rust_bunna\test\fixed-width-nn-bench + +vcpkg new --application +vcpkg add port sqlite3 +vcpkg install + +vcpkg install sqlite3[core,json1,tool] --recurse + +#------------- If using carg-vcpkg + +# https://crates.io/crates/cargo-vcpkg +cargo install cargo-vcpkg + +$env.VCPKGRS_DYNAMIC = 1 + +vcpkg_cli probe -l static sqlite3 + +# In projects +cargo add cargo-vcpkg + + +cargo vcpkg build + + +C:\w\snc\eg\electionguard-rust_bunna\test\fixed-width-nn-bench> cargo vcpkg -v build 03/14/24 22:29:03 PM +vcpkg root is C:\w\vcpkg + Fetching vcpkg +POST git-upload-pack (198 bytes) +From https://github.com/microsoft/vcpkg + = [up to date] master -> origin/master + Checkout rev 000d1bda1ffa95a73e0b40334fa4103d6f4d3d48 +Updating files: 100% (2910/2910), done. +Note: switching to '000d1bda1ffa95a73e0b40334fa4103d6f4d3d48'. + +You are in 'detached HEAD' state. You can look around, make experimental +changes and commit them, and you can discard any commits you make in this +state without impacting any branches by switching back to a branch. + +If you want to create a new branch to retain commits you create, you may +do so (now or later) by using -c with the switch command. Example: + + git switch -c + +Or undo this operation with: + + git switch - + +Turn off this advice by setting config variable advice.detachedHead to false + +HEAD is now at 000d1bda1 [glslang] update to 13.1.1, opt feature [shaderc] update to 2023.7 (#35219) +-- stdout -- +Using local portfile versions. To update the local portfiles, use `git pull`. +The following packages differ from their port versions: + sqlite3:x64-windows 3.45.2 -> 3.43.2 +To update these packages and all dependencies, run +.\vcpkg upgrade' +To only remove outdated packages, run +.\vcpkg remove --outdated + +-- stderr -- + +ExitStatus(ExitStatus(0)) + Installing sqlite3 +warning: In the September 2023 release, the default triplet for vcpkg libraries changed from x86-windows to the detected host triplet (x64-windows). For the old behavior, add --triplet x86-windows . To suppress this message, add --triplet x64-windows . +Computing installation plan... +The following packages are already installed: + sqlite3[core,json1]:x64-windows@3.45.2 +sqlite3:x64-windows is already installed +Total install time: 26.9 us +sqlite3 provides pkgconfig bindings. +sqlite3 provides CMake targets: + + find_package(unofficial-sqlite3 CONFIG REQUIRED) + target_link_libraries(main PRIVATE unofficial::sqlite3::sqlite3) + + Finished in 2.26s + +C:\w\snc\eg\electionguard-rust_bunna\test\fixed-width-nn-bench +================================================================================= +C:\w\snc\eg\electionguard-rust_bunna\test\fixed-width-nn-bench-db> diesel --help +Usage: diesel [OPTIONS] + +Commands: + migration A group of commands for generating, running, and reverting migrations. + setup Creates the migrations directory, creates the database specified in your DATABASE_URL, and runs existing migrations. + database A group of commands for setting up and resetting your database. + completions Generate shell completion scripts for the diesel command. + print-schema Print table definitions for database schema. + help Print this message or the help of the given subcommand(s) + +Options: + --database-url + Specifies the database URL to connect to. Falls back to the DATABASE_URL environment variable if unspecified. + --config-file + The location of the configuration file to use. Falls back to the `DIESEL_CONFIG_FILE` environment variable if unspecified. Defaults to `diesel.toml` in your project root. See diesel.rs/guides/configuring-diesel-cli for documentation on this file. + --locked-schema + When `print_schema.file` is specified in your config file, this flag will cause Diesel CLI to error if any command would result in changes to that file. It is recommended that you use this flag when running migrations in CI or production. + -h, --help + Print help (see a summary with '-h') + -V, --version + Print version +================================================================================= +C:\w\snc\eg\electionguard-rust_bunna\test\fixed-width-nn-bench-db> diesel setup --help 03/15/24 17:37:46 PM +Creates the migrations directory, creates the database specified in your DATABASE_URL, and runs existing migrations. + +Usage: diesel setup [OPTIONS] + +Options: + --database-url + --migration-dir + --config-file + --locked-schema +================================================================================= +A group of commands for setting up and resetting your database. +Usage: diesel database [OPTIONS] +Commands: + setup Creates the database specified in your DATABASE_URL, and then runs any existing migrations. + reset Resets your database by dropping the database specified in your DATABASE_URL and then running `diesel database setup`. + help Print this message or the help of the given subcommand(s) +Options: + --database-url + --migration-dir + --config-file + --locked-schema +---------------------------------------------------------- +Creates the database specified in your DATABASE_URL, and then runs any existing migrations. +Usage: diesel database setup [OPTIONS] + --database-url + --migration-dir + --config-file + --locked-schema +---------------------------------------------------------- +Resets your database by dropping the database specified in your DATABASE_URL and then running `diesel database setup`. +Usage: diesel database reset [OPTIONS] + --database-url + --migration-dir + --config-file + --locked-schema + +---------------------------------------------------------- +---------------------------------------------------------- + + +================================================================================= +A group of commands for generating, running, and reverting migrations. + +Usage: diesel migration [OPTIONS] + +Commands: + run Runs all pending migrations. + revert Reverts the specified migrations. + redo Reverts and re-runs the latest migration. Useful for testing that a migration can in fact be reverted. + list Lists all available migrations, marking those that have been applied. + pending Returns true if there are any pending migrations. + generate Generate a new migration with the given name, and the current timestamp as the version. + help Print this message or the help of the given subcommand(s) + +Options: + --database-url + --migration-dir + --config-file + --locked-schema +================================================================================= +Generate a new migration with the given name, and the current timestamp as the version. + +Usage: diesel migration generate [OPTIONS] [table-name]... + + The name of the migration to create. + [table-name]... + Table names to filter. + --database-url + --migration-dir + --version + --config-file + -u, --no-down + Don't generate a down.sql file. You won't be able to run migration `revert` or `redo`. + --format The format of the migration to be generated. + [default: sql] [possible values: sql] + --locked-schema + When `print_schema.file` is specified in your config file, this flag will cause Diesel CLI to error if any command would result in changes to that file. It is recommended that you use this flag when running migrations in CI or production. + --diff-schema[=] + Populate the generated migrations based on the current difference between your `schema.rs` file and the specified database. The generated migrations are not expected to be perfect. Be sure to check whether they meet your expectations. Adjust the generated output if that's not the case. + -o, --only-tables + Only include tables from table-name that matches regexp. + -e, --except-tables + Exclude tables from table-name that matches regex. +================================================================================= + +$env.DATABASE_URL = 'C:/w/snc/eg/artifacts/fixed-width-nn-bench-test.sqlite3' + + +diesel database --locked-schema +diesel migration generate --diff-schema create_table_exes + +diesel migration list +diesel migration --locked-schema run + +C:\w\snc\eg\electionguard-rust_bunna\test\fixed-width-nn-bench-db> +diesel database reset +Dropping database: C:/w/snc/eg/artifacts/fixed-width-nn-bench-test.sqlite3 +Creating database: C:/w/snc/eg/artifacts/fixed-width-nn-bench-test.sqlite3 +Running migration 2024-03-16-005804_create_table_exes + +C:\w\snc\eg>sqlite3 artifacts\fixed-width-nn-bench-test.sqlite3 +SQLite version 3.43.2 2023-10-10 12:14:04 +Enter ".help" for usage hints. +sqlite> .tables +__diesel_schema_migrations exes +sqlite> +sqlite> .schema +CREATE TABLE __diesel_schema_migrations ( + version VARCHAR(50) PRIMARY KEY NOT NULL, + run_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE TABLE `exes`( + `id` INTEGER NOT NULL PRIMARY KEY, + `file_name` TEXT NOT NULL, + `file_path` TEXT NOT NULL, + `file_type` TEXT NOT NULL, + `file_modified_time` TIMESTAMPTZSQLITE NOT NULL, + `sha_2_256_uchex` TEXT NOT NULL +); + + + + + + diff --git a/test/fixed-width-nn-bench/src/main.rs b/test/fixed-width-nn-bench/src/main.rs new file mode 100644 index 0000000..d1d2715 --- /dev/null +++ b/test/fixed-width-nn-bench/src/main.rs @@ -0,0 +1,551 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] // Those are the best kind + +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +mod time_fns_00; + +//use std::borrow::Cow; +//use std::collections::HashSet; +//use std::convert::AsRef; +//use std::io::{BufRead, Cursor}; +//use std::mem::{size_of, size_of_val}; +use std::ops::DerefMut; +//use std::path::{Path, PathBuf}; +//use std::str::FromStr; +//use std::sync::OnceLock; + +use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +use diesel::prelude::*; +use log::{debug, error, info, trace, warn}; +//use proc_macro2::{Ident,Literal,TokenStream}; +//use quote::{format_ident, quote, ToTokens, TokenStreamExt}; + +pub use fixed_width_nonnegative::types::CowStaticStr; + +use fixed_width_nonnegative::{ + power_of_two::TwoRaisedToANonnegativePower, + types::{ + all_numimpls, all_types, LimbType, NumericImpl, NumericImplExt, Subtype, TypeInfo, + TypeInfoExt, TypeInfoFixedSizeLimbArray, + }, +}; + +use fixed_width_nn_bench_db::{ + db::{self, db_conn_lock}, + db_models::*, +}; + +//=================================================================================================| + +fn main() -> Result<()> { + logging::init("fwnn-bench"); + info!("Program started"); + logging::demo_active_levels(); + + let r = main2(); + logging::demo_active_levels(); + match &r { + Ok(_) => { + info!("Exit: success"); + } + Err(e) => { + error!("{e}"); + for cause in e.chain() { + error!("cause: {cause}"); + } + } + }; + r +} + +/* +pub(crate) struct NumimplTypeInfo { + numimpl_ix: usize, + numimpl_crate_name: &'static str, + //numimpl_supported_subtypes: &'static [Subtype], + numimpl_repr_is_fixed_size_limbs_array: bool, + //numimpl_can_support_multiple_limb_types: bool, + //numimpl_supported_limb_types: &'static [LimbType], + //numimpl_supported_bits: &'static [usize], + //numimpl_supports_secure_zeroize: bool, + type_info_ix: usize, + type_module_name: String, + type_name: String, + limb_type: LimbType, + is_fixed_sized_limb_array: bool, + subtype: Subtype, + bits: usize, + zeroize: bool, +} + +impl NumimplTypeInfo { + #[rustfmt::skip] + fn to_string(&self) -> String { +/* + supported_subtypes: {:?}, + can_support_multiple_limb_types: {:?}, + supported_limb_types: {:?}, + supported_bits: {:?}, + supports_secure_zeroize: {:?}, +// */ + format!( +r##"NumimplTypeInfo {{ + numimpl_ix: {}, + crate_name: {:?}, + repr_is_fixed_size_limbs_array: {:?}, + type_info_ix: {}, + type_module_name: {:?}, + type_name: {:?}, + limb_type: {:?}, + is_fixed_sized_limb_array: {:?}, + subtype: {:?}, + bits: {:?}, + zeroize: {:?}, +}}"##, + self.numimpl_ix, + self.numimpl_crate_name, + //self.numimpl_supported_subtypes, + self.numimpl_repr_is_fixed_size_limbs_array, + //self.numimpl_can_support_multiple_limb_types, + //self.numimpl_supported_limb_types, + //self.numimpl_supported_bits, + //self.numimpl_supports_secure_zeroize, + self.type_info_ix, + self.type_module_name, + self.type_name, + self.limb_type, + self.is_fixed_sized_limb_array, + self.subtype, + self.bits, + self.zeroize, + ) + } +} +// */ + +macro_rules! cb_for_each_nonnegative_type_make_unique_test_mod { + ( + $test_module_name:ident, + $numimpl_ix:tt, + $type_info_ix:tt, + $module_name:ident, + $module_name_fq:path, + $type_name:ident, + $type_name_fq:path, + $bits:tt, + ) => { + /* + const TEST_MODULE_NAME: &'static str = stringify!($test_module_name); + + pub(crate) fn new_numimpl_type_info() -> super::super::NumimplTypeInfo { + use fixed_width_nonnegative::types::{NumericImpl, TypeInfo}; + let numimpl = ::fixed_width_nonnegative::types::all_numimpls()[$numimpl_ix]; + let type_info = ::fixed_width_nonnegative::types::all_types()[$type_info_ix]; + + super::super::NumimplTypeInfo { + numimpl_ix: $numimpl_ix, + numimpl_crate_name: numimpl.crate_name(), + //numimpl_supported_subtypes: numimpl.supported_subtypes(), + numimpl_repr_is_fixed_size_limbs_array: numimpl.repr_is_fixed_size_limbs_array(), + //numimpl_can_support_multiple_limb_types: numimpl.can_support_multiple_limb_types(), + //numimpl_supported_limb_types: numimpl.supported_limb_types(), + //numimpl_supported_bits: numimpl.supported_bits(), + //numimpl_supports_secure_zeroize: numimpl.supports_secure_zeroize(), + type_info_ix: $type_info_ix, + type_module_name: type_info.module_name().to_string(), + type_name: type_info.type_name().to_string(), + limb_type: type_info.limb_type(), + is_fixed_sized_limb_array: type_info.type_info_fsla_opt().is_some(), + subtype: type_info.subtype(), + bits: type_info.bits(), + zeroize: type_info.zeroize(), + } + } + // */ + }; +} + +mod test_mods { + fixed_width_nonnegative::for_each_nonnegative_type_make_unique_test_mod!( + cb_for_each_nonnegative_type_make_unique_test_mod() + ); +} + +/* +pub(crate) fn make_numimpl_type_infos() -> Vec { + let mut v = vec![]; + macro_rules! cb_for_each_nonnegative_type_about_unique_test_mod { + ( + $test_module_name:ident, + $numimpl_ix:literal, + $type_info_ix:literal, + $module_name:ident, + $module_name_fq:path, + $type_name:ident, + $type_name_fq:path, + $bits:literal, + ) => { + v.push(crate::test_mods::$test_module_name::new_numimpl_type_info()); + }; + } + fixed_width_nonnegative::for_each_nonnegative_type_about_unique_test_mod!( + cb_for_each_nonnegative_type_about_unique_test_mod() + ); + v +} +// */ + +//=================================================================================================| + +/* pub fn all_buildrs_knownfeatures() -> &'static Vec { + static V: OnceLock> = OnceLock::new(); + V.get_or_init(|| { + }) +} + */ + +//=================================================================================================| + +fn do_stuff() -> Result<()> { + use fixed_width_nonnegative::power_of_two::*; + + //let R = TwoRaisedToANonnegativePower::new(4096_u16); + macro_rules! cb_for_each_nonnegative_type_do_stuff { + ( + $test_module_name:ident, + $numimpl_ix:literal, + $type_info_ix:literal, + $module_name:ident, + $module_name_fq:path, + $limb_type:ident, + $limb_type_fq:path, + $type_name:ident, + $type_name_fq:path, + $bits:tt, + ) => {{ + const NUMIMPL_IX: usize = $numimpl_ix; + const MODULE_NAME: &'static str = stringify!($module_name); + const MODULE_NAME_FQ: &'static str = stringify!($module_name_fq); + const LIMB_TYPE: &'static str = stringify!($limb_type); + const LIMB_TYPE_FQ: &'static str = stringify!($limb_type_fq); + const TYPE_NAME: &'static str = stringify!($type_name); + const TYPE_NAME_FQ: &'static str = stringify!($type_name_fq); + + debug!("vvvvvvvvvvvvvvvvvv type_name_fq: {TYPE_NAME_FQ}, limb_type: {LIMB_TYPE} vvvvvvvvvvvvvvvvvv"); + + fixed_width_nonnegative::bits_eq_4096! { $bits => { + let all_ones = // $type_name_fq ::all_ones(); + ::fixed_width_nonnegative::basicarray_u64::Nonnegative_4096::all_ones(); + let s = format!("{:0X}", all_ones); + assert_eq!(s, "F".repeat(4096/4)); + } }; + + debug!("^^^^^^^^^^^^^^^^^^ type_name_fq: {TYPE_NAME_FQ}, limb_type: {LIMB_TYPE} ^^^^^^^^^^^^^^^^^^"); + }}; + } + fixed_width_nonnegative::for_each_nonnegative_type_about_unique_test_mod!( + cb_for_each_nonnegative_type_do_stuff() + ); + + /* + macro_rules! cb_for_each_nonnegative_type_do_stuff { + ( + $test_module_name:ident, + $numimpl_ix:literal, + $type_info_ix:literal, + $module_name:ident, + $module_name_fq:path, + $limb_type:ident, + $limb_type_fq:path, + $type_name:ident, + $type_name_fq:path, + $bits:tt, + ) => {{ + debug!("vvvvvvvvvvvvvvvvvv module_name_fq: {MODULE_NAME_S}, limb_type: {LIMB_TYPE_S} vvvvvvvvvvvvvvvvvv"); + fixed_width_nonnegative::bits_eq_4096! { $bits => { + let p = standard_parameter_p(); + + debug!("p: {p:0wid$X}", wid=TEST_HEXDG); + } }; + debug!("^^^^^^^^^^^^^^^^^^ module_name_fq: {MODULE_NAME_S}, limb_type: {LIMB_TYPE_S} ^^^^^^^^^^^^^^^^^^"); + }}; + } + + fixed_width_nonnegative::for_each_nonnegative_type_about_unique_test_mod!( + cb_for_each_nonnegative_type_do_stuff() + ); + // */ + + /* + macro_rules! cb_for_each_numimpl_limbtype_nonnegative_code_block { + ( + $module_name_fq:path, + $limb_type_fq:path, + ) => { + debug!("vvvvvvvvvvvvvvvvvv module_name_fq: {MODULE_NAME_S}, limb_type: {LIMB_TYPE_S} vvvvvvvvvvvvvvvvvv"); + debug!("numimpl_ix: {NUMIMPL_IX}"); + debug!("module_name: {MODULE_NAME_S}"); + debug!("module_name_fq: {MODULE_NAME_FQ_S}"); + debug!("limb_type: {LIMB_TYPE_S}"); + debug!("limb_type_fq: {LIMB_TYPE_FQ_S}"); + + fixed_width_nonnegative::bits_eq_4096!( $bits => { + let p = standard_parameter_p(); + + debug!("p: {p:0wid$X}", wid=TEST_HEXDG); + }); + + + /* + type Nn256 = type_name_fq_for_bits!(256); + type BasicarrayNonnegative4096 = type_name_fq_for_bits!(4096); + type BasicarrayMontgomery4096 = type_name_fq_for_bits!(4096); + type Numbi256 = ::fixed_width_nonnegative::numbigint::Nonnegative_256; + type Numbi4096 = ::fixed_width_nonnegative::numbigint::Nonnegative_4096; + type Crybi256 = ::fixed_width_nonnegative::cryptobigint::Nonnegative_256; + type Crybi4096 = ::fixed_width_nonnegative::cryptobigint::Nonnegative_4096; + type CrybiU4096 = ::crypto_bigint::U4096; + + debug!("vvvvvvvvvvvvvvvvvv module_name_fq: {MODULE_NAME_S}, limb_type: {LIMB_TYPE_S} vvvvvvvvvvvvvvvvvv"); + + let p_biguint: &'static ::num_bigint::BigUint = ::eg::standard_parameters::STANDARD_PARAMETERS.p.as_ref(); + let p_numbi: Numbi4096 = p_biguint.try_into().unwrap(); + let p: BasicarrayNonnegative4096 = BasicarrayNonnegative4096::from(&p_numbi); + let p_crybi = Crybi4096::from(&p); + let p_crybi_u4096: &CrybiU4096 = p_crybi.as_ref(); + //debug!("p_numbi = {p_numbi:X}"); + //debug!("p_crybi = {p_crybi:X}"); + //debug!("p_crybi_u4096 = {p_crybi_u4096:X}"); + debug!("p = {p:X}"); + + let residue_params = ::crypto_bigint::modular::runtime_mod::DynResidueParams::new(p_crybi_u4096); + debug!("residue_params = {residue_params:?}"); + + let r_inv = { + let one = ::crypto_bigint::modular::runtime_mod::DynResidue::one(residue_params); + let mut r = ::crypto_bigint::modular::runtime_mod::DynResidue::new(&CrybiU4096::MAX, residue_params); + debug!("================================"); + debug!("."); + debug!("."); + debug!("."); + debug!("R - 1 = 2^4096 - 1 (mod p, reg) = {}", r.retrieve()); + debug!("R - 1 = 2^4096 - 1 (mod p, mgf) = {}", r.as_montgomery()); + r += one; + debug!("R = 2^4096 (mod p, reg) = {}", r.retrieve()); + debug!("R = 2^4096 (mod p, mgf) = {}", r.as_montgomery()); + let (r_inv, success) = r.invert(); + if ! Into::::into(success) { + bail!("Couldn't invert 1 << 4096?"); + } + debug!("R^-1 (mod p, reg) = {}", r_inv.retrieve()); + debug!("R^-1 (mod p, mgf) = {}", r_inv.as_montgomery()); + + r_inv + }; + + + // p = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB17217F7D1CF79ABC9E3B39803F2F6AF40F343267298B62D8A0D175B8BAAFA2BE7B876206DEBAC98559552FB4AFA1B10ED2EAE35C138214427573B291169B8253E96CA16224AE8C51ACBDA11317C387EB9EA9BC3B136603B256FA0EC7657F74B72CE87B19D6548CAF5DFA6BD38303248655FA1872F20E3A2DA2D97C50F3FD5C607F4CA11FB5BFB90610D30F88FE551A2EE569D6DFC1EFA157D2E23DE1400B39617460775DB8990E5C943E732B479CD33CCCC4E659393514C4C1A1E0BD1D6095D25669B333564A3376A9C7F8A5E148E82074DB6015CFE7AA30C480A5417350D2C955D5179B1E17B9DAE313CDB6C606CB1078F735D1B2DB31B5F50B5185064C18B4D162DB3B365853D7598A1951AE273EE5570B6C68F96983496D4E6D330AF889B44A02554731CDC8EA17293D1228A4EF98D6F5177FBCF0755268A5C1F9538B98261AFFD446B1CA3CF5E9222B88C66D3C5422183EDC99421090BBB16FAF3D949F236E02B20CEE886B905C128D53D0BD2F9621363196AF503020060E49908391A0C57339BA2BEBA7D052AC5B61CC4E9207CEF2F0CE2D7373958D762265890445744FB5F2DA4B751005892D356890DEFE9CAD9B9D4B713E06162A2D8FDD0DF2FD608FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + // crybi.params.modulus: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB17217F7D1CF79ABC9E3B39803F2F6AF40F343267298B62D8A0D175B8BAAFA2BE7B876206DEBAC98559552FB4AFA1B10ED2EAE35C138214427573B291169B8253E96CA16224AE8C51ACBDA11317C387EB9EA9BC3B136603B256FA0EC7657F74B72CE87B19D6548CAF5DFA6BD38303248655FA1872F20E3A2DA2D97C50F3FD5C607F4CA11FB5BFB90610D30F88FE551A2EE569D6DFC1EFA157D2E23DE1400B39617460775DB8990E5C943E732B479CD33CCCC4E659393514C4C1A1E0BD1D6095D25669B333564A3376A9C7F8A5E148E82074DB6015CFE7AA30C480A5417350D2C955D5179B1E17B9DAE313CDB6C606CB1078F735D1B2DB31B5F50B5185064C18B4D162DB3B365853D7598A1951AE273EE5570B6C68F96983496D4E6D330AF889B44A02554731CDC8EA17293D1228A4EF98D6F5177FBCF0755268A5C1F9538B98261AFFD446B1CA3CF5E9222B88C66D3C5422183EDC99421090BBB16FAF3D949F236E02B20CEE886B905C128D53D0BD2F9621363196AF503020060E49908391A0C57339BA2BEBA7D052AC5B61CC4E9207CEF2F0CE2D7373958D762265890445744FB5F2DA4B751005892D356890DEFE9CAD9B9D4B713E06162A2D8FDD0DF2FD608FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + // R = 2^4096 (mod p, reg) = 00000000000000000000000000000000000000000000000000000000000000004E8DE8082E308654361C4C67FC0D0950BF0CBCD98D6749D275F2E8A4745505D4184789DF92145367AA6AAD04B505E4EF12D151CA3EC7DEBBD8A8C4D6EE9647DAC16935E9DDB5173AE53425EECE83C7814615643C4EC99FC4DA905F1389A808B48D31784E629AB7350A205942C7CFCDB79AA05E78D0DF1C5D25D2683AF0C02A39F80B35EE04A4046F9EF2CF07701AAE5D11A9629203E105EA82D1DC21EBFF4C69E8B9F88A24766F1A36BC18CD4B8632CC3333B19A6C6CAEB3B3E5E1F42E29F6A2DA9964CCCA9B5CC895638075A1EB717DF8B249FEA301855CF3B7F5ABE8CAF2D36AA2AE864E1E846251CEC324939F934EF8708CA2E4D24CE4A0AF4AE7AF9B3E74B2E9D24C4C9A7AC28A675E6AE51D8C11AA8F4939706967CB692B192CCF507764BB5FDAAB8CE323715E8D6C2EDD75B1067290AE880430F8AAD975A3E06AC7467D9E5002BB94E35C30A16DDD4773992C3ABDDE7C12366BDEF6F444E9050C26B60DC91FD4DF31177946FA3ED72AC2F42D069DEC9CE6950AFCFDFF9F1B66F7C6E5F3A8CC645D414582FAD53A49E33B16DF8310D0F31D28C8C6A7289DD9A76FBBA8BB04A0D25B48AEFFA76D2CA976F210163526462B48EC1F9E9D5D27022F20D029F70000000000000000000000000000000000000000000000000000000000000001 + // crybi.params.r: 00000000000000000000000000000000000000000000000000000000000000004E8DE8082E308654361C4C67FC0D0950BF0CBCD98D6749D275F2E8A4745505D4184789DF92145367AA6AAD04B505E4EF12D151CA3EC7DEBBD8A8C4D6EE9647DAC16935E9DDB5173AE53425EECE83C7814615643C4EC99FC4DA905F1389A808B48D31784E629AB7350A205942C7CFCDB79AA05E78D0DF1C5D25D2683AF0C02A39F80B35EE04A4046F9EF2CF07701AAE5D11A9629203E105EA82D1DC21EBFF4C69E8B9F88A24766F1A36BC18CD4B8632CC3333B19A6C6CAEB3B3E5E1F42E29F6A2DA9964CCCA9B5CC895638075A1EB717DF8B249FEA301855CF3B7F5ABE8CAF2D36AA2AE864E1E846251CEC324939F934EF8708CA2E4D24CE4A0AF4AE7AF9B3E74B2E9D24C4C9A7AC28A675E6AE51D8C11AA8F4939706967CB692B192CCF507764BB5FDAAB8CE323715E8D6C2EDD75B1067290AE880430F8AAD975A3E06AC7467D9E5002BB94E35C30A16DDD4773992C3ABDDE7C12366BDEF6F444E9050C26B60DC91FD4DF31177946FA3ED72AC2F42D069DEC9CE6950AFCFDFF9F1B66F7C6E5F3A8CC645D414582FAD53A49E33B16DF8310D0F31D28C8C6A7289DD9A76FBBA8BB04A0D25B48AEFFA76D2CA976F210163526462B48EC1F9E9D5D27022F20D029F70000000000000000000000000000000000000000000000000000000000000001 + // R = 2^4096 (mod p, mgf) = F14289EBCA85CA3DD99E8057F9301CC5A59C92E97A366067294BF176AD7B15C467357D5C8D03E48F5FE7184338D54722033785E8B92E7966250711C96064F9FAA429B1A9DB50E5F2B4D89614AB5B3195B687819C0F4DB64E465562A9036C59723F12460273FEC0085982659BEE81DC629D5E684626E421700E34E572634E8C5E210EB9A77B9C0DCE00CE207A180EA6F244D54F6D620FA96D5C53CDF818DB2F517B2FFEC42B03348DF367646BB1310D58F8EFCA728312FE4A6534A7B68799FE8FA2FA72447B34E7ACCC7FA85041E3345CCA9627DA9AD4577D42A40F807940102482C26C413F3840CA020918EBD0EF401F55FEBC3F958F77EDC375B4DC056376E6EC0085BC30E3921B32D3A3C709C659B42F1101D8BD86609BFB1DA2DC4B0651681AE14D904FC4A65BFB97FE85562BFD5BEE75B5CF711F60EF6B03639559D3517D775D2FDABF5E66790CB814FF2DD13C78830AFBA02B46F4F38CD37F5E31D1C90EF8FA156506E0890360A45E70959193D2ABD481E0D74DD6F99BE046B06366886876B2E6B3DDE588B821F749BE08504A23B70889BADE80A3B3A2184FF606CE3E8ED7AF187D9985D50B3AB751E954C0AAC3C0F4B46B044125D3821429F0F620B6925A22318F1F5D22F317E26C8F869BC48E2F9D4D4E2C0D351E267724FD0AD9B901C4C167EC12749DA449E5FBCB6BF39E2B31CB2A39B46CF6652E5953604AEF4FF2 + // crybi.params.r2: F14289EBCA85CA3DD99E8057F9301CC5A59C92E97A366067294BF176AD7B15C467357D5C8D03E48F5FE7184338D54722033785E8B92E7966250711C96064F9FAA429B1A9DB50E5F2B4D89614AB5B3195B687819C0F4DB64E465562A9036C59723F12460273FEC0085982659BEE81DC629D5E684626E421700E34E572634E8C5E210EB9A77B9C0DCE00CE207A180EA6F244D54F6D620FA96D5C53CDF818DB2F517B2FFEC42B03348DF367646BB1310D58F8EFCA728312FE4A6534A7B68799FE8FA2FA72447B34E7ACCC7FA85041E3345CCA9627DA9AD4577D42A40F807940102482C26C413F3840CA020918EBD0EF401F55FEBC3F958F77EDC375B4DC056376E6EC0085BC30E3921B32D3A3C709C659B42F1101D8BD86609BFB1DA2DC4B0651681AE14D904FC4A65BFB97FE85562BFD5BEE75B5CF711F60EF6B03639559D3517D775D2FDABF5E66790CB814FF2DD13C78830AFBA02B46F4F38CD37F5E31D1C90EF8FA156506E0890360A45E70959193D2ABD481E0D74DD6F99BE046B06366886876B2E6B3DDE588B821F749BE08504A23B70889BADE80A3B3A2184FF606CE3E8ED7AF187D9985D50B3AB751E954C0AAC3C0F4B46B044125D3821429F0F620B6925A22318F1F5D22F317E26C8F869BC48E2F9D4D4E2C0D351E267724FD0AD9B901C4C167EC12749DA449E5FBCB6BF39E2B31CB2A39B46CF6652E5953604AEF4FF2 + // R^-1 (mod p, reg) = BF9B405D1C28111D456ACF32DC12355CFED23236158F7D0E08954A8DF3978161E60C086BC1B7D5CEB94BA33E8675144B23A08FEE288C52859F283EA3E0CDEF27CE2135E2D48247EE4C4897B8ADACE025A019A63020896CD0FFFC92402604FD0FE1146BB349EF9E766B3DEFFACB048C233D243D38389A100F02919B124321D9C587DA5666111886886CACB21CFAA4526E9F0906E206150704040E433FC37B76587591D7534B5DCDAF892C63D8B295B043EA4C8B3D45CB41B0881A5DB3E37D76C342942411C34CE98D8357D24304D0978E4EFD12121189E8334DE1AF2FA77E9D961E0FC19A9BDA50823B21301C842CE8D5FBA9E9149DCF6A821B66F2D67443DAB030FE5A4665FA142F3AE8F9D0631147A864223CACDE9DD2C8F279C87953F8158AC35CA846B85C0F9E6BC43CD3844FA27F1D6C91FEC8446C9F3CB57FC3B2877E8C4BA7468278C2E6C30D80131D0547EB2B48C5D64341131B3A19CA9326EEB3B291866E7F060C8DF689CEBAD599DD80E0D094C772ABBD792E61D2352985C38C12B6A6E10DD80DDF25DBF7126F1436115424B5875AB84E78658F510EAA11EB33D370AC8B054454EF7153EC3F9575FC5C6BC5A81827569C0340D0BDFFB1A2CCE93C6172493CFD89771BA4C941CA5AF982EE1E6C14A4AA36071B3AD39091AC0A4AA7ABE24D8629D35172BC29D3B48974E8C869BD8C92FE2E25D9EF35AD380BDE4A14EC + // R^-1 (mod p, mgf) = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + // crybi.params.r3: 078A8C60FFE1E25D8553E341229ABC896E48D3ED001BC6AE8CCD6929322664C49270F2509032008FC41F7881F9B1E6AF828A5A7C640B6A4496CA20A1F0DB3A3F73B9C643AE72D76C4E9DAAE0E46DB5CFE23669306DC54AFC2EE6A70944D5460D82025A365CE5C4498AF26ACF0508028D4B471F92C158725F6F67C1A347A60374EF24085AF20AE98A83F3C0341809F0E95A28F4CA5C2E2C4A00E97B0F485CB3FE2388B5BFC8B41984EBC4C6BD709C0D1E8C07D971D19DDFC9B597C7D9881CF5039D12399A198E8C2E148E79F3054F727416E6DBC4190649A955C5B2C8552185C378E70724E3FC261AF7949F4FC0B367770E588937BC1B572139603A3E19FDB9CF98642A31156B8F4E6E259F8B2CD14F4B313921BE0DBB7E25AD07D8822A64BF7E2F4A254843C548B1FC2E52C35C4D96C5CD3DFD6A46077AE1E169BAF51A395F317A7EB03E35E09C98933E721624EC6E0D49F705C37FD15ECE0C9CE3B00005CB1B0AD30A09156471B97E043FDE5E94EC334171CE979DFF755E0006C8A3E96769A28143F2214C1FB80D66AD3FE98913D024DA08C2A0A614131BC8815C5B8EE9F55D2D52BF42604CC7D149552376789F8E4DCF6E2EA4FBE899137022BD2DB92EC06734D4D02B8168A06220C2271342BDDC44FADAB14DB89D82172AC52B37D7F3734F9324C2F0992DE9A50334BFCA0CC4AFAAE97D309D7F2C92A876AB4378276B91D0 + // crybi.params.mod_neg_inv: Limb(0x0000000000000001) } + + let q_biguint: &'static ::num_bigint::BigUint = ::eg::standard_parameters::STANDARD_PARAMETERS.q.as_ref(); + let q_numbi: Numbi256 = q_biguint.try_into().unwrap(); + let q: Nn256 = q_numbi.into(); + debug!("q = {q:X}"); + + */ + debug!("^^^^^^^^^^^^^^^^^^ module_name_fq: {MODULE_NAME_S}, limb_type: {LIMB_TYPE_S} ^^^^^^^^^^^^^^^^^^"); + }; + } + + fixed_width_nonnegative::for_each_numimpl_limbtype_nonnegative_code_block!( + cb_for_each_numimpl_limbtype_nonnegative_code_block() + ); + // */ + + Ok(()) +} + +//=================================================================================================| + +fn main2() -> Result<()> { + info!("main2"); + + { + let pb = dotenvy::dotenv().context("dotenvy::dotenv")?; + info!("Merged env vars from file: {}", pb.display()); + } + + /* + let v = make_numimpl_type_infos(); + for numimpl_type_info in v.iter() { + println!("numimpl_type_info: {}", numimpl_type_info.to_string()); + } + //do_stuff()?; + // */ + + do_stuff2()?; + + Ok(()) +} + +//=================================================================================================| + +fn do_stuff2() -> Result<()> { + let exe_id = get_exe_id()?; + + //let conn = &mut *db::db_conn_lock()?; + + use fixed_width_nn_bench_db::db_schema::exes::dsl::*; + let results = exes + //.filter(published.eq(true)) + //.limit(5) + .select(Exe::as_select()) + .load(&mut *db_conn_lock()?) + .expect("Error loading exes"); + + println!("Displaying {} exes", results.len()); + for exe in results { + println!("id: {:?}, file_name: {:?} file_path: {:?}, file_modified_time: {:?}, file_sha256_uchex: {:?}, len_bytes: {}", + exe.id, + exe.file_name, + exe.file_path, + exe.file_modified_time, + exe.file_sha256_uchex, + exe.file_len_bytes ); + } + + Ok(()) +} + +//-------------------------------------------------------------------------------------------------| + +fn get_exe_id() -> Result { + let exe_file_pb = std::env::current_exe()?; + info!("Exe: {}", exe_file_pb.display()); + + let exe_file_metadata = exe_file_pb.metadata() + .with_context(|| format!("Couldn't get filesystem metadata for exe: {}", exe_file_pb.display()))?; + + let exe_file_name = exe_file_pb.file_name() + .ok_or_else(||anyhow!("No file name: {}", exe_file_pb.display()))?.to_string_lossy(); + + let exe_file_path = exe_file_pb.to_string_lossy(); + + let exe_file_len_bytes: i64 = exe_file_metadata.len().try_into()?; + + let exe_file_modified_time = exe_file_metadata.modified() + .inspect_err(|err| warn!("Couldn't get modification time for exe: {err}")) + .map(chrono::DateTime::::from)?; + debug!("Exe modified at: {exe_file_modified_time:?}"); + + let (exe_file_len_bytes2, exe_file_sha256) = file_sha_256(&exe_file_pb)?; + assert_eq!(exe_file_len_bytes2, exe_file_len_bytes); + let exe_file_sha256_uchex = format!("{:X}", exe_file_sha256); + + let exe = db::create_exe( + &mut *db_conn_lock()?, + &exe_file_name, + &exe_file_path, + exe_file_modified_time, + &exe_file_sha256_uchex, + exe_file_len_bytes )?; + + println!("Exe record: {exe:?}"); + + Ok(exe.id) +} + +//-------------------------------------------------------------------------------------------------| + +fn file_sha_256( + path: &std::path::Path +) -> Result<(i64, ::fixed_width_nonnegative::basicarray_u64::Nonnegative_256)> { + use sha2::{Digest, Sha256}; + use std::io::Write; + + let mut f = std::fs::File::open(&path) + .with_context(|| format!("Couldn't open file for reading: {}", path.display()))?; + let mut h = Sha256::new(); + let cb = std::io::copy(&mut f, &mut h) + .with_context(|| format!("Couldn't read file: {}", path.display()))?; + debug!("cb: {cb} bytes"); + let cb: i64 = cb.try_into()?; + + //let result = h.finalize(); + let hash_value: [u8; 32] = h.finalize().into(); + //debug!("hash_value: {hash_value:02X}"); + + let hash_value = ::fixed_width_nonnegative::basicarray_u64::Nonnegative_256::from_le_bytes_arr(hash_value); + debug!("hash_value: {hash_value:X}"); + + Ok((cb, hash_value)) +} + +//-------------------------------------------------------------------------------------------------| diff --git a/test/fixed-width-nn-bench/src/time_fns_00.rs b/test/fixed-width-nn-bench/src/time_fns_00.rs new file mode 100644 index 0000000..b110aa9 --- /dev/null +++ b/test/fixed-width-nn-bench/src/time_fns_00.rs @@ -0,0 +1,130 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +//#![cfg_attr(rustfmt, rustfmt_skip)] +#![deny(clippy::expect_used)] +#![deny(clippy::manual_assert)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![allow(clippy::assertions_on_constants)] // Those are the best kind +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(non_snake_case)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +//use std::borrow::Cow; +//use std::collections::HashSet; +//use std::convert::AsRef; +//use std::io::{BufRead, Cursor}; +//use std::mem::{size_of, size_of_val}; +//use std::path::{Path, PathBuf}; +//use std::str::FromStr; +//use std::sync::OnceLock; + +//use anyhow::{anyhow, bail, ensure, Context, Result}; +//use either::Either; +//use log::{debug, error, info, trace, warn}; +//use proc_macro2::{Ident,Literal,TokenStream}; +//use quote::{format_ident, quote, ToTokens, TokenStreamExt}; + +use core::time; + +//use crate::{}; +use crate::*; +/* + +//=================================================================================================| + +pub struct TimedFn +where + F: Fn(&mut TimingTools), +{ + name: CowStaticStr, + bx_f: Box, +} + +impl TimedFn +where + F: Fn(&mut TimingTools), +{ + pub fn new(name: String, bx_f: Box) -> Self + where + S: Into, + { + Self { + name: name.into(), + bx_f, + } + } + + pub fn call_timed_fn() {} +} + +//=================================================================================================| + +pub struct TimingTools { + // fn call time + // fn return time + // paniced +} + +impl TimingTools { + pub fn new() -> Self { + Self {} + } +} + +//=================================================================================================| + +pub fn timed_fn_00(timing_tools: &mut TimingTools) { + //? timing_tools.start(); +} +// */ diff --git a/test/test-eg/Cargo.toml b/test/test-eg/Cargo.toml new file mode 100644 index 0000000..42d81b6 --- /dev/null +++ b/test/test-eg/Cargo.toml @@ -0,0 +1,79 @@ +[package] +name = "test-eg" +version = "0.1.0" +edition = "2021" + +[features] +default = [ + "basic-array", + "crypto-bigint", + # required: "num-bigint", + "bits-256", + "bits-4096", + "zeroize" +] + +basic-array = [ "fixed-width-nonnegative/basic-array" ] +basic-array-u8 = [ "fixed-width-nonnegative/basic-array-u8" ] +basic-array-u16 = [ "fixed-width-nonnegative/basic-array-u16" ] +basic-array-u32 = [ "fixed-width-nonnegative/basic-array-u32" ] +basic-array-u64 = [ "fixed-width-nonnegative/basic-array-u64" ] +# basic-array-u128 = [ "fixed-width-nonnegative/basic-array-u128" ] TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry +crypto-bigint = [ "fixed-width-nonnegative/crypto-bigint" ] +hacl-rs = [ "fixed-width-nonnegative/hacl-rs" ] +hacl-rs-u32 = [ "fixed-width-nonnegative/hacl-rs-u32" ] +hacl-rs-u64 = [ "fixed-width-nonnegative/hacl-rs-u64" ] +# required: num-bigint = [ "fixed-width-nonnegative/num-bigint" ] +num-integer = [ "fixed-width-nonnegative/num-integer" ] +# bits-8 = [ "fixed-width-nonnegative/bits-8" ] TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry +# bits-16 = [ "fixed-width-nonnegative/bits-16" ] +# bits-32 = [ "fixed-width-nonnegative/bits-32" ] +# bits-64 = [ "fixed-width-nonnegative/bits-64" ] +bits-128 = [ "fixed-width-nonnegative/bits-128" ] +bits-256 = [ "fixed-width-nonnegative/bits-256" ] +bits-512 = [ "fixed-width-nonnegative/bits-512" ] +bits-1024 = [ "fixed-width-nonnegative/bits-1024" ] +bits-2048 = [ "fixed-width-nonnegative/bits-2048" ] +bits-4096 = [ "fixed-width-nonnegative/bits-4096" ] +zeroize = [ "fixed-width-nonnegative/zeroize" ] + +[dependencies] + +[dependencies.fixed-width-nonnegative] +workspace = true +features = [ + "bits-256", + "bits-4096", + "basic-array", + "basic-array-u64", + "crypto-bigint", + "hacl-rs", + "montgomery", + "num-bigint", + "zeroize" +] + +[dev-dependencies] +anyhow.workspace = true +criterion.workspace = true +lazy_static.workspace = true +crypto-bigint = { workspace = true, features = ["serde", "zeroize"] } +num-bigint = { workspace = true, features = [] } +# num-derive = { workspace = true, optional = true, features = [] } +# num-integer = { workspace = true, optional = true, features = ["std"] } +# num-traits = { workspace = true, optional = true, features = ["std", "i128"] } +rand.workspace = true +rand_core.workspace = true +rand_pcg.workspace = true +rayon.workspace = true +static_assertions.workspace = true + +bench-util.workspace = true +eg.workspace = true +eg.features = ["bench"] +eg-artifacts-dir.workspace = true +util.workspace = true + +[[bench]] +name = "bench" +harness = false diff --git a/test/test-eg/benches/b/b1_ballotnonce_generate.rs b/test/test-eg/benches/b/b1_ballotnonce_generate.rs new file mode 100644 index 0000000..7d3bca7 --- /dev/null +++ b/test/test-eg/benches/b/b1_ballotnonce_generate.rs @@ -0,0 +1,77 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![allow(non_snake_case)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use criterion::{black_box, Criterion}; + +use eg::nonce::BallotNonce; +use util::csprng::Csprng; + +//use bench_util::{CheapPrng, CheapPrng_from_u128}; + +use crate::b::*; + +pub fn bench(c: &mut Criterion) { + let bench_function_id = "b1 BallotNonce::generate"; + + let mut csprng = Csprng::new(bench_function_id.as_ref()); + + for (group_n, (warm_up_s, measurement_s, cnt_passes)) in groups_passes().enumerate() { + let group_name = format!("{bench_function_id} g{group_n}"); + + let mut group = c.benchmark_group(group_name); + group.warm_up_time(std::time::Duration::from_secs(warm_up_s)); + group.measurement_time(std::time::Duration::from_secs(measurement_s)); + + for pass_n in 1..=cnt_passes { + let str_pn = if cnt_passes == 1 { + String::new() + } else { + format!("_p{pass_n}") + }; + let id = format!("f{str_pn}"); + + group.bench_function(bench_function_id, |b| { + b.iter(|| black_box(BallotNonce::generate(&mut csprng))) + }); + } + + group.finish() + } +} diff --git a/test/test-eg/benches/b/b2_ballotnonce_derive_ballotcontestoptionfieldnonce.rs b/test/test-eg/benches/b/b2_ballotnonce_derive_ballotcontestoptionfieldnonce.rs new file mode 100644 index 0000000..48f659e --- /dev/null +++ b/test/test-eg/benches/b/b2_ballotnonce_derive_ballotcontestoptionfieldnonce.rs @@ -0,0 +1,96 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![allow(non_snake_case)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use criterion::{black_box, Criterion}; + +use eg::nonce::BallotNonce; +use eg_artifacts_dir::{load_hashes_ext, ArtifactsDir}; +use util::csprng::Csprng; + +//use bench_util::{CheapPrng, CheapPrng_from_u128}; + +use crate::b::*; + +#[rustfmt::skip] +pub fn bench(c: &mut Criterion) { + let bench_function_id = "b2 BallotNonce::derive_contest_option_field_nonce"; + + let h_ext = { + let artifacts_dir = ArtifactsDir::new().unwrap(); + load_hashes_ext(&artifacts_dir).unwrap() + }; + + let ballot_nonce = { + let mut csprng = Csprng::new(bench_function_id.as_ref()); + BallotNonce::generate(&mut csprng) + }; + + let i = eg::election_manifest::ContestIndex::MIN; + + let j = eg::election_manifest::ContestOptionIndex::MIN; + + let afn = [ + BallotNonce::derive_contest_option_field_nonce_a, + BallotNonce::derive_contest_option_field_nonce_b, + BallotNonce::derive_contest_option_field_nonce_c, + BallotNonce::derive_contest_option_field_nonce_d, + BallotNonce::derive_contest_option_field_nonce_e, + BallotNonce::derive_contest_option_field_nonce_f, + ]; + + for (group_n, (warm_up_s, measurement_s, cnt_passes)) in groups_passes().enumerate() { + let group_name = format!("{bench_function_id} g{group_n}"); + + let mut group = c.benchmark_group(group_name); + group.warm_up_time(std::time::Duration::from_secs(warm_up_s)); + group.measurement_time(std::time::Duration::from_secs(measurement_s)); + + for pass_n in 1 ..= cnt_passes { + let str_pn = if cnt_passes == 1 { String::new() } else { format!("_p{pass_n}") }; + + for (ix_fn_, fn_) in afn.iter().enumerate() { + let id = format!("dcofn_{}{str_pn}", ('a' as usize + ix_fn_.min(25)) as u8 as char); + group.bench_function(id, |b| { b.iter(|| black_box(fn_(&ballot_nonce, &h_ext, i, j))) }); + } + } + + group.finish() + } +} diff --git a/test/test-eg/benches/b/b3_encrypt_contestoptionselectionvalue.rs b/test/test-eg/benches/b/b3_encrypt_contestoptionselectionvalue.rs new file mode 100644 index 0000000..0ad4fbe --- /dev/null +++ b/test/test-eg/benches/b/b3_encrypt_contestoptionselectionvalue.rs @@ -0,0 +1,130 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![allow(non_snake_case)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use criterion::{black_box, Criterion}; +use eg::fixed_parameters::FixedParameters; +use num_bigint::BigUint; + +use eg::election_parameters; +use eg::joint_election_public_key::{Ciphertext, JointElectionPublicKey}; +use eg::nonce::BallotNonce; +use eg_artifacts_dir::{ + load_election_parameters, load_hashes_ext, load_joint_election_public_key, ArtifactsDir, +}; +use util::algebra::FieldElement; +use util::csprng::Csprng; + +//use bench_util::{CheapPrng, CheapPrng_from_u128}; + +use crate::b::*; + +#[rustfmt::skip] +pub fn bench(c: &mut Criterion) { + let bench_function_id = "b3 encrypt contest option field"; + + let artifacts_dir = ArtifactsDir::new().unwrap(); + + let mut csprng = Csprng::new(bench_function_id.as_ref()); + let csprng = &mut csprng; + + let election_parameters = load_election_parameters(&artifacts_dir, csprng).unwrap(); + let fixed_parameters: FixedParameters = election_parameters.fixed_parameters.clone(); + let h_ext = load_hashes_ext(&artifacts_dir).unwrap(); + + let joint_election_public_key = load_joint_election_public_key(&artifacts_dir, &election_parameters).unwrap(); + + let cof_nonce = { + let ballot_nonce = BallotNonce::generate(csprng); + + let i = eg::election_manifest::ContestIndex::MIN; + + let j = eg::election_manifest::ContestOptionIndex::MIN; + + BallotNonce::derive_contest_option_field_nonce(&ballot_nonce, &h_ext, i, j) + }; + + let cof_nonce_biguint = BigUint::from_bytes_be(cof_nonce.0.as_slice()); + + let field = &fixed_parameters.field; + + let cof_nonce_fieldelement = FieldElement::from( + cof_nonce_biguint, + &fixed_parameters.field ); + + let afn = [ + jepk_encrypt_to_a, + /* + jepk_encrypt_to_b, + // */ + ]; + + for (group_n, (warm_up_s, measurement_s, cnt_passes)) in groups_passes().enumerate() { + let group_name = format!("{bench_function_id} g{group_n}"); + + let mut group = c.benchmark_group(group_name); + group.warm_up_time(std::time::Duration::from_secs(warm_up_s)); + group.measurement_time(std::time::Duration::from_secs(measurement_s)); + + for pass_n in 1 ..= cnt_passes { + let str_pn = if cnt_passes == 1 { String::new() } else { format!("_p{pass_n}") }; + + let vote: usize = pass_n; + + for (ix_fn_, fn_) in afn.iter().enumerate() { + let id = format!("enc_{}{str_pn}", ('a' as usize + ix_fn_.min(25)) as u8 as char); + group.bench_function(id, |b| { b.iter(|| black_box(fn_( + &joint_election_public_key, &fixed_parameters, + &cof_nonce_fieldelement, + vote))) }); + } + } + + group.finish() + } +} + +fn jepk_encrypt_to_a( + jepk: &JointElectionPublicKey, + fixed_parameters: &FixedParameters, + cof_nonce_fieldelement: &FieldElement, + vote: usize, +) -> Ciphertext { + jepk.encrypt_to(fixed_parameters, cof_nonce_fieldelement, vote) +} diff --git a/test/test-eg/benches/b/b4_encrypt_ballot.rs b/test/test-eg/benches/b/b4_encrypt_ballot.rs new file mode 100644 index 0000000..3ff5e47 --- /dev/null +++ b/test/test-eg/benches/b/b4_encrypt_ballot.rs @@ -0,0 +1,604 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use std::borrow::Borrow; +use std::mem::{align_of, size_of}; + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use criterion::*; +use criterion::{black_box, Bencher, Criterion}; +use crypto_bigint::modular::Retrieve; +use lazy_static::lazy_static; +use num_bigint::BigUint; +use rand_core::RngCore; +use rayon::prelude::*; +use static_assertions::{assert_eq_size, const_assert, const_assert_eq}; + +use eg::election_manifest::{ContestIndex, ContestOptionIndex}; +use eg::election_parameters; +use eg::fixed_parameters::FixedParameters; +use eg::hash::HValueByteArray; +use eg::hashes_ext::HashesExt; +use eg::index::Index; +use eg::joint_election_public_key::JointElectionPublicKey; +use eg::nonce::BallotNonce; +use eg::vec1::{HasIndexTypeMarker, Vec1}; +use eg_artifacts_dir::{ + load_election_parameters, load_hashes_ext, load_joint_election_public_key, ArtifactsDir, +}; +use util::csprng::Csprng; + +use bench_util::*; + +use crate::b::*; + +// "Standard" ballot for benchmarking purposes +const STANDARD_BALLOT_CNT_CONTESTS: usize = 12; +const STANDARD_BALLOT_CNT_CONTEST_FIELDS: usize = 4; + +const PASS_BENCHFN_CNT_BALLOTS: usize = 48; // 100 +const BALLOT_TOTAL_CNT_FIELDS: usize = + STANDARD_BALLOT_CNT_CONTESTS * STANDARD_BALLOT_CNT_CONTEST_FIELDS; +const PASS_BENCHFN_TOTAL_CNT_FIELDS: usize = PASS_BENCHFN_CNT_BALLOTS * BALLOT_TOTAL_CNT_FIELDS; +const PASS_BENCHFN_TOTAL_CNT_MODEXPS: usize = PASS_BENCHFN_TOTAL_CNT_FIELDS * 2; + +type bau64_Nonnegative_4096 = fixed_width_nonnegative::basicarray_u64::Nonnegative_4096; +type bau64_Montgomery_4096 = fixed_width_nonnegative::basicarray_u64::Montgomery_4096; +type bau64_MontgomeryPrecomputation_4096 = fixed_width_nonnegative::basicarray_u64::MontgomeryPrecomputation_4096; +type crybi_Nonnegative_256 = fixed_width_nonnegative::cryptobigint::Nonnegative_256; +type crybi_Nonnegative_4096 = fixed_width_nonnegative::cryptobigint::Nonnegative_4096; +type numbi_Nonnegative_256 = fixed_width_nonnegative::numbigint::Nonnegative_256; +type numbi_Nonnegative_4096 = fixed_width_nonnegative::numbigint::Nonnegative_4096; + +fn numbigint_to_nonnegative256>(b: &T) -> Result { + let n: numbi_Nonnegative_256 = b.borrow().try_into()?; + Ok(n.into()) +} + +fn numbigint_to_nonnegative4096>( + b: &T, +) -> Result { + let n: numbi_Nonnegative_4096 = b.borrow().try_into()?; + Ok(n.into()) +} + +#[rustfmt::skip] +pub fn bench(c: &mut Criterion) { + let bench_function_id = format!("b4 enc {PASS_BENCHFN_CNT_BALLOTS} ballots"); + + eprintln!("\n================================================================ Bench: {bench_function_id}\n"); + + let min_cnt_threads = 8; + eprintln!("min_cnt_threads: {min_cnt_threads}"); + + let max_cnt_threads = rayon::max_num_threads(); + eprintln!("Rayon max_cnt_threads: {max_cnt_threads}"); + + let csprng = Csprng::new(bench_function_id.as_ref()); + + let mut bench_data = BenchData::new(csprng); + + let vfns: Vec<(&str, Box)> = vec![ + //("encrypt_ballots_st", Box::new(|bd: &mut BenchData| encrypt_ballots_st(bd))), + //("encrypt_ballots_mt", Box::new(|bd: &mut BenchData| encrypt_ballots_mt(bd))), + ("encrypt_ballots_mt_mg", Box::new(|bd: &mut BenchData| encrypt_ballots_mt_mg(bd))), + ("encrypt_ballots_mt_mg_et", Box::new(|bd: &mut BenchData| encrypt_ballots_mt_mg_et(bd))), + ]; + + let over_fn_cnt = (1 < vfns.len()).then(|| format!(", each over {} functions", vfns.len())) + .unwrap_or_default(); + + let v_group_pass_infos = group_pass_infos(); + for (group_n, group_pass_info) in v_group_pass_infos.iter().enumerate() { + let group_name = format!("{bench_function_id} g{group_n}"); + let cnt_passes = group_pass_info.cnt_passes; + let pass_digits = format!("{cnt_passes}").len(); + + eprintln!("\n================================================ Group {group_n} of {}", v_group_pass_infos.len()); + eprintln!("\n{group_pass_info:#?}"); + + eprintln!("\nRunning {cnt_passes:0wid$} passes{over_fn_cnt}.", wid=pass_digits); + + let mut group = c.benchmark_group(group_name); + group_pass_info.opt_warm_up_time_s.map(|s| group.warm_up_time(std::time::Duration::from_secs(s))); + group_pass_info.opt_target_measurement_time_s.map(|s| group.measurement_time(std::time::Duration::from_secs(s))); + group.sample_size(group_pass_info.sample_size); + + group.throughput(Throughput::Elements(PASS_BENCHFN_CNT_BALLOTS as u64)); + + for pass_n in 1 ..= cnt_passes { + let str_pn = (cnt_passes != 1).then(|| format!(" p{pass_n:0wid$}", wid=pass_digits)) + .unwrap_or_default(); + + eprintln!("\n============================== Pass {pass_n} of {cnt_passes}{over_fn_cnt}"); + + for (fn_ch, bx_fn) in &vfns { + //let diff_cnt_threads = max_cnt_threads.saturating_sub(min_cnt_threads).max(1); + //let cnt_threads = min_cnt_threads + (pass_n as usize % diff_cnt_threads); + let cnt_threads = PASS_BENCHFN_CNT_BALLOTS/4; + + let benchfn_id = format!("{fn_ch}{str_pn}_{cnt_threads}th"); + + eprintln!("\n------------------------- bench fn {benchfn_id}\n"); + + eprintln!("cnt_threads: {cnt_threads}"); + + group.bench_function(benchfn_id, |b| { + b.iter(|| { + bench_data.v_ballot_encryption_results.clear(); + bench_data.v_ballot_encryption_results.reserve(PASS_BENCHFN_CNT_BALLOTS); + bench_data.cnt_threads = cnt_threads; + + bx_fn(black_box(&mut bench_data)) + }) + }); + } + } + + group.finish(); + eprintln!("\n[group finish]"); + } + + eprintln!("\n[bench finish]"); +} + +//=================================================================================================| + +struct BallotStyle { + ballot_style_ix: BallotStyleIndex, + ballot_contest_field_indices: [(ContestIndex, ContestOptionIndex); BALLOT_TOTAL_CNT_FIELDS], +} +type BallotStyleIndex = Index; + +impl BallotStyle { + pub fn new_standard(ballot_style_ix: BallotStyleIndex) -> BallotStyle { + let mut bcfis = [(ContestIndex::MIN, ContestOptionIndex::MIN); BALLOT_TOTAL_CNT_FIELDS]; + + let mut bfci_ix = 0_usize; + for contest_ix in 1..=STANDARD_BALLOT_CNT_CONTESTS { + let i = ContestIndex::from_one_based_index(contest_ix.try_into().unwrap()).unwrap(); + + for contest_field_ix in 1..=STANDARD_BALLOT_CNT_CONTEST_FIELDS { + let j = + ContestOptionIndex::from_one_based_index(contest_field_ix.try_into().unwrap()) + .unwrap(); + + bcfis[bfci_ix] = (i, j); + bfci_ix += 1; + } + } + + BallotStyle { + ballot_style_ix, + ballot_contest_field_indices: bcfis, + } + } +} + +impl HasIndexTypeMarker for BallotStyle {} + +lazy_static! { + static ref BALLOT_STYLES: Vec1 = Vec1::try_from([BallotStyle::new_standard( + BallotStyleIndex::from_one_based_index_const(1).unwrap() + ),]) + .unwrap(); +} + +struct BenchData { + csprng: Csprng, + fixed_parameters: FixedParameters, + p: crybi_Nonnegative_4096, + q: crybi_Nonnegative_256, + g: crybi_Nonnegative_4096, + h_ext: HashesExt, + joint_election_public_key: JointElectionPublicKey, + ballot_style_ix: BallotStyleIndex, + selectioned_ballots: [SelectionedBallot; PASS_BENCHFN_CNT_BALLOTS], + cnt_threads: usize, + v_ballot_encryption_results: Vec, +} +impl BenchData { + pub fn new(csprng: Csprng) -> BenchData { + let mut csprng = csprng; + + let artifacts_dir = ArtifactsDir::new().unwrap(); + + let election_parameters = load_election_parameters(&artifacts_dir, &mut csprng).unwrap(); + let fixed_parameters = election_parameters.fixed_parameters.clone(); + + let p = numbigint_to_nonnegative4096(fixed_parameters.p()).unwrap(); + //eprintln!("\np={p:X}"); + + let q = numbigint_to_nonnegative256(fixed_parameters.q()).unwrap(); + //eprintln!("\nq={q:X}"); + + let g = numbigint_to_nonnegative4096(fixed_parameters.g()).unwrap(); + //eprintln!("\ng={g:X}"); + + let h_ext = load_hashes_ext(&artifacts_dir).unwrap(); + + let joint_election_public_key = + load_joint_election_public_key(&artifacts_dir, &election_parameters).unwrap(); + eprintln!( + "joint_election_public_key: {} bits", + joint_election_public_key.joint_election_public_key.as_biguint().bits() + ); + assert!(4000 < joint_election_public_key.joint_election_public_key.as_biguint().bits()); + + eprintln!("PASS_BENCHFN_CNT_BALLOTS: {PASS_BENCHFN_CNT_BALLOTS} ballots per bench pass - these are reported as 'elems' e.g 'Kelem/s"); + eprintln!( + "STANDARD_BALLOT_CNT_CONTESTS: {STANDARD_BALLOT_CNT_CONTESTS} contests per ballot" + ); + eprintln!("STANDARD_BALLOT_CNT_CONTEST_FIELDS: {STANDARD_BALLOT_CNT_CONTEST_FIELDS} fields per contest"); + eprintln!("STANDARD_BALLOT_TOTAL_CNT_FIELDS_IMPLIED: {BALLOT_TOTAL_CNT_FIELDS} total fields per ballot"); + eprintln!("PASS_BENCHFN_TOTAL_CNT_FIELDS {PASS_BENCHFN_TOTAL_CNT_FIELDS} fields per bench pass, total"); + eprintln!("PASS_BENCHFN_TOTAL_CNT_MODEXPS: {PASS_BENCHFN_TOTAL_CNT_MODEXPS} modular exponentiations per bench pass"); + + let ballot_style_ix = BallotStyleIndex::MIN; + let _ballot_style: &'static BallotStyle = &BALLOT_STYLES.get(ballot_style_ix).unwrap(); + + let selectioned_ballots = + std::array::from_fn(|_ix| SelectionedBallot::new(&mut csprng, ballot_style_ix)); + + BenchData { + csprng, + fixed_parameters, + p, + q, + g, + h_ext, + joint_election_public_key, + ballot_style_ix, + selectioned_ballots, + cnt_threads: 1, + v_ballot_encryption_results: Vec::with_capacity(PASS_BENCHFN_CNT_BALLOTS), + } + } +} + +struct SelectionedBallot { + ballot_style_ix: BallotStyleIndex, + ballot_contest_field_values: [u8; BALLOT_TOTAL_CNT_FIELDS], +} + +impl SelectionedBallot { + pub fn new(csprng: &mut Csprng, ballot_style_ix: BallotStyleIndex) -> Self { + let _ballot_style: &BallotStyle = &BALLOT_STYLES.get(ballot_style_ix).unwrap(); + + let mut ballot_contest_field_values = [0; BALLOT_TOTAL_CNT_FIELDS]; + + for bcfv in ballot_contest_field_values.iter_mut() { + *bcfv = csprng.next_bool().into(); + } + + SelectionedBallot { + ballot_style_ix, + ballot_contest_field_values, + } + } +} + +struct FieldCiphertext { + // Compare to: eg::joint_election_public_key::Ciphertext + pub alpha: crybi_Nonnegative_4096, + pub beta: crybi_Nonnegative_4096, +} + +struct EncryptedBallot { + ballot_style_ix: BallotStyleIndex, + encryptions: [FieldCiphertext; BALLOT_TOTAL_CNT_FIELDS], +} + +//? TODO replace with some kind of fill_random support on Nonnegative types +fn random_nn_256(csprng: &mut Csprng) -> crybi_Nonnegative_256 { + let mut aby = [0_u8; crybi_Nonnegative_256::BYTES]; + csprng.fill_bytes(&mut aby); + crybi_Nonnegative_256::from_be_bytes_arr(aby) +} + +fn encrypt_ballots_st(data: &mut BenchData) { + //eprintln!("encrypt_ballots_st"); + + assert_eq!(data.selectioned_ballots.len(), PASS_BENCHFN_CNT_BALLOTS); + assert!(data.v_ballot_encryption_results.is_empty()); + + /*std::array::from_fn(|ix| { + let selectioned_ballot = &data.selectioned_ballots[ix]; + BallotEncryptionResult::from_selectioned_ballot(&mut data.csprng, &data.h_ext, selectioned_ballot) + });// */ + + let it = data.selectioned_ballots.iter().map(|selectioned_ballot| { + BallotEncryptionResult::from_selectioned_ballot( + &mut data.csprng, + &data.fixed_parameters, + &data.h_ext, + &data.joint_election_public_key, + selectioned_ballot, + "encrypt_ballots_st", + ) + }); + + data.v_ballot_encryption_results.extend(it); +} + +fn encrypt_ballots_mt(data: &mut BenchData) { + encrypt_ballots_bc(data, "encrypt_ballots_mt"); +} + +fn encrypt_ballots_mt_mg(data: &mut BenchData) { + encrypt_ballots_bc(data, "encrypt_ballots_mt_mg"); +} + +fn encrypt_ballots_mt_mg_et(data: &mut BenchData) { + encrypt_ballots_bc(data, "encrypt_ballots_mt_mg_et"); +} + +fn encrypt_ballots_bc(data: &mut BenchData, fn_name: &str) { + //eprintln!("{name}"); + + assert_eq!(data.selectioned_ballots.len(), PASS_BENCHFN_CNT_BALLOTS); + assert!(data.v_ballot_encryption_results.is_empty()); + + let cnt_threads = data.cnt_threads.min(rayon::max_num_threads()); + + //eprintln!("Creating pool for {cnt_threads} threads."); + let pool = rayon::ThreadPoolBuilder::new() + .num_threads(cnt_threads) + .build() + .unwrap(); + + pool.install(|| { + data.selectioned_ballots + .par_iter() + .map_init( + || { + let ti = rayon::current_thread_index().unwrap_or(usize::MAX); + let aby_seed = ti.to_be_bytes(); + Csprng::new(&aby_seed) + }, + |csprng: &mut Csprng, selectioned_ballot| { + BallotEncryptionResult::from_selectioned_ballot( + csprng, + &data.fixed_parameters, + &data.h_ext, + &data.joint_election_public_key, + selectioned_ballot, + fn_name, + ) + }, + ) + .collect_into_vec(&mut data.v_ballot_encryption_results); + }); + + assert_eq!(data.selectioned_ballots.len(), PASS_BENCHFN_CNT_BALLOTS); + assert_eq!( + data.v_ballot_encryption_results.len(), + PASS_BENCHFN_CNT_BALLOTS + ); +} + +struct BallotEncryptionResult { + ballot_nonce: crybi_Nonnegative_256, + encrypted_ballot: EncryptedBallot, +} + +impl BallotEncryptionResult { + pub fn from_selectioned_ballot( + csprng: &mut Csprng, + fixed_parameters: &FixedParameters, + h_ext: &HashesExt, + joint_election_public_key: &JointElectionPublicKey, + selectioned_ballot: &SelectionedBallot, + fn_name: &str, + ) -> Self { + let ballot_style_ix = selectioned_ballot.ballot_style_ix; + let ballot_style: &BallotStyle = &BALLOT_STYLES.get(ballot_style_ix).unwrap(); + + let ballot_nonce: crybi_Nonnegative_256 = random_nn_256(csprng); + let ballot_nonce_eg = eg::nonce::BallotNonce(ballot_nonce.to_be_bytes_arr()); + + // Standard parameter 'p' in different formats. + + let p_bau64_Nonnegative_4096: bau64_Nonnegative_4096 = standard_parameter_p().into(); + let p_crybi_Nonnegative_4096: crybi_Nonnegative_4096 = (&p_bau64_Nonnegative_4096).into(); + let p_crybi_u4096: &::crypto_bigint::U4096 = p_crybi_Nonnegative_4096.as_ref(); + let p_bits = p_crybi_u4096.bits(); + assert_eq!(p_bits, 4096); + + let p_numbi_Nonnegative_4096: numbi_Nonnegative_4096 = numbi_Nonnegative_4096::from(&p_bau64_Nonnegative_4096); + let p_numbi_biguint: &num_bigint::BigUint = p_numbi_Nonnegative_4096.borrow(); + assert_eq!(p_numbi_biguint.bits(), p_bits as u64); + + let short_circuit = false; //???? + if short_circuit { + return BallotEncryptionResult { + ballot_nonce, + encrypted_ballot: EncryptedBallot { + ballot_style_ix, + encryptions: std::array::from_fn(|_| { + let alpha = p_crybi_Nonnegative_4096.clone(); //? + let beta = p_crybi_Nonnegative_4096.clone(); //? + FieldCiphertext { alpha, beta }}), + }, + } + }; + + // Standard parameter 'g' in different formats. + + let g_bau64_Nonnegative_4096: bau64_Nonnegative_4096 = standard_parameter_g().into(); + let g_crybi_Nonnegative_4096: crybi_Nonnegative_4096 = (&g_bau64_Nonnegative_4096).into(); + let g_crybi_u4096: &::crypto_bigint::U4096 = g_crybi_Nonnegative_4096.as_ref(); + let g_bits = g_crybi_u4096.bits(); + assert_eq!(g_bits, 4096); + + let g_numbi_Nonnegative_4096: numbi_Nonnegative_4096 = g_bau64_Nonnegative_4096.into(); + let g_numbi_biguint: &num_bigint::BigUint = g_numbi_Nonnegative_4096.borrow(); + assert_eq!(g_numbi_biguint.bits(), g_bits as u64); + + let jepk_numbi_biguint: &num_bigint::BigUint = joint_election_public_key.joint_election_public_key.as_biguint(); + + let encryptions = if fn_name == "encrypt_ballots_st" || fn_name == "encrypt_ballots_mt" { + // don't use_crybi_dynresidue + //eprintln_here(); + + std::array::from_fn(|ballot_field_ix| { + let (i, j) = ballot_style.ballot_contest_field_indices[ballot_field_ix]; + + let cof_nonce_eg = + BallotNonce::derive_contest_option_field_nonce(&ballot_nonce_eg, &h_ext, i, j); + let cof_nonce = crybi_Nonnegative_256::from_be_bytes_arr(cof_nonce_eg.0); + let cof_nonce_nb: numbi_Nonnegative_256 = cof_nonce.into(); + + let alpha_bu = g_numbi_biguint.modpow( + cof_nonce_nb.borrow(), p_numbi_biguint); + assert!(alpha_bu.bits() <= 4096); + let alpha_nb: numbi_Nonnegative_4096 = alpha_bu.try_into().unwrap(); + let alpha = crybi_Nonnegative_4096::from(&alpha_nb); + + let field_value = selectioned_ballot.ballot_contest_field_values[ballot_field_ix]; + + let cofn_plus_field_value_nb = cof_nonce_nb.wrapping_add_u8(field_value); + + let beta_bu = jepk_numbi_biguint.modpow( + cofn_plus_field_value_nb.borrow(), + p_numbi_biguint, + ); + assert!(beta_bu.bits() <= 4096); + let beta_nb: numbi_Nonnegative_4096 = beta_bu.try_into().unwrap(); + let beta: crybi_Nonnegative_4096 = beta_nb.into(); + + FieldCiphertext { alpha, beta } + }) + } else if fn_name == "encrypt_ballots_mt_mg" { + // use_crybi_dynresidue + //eprintln_there(); + + let p_crypbi_dynresidueparams = standard_parameter_p_dynresidueparams_cloned(); + let g_crybi_dynresidue = standard_parameter_g_dynresidue_mod_p(); + + let jepk_numbi_nn_4096: numbi_Nonnegative_4096 = jepk_numbi_biguint.try_into().unwrap(); + + //let jepk_bau64_nn_4096 = bau64_Nonnegative_4096::from(jepk_numbi_nn_4096); + let jepk_crybi_nn_4096 = crybi_Nonnegative_4096::from(jepk_numbi_nn_4096); + let jepk_crybi: &::crypto_bigint::U4096 = jepk_crybi_nn_4096.as_ref(); + let jepk_crybi_dynresidue = crybi_DynResidue_4096::new(&jepk_crybi, p_crypbi_dynresidueparams); + + std::array::from_fn(|ballot_field_ix| { + let (i, j) = ballot_style.ballot_contest_field_indices[ballot_field_ix]; + + let cof_nonce_eg = + BallotNonce::derive_contest_option_field_nonce(&ballot_nonce_eg, &h_ext, i, j); + let cof_nonce = crybi_Nonnegative_256::from_be_bytes_arr(cof_nonce_eg.0); + //let cof_nonce_nb: numbi_Nonnegative_256 = cof_nonce.into(); + let cof_nonce_crybi = cof_nonce.into_inner(); + + let alpha = g_crybi_dynresidue.pow(&cof_nonce_crybi); + let alpha: crybi_Nonnegative_4096 = alpha.retrieve().into(); + + // Beta + + let field_value = selectioned_ballot.ballot_contest_field_values[ballot_field_ix]; + + //let cofn_plus_field_value_nb = cof_nonce_nb.wrapping_add_u8(field_value); + let field_value = ::crypto_bigint::U256::from(field_value); + let cofn_plus_field_value_crybi = cof_nonce_crybi.adc(&field_value, crypto_bigint::Limb(0)).0; + + let beta = jepk_crybi_dynresidue.pow(&cofn_plus_field_value_crybi); + let beta: crybi_Nonnegative_4096 = beta.retrieve().into(); + + FieldCiphertext { alpha, beta } + }) + } else if fn_name == "encrypt_ballots_mt_mg_et" { + // use_crybi_dynresidue + //eprintln_there(); + + let p_crypbi_dynresidueparams = standard_parameter_p_dynresidueparams_cloned(); + let g_crybi_dynresidue = standard_parameter_g_dynresidue_mod_p(); + + let jepk_numbi_nn_4096: numbi_Nonnegative_4096 = jepk_numbi_biguint.try_into().unwrap(); + + //let jepk_bau64_nn_4096 = bau64_Nonnegative_4096::from(jepk_numbi_nn_4096); + let jepk_crybi_nn_4096 = crybi_Nonnegative_4096::from(jepk_numbi_nn_4096); + + let p_exptable = standard_parameter_exptable_g_p_4(); + let jepk_exptable = ExpTable4096::new( + jepk_crybi_nn_4096, + standard_parameter_p_dynresidueparams_cloned(), + 4 ).unwrap(); + + std::array::from_fn(|ballot_field_ix| { + let (i, j) = ballot_style.ballot_contest_field_indices[ballot_field_ix]; + + let cof_nonce_eg = + BallotNonce::derive_contest_option_field_nonce(&ballot_nonce_eg, &h_ext, i, j); + let cof_nonce = crybi_Nonnegative_256::from_be_bytes_arr(cof_nonce_eg.0); + //let cof_nonce_nb: numbi_Nonnegative_256 = cof_nonce.into(); + let cof_nonce_crybi = cof_nonce.into_inner(); + + let alpha = p_exptable.pow(cof_nonce_crybi).unwrap(); + let alpha: crybi_Nonnegative_4096 = alpha.retrieve().into(); + + // Beta + + let field_value = selectioned_ballot.ballot_contest_field_values[ballot_field_ix]; + + //let cofn_plus_field_value_nb = cof_nonce_nb.wrapping_add_u8(field_value); + let field_value = ::crypto_bigint::U256::from(field_value); + let cofn_plus_field_value_crybi = cof_nonce_crybi.adc(&field_value, crypto_bigint::Limb(0)).0; + + let beta = jepk_exptable.pow(cofn_plus_field_value_crybi).unwrap(); + let beta: crybi_Nonnegative_4096 = beta.retrieve().into(); + + FieldCiphertext { alpha, beta } + }) + } else { + assert_eq!(fn_name, "didn't match"); + todo!(); + }; + + BallotEncryptionResult { + ballot_nonce, + encrypted_ballot: EncryptedBallot { + ballot_style_ix, + encryptions, + }, + } + } +} diff --git a/test/test-eg/benches/b/b5_make_exptable.rs b/test/test-eg/benches/b/b5_make_exptable.rs new file mode 100644 index 0000000..c44e4fe --- /dev/null +++ b/test/test-eg/benches/b/b5_make_exptable.rs @@ -0,0 +1,260 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![allow(non_snake_case)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use std::borrow::Borrow; +use std::mem::{align_of, size_of}; +use std::num::Wrapping; + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use criterion::*; +use criterion::{black_box, Bencher, Criterion}; +use crypto_bigint::modular::Retrieve; +use fixed_width_nonnegative::primitive_unsigned::PrimitiveUnsigned; +use lazy_static::lazy_static; +use num_bigint::BigUint; +use rand_core::RngCore; +use rayon::prelude::*; +use static_assertions::{assert_eq_size, const_assert, const_assert_eq}; + +use eg::election_manifest::{ContestIndex, ContestOptionIndex}; +use eg::election_parameters; +use eg::fixed_parameters::FixedParameters; +use eg::hash::HValueByteArray; +use eg::hashes_ext::HashesExt; +use eg::index::Index; +use eg::joint_election_public_key::JointElectionPublicKey; +use eg::nonce::BallotNonce; +use eg::vec1::Vec1; +use eg_artifacts_dir::{ + load_election_parameters, load_hashes_ext, load_joint_election_public_key, ArtifactsDir, +}; +use util::csprng::Csprng; + +use bench_util::*; + +use crate::b::*; + +const DEFAULT_THREADS: usize = 8; +const PASS_BENCHFN_CNT_ITERS: usize = 10; + +pub(crate) fn group_pass_infos() -> Vec { + [ // warmup_s, meas_s, samp_sz, passes + ( None, None, 10, 1 ), + //( Some(1), Some(1), 10, 1 ), + //( Some(2), Some(5), 20, 1 ), + //( None, None, 100, 1 ), + //( Some(2), Some(5), 100, 1 ), + //( Some(3), Some(10), 100, 1 ), + ].iter() + .map(|&(opt_warm_up_time_s, opt_target_measurement_time_s, sample_size, cnt_passes)| { + if sample_size < 10 { + eprintln!("\nWARN: Sample size of {sample_size} is less than the minimum recommended value of 10"); + } + GroupPassInfo { opt_warm_up_time_s, opt_target_measurement_time_s, sample_size, cnt_passes } + }).collect() +} + +#[rustfmt::skip] +pub fn bench(c: &mut Criterion) { + let bench_function_id = format!("b5 make exptable"); + + eprintln!("\n================================================================ Bench: {bench_function_id}\n"); + + let min_cnt_threads = 1; + eprintln!("min_cnt_threads: {min_cnt_threads}"); + + let max_cnt_threads = rayon::max_num_threads(); + eprintln!("Rayon max_cnt_threads: {max_cnt_threads}"); + + let csprng = Csprng::new(bench_function_id.as_ref()); + + let p: crybi_Nonnegative_4096 = standard_parameter_p().into(); + + let vfns: Vec<(&str, Box<(dyn Fn(&u64) -> u64 + Send + Sync)>)> = vec![ + ("make_exptable_a", Box::new(make_exptable_a)), + ("use_exptable_a", Box::new(use_exptable_a)), + ]; + + let over_fn_cnt = (1 < vfns.len()).then(|| format!(", each over {} functions", vfns.len())) + .unwrap_or_default(); + + let v_group_pass_infos = group_pass_infos(); + for (group_n, group_pass_info) in v_group_pass_infos.iter().enumerate() { + let group_name = format!("{bench_function_id} g{group_n}"); + let cnt_passes = group_pass_info.cnt_passes; + let pass_digits = format!("{cnt_passes}").len(); + + eprintln!("\n================================================ Group {group_n} of {}", v_group_pass_infos.len()); + eprintln!("\n{group_pass_info:#?}"); + + eprintln!("\nRunning {cnt_passes:0wid$} passes{over_fn_cnt}.", wid=pass_digits); + + let mut group = c.benchmark_group(group_name); + group_pass_info.opt_warm_up_time_s.map(|s| group.warm_up_time(std::time::Duration::from_secs(s))); + group_pass_info.opt_target_measurement_time_s.map(|s| group.measurement_time(std::time::Duration::from_secs(s))); + group.sample_size(group_pass_info.sample_size); + + group.throughput(Throughput::Elements(PASS_BENCHFN_CNT_ITERS as u64)); + + for pass_n in 1 ..= cnt_passes { + let str_pn = (cnt_passes != 1).then(|| format!(" p{pass_n:0wid$}", wid=pass_digits)) + .unwrap_or_default(); + + eprintln!("\n============================== Pass {pass_n} of {cnt_passes}{over_fn_cnt}"); + + for (fn_ch, bx_fn) in &vfns { + //let diff_cnt_threads = max_cnt_threads.saturating_sub(min_cnt_threads).max(1); + //let cnt_threads = min_cnt_threads + (pass_n as usize % diff_cnt_threads); + //let cnt_threads = cnt_threads.min(rayon::max_num_threads()); + let cnt_threads = 24; //cnt_threads.min(rayon::max_num_threads()); + + let benchfn_id = format!("{fn_ch}{str_pn}_{cnt_threads}th"); + + eprintln!("\n------------------------- bench fn {benchfn_id}\n"); + + eprintln!("cnt_threads: {cnt_threads}"); + + let pool = rayon::ThreadPoolBuilder::new() + .num_threads(cnt_threads) + .build() + .unwrap(); + + let ref_fn: &_ = bx_fn.as_ref(); + + let src_data = vec![0_u64; cnt_threads]; + let mut result_data = Vec::::with_capacity(cnt_threads); + + group.bench_function(benchfn_id, |b| { + pool.install(|| { + b.iter(|| { + src_data + .par_iter() + .map(|x| ref_fn(x)) + .collect_into_vec(&mut result_data); + }) + }); + }); + } + } + + group.finish(); + eprintln!("\n[group finish]"); + } + + eprintln!("\n[bench finish]"); +} + +//=================================================================================================| + +use ::rand_core::SeedableRng; +use ::rand::Rng; + +pub fn make_exptable_a(src: &u64) -> u64 { + let mut rng = ::rand_pcg::Pcg64Mcg::seed_from_u64(*src); + let mut result = Wrapping(0_u64); + + let p: crybi_Nonnegative_4096 = standard_parameter_p().into(); + + for _ in 0..PASS_BENCHFN_CNT_ITERS { + let exptable = ExpTable4096::new( + p.clone(), + standard_parameter_p_dynresidueparams_cloned(), + 4 + ).unwrap(); + + result += exptable.get_something(&mut rng); + } + + result.0 +} + +pub fn use_exptable_a(src: &u64) -> u64 { + let mut rng = ::rand_pcg::Pcg64Mcg::seed_from_u64(*src); + let mut result = Wrapping(0_u64); + + let p_exptable = standard_parameter_exptable_g_p_4(); + + for _ in 0..PASS_BENCHFN_CNT_ITERS { + let mut exp = crybi_U256::ZERO; + for w in exp.as_words_mut() { + *w = rng.next_u64().into(); + #[allow(arithmetic_overflow)] + for _ in 1..(std::mem::size_of_val(w).div_ceil(8)) { + *w <<= 64; + *w = rng.next_u64().into(); + } + } + + let dynresidue = p_exptable.pow(exp).unwrap(); + let words = dynresidue.as_montgomery().as_words(); + assert!(words.len() != 0); + + result += words[rng.gen_range(0..words.len())] as u64; + } + + result.0 +} + +pub fn use_exptable_b(src: &u64) -> u64 { + let mut rng = ::rand_pcg::Pcg64Mcg::seed_from_u64(*src); + let mut result = Wrapping(0_u64); + + let p_exptable = standard_parameter_exptable_g_p_4(); + + for _ in 0..PASS_BENCHFN_CNT_ITERS { + let mut exp = crybi_U256::ZERO; + for w in exp.as_words_mut() { + *w = rng.next_u64().into(); + #[allow(arithmetic_overflow)] + for _ in 1..(std::mem::size_of_val(w).div_ceil(8)) { + *w <<= 64; + *w = rng.next_u64().into(); + } + } + + let dynresidue = p_exptable.pow(exp).unwrap(); + let words = dynresidue.as_montgomery().as_words(); + assert!(words.len() != 0); + + result += words[rng.gen_range(0..words.len())] as u64; + } + + result.0 +} diff --git a/test/test-eg/benches/b/b6_use_exptable.rs b/test/test-eg/benches/b/b6_use_exptable.rs new file mode 100644 index 0000000..f13a144 --- /dev/null +++ b/test/test-eg/benches/b/b6_use_exptable.rs @@ -0,0 +1,261 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![allow(non_snake_case)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_mut)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use std::borrow::Borrow; +use std::mem::{align_of, size_of}; +use std::num::Wrapping; + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use criterion::*; +use criterion::{black_box, Bencher, Criterion}; +use crypto_bigint::modular::Retrieve; +use fixed_width_nonnegative::primitive_unsigned::PrimitiveUnsigned; +use lazy_static::lazy_static; +use num_bigint::BigUint; +use rand_core::RngCore; +use rayon::prelude::*; +use static_assertions::{assert_eq_size, const_assert, const_assert_eq}; + +use eg::election_manifest::{ContestIndex, ContestOptionIndex}; +use eg::election_parameters; +use eg::fixed_parameters::FixedParameters; +use eg::hash::HValueByteArray; +use eg::hashes_ext::HashesExt; +use eg::index::Index; +use eg::joint_election_public_key::JointElectionPublicKey; +use eg::nonce::BallotNonce; +use eg::vec1::Vec1; +use eg_artifacts_dir::{ + load_election_parameters, load_hashes_ext, load_joint_election_public_key, ArtifactsDir, +}; +use util::csprng::Csprng; + +use bench_util::*; + +use crate::b::*; + +const DEFAULT_THREADS: usize = 8; +const PASS_BENCHFN_CNT_ITERS: usize = 1; + +pub(crate) fn group_pass_infos() -> Vec { + [ // warmup_s, meas_s, samp_sz, passes + ( None, None, 10, 9 ), + //( Some(1), Some(1), 10, 1 ), + //( Some(2), Some(5), 20, 1 ), + //( None, None, 100, 1 ), + //( Some(2), Some(5), 100, 1 ), + //( Some(3), Some(10), 100, 1 ), + ].iter() + .map(|&(opt_warm_up_time_s, opt_target_measurement_time_s, sample_size, cnt_passes)| { + if sample_size < 10 { + eprintln!("\nWARN: Sample size of {sample_size} is less than the minimum recommended value of 10"); + } + GroupPassInfo { opt_warm_up_time_s, opt_target_measurement_time_s, sample_size, cnt_passes } + }).collect() +} + +#[rustfmt::skip] +pub fn bench(c: &mut Criterion) { + let bench_function_id = format!("b6 use exptable"); + + eprintln!("\n================================================================ Bench: {bench_function_id}\n"); + + //let min_cnt_threads = 1; + //eprintln!("min_cnt_threads: {min_cnt_threads}"); + + //let max_cnt_threads = rayon::max_num_threads(); + //eprintln!("Rayon max_cnt_threads: {max_cnt_threads}"); + + let csprng = Csprng::new(bench_function_id.as_ref()); + + let p: crybi_Nonnegative_4096 = standard_parameter_p().into(); + + //let vfns: Vec<(&str, Box<(dyn Fn(&u64) -> u64 + Send + Sync)>)> = vec![ + // ("use_exptables", Box::new(use_exptables)), + //]; + let vfns = vec![0]; + + let over_fn_cnt = (1 < vfns.len()).then(|| format!(", each over {} functions", vfns.len())) + .unwrap_or_default(); + + let v_group_pass_infos = group_pass_infos(); + for (group_n, group_pass_info) in v_group_pass_infos.iter().enumerate() { + let group_name = format!("{bench_function_id} g{group_n}"); + let cnt_passes = group_pass_info.cnt_passes; + let pass_digits = format!("{cnt_passes}").len(); + + eprintln!("\n================================================ Group {group_n} of {}", v_group_pass_infos.len()); + eprintln!("\n{group_pass_info:#?}"); + + eprintln!("\nMaking exptables...\n"); + let v_exptables = make_exptables(cnt_passes); + + eprintln!("\nRunning {cnt_passes:0wid$} passes{over_fn_cnt}.", wid=pass_digits); + + let mut group = c.benchmark_group(group_name); + group_pass_info.opt_warm_up_time_s.map(|s| group.warm_up_time(std::time::Duration::from_secs(s))); + group_pass_info.opt_target_measurement_time_s.map(|s| group.measurement_time(std::time::Duration::from_secs(s))); + group.sample_size(group_pass_info.sample_size); + + //group.throughput(Throughput::Elements(PASS_BENCHFN_CNT_ITERS as u64)); + + for pass_n in 1 ..= cnt_passes { + let bits = pass_n.max(1); + let str_pn = (cnt_passes != 1).then(|| format!(" p{pass_n:0wid$}", wid=pass_digits)) + .unwrap_or_default(); + + eprintln!("\n============================== Pass {pass_n} of {cnt_passes}{over_fn_cnt} bits {bits}"); + + //for (fn_ch, bx_fn) in &vfns { + //let fn_ch = 'x'; + //let diff_cnt_threads = max_cnt_threads.saturating_sub(min_cnt_threads).max(1); + //let cnt_threads = min_cnt_threads + (pass_n as usize % diff_cnt_threads); + //let cnt_threads = cnt_threads.min(rayon::max_num_threads()); + //let cnt_threads = 12; //cnt_threads.min(rayon::max_num_threads()); + let cnt_threads = 1; //cnt_threads.min(rayon::max_num_threads()); + + let benchfn_id = format!("{str_pn}_b{bits}"); + + eprintln!("\n------------------------- bench fn {benchfn_id}\n"); + + let id = format!("f{str_pn}"); + + let exptable = &v_exptables[pass_n - 1].clone(); + + group.bench_function(benchfn_id, |b| { + b.iter(|| black_box(use_exptable(pass_n, exptable))) + }); + + /* + eprintln!("cnt_threads: {cnt_threads}"); + + let pool = rayon::ThreadPoolBuilder::new() + .num_threads(cnt_threads) + .build() + .unwrap(); + + let ref_fn: &_ = bx_fn.as_ref(); + + //let src_data = vec![0_u64; cnt_threads]; + let src_data = v_exptables; + let mut result_data = Vec::::with_capacity(cnt_threads); + + group.bench_function(benchfn_id, |b| { + pool.install(|| { + b.iter(|| { + src_data + .par_iter() + .map(|x| ref_fn(x)) + .collect_into_vec(&mut result_data); + }) + }); + }); + // */ + //} + } + + group.finish(); + eprintln!("\n[group finish]"); + } + + eprintln!("\n[bench finish]"); +} + +//=================================================================================================| + +use ::rand_core::SeedableRng; +use ::rand::Rng; + +fn make_exptables(cnt_passes: usize) -> Vec { + let cnt_threads = 24; //cnt_threads.min(rayon::max_num_threads()); + eprintln!("\nMaking exptables, cnt_threads = {cnt_threads}"); + + let pool = rayon::ThreadPoolBuilder::new() + .num_threads(cnt_threads) + .build() + .unwrap(); + + let src_data = (1_usize..=cnt_passes).into_iter().collect::>(); + + let mut result_exptables = Vec::::with_capacity(cnt_passes); + + pool.install(|| { + src_data + .par_iter() + .map(|&bits_per_col| { + eprintln!("Making exptable bit_per_col={bits_per_col}"); + let exptable = ExpTable4096::new( + standard_parameter_p().into(), // base: crybi_Nonnegative_4096, + standard_parameter_p_dynresidueparams_cloned(), // modulus_dynresidueparams: crybi_DynResidueParams_4096, + bits_per_col + ).unwrap(); + eprintln!("Exptable bit_per_col={bits_per_col} finished."); + exptable + }) + .collect_into_vec(&mut result_exptables); + }); + + result_exptables +} + + +pub fn use_exptable(pass_n: usize, exptable: &ExpTable4096) -> u64 { + let mut rng = ::rand_pcg::Pcg64Mcg::seed_from_u64(pass_n as u64); + let mut result = Wrapping(0_u64); + + let mut exp = crybi_U256::ZERO; + for w in exp.as_words_mut() { + *w = rng.next_u64().into(); + #[allow(arithmetic_overflow)] + for _ in 1..(std::mem::size_of_val(w).div_ceil(8)) { + *w <<= 64; + *w = rng.next_u64().into(); + } + } + + let dynresidue = exptable.pow(exp).unwrap(); + let words = dynresidue.as_montgomery().as_words(); + assert!(words.len() != 0); + + result += words[rng.gen_range(0..words.len())] as u64; + + result.0 +} + diff --git a/test/test-eg/benches/b/mod.rs b/test/test-eg/benches/b/mod.rs new file mode 100644 index 0000000..9fd8239 --- /dev/null +++ b/test/test-eg/benches/b/mod.rs @@ -0,0 +1,63 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +pub mod b1_ballotnonce_generate; +pub mod b2_ballotnonce_derive_ballotcontestoptionfieldnonce; +pub mod b3_encrypt_contestoptionselectionvalue; +pub mod b4_encrypt_ballot; +pub mod b5_make_exptable; +pub mod b6_use_exptable; + +#[derive(Debug, Default)] +pub(crate) struct GroupPassInfo { + opt_warm_up_time_s: Option, + opt_target_measurement_time_s: Option, + sample_size: usize, + cnt_passes: usize, +} + +#[rustfmt::skip] +pub(crate) fn group_pass_infos() -> Vec { + [ // warmup_s, meas_s, samp_sz, passes + //( None, None, 20, 16 ), + //( Some(1), Some(1), 10, 1 ), + //( Some(2), Some(5), 20, 1 ), + ( None, None, 100, 1 ), + //( Some(2), Some(5), 100, 1 ), + //( Some(3), Some(10), 100, 1 ), + ].iter() + .map(|&(opt_warm_up_time_s, opt_target_measurement_time_s, sample_size, cnt_passes)| { + if sample_size < 10 { + eprintln!("\nWARN: Sample size of {sample_size} is less than the minimum recommended value of 10"); + } + GroupPassInfo { opt_warm_up_time_s, opt_target_measurement_time_s, sample_size, cnt_passes } + }).collect() +} + +#[rustfmt::skip] +pub(crate) fn groups_passes() -> impl Iterator { + vec![ + (3_u64, 10_u64, 1), + ].into_iter() +} diff --git a/test/test-eg/benches/bench.rs b/test/test-eg/benches/bench.rs new file mode 100644 index 0000000..22fc767 --- /dev/null +++ b/test/test-eg/benches/bench.rs @@ -0,0 +1,55 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(non_snake_case)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +mod b; + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; + +#[rustfmt::skip] +criterion_group!( + benches, + b::b1_ballotnonce_generate::bench, + b::b2_ballotnonce_derive_ballotcontestoptionfieldnonce::bench, + b::b3_encrypt_contestoptionselectionvalue::bench, + b::b4_encrypt_ballot::bench, + b::b5_make_exptable::bench, + b::b6_use_exptable::bench, +); +criterion_main!(benches); diff --git a/test/test-eg/src/lib.rs b/test/test-eg/src/lib.rs new file mode 100644 index 0000000..012838b --- /dev/null +++ b/test/test-eg/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +//use anyhow::{anyhow, bail, ensure, Context, Result}; diff --git a/test/test-fixed-width-nonnegative/Cargo.toml b/test/test-fixed-width-nonnegative/Cargo.toml new file mode 100644 index 0000000..db80bc5 --- /dev/null +++ b/test/test-fixed-width-nonnegative/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "test-fixed-width-nonnegative" +version = "0.1.0" +edition = "2021" + +[features] +default = [ + "basic-array", + "crypto-bigint", + "num-bigint", + "bits-256", + "bits-4096", + "zeroize" +] + +basic-array = [ "fixed-width-nonnegative/basic-array" ] +basic-array-u8 = [ "fixed-width-nonnegative/basic-array-u8" ] +basic-array-u16 = [ "fixed-width-nonnegative/basic-array-u16" ] +basic-array-u32 = [ "fixed-width-nonnegative/basic-array-u32" ] +basic-array-u64 = [ "fixed-width-nonnegative/basic-array-u64" ] +# basic-array-u128 = [ "fixed-width-nonnegative/basic-array-u128" ] TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry +crypto-bigint = [ "fixed-width-nonnegative/crypto-bigint" ] +hacl-rs = [ "fixed-width-nonnegative/hacl-rs" ] +hacl-rs-u32 = [ "fixed-width-nonnegative/hacl-rs-u32" ] +hacl-rs-u64 = [ "fixed-width-nonnegative/hacl-rs-u64" ] +num-bigint = [ "fixed-width-nonnegative/num-bigint" ] +num-integer = [ "fixed-width-nonnegative/num-integer" ] +# bits-8 = [ "fixed-width-nonnegative/bits-8" ] TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry +bits-16 = [ "fixed-width-nonnegative/bits-16" ] +bits-32 = [ "fixed-width-nonnegative/bits-32" ] +bits-64 = [ "fixed-width-nonnegative/bits-64" ] +bits-128 = [ "fixed-width-nonnegative/bits-128" ] +bits-256 = [ "fixed-width-nonnegative/bits-256" ] +bits-512 = [ "fixed-width-nonnegative/bits-512" ] +bits-1024 = [ "fixed-width-nonnegative/bits-1024" ] +bits-2048 = [ "fixed-width-nonnegative/bits-2048" ] +bits-4096 = [ "fixed-width-nonnegative/bits-4096" ] +zeroize = [ "fixed-width-nonnegative/zeroize" ] + +[dependencies] +cfg-if.workspace = true +diesel = { version = "2.1.4", default-features = false, features = ["sqlite"] } +dotenvy = { version = "0.15.7", default-features = false, features = [] } +num-traits = { version = "0.2.17", default-features = false, features = ["std"] } +static_assertions.workspace = true + +[dependencies.fixed-width-nonnegative] +workspace = true +features = [ ] + +[dev-dependencies] +anyhow.workspace = true +bench-util.workspace = true +cfg-if.workspace = true +criterion.workspace = true +rand.workspace = true +util.workspace = true + +# # This is so we can use `--verbose` with `cargo bench` and criterion +# [[bin]] +# name = "test-fixed-width-nonnegative" +# test = true +# bench = false + +[[bench]] +name = "bench" +harness = false diff --git a/test/test-fixed-width-nonnegative/benches/bench.rs b/test/test-fixed-width-nonnegative/benches/bench.rs new file mode 100644 index 0000000..0dcf13e --- /dev/null +++ b/test/test-fixed-width-nonnegative/benches/bench.rs @@ -0,0 +1,323 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +//#![rustfmt::skip] + +#![allow(clippy::assertions_on_constants)] +#![deny(clippy::unwrap_used)] +#![deny(clippy::expect_used)] +#![deny(clippy::panic)] +#![deny(clippy::manual_assert)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +//use std::{ +//}; + +use anyhow::{anyhow, bail, ensure, Context, Result}; + +/* +use cfg_if::cfg_if; +use criterion::{criterion_group, criterion_main}; +use num_traits::identities::Zero; +use rand::Rng; + +use bench_util::CheapPrng_new; + +//=================================================================================================| + +macro_rules! test_bits3 { + ($bits:literal, $module:ident, $type:ident) => { + mod $module { + const TEST_BITS: usize = 256; + //type Nonnegative = $type; + include!("tests-impl-n.inc.rs"); + } + }; +} + +macro_rules! test_bits2 { +// ( 8 ) => { #[cfg( feature="bits-8" )] test_bits3!( 8, bits_8, Nonnegative_8 ); }; TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + ( 16 ) => { #[cfg( feature="bits-16" )] test_bits3!( 16, bits_16, Nonnegative_16 ); }; + ( 32 ) => { #[cfg( feature="bits-32" )] test_bits3!( 32, bits_32, Nonnegative_32 ); }; + ( 64 ) => { #[cfg( feature="bits-64" )] test_bits3!( 64, bits_64, Nonnegative_64 ); }; + ( 128 ) => { #[cfg( feature="bits-128" )] test_bits3!( 128, bits_128, Nonnegative_128 ); }; + ( 256 ) => { #[cfg( feature="bits-256" )] test_bits3!( 256, bits_256, Nonnegative_256 ); }; + ( 512 ) => { #[cfg( feature="bits-512" )] test_bits3!( 512, bits_512, Nonnegative_512 ); }; + ( 1024 ) => { #[cfg( feature="bits-1024" )] test_bits3!( 1024, bits_1024, Nonnegative_1024 ); }; + ( 2048 ) => { #[cfg( feature="bits-2048" )] test_bits3!( 2048, bits_2048, Nonnegative_2048 ); }; + ( 4096 ) => { #[cfg( feature="bits-4096" )] test_bits3!( 4096, bits_4096, Nonnegative_4096 ); }; + ( $bits:literal ) => { } +} + +//=================================================================================================| + +cfg_if! { if #[cfg( + all( feature="basic-array", + any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64", + feature="basic-array-u128" ), + any( // all( feature="bits-8", feature="basic-array-u8" ),TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + all( feature="bits-16", any( feature="basic-array-u8", + feature="basic-array-u16" ) ), + all( feature="bits-32", any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32" ) ), + all( feature="bits-64", any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64" ) ), + all( feature="bits-128", any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64", + feature="basic-array-u128" ) ), + feature="bits-256", + feature="bits-512", + feature="bits-1024", + feature="bits-2048", + feature="bits-4096" ) ) +)] { + macro_rules! test_bits { + ( 8 ) => { #[cfg( // all( feature="bits-8", TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + feature="basic-array-u8" ) )] test_bits2!( 8 ); }; + ( 16 ) => { #[cfg( all( feature="bits-16", + any( feature="basic-array-u8", + feature="basic-array-u16" ) ) )] test_bits2!( 16 ); }; + ( 32 ) => { #[cfg( all( feature="bits-32", + any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32" ) ) )] test_bits2!( 32 ); }; + ( 64 ) => { #[cfg( all( feature="bits-64", + any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64" ) ) )] test_bits2!( 64 ); }; + ( 128 ) => { #[cfg( all( feature="bits-128", + any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64", + feature="basic-array-u128" ) ) )] test_bits2!( 128 ); }; + ( 256 ) => { #[cfg( feature="bits-256" )] test_bits2!( 256 ); }; + ( 512 ) => { #[cfg( feature="bits-512" )] test_bits2!( 512 ); }; + ( 1024 ) => { #[cfg( feature="bits-1024" )] test_bits2!( 1024 ); }; + ( 2048 ) => { #[cfg( feature="bits-2048" )] test_bits2!( 2048 ); }; + ( 4096 ) => { #[cfg( feature="bits-4096" )] test_bits2!( 4096 ); }; + ( $bits:literal ) => { }; + } + + /* + criterion_group!( + benches_basicarray, + bench_basicarray::u8::b8::bench, + bench_basicarray::u8::b16::bench, + bench_basicarray::u8::b32::bench, + bench_basicarray::u8::b64::bench, + bench_basicarray::u8::b128::bench, + bench_basicarray::u8::b256::bench, + bench_basicarray::u8::b512::bench, + bench_basicarray::u8::b1024::bench, + bench_basicarray::u8::b2048::bench, + bench_basicarray::u8::b4096::bench, + bench_basicarray::u16::b8::bench, + bench_basicarray::u16::b16::bench, + bench_basicarray::u16::b32::bench, + bench_basicarray::u16::b64::bench, + bench_basicarray::u16::b128::bench, + bench_basicarray::u16::b256::bench, + bench_basicarray::u16::b512::bench, + bench_basicarray::u16::b1024::bench, + bench_basicarray::u16::b2048::bench, + bench_basicarray::u16::b4096::bench, + bench_basicarray::u32::b8::bench, + bench_basicarray::u32::b16::bench, + bench_basicarray::u32::b32::bench, + bench_basicarray::u32::b64::bench, + bench_basicarray::u32::b128::bench, + bench_basicarray::u32::b256::bench, + bench_basicarray::u32::b512::bench, + bench_basicarray::u32::b1024::bench, + bench_basicarray::u32::b2048::bench, + bench_basicarray::u32::b4096::bench, + bench_basicarray::u64::b8::bench, + bench_basicarray::u64::b16::bench, + bench_basicarray::u64::b32::bench, + bench_basicarray::u64::b64::bench, + bench_basicarray::u64::b128::bench, + bench_basicarray::u64::b256::bench, + bench_basicarray::u64::b512::bench, + bench_basicarray::u64::b1024::bench, + bench_basicarray::u64::b2048::bench, + bench_basicarray::u64::b4096::bench, + bench_basicarray::u128::b8::bench, + bench_basicarray::u128::b16::bench, + bench_basicarray::u128::b32::bench, + bench_basicarray::u128::b64::bench, + bench_basicarray::u128::b128::bench, + bench_basicarray::u128::b256::bench, + bench_basicarray::u128::b512::bench, + bench_basicarray::u128::b1024::bench, + bench_basicarray::u128::b2048::bench, + bench_basicarray::u128::b4096::bench, + ); + */ + + #[cfg(feature="basic-array-u8")] mod basicarray_u8 { use fixed_width_nonnegative::basicarray_u8::*; include!("bench/bench-impl.inc.rs"); } + #[cfg(feature="basic-array-u16")] mod basicarray_u16 { use fixed_width_nonnegative::basicarray_u16::*; include!("bench/bench-impl.inc.rs"); } + #[cfg(feature="basic-array-u32")] mod basicarray_u32 { use fixed_width_nonnegative::basicarray_u32::*; include!("bench/bench-impl.inc.rs"); } + #[cfg(feature="basic-array-u64")] mod basicarray_u64 { use fixed_width_nonnegative::basicarray_u64::*; include!("bench/bench-impl.inc.rs"); } + #[cfg(feature="basic-array-u128")] mod basicarray_u128 { use fixed_width_nonnegative::basicarray_u128::*; include!("bench/bench-impl.inc.rs"); } +} } // if cfg_if! + +//=================================================================================================| + +cfg_if! { if #[cfg( + all( feature="crypto-bigint", + any( feature="bits-64", + feature="bits-128", + feature="bits-256", + feature="bits-512", + feature="bits-1024", + feature="bits-2048", + feature="bits-4096" ) ) +)] { + macro_rules! test_bits { + ( 8) => { test_bits2!( 8 ); }; + ( 16) => { test_bits2!( 16 ); }; + ( 32) => { test_bits2!( 32 ); }; + ( 64) => { test_bits2!( 64 ); }; + ( 128) => { test_bits2!( 128 ); }; + ( 256) => { test_bits2!( 256 ); }; + ( 512) => { test_bits2!( 512 ); }; + (1024) => { test_bits2!( 1024 ); }; + (2048) => { test_bits2!( 2048 ); }; + (4096) => { test_bits2!( 4096 ); }; + ( $bits:literal ) => { }; + } + + pub(crate) mod cryptobigint { + use fixed_width_nonnegative::cryptobigint::*; + #[cfg(feature="bits-256")] pub(crate) mod b256 { + const FUNCTION_ID: &'static str = "b256-cryptobigint"; + include!("bench/bench-impl-n.inc.rs"); } + #[cfg(feature="bits-4096")] pub(crate) mod b4096 { + const FUNCTION_ID: &'static str = "b4096-cryptobigint"; + include!("bench/bench-impl-n.inc.rs"); } + } +} } // if cfg_if! + +//=================================================================================================| + +cfg_if! { if #[cfg( + all( feature="hacl-rs", + any( feature="hacl-rs-u32", + feature="hacl-rs-u64" ), + any( feature="bits-256", + feature="bits-4096" ) ) +)] { + macro_rules! test_bits { + ( 256 ) => { test_bits2!( 256 ); }; + ( 4096 ) => { test_bits2!( 4096 ); }; + ( $bits:literal ) => { }; + } + + #[cfg(all(feature="hacl-rs", feature="hacl-rs-u32"))] pub(crate) mod haclrs_u32 { + use fixed_width_nonnegative::haclrs_u32::*; + #[cfg(feature="bits-256" )] pub(crate) mod b256 { const FUNCTION_ID: &'static str = "b256-haclrs-u32"; include!("bench/bench-impl-n.inc.rs"); } + #[cfg(feature="bits-4096")] pub(crate) mod b4096 { const FUNCTION_ID: &'static str = "b4096-haclrs-u32"; include!("bench/bench-impl-n.inc.rs"); } + } + + #[cfg(all(feature="hacl-rs", feature="hacl-rs-u64"))] pub(crate) mod haclrs_u64 { + use fixed_width_nonnegative::haclrs_u64::*; + #[cfg(feature="bits-256" )] pub(crate) mod b256 { const FUNCTION_ID: &'static str = "b256-haclrs-u64"; include!("bench/bench-impl-n.inc.rs"); } + #[cfg(feature="bits-4096")] pub(crate) mod b4096 { const FUNCTION_ID: &'static str = "b4096-haclrs-u64"; include!("bench/bench-impl-n.inc.rs"); } + } +} } // if cfg_if! + +//=================================================================================================| + +cfg_if! { if #[cfg( + all( feature="num-bigint", + any( // feature="bits-8",TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + feature="bits-16", + feature="bits-32", + feature="bits-64", + feature="bits-128", + feature="bits-256", + feature="bits-512", + feature="bits-1024", + feature="bits-2048", + feature="bits-4096" ) ) +)] { + macro_rules! test_bits { + ( 8 ) => { test_bits2!( 8 ); }; + ( 16 ) => { test_bits2!( 16 ); }; + ( 32 ) => { test_bits2!( 32 ); }; + ( 64 ) => { test_bits2!( 64 ); }; + ( 128 ) => { test_bits2!( 128 ); }; + ( 256 ) => { test_bits2!( 256 ); }; + ( 512 ) => { test_bits2!( 512 ); }; + ( 1024 ) => { test_bits2!( 1024 ); }; + ( 2048 ) => { test_bits2!( 2048 ); }; + ( 4096 ) => { test_bits2!( 4096 ); }; + ( $bits:literal ) => { }; + } + + /* + mod bench_numbigint { + #[cfg(feature = "num-bigint")] use fixed_width_nonnegative::numbigint::*; + #[cfg(feature = "num-bigint")] include!("bench/bench-impl.inc.rs"); + } + //#[cfg(feature = "num-bigint")] criterion_group!(benches_numbigint, bench_numbigint::b256::bench, bench_numbigint::b4096::bench); + */ + + mod numbigint { use fixed_width_nonnegative::numbigint::*; include!("bench/bench-impl.inc.rs"); } +} } // if cfg_if! + +//=================================================================================================| + +cfg_if! { if #[cfg(all( feature="bits-256", feature="crypto-bigint" ))] { criterion_group!( b256, cryptobigint :: b256 :: bench ); } } +cfg_if! { if #[cfg(all( feature="bits-256", feature="hacl-rs", feature="hacl-rs-u32" ))] { criterion_group!( b256, haclrs_u32 :: b256 :: bench ); } } +cfg_if! { if #[cfg(all( feature="bits-256", feature="hacl-rs", feature="hacl-rs-u64" ))] { criterion_group!( b256, haclrs_u64 :: b256 :: bench ); } } +cfg_if! { if #[cfg(all( feature="bits-4096", feature="crypto-bigint" ))] { criterion_group!( b4096, cryptobigint :: b4096 :: bench ); } } +cfg_if! { if #[cfg(all( feature="bits-4096", feature="hacl-rs", feature="hacl-rs-u32" ))] { criterion_group!( b4096, haclrs_u32 :: b4096 :: bench ); } } +cfg_if! { if #[cfg(all( feature="bits-4096", feature="hacl-rs", feature="hacl-rs-u64" ))] { criterion_group!( b4096, haclrs_u64 :: b4096 :: bench ); } } +// */ + +fn main() -> Result<()> { + /* + #[cfg(feature="bits-256" )] b256(); + #[cfg(feature="bits-4096")] b4096(); + // */ + Ok(()) +} diff --git a/test/test-fixed-width-nonnegative/benches/bench/bench-impl-n.inc.rs b/test/test-fixed-width-nonnegative/benches/bench/bench-impl-n.inc.rs new file mode 100644 index 0000000..e81db49 --- /dev/null +++ b/test/test-fixed-width-nonnegative/benches/bench/bench-impl-n.inc.rs @@ -0,0 +1,162 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +// NOTE: This file is intended to be include!()ed, in a specific context and not built directly. + +use super::*; +use criterion::{black_box, BatchSize, Criterion}; +use bench_util::CheapPrng; +//? type FwnnN = Fwnn::; + +pub(crate) fn bench(c: &mut Criterion) { + /* + let bench_param_s = format!("Fwnn<{}>", FwnnN::BITS ); + let bench_function_id = format!("{}::{bench_param_s}", FwnnN::UNDERLYING_BIGNUM_IMPLEMENTATION); + + let test_data = create_data(&bench_param_s); + + c.bench_function( + &bench_function_id, + move |b| { + b.iter_batched( + || test_data.clone(), + TestData::bench_function, + BatchSize::SmallInput + ) + }); + // */ + let bench_function_id = "bench_function_id_todo"; + c.bench_function( + bench_function_id, + move |b| { + b.iter(|| black_box(black_box(3_u128)*17_u128)) + }); + } +/* + +fn create_data(bench_param_s: &str) -> TestData { + let prng = CheapPrng_new(&bench_param_s); + + let cnt_n = 1000_usize; + let cnt_n = 5_usize; + + let cnt_passes = 1000_usize; + let cnt_passes = 10_usize; + + //? TODO initialize randomly + let v = vec![FwnnN::zero(); cnt_n]; + + let v_ix: Vec = Vec::with_capacity(3); + + TestData { + prng, + cnt_passes, + v, + } +} + +fn some_dupes(a: &[usize]) -> bool { + use std::collections::HashSet; + let mut hs = HashSet::new(); + for ix in a { + let newly_inserted = hs.insert(ix); + if !newly_inserted { + return true; + } + } + false +} + +#[derive(Clone)] +struct TestData { + prng: CheapPrng, + cnt_passes: usize, + v: Vec, +} + +#[allow(unused_mut)] +impl TestData { + fn bench_function(mut self) { + //FwnnN::add(black_box(1), black_box(20)); + + let cnt_n = self.v.len(); + + let mut a_ix = [0; 3]; + + for pass_n in 0..self.cnt_passes { + //eprintln!("============================================= pass_n: {pass_n}"); + for ix0 in 0..cnt_n { + a_ix[0] = ix0; + Self::pick_indices(&mut self.prng, 1, &mut a_ix, cnt_n); + //eprintln!("indices: {:?}", a_ix); + + //self.fwnn_op(a_ix); + + let (ix0, ix1, ix2) = a_ix.into(); + self.fwnn_op(ix0, ix1, ix2); + + /* let r0 = &mut v[ar0]; + let r1 = &mut v[ar1]; + let r2 = &mut v[ar2]; + Self::fwnn_op(r0, r1, r2) */ + } + } + } + + fn fwnn_op(&mut self, ix0: usize, ix1: usize, ix2: usize) { + let r1 = self.v[ix1].clone(); + let r2 = self.v[ix2].clone(); + self.v[ix0] = r1 + r2; + } + + /* fn fwnn_op(r0: &mut FwnnN, r1: &mut FwnnN, r2: &mut FwnnN) { + *r0 = *r1 + *r2; + } */ + + fn pick_indices(prng: &mut CheapPrng, cnt_filled: usize, a_ix: &mut [usize; CNT_IX], cnt_n: usize) { + let mut cf = cnt_filled; + + while cf < CNT_IX { + let (sl_f, sl_uf) = a_ix.split_at_mut(cf); + + let mut cnt_iters = 0_usize; + let mut ix = usize::MAX; + loop { + let ix2 = prng.gen_range(0..(cnt_n as u64)) as usize; + if !sl_f.contains(&ix2) { + ix = ix2; + break; + } + cnt_iters += 1; + assert!(cnt_iters < 10_000_000, "This will break if you ask for all the indices in a huge container. If this happens, get a better algorithm."); + } + sl_uf[0] = ix; + cf += 1; + } + + assert!(!some_dupes(&a_ix[..]), "duplicate indices found"); + assert!(a_ix.iter().all(|&ix| ix < cnt_n), "indices out of range"); + } +} +*/ diff --git a/test/test-fixed-width-nonnegative/benches/bench/bench-impl.inc.rs b/test/test-fixed-width-nonnegative/benches/bench/bench-impl.inc.rs new file mode 100644 index 0000000..e4bb7d9 --- /dev/null +++ b/test/test-fixed-width-nonnegative/benches/bench/bench-impl.inc.rs @@ -0,0 +1,41 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +// NOTE: This file is intended to be include!()ed, in a specific context and not built directly. +/* + +use super::*; + +pub mod b256 { + const BENCH_REPR_BITS: usize = 256; + include!("bench-impl-n.inc.rs"); +} + +pub mod b4096 { + const BENCH_REPR_BITS: usize = 4096; + include!("bench-impl-n.inc.rs"); +} + +//criterion_group!(benches, b256::bench, b4096::bench); +*/ diff --git a/test/test-fixed-width-nonnegative/src/lib.rs b/test/test-fixed-width-nonnegative/src/lib.rs new file mode 100644 index 0000000..abb2e21 --- /dev/null +++ b/test/test-fixed-width-nonnegative/src/lib.rs @@ -0,0 +1,38 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![deny(clippy::unwrap_used)] +#![deny(clippy::expect_used)] +#![deny(clippy::panic)] +#![deny(clippy::manual_assert)] +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code + +#[cfg(test)] +mod tests; diff --git a/test/test-fixed-width-nonnegative/src/tests-impl-n.inc.rs b/test/test-fixed-width-nonnegative/src/tests-impl-n.inc.rs new file mode 100644 index 0000000..7e148b0 --- /dev/null +++ b/test/test-fixed-width-nonnegative/src/tests-impl-n.inc.rs @@ -0,0 +1,43 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +// NOTE: This file is intended to be include!()ed, in a specific context and not built directly. + +use super::*; + +//?type FwnnN = Fwnn; + +#[test] +fn nn_const() { + assert!(TEST_BITS != 0); + assert_eq!(TEST_BITS % 8, 0); + + //? assert_impl_all!(FwnnN: FwnnOps); + + //? assert_eq!(FwnnN::BITS, TEST_BITS); + + //? TODO actual tests +} + +//? TODO more tests diff --git a/test/test-fixed-width-nonnegative/src/tests-impl.inc.rs b/test/test-fixed-width-nonnegative/src/tests-impl.inc.rs new file mode 100644 index 0000000..b407844 --- /dev/null +++ b/test/test-fixed-width-nonnegative/src/tests-impl.inc.rs @@ -0,0 +1,48 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +// NOTE: This file is intended to be include!()ed, in a specific context and not built directly. + +test_bits!( 8 ); +test_bits!( 16 ); +test_bits!( 32 ); +test_bits!( 64 ); +test_bits!( 128 ); +test_bits!( 256 ); +test_bits!( 512 ); +test_bits!( 1024 ); +test_bits!( 2048 ); +test_bits!( 4096 ); + +/* +mod b256 { + const TEST_BITS: u32 = 256; + include!("tests-impl-n.inc.rs"); +} + +mod b4096 { + const TEST_BITS: u32 = 4096; + include!("tests-impl-n.inc.rs"); +} +*/ diff --git a/test/test-fixed-width-nonnegative/src/tests.rs b/test/test-fixed-width-nonnegative/src/tests.rs new file mode 100644 index 0000000..e60a2da --- /dev/null +++ b/test/test-fixed-width-nonnegative/src/tests.rs @@ -0,0 +1,229 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// MIT License +// +// Copyright (c) Microsoft Corporation. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + + +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::unwrap_used)] // This is test code +#![allow(clippy::expect_used)] // This is test code +#![allow(clippy::panic)] // This is test code +#![allow(clippy::manual_assert)] // This is test code +#![cfg_attr(rustfmt, rustfmt_skip)] + +#![allow(dead_code)] //? TODO: Remove temp development code +#![allow(unused_assignments)] //? TODO: Remove temp development code +#![allow(unused_imports)] //? TODO: Remove temp development code +#![allow(unused_variables)] //? TODO: Remove temp development code +#![allow(unreachable_code)] //? TODO: Remove temp development code +#![allow(non_camel_case_types)] //? TODO: Remove temp development code +#![allow(noop_method_call)] //? TODO: Remove temp development code +#![allow(unused_braces)] //? TODO: Remove temp development code + +use anyhow::{anyhow, bail, ensure, Context, Result}; +use cfg_if::cfg_if; +use static_assertions::{assert_impl_all, const_assert, const_assert_eq}; + +//=================================================================================================| + +macro_rules! test_bits3 { + ($bits:literal, $module:ident, $type:ident) => { + mod $module { + const TEST_BITS: usize = 256; + //type Nonnegative = $type; + include!("tests-impl-n.inc.rs"); + } + }; +} + +macro_rules! test_bits2 { + //( 8 ) => { #[cfg( feature="bits-8" )] test_bits3!( 8, bits_8, Nonnegative_8 ); }; TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + //( 16 ) => { #[cfg( feature="bits-16" )] test_bits3!( 16, bits_16, Nonnegative_16 ); }; + //( 32 ) => { #[cfg( feature="bits-32" )] test_bits3!( 32, bits_32, Nonnegative_32 ); }; + //( 64 ) => { #[cfg( feature="bits-64" )] test_bits3!( 64, bits_64, Nonnegative_64 ); }; + ( 128 ) => { #[cfg( feature="bits-128" )] test_bits3!( 128, bits_128, Nonnegative_128 ); }; + ( 256 ) => { #[cfg( feature="bits-256" )] test_bits3!( 256, bits_256, Nonnegative_256 ); }; + ( 512 ) => { #[cfg( feature="bits-512" )] test_bits3!( 512, bits_512, Nonnegative_512 ); }; + ( 1024 ) => { #[cfg( feature="bits-1024" )] test_bits3!( 1024, bits_1024, Nonnegative_1024 ); }; + ( 2048 ) => { #[cfg( feature="bits-2048" )] test_bits3!( 2048, bits_2048, Nonnegative_2048 ); }; + ( 4096 ) => { #[cfg( feature="bits-4096" )] test_bits3!( 4096, bits_4096, Nonnegative_4096 ); }; + ( $bits:literal ) => { } +} + +//=================================================================================================| + +cfg_if! { if #[cfg( + all( feature="basic-array", + any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64", + feature="basic-array-u128" ), + any( // all( feature="bits-8", feature="basic-array-u8" ),TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + all( feature="bits-16", any( feature="basic-array-u8", + feature="basic-array-u16" ) ), + all( feature="bits-32", any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32" ) ), + all( feature="bits-64", any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64" ) ), + all( feature="bits-128", any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64", + feature="basic-array-u128" ) ), + feature="bits-256", + feature="bits-512", + feature="bits-1024", + feature="bits-2048", + feature="bits-4096" ) ) +)] { + macro_rules! test_bits { + //( 8 ) => { #[cfg( all( feature="bits-8", TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + // feature="basic-array-u8" ) )] test_bits2!( 8 ); }; + ( 16 ) => { #[cfg( all( feature="bits-16", + any( feature="basic-array-u8", + feature="basic-array-u16" ) ) )] test_bits2!( 16 ); }; + ( 32 ) => { #[cfg( all( feature="bits-32", + any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32" ) ) )] test_bits2!( 32 ); }; + ( 64 ) => { #[cfg( all( feature="bits-64", + any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64" ) ) )] test_bits2!( 64 ); }; + ( 128 ) => { #[cfg( all( feature="bits-128", + any( feature="basic-array-u8", + feature="basic-array-u16", + feature="basic-array-u32", + feature="basic-array-u64", + feature="basic-array-u128" ) ) )] test_bits2!( 128 ); }; + ( 256 ) => { #[cfg( feature="bits-256" )] test_bits2!( 256 ); }; + ( 512 ) => { #[cfg( feature="bits-512" )] test_bits2!( 512 ); }; + ( 1024 ) => { #[cfg( feature="bits-1024" )] test_bits2!( 1024 ); }; + ( 2048 ) => { #[cfg( feature="bits-2048" )] test_bits2!( 2048 ); }; + ( 4096 ) => { #[cfg( feature="bits-4096" )] test_bits2!( 4096 ); }; + ( $bits:literal ) => { }; + } + + #[cfg(feature="basic-array-u8")] mod basicarray_u8 { use fixed_width_nonnegative::basicarray_u8::*; include!("tests-impl.inc.rs"); } + #[cfg(feature="basic-array-u16")] mod basicarray_u16 { use fixed_width_nonnegative::basicarray_u16::*; include!("tests-impl.inc.rs"); } + #[cfg(feature="basic-array-u32")] mod basicarray_u32 { use fixed_width_nonnegative::basicarray_u32::*; include!("tests-impl.inc.rs"); } + #[cfg(feature="basic-array-u64")] mod basicarray_u64 { use fixed_width_nonnegative::basicarray_u64::*; include!("tests-impl.inc.rs"); } + // #[cfg(feature="basic-array-u128")] mod basicarray_u128 { use fixed_width_nonnegative::basicarray_u128::*; include!("tests-impl.inc.rs"); } TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry +} } // if cfg_if! + +//=================================================================================================| + +cfg_if! { if #[cfg( + all( feature="crypto-bigint", + any( feature="bits-64", + feature="bits-128", + feature="bits-256", + feature="bits-512", + feature="bits-1024", + feature="bits-2048", + feature="bits-4096" ) ) +)] { + macro_rules! test_bits { + // ( 8) => { test_bits2!( 8 ); }; TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + ( 16) => { test_bits2!( 16 ); }; + ( 32) => { test_bits2!( 32 ); }; + ( 64) => { test_bits2!( 64 ); }; + ( 128) => { test_bits2!( 128 ); }; + ( 256) => { test_bits2!( 256 ); }; + ( 512) => { test_bits2!( 512 ); }; + (1024) => { test_bits2!( 1024 ); }; + (2048) => { test_bits2!( 2048 ); }; + (4096) => { test_bits2!( 4096 ); }; + ( $bits:literal ) => { }; + } + + mod cryptobigint { use fixed_width_nonnegative::cryptobigint::*; include!("tests-impl.inc.rs"); } +} } // if cfg_if! + +//=================================================================================================| + +cfg_if! { if #[cfg( + all( feature="hacl-rs", + any( feature="hacl-rs-u32", + feature="hacl-rs-u64" ), + any( feature="bits-256", + feature="bits-4096" ) ) +)] { + macro_rules! test_bits { + ( 256 ) => { test_bits2!( 256 ); }; + ( 4096 ) => { test_bits2!( 4096 ); }; + ( $bits:literal ) => { }; + } + + #[cfg(feature="hacl-rs-u32")] mod haclrs_u32 { use fixed_width_nonnegative::haclrs_u32::*; include!("tests-impl.inc.rs"); } + #[cfg(feature="hacl-rs-u64")] mod haclrs_u64 { use fixed_width_nonnegative::haclrs_u64::*; include!("tests-impl.inc.rs"); } +} } // if cfg_if! + +//=================================================================================================| + +cfg_if! { if #[cfg( + all( feature="num-bigint", + any( // feature="bits-8",TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + feature="bits-16", + feature="bits-32", + feature="bits-64", + feature="bits-128", + feature="bits-256", + feature="bits-512", + feature="bits-1024", + feature="bits-2048", + feature="bits-4096" ) ) +)] { + macro_rules! test_bits { + // ( 8 ) => { test_bits2!( 8 ); };TODO temp commented out due to limitations of crate::significant_first::ops::add_returning_carry + ( 16 ) => { test_bits2!( 16 ); }; + ( 32 ) => { test_bits2!( 32 ); }; + ( 64 ) => { test_bits2!( 64 ); }; + ( 128 ) => { test_bits2!( 128 ); }; + ( 256 ) => { test_bits2!( 256 ); }; + ( 512 ) => { test_bits2!( 512 ); }; + ( 1024 ) => { test_bits2!( 1024 ); }; + ( 2048 ) => { test_bits2!( 2048 ); }; + ( 4096 ) => { test_bits2!( 4096 ); }; + ( $bits:literal ) => { }; + } + + mod numbigint { use fixed_width_nonnegative::numbigint::*; include!("tests-impl.inc.rs"); } +} } // if cfg_if! + +//=================================================================================================| + +#[test] +fn some_impl_feature_was_specified() { + let mut some_impl = false; + #[cfg(feature="basic-array" )] { some_impl = true; } + #[cfg(feature="crypto-bigint")] { some_impl = true; } + #[cfg(feature="hacl-rs" )] { some_impl = true; } + #[cfg(feature="num-bigint" )] { some_impl = true; } + assert!(some_impl, "No implementation feature was specified. You can specify the feature to `cargo test` using the `--features basic-array,crypto-bigint,hacl-rs,num-bigint` parameter."); +} + +//=================================================================================================|