Skip to content

Commit fc3ce34

Browse files
Make the verify ballot encryption only run for admin and not for guar… (#758)
* Make the verify ballot encryption only run for admin and not for guardians * removed extra parens * reformatted a function * changed isAdmin to should_validate to more like normal python. * fixed some local variable names in the python * changes some of the tally.append calls to use should_validate to false
1 parent f20911c commit fc3ce34

9 files changed

Lines changed: 42 additions & 35 deletions

File tree

src/electionguard/ballot_box.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def submit_ballot_to_box(
5757
that the ballot has not already been cast or spoiled.
5858
:return: a `SubmittedBallot` or `None` if there was an error
5959
"""
60-
if not ballot_is_valid_for_election(ballot, internal_manifest, context):
60+
if not ballot_is_valid_for_election(ballot, internal_manifest, context, True):
6161
log_warning(f"ballot: {ballot.object_id} failed validity check")
6262
return None
6363

src/electionguard/ballot_validator.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def ballot_is_valid_for_election(
1212
ballot: CiphertextBallot,
1313
internal_manifest: InternalManifest,
1414
context: CiphertextElectionContext,
15+
should_validate: bool,
1516
) -> bool:
1617
"""
1718
Determine if a ballot is valid for a given election
@@ -20,15 +21,16 @@ def ballot_is_valid_for_election(
2021
if not ballot_is_valid_for_style(ballot, internal_manifest):
2122
return False
2223

23-
if not ballot.is_valid_encryption(
24-
internal_manifest.manifest_hash,
25-
context.elgamal_public_key,
26-
context.crypto_extended_base_hash,
27-
):
28-
log_warning(
29-
f"ballot_is_valid_for_election: mismatching ballot encryption {ballot.object_id}"
30-
)
31-
return False
24+
if should_validate:
25+
if not ballot.is_valid_encryption(
26+
internal_manifest.manifest_hash,
27+
context.elgamal_public_key,
28+
context.crypto_extended_base_hash,
29+
):
30+
log_warning(
31+
f"ballot_is_valid_for_election: mismatching ballot encryption {ballot.object_id}"
32+
)
33+
return False
3234

3335
return True
3436

src/electionguard/tally.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,10 @@ def __contains__(self, item: object) -> bool:
226226
return False
227227

228228
def append(
229-
self, ballot: SubmittedBallot, scheduler: Optional[Scheduler] = None
229+
self,
230+
ballot: SubmittedBallot,
231+
should_validate: bool,
232+
scheduler: Optional[Scheduler] = None,
230233
) -> bool:
231234
"""
232235
Append a ballot to the tally and recalculate the tally.
@@ -240,7 +243,7 @@ def append(
240243
return False
241244

242245
if not ballot_is_valid_for_election(
243-
ballot, self._internal_manifest, self._encryption
246+
ballot, self._internal_manifest, self._encryption, should_validate
244247
):
245248
return False
246249

@@ -256,6 +259,7 @@ def append(
256259
def batch_append(
257260
self,
258261
ballots: Iterable[Tuple[Any, SubmittedBallot]],
262+
should_validate: bool,
259263
scheduler: Optional[Scheduler] = None,
260264
) -> bool:
261265
"""
@@ -268,7 +272,7 @@ def batch_append(
268272
# get the value of the dict
269273
ballot_value = ballot[1]
270274
if not self.__contains__(ballot) and ballot_is_valid_for_election(
271-
ballot_value, self._internal_manifest, self._encryption
275+
ballot_value, self._internal_manifest, self._encryption, should_validate
272276
):
273277
if ballot_value.state == BallotBoxState.CAST:
274278

@@ -431,7 +435,7 @@ def tally_ballot(
431435
)
432436
return None
433437

434-
if tally.append(ballot):
438+
if tally.append(ballot, True):
435439
return tally
436440

437441
return None
@@ -450,6 +454,6 @@ def tally_ballots(
450454
tally: CiphertextTally = CiphertextTally(
451455
"election-results", internal_manifest, context
452456
)
453-
if tally.batch_append(store):
457+
if tally.batch_append(store, True):
454458
return tally
455459
return None

src/electionguard_cli/cli_steps/tally_step.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def _get_tally(
4444
build_election_results.context,
4545
)
4646
with Scheduler() as scheduler:
47-
tally.batch_append(ballots, scheduler)
47+
tally.batch_append(ballots, True, scheduler)
4848
self.print_value("Ballots in tally", tally.__len__())
4949
return tally
5050

src/electionguard_gui/services/decryption_stages/decryption_s1_join_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def run(self, db: Database, decryption: DecryptionDto) -> None:
2525
spoiled_ballots = [
2626
ballot for ballot in ballots if ballot.state == BallotBoxState.SPOILED
2727
]
28-
ciphertext_tally = get_tally(manifest, context, ballots)
28+
ciphertext_tally = get_tally(manifest, context, ballots, False)
2929
decryption_share = guardian.compute_tally_share(ciphertext_tally, context)
3030
if decryption_share is None:
3131
raise Exception("No decryption_shares found")

src/electionguard_gui/services/decryption_stages/decryption_s2_announce_service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ class DecryptionS2AnnounceService(DecryptionStageBase):
1313
"""Responsible for the 2nd stage in decryptions where the admin announces guardian decryptions"""
1414

1515
def should_run(self, db: Database, decryption: DecryptionDto) -> bool:
16-
isAdmin = self._auth_service.is_admin()
17-
allGuardiansJoined = len(decryption.guardians_joined) >= decryption.guardians
18-
isCompleted = decryption.completed_at_utc is not None
19-
return isAdmin and allGuardiansJoined and not isCompleted
16+
is_admin = self._auth_service.is_admin()
17+
all_guardians_joined = len(decryption.guardians_joined) >= decryption.guardians
18+
is_completed = decryption.completed_at_utc is not None
19+
return is_admin and all_guardians_joined and not is_completed
2020

2121
def run(self, db: Database, decryption: DecryptionDto) -> None:
2222
self._log.info(f"S2: Announcing decryption {decryption.decryption_id}")
@@ -47,7 +47,7 @@ def run(self, db: Database, decryption: DecryptionDto) -> None:
4747
spoiled_ballots = [
4848
ballot for ballot in ballots if ballot.state == BallotBoxState.SPOILED
4949
]
50-
ciphertext_tally = get_tally(manifest, context, ballots)
50+
ciphertext_tally = get_tally(manifest, context, ballots, True)
5151
self._log.debug("getting plaintext tally")
5252
plaintext_tally = decryption_mediator.get_plaintext_tally(
5353
ciphertext_tally, manifest

src/electionguard_gui/services/decryption_stages/decryption_stage_base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def get_tally(
5959
manifest: Manifest,
6060
context: CiphertextElectionContext,
6161
ballots: List[SubmittedBallot],
62+
should_validate: bool,
6263
) -> CiphertextTally:
6364
internal_manifest = InternalManifest(manifest)
6465

@@ -69,5 +70,5 @@ def get_tally(
6970
)
7071
ballot_tuples = [(None, ballot) for ballot in ballots]
7172
with Scheduler() as scheduler:
72-
tally.batch_append(ballot_tuples, scheduler)
73+
tally.batch_append(ballot_tuples, should_validate, scheduler)
7374
return tally

src/electionguard_verify/verify.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def verify_aggregation(
7777
new_tally = CiphertextTally("verify", InternalManifest(manifest), context)
7878

7979
for ballot in submitted_ballots:
80-
new_tally.append(ballot)
80+
new_tally.append(ballot, True)
8181

8282
if (
8383
isinstance(tally, CiphertextTally)

tests/property/test_tally.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,15 @@ def test_tally_ballot_invalid_input_fails(
175175

176176
# verify an UNKNOWN state ballot fails
177177
self.assertIsNone(tally_ballot(first_ballot, tally))
178-
self.assertFalse(tally.append(first_ballot))
178+
self.assertFalse(tally.append(first_ballot, True))
179179

180180
# cast a ballot
181181
first_ballot.state = BallotBoxState.CAST
182-
self.assertTrue(tally.append(first_ballot))
182+
self.assertTrue(tally.append(first_ballot, False))
183183

184184
# try to append a spoiled ballot
185185
first_ballot.state = BallotBoxState.SPOILED
186-
self.assertFalse(tally.append(first_ballot))
186+
self.assertFalse(tally.append(first_ballot, True))
187187

188188
# Verify accumulation fails if the selection collection is empty
189189
if first_ballot.state == BallotBoxState.CAST:
@@ -217,12 +217,12 @@ def test_tally_ballot_invalid_input_fails(
217217

218218
# verify a cast ballot cannot be added twice
219219
first_ballot.state = BallotBoxState.CAST
220-
self.assertTrue(tally.append(first_ballot))
221-
self.assertFalse(tally.append(first_ballot))
220+
self.assertTrue(tally.append(first_ballot, True))
221+
self.assertFalse(tally.append(first_ballot, False))
222222

223223
# verify an already submitted ballot cannot be changed or readded
224224
first_ballot.state = BallotBoxState.SPOILED
225-
self.assertFalse(tally.append(first_ballot))
225+
self.assertFalse(tally.append(first_ballot, True))
226226

227227
@staticmethod
228228
def _decrypt_with_secret(
@@ -255,7 +255,7 @@ def _cannot_erroneously_mutate_state(
255255
ballot.contests[0].ballot_selections.remove(first_selection)
256256

257257
self.assertIsNone(tally_ballot(ballot, tally))
258-
self.assertFalse(tally.append(ballot))
258+
self.assertFalse(tally.append(ballot, True))
259259

260260
# Verify accumulation fails if the selection count does not match
261261
if ballot.state == BallotBoxState.CAST:
@@ -278,15 +278,15 @@ def _cannot_erroneously_mutate_state(
278278
first_contest_hash = ballot.contests[0].description_hash
279279
ballot.contests[0].description_hash = ONE_MOD_Q
280280
self.assertIsNone(tally_ballot(ballot, tally))
281-
self.assertFalse(tally.append(ballot))
281+
self.assertFalse(tally.append(ballot, True))
282282

283283
ballot.contests[0].description_hash = first_contest_hash
284284

285285
# modify a contest object id
286286
first_contest_object_id = ballot.contests[0].object_id
287287
ballot.contests[0].object_id = "a-bad-object-id"
288288
self.assertIsNone(tally_ballot(ballot, tally))
289-
self.assertFalse(tally.append(ballot))
289+
self.assertFalse(tally.append(ballot, True))
290290

291291
ballot.contests[0].object_id = first_contest_object_id
292292

@@ -297,7 +297,7 @@ def _cannot_erroneously_mutate_state(
297297
ballot.contests[0].ballot_selections[0].object_id = "another-bad-object-id"
298298

299299
self.assertIsNone(tally_ballot(ballot, tally))
300-
self.assertFalse(tally.append(ballot))
300+
self.assertFalse(tally.append(ballot, True))
301301

302302
# Verify accumulation fails if the selection object id does not match
303303
if ballot.state == BallotBoxState.CAST:
@@ -315,7 +315,7 @@ def _cannot_erroneously_mutate_state(
315315
first_ballot_hash = ballot.manifest_hash
316316
ballot.manifest_hash = ONE_MOD_Q
317317
self.assertIsNone(tally_ballot(ballot, tally))
318-
self.assertFalse(tally.append(ballot))
318+
self.assertFalse(tally.append(ballot, True))
319319

320320
ballot.manifest_hash = first_ballot_hash
321321
ballot.state = input_state

0 commit comments

Comments
 (0)