Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions docs/0_Configure_Election.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,41 @@

An election in ElectionGuard is defined as a set of metadata and cryptographic artifacts necessary to encrypt, conduct, tally, decrypt, and verify an election. The Data format used for election metadata is based on the [NIST Election Common Standard Data Specification](https://www.nist.gov/itl/voting/interoperability) but includes some modifications to support the end-to-end cryptography of ElectionGuard.

Election metadata is described in a specific format parseable into an `ElectionDescription` and it's validity is checked to ensure that it is of an appropriate structure to conduct an End-to-End Verified ElectionGuard Election. ElectionGuard only verifies the components of the election metadata that are necessary to encrypt and decrypt the election. Some components of the election metadata are not checked for structural validity, but are used when generating a hash representation of the `Election Description`.
Election metadata is described in a specific format parseable into an `Manifest` and it's validity is checked to ensure that it is of an appropriate structure to conduct an End-to-End Verified ElectionGuard Election. ElectionGuard only verifies the components of the election metadata that are necessary to encrypt and decrypt the election. Some components of the election metadata are not checked for structural validity, but are used when generating a hash representation of the `Manifest`.

From an `ElectionDescription` we derive an `InternalElectionDescription` that includes a subset of the elements from the `ElectionDescription` required to verify ballots are correct. Additionally a `CiphertextElectionContext` is created during the [Key Ceremony](/1_Key_Ceremony.md) that includes the cryptographic artifacts necessary for encrypting ballots.
From an `Manifest` we derive an `InternalManifest` that includes a subset of the elements from the `Manifest` required to verify ballots are correct. Additionally a `CiphertextElectionContext` is created during the [Key Ceremony](/1_Key_Ceremony.md) that includes the cryptographic artifacts necessary for encrypting ballots.

## Glossary

- **Election Manifest** The election metadata in json format that is parsed into an Election Description
- **Election Description** The election metadata that describes the structure and type of the election, including geopolitical units, contests, candidates, and ballot styles, etc.
- **Internal Election Description** The subset of the `ElectionDescription` required by ElectionGuard to validate ballots are correctly associated with an election. This component mutates the state of the Election Description.
- **Internal Election Description** The subset of the `Manifest` required by ElectionGuard to validate ballots are correctly associated with an election. This component mutates the state of the Election Description.
- **Ciphertext Election Context** The cryptographic context of an election that is configured during the `Key Ceremony`
- **Description Hash** a Hash representation of the original ElectionDescription.
- **Description Hash** a Hash representation of the original Manifest.

## Process

1. Define an election according to the `ElectionDescription` requirements.
1. Define an election according to the `Manifest` requirements.
2. Use the [NIST Common Standard Data Specification](https://www.nist.gov/itl/voting/interoperability) as a guide, but note the differences in [election.py](https://github.com/microsoft/electionguard-python/tree/main/src/electionguard.election.py) and the provided [sample manifest](https://github.com/microsoft/electionguard-python/tree/main/data/election_manifest_simple.json).
3. Parse the `ElectionDescription` into the application.
3. Parse the `Manifest` into the application.
4. Define the encryption parameters necessary for conducting an election (see `Key Ceremony`).
5. Create the Pubic Key either from a single secret, or from the Key Ceremony.
6. Build the `InternalElectionDescription` and `CiphertextElectionContext` from the `ElectionDescription` and `ElGamalKeyPair.public_key`.
6. Build the `InternalManifest` and `CiphertextElectionContext` from the `Manifest` and `ElGamalKeyPair.public_key`.

## Usage Example

```python

import os
from electionguard.election import ElectionDescription, InternalElectionDescription, CiphertextElectionContext
from electionguard.election import CiphertextElectionContext
from electionguard.election_builder import ElectionBuilder
from electionguard.elgamal import ElGamalKeyPair, elgamal_keypair_from_secret
from electionguard.manifest import Manifest, InternalManifest

# Open an election manifest file
with open(os.path.join(some_path, "election-manifest.json"), "r") as manifest:
string_representation = manifest.read()
election_description = ElectionDescription.from_json(string_representation)
election_description = Manifest.from_json(string_representation)

# Create an election builder instance, and configure it for a single public-private keypair.
# in a real election, you would configure this for a group of guardians. See Key Ceremony for more information.
Expand Down
22 changes: 13 additions & 9 deletions src/electionguard/ballot_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
SubmittedBallot,
from_ciphertext_ballot,
)
from .ballot_validator import ballot_is_valid_for_election
from .data_store import DataStore

from .election import CiphertextElectionContext, InternalElectionDescription
from .election import CiphertextElectionContext
from .logs import log_warning
from .ballot_validator import ballot_is_valid_for_election
from .manifest import InternalManifest


@dataclass
Expand All @@ -20,7 +20,7 @@ class BallotBox:
A stateful convenience wrapper to cache election data
"""

_metadata: InternalElectionDescription = field()
_internal_manifest: InternalManifest = field()
_encryption: CiphertextElectionContext = field()
_store: DataStore = field(default_factory=lambda: DataStore())

Expand All @@ -29,7 +29,11 @@ def cast(self, ballot: CiphertextBallot) -> Optional[SubmittedBallot]:
Cast a specific encrypted `CiphertextBallot`
"""
return accept_ballot(
ballot, BallotBoxState.CAST, self._metadata, self._encryption, self._store
ballot,
BallotBoxState.CAST,
self._internal_manifest,
self._encryption,
self._store,
)

def spoil(self, ballot: CiphertextBallot) -> Optional[SubmittedBallot]:
Expand All @@ -39,7 +43,7 @@ def spoil(self, ballot: CiphertextBallot) -> Optional[SubmittedBallot]:
return accept_ballot(
ballot,
BallotBoxState.SPOILED,
self._metadata,
self._internal_manifest,
self._encryption,
self._store,
)
Expand All @@ -48,17 +52,17 @@ def spoil(self, ballot: CiphertextBallot) -> Optional[SubmittedBallot]:
def accept_ballot(
ballot: CiphertextBallot,
state: BallotBoxState,
metadata: InternalElectionDescription,
internal_manifest: InternalManifest,
context: CiphertextElectionContext,
store: DataStore,
) -> Optional[SubmittedBallot]:
"""
Submit a ballot within the context of a specified election and against an existing data store
Verified that the ballot is valid for the election `metadata` and `context` and
Verified that the ballot is valid for the election `internal_manifest` and `context` and
that the ballot has not already been cast or spoiled.
:return: a `SubmittedBallot` or `None` if there was an error
"""
if not ballot_is_valid_for_election(ballot, metadata, context):
if not ballot_is_valid_for_election(ballot, internal_manifest, context):
return None

existing_ballot = store.get(ballot.object_id)
Expand Down
21 changes: 10 additions & 11 deletions src/electionguard/ballot_validator.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
from .ballot import CiphertextBallot, CiphertextBallotContest, CiphertextBallotSelection
from .election import (
CiphertextElectionContext,
from .election import CiphertextElectionContext
from .logs import log_warning
from .manifest import (
ContestDescriptionWithPlaceholders,
InternalElectionDescription,
InternalManifest,
SelectionDescription,
)

from .logs import log_warning


def ballot_is_valid_for_election(
ballot: CiphertextBallot,
metadata: InternalElectionDescription,
internal_manifest: InternalManifest,
context: CiphertextElectionContext,
) -> bool:
"""
Determine if a ballot is valid for a given election
"""

if not ballot_is_valid_for_style(ballot, metadata):
if not ballot_is_valid_for_style(ballot, internal_manifest):
return False

if not ballot.is_valid_encryption(
metadata.description_hash,
internal_manifest.manifest_hash,
context.elgamal_public_key,
context.crypto_extended_base_hash,
):
Expand Down Expand Up @@ -87,15 +86,15 @@ def contest_is_valid_for_style(


def ballot_is_valid_for_style(
ballot: CiphertextBallot, metadata: InternalElectionDescription
ballot: CiphertextBallot, internal_manifest: InternalManifest
) -> bool:
"""
Determine if ballot is valid for ballot style
:param ballot: Ballot
:param metadata: Internal election description
:param internal_manifest: Internal election description
:return: Is valid
"""
descriptions = metadata.get_contests_for(ballot.style_id)
descriptions = internal_manifest.get_contests_for(ballot.style_id)

for description in descriptions:
use_contest = None
Expand Down
26 changes: 13 additions & 13 deletions src/electionguard/decrypt_with_secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
PlaintextBallotContest,
PlaintextBallotSelection,
)
from .election import (
InternalElectionDescription,
from .group import ElementModP, ElementModQ
from .logs import log_warning
from .manifest import (
InternalManifest,
ContestDescriptionWithPlaceholders,
SelectionDescription,
)
from .group import ElementModP, ElementModQ
from .logs import log_warning
from .nonces import Nonces

from .utils import get_optional
Expand Down Expand Up @@ -238,7 +238,7 @@ def decrypt_contest_with_nonce(

def decrypt_ballot_with_secret(
ballot: CiphertextBallot,
election_metadata: InternalElectionDescription,
internal_manifest: InternalManifest,
crypto_extended_base_hash: ElementModQ,
public_key: ElementModP,
secret_key: ElementModQ,
Expand All @@ -249,7 +249,7 @@ def decrypt_ballot_with_secret(
Decrypt the specified `CiphertextBallot` within the context of the specified election.

:param ballot: the ballot to decrypt
:param election_metadata: the qualified election metadata that includes placeholder selections
:param internal_manifest: the qualified election metadata that includes placeholder selections
:param crypto_extended_base_hash: the extended base hash code (𝑄') for the election
:param public_key: the public key for the election (K)
:param secret_key: the known secret key used to generate the public key for this election
Expand All @@ -258,14 +258,14 @@ def decrypt_ballot_with_secret(
"""

if not suppress_validity_check and not ballot.is_valid_encryption(
election_metadata.description_hash, public_key, crypto_extended_base_hash
internal_manifest.manifest_hash, public_key, crypto_extended_base_hash
):
return None

plaintext_contests: List[PlaintextBallotContest] = list()

for contest in ballot.contests:
description = election_metadata.contest_for(contest.object_id)
description = internal_manifest.contest_for(contest.object_id)
plaintext_contest = decrypt_contest_with_secret(
contest,
get_optional(description),
Expand All @@ -288,7 +288,7 @@ def decrypt_ballot_with_secret(

def decrypt_ballot_with_nonce(
ballot: CiphertextBallot,
election_metadata: InternalElectionDescription,
internal_manifest: InternalManifest,
crypto_extended_base_hash: ElementModQ,
public_key: ElementModP,
nonce: Optional[ElementModQ] = None,
Expand All @@ -299,7 +299,7 @@ def decrypt_ballot_with_nonce(
Decrypt the specified `CiphertextBallot` within the context of the specified election.

:param ballot: the ballot to decrypt
:param election_metadata: the qualified election metadata that includes placeholder selections
:param internal_manifest: the qualified election metadata that includes placeholder selections
:param crypto_extended_base_hash: the extended base hash code (𝑄') for the election
:param public_key: the public key for the election (K)
:param nonce: the optional master ballot nonce that was either seeded to, or gernated by the encryption function
Expand All @@ -308,7 +308,7 @@ def decrypt_ballot_with_nonce(
"""

if not suppress_validity_check and not ballot.is_valid_encryption(
election_metadata.description_hash, public_key, crypto_extended_base_hash
internal_manifest.manifest_hash, public_key, crypto_extended_base_hash
):
return None

Expand All @@ -318,7 +318,7 @@ def decrypt_ballot_with_nonce(
nonce_seed = ballot.hashed_ballot_nonce()
else:
nonce_seed = CiphertextBallot.nonce_seed(
election_metadata.description_hash, ballot.object_id, nonce
internal_manifest.manifest_hash, ballot.object_id, nonce
)

if nonce_seed is None:
Expand All @@ -330,7 +330,7 @@ def decrypt_ballot_with_nonce(
plaintext_contests: List[PlaintextBallotContest] = list()

for contest in ballot.contests:
description = election_metadata.contest_for(contest.object_id)
description = internal_manifest.contest_for(contest.object_id)
plaintext_contest = decrypt_contest_with_nonce(
contest,
get_optional(description),
Expand Down
6 changes: 1 addition & 5 deletions src/electionguard/decryption_mediator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
)
from .decryption_share import DecryptionShare, CompensatedDecryptionShare
from .decrypt_with_shares import decrypt_ballots, decrypt_tally
from .election import (
CiphertextElectionContext,
InternalElectionDescription,
)
from .election import CiphertextElectionContext
from .election_polynomial import compute_lagrange_coefficient
from .group import ElementModP, ElementModQ
from .guardian import Guardian
Expand Down Expand Up @@ -45,7 +42,6 @@ class DecryptionMediator:
to form a decrypted representation of an election tally
"""

_metadata: InternalElectionDescription
_encryption: CiphertextElectionContext

# Tally to Decrypt
Expand Down
Loading