def test_tally_spoiled_ballots_accumulates_valid_tally( self, everything: ELECTIONS_AND_BALLOTS_TUPLE_TYPE): # Arrange metadata, ballots, secret_key, context = everything # Tally the plaintext ballots for comparison later plaintext_tallies = accumulate_plaintext_ballots(ballots) # encrypt each ballot store = BallotStore() seed_hash = EncryptionDevice("Location").get_hash() for ballot in ballots: encrypted_ballot = encrypt_ballot(ballot, metadata, context, seed_hash) seed_hash = encrypted_ballot.tracking_hash self.assertIsNotNone(encrypted_ballot) # add to the ballot store store.set( encrypted_ballot.object_id, from_ciphertext_ballot(encrypted_ballot, BallotBoxState.SPOILED), ) # act result = tally_ballots(store, metadata, context) self.assertIsNotNone(result) # Assert decrypted_tallies = self._decrypt_with_secret(result, secret_key) self.assertCountEqual(plaintext_tallies, decrypted_tallies) for value in decrypted_tallies.values(): self.assertEqual(0, value) self.assertEqual(len(ballots), len(result.spoiled_ballots))
def _generate_encrypted_tally( self, metadata: InternalElectionDescription, context: CiphertextElectionContext, ballots: List[PlaintextBallot], ) -> CiphertextTally: # encrypt each ballot store = BallotStore() for ballot in ballots: encrypted_ballot = encrypt_ballot(ballot, metadata, context, int_to_q_unchecked(1)) self.assertIsNotNone(encrypted_ballot) # add to the ballot store store.set( encrypted_ballot.object_id, from_ciphertext_ballot(encrypted_ballot, BallotBoxState.CAST), ) tally = tally_ballots(store, metadata, context) self.assertIsNotNone(tally) return get_optional(tally)
def test_ballot_store(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) election = election_factory.get_fake_election() metadata, context = election_factory.get_fake_ciphertext_election( election, keypair.public_key) # get an encrypted fake ballot to work with fake_ballot = election_factory.get_fake_ballot(metadata) encrypted_ballot = encrypt_ballot(fake_ballot, metadata, context, SEED_HASH) # Set up the ballot store subject = BallotStore() data_cast = CiphertextAcceptedBallot( encrypted_ballot.object_id, encrypted_ballot.ballot_style, encrypted_ballot.description_hash, encrypted_ballot.previous_tracking_hash, encrypted_ballot.contests, encrypted_ballot.tracking_hash, encrypted_ballot.timestamp, ) data_cast.state = BallotBoxState.CAST data_spoiled = CiphertextAcceptedBallot( encrypted_ballot.object_id, encrypted_ballot.ballot_style, encrypted_ballot.description_hash, encrypted_ballot.previous_tracking_hash, encrypted_ballot.contests, encrypted_ballot.tracking_hash, encrypted_ballot.timestamp, ) data_spoiled.state = BallotBoxState.SPOILED self.assertIsNone(subject.get("cast")) self.assertIsNone(subject.get("spoiled")) # try to set a ballot with an unknown state self.assertFalse( subject.set( "unknown", CiphertextAcceptedBallot( encrypted_ballot.object_id, encrypted_ballot.ballot_style, encrypted_ballot.description_hash, encrypted_ballot.previous_tracking_hash, encrypted_ballot.contests, encrypted_ballot.tracking_hash, encrypted_ballot.timestamp, ), )) # Act self.assertTrue(subject.set("cast", data_cast)) self.assertTrue(subject.set("spoiled", data_spoiled)) self.assertEqual(subject.get("cast"), data_cast) self.assertEqual(subject.get("spoiled"), data_spoiled) self.assertEqual(subject.exists("cast"), (True, data_cast)) self.assertEqual(subject.exists("spoiled"), (True, data_spoiled)) # test mutate state data_cast.state = BallotBoxState.UNKNOWN self.assertEqual(subject.exists("cast"), (False, data_cast)) # test remove self.assertTrue(subject.set("cast", None)) self.assertEqual(subject.exists("cast"), (False, None))
def test_tally_ballot_invalid_input_fails( self, everything: ELECTIONS_AND_BALLOTS_TUPLE_TYPE): # Arrange metadata, ballots, secret_key, context = everything # encrypt each ballot store = BallotStore() seed_hash = EncryptionDevice("Location").get_hash() for ballot in ballots: encrypted_ballot = encrypt_ballot(ballot, metadata, context, seed_hash) seed_hash = encrypted_ballot.tracking_hash self.assertIsNotNone(encrypted_ballot) # add to the ballot store store.set( encrypted_ballot.object_id, from_ciphertext_ballot(encrypted_ballot, BallotBoxState.CAST), ) subject = CiphertextTally("my-tally", metadata, context) # act cached_ballots = store.all() first_ballot = cached_ballots[0] first_ballot.state = BallotBoxState.UNKNOWN # verify an UNKNOWN state ballot fails self.assertIsNone(tally_ballot(first_ballot, subject)) self.assertFalse(subject.append(first_ballot)) # cast a ballot first_ballot.state = BallotBoxState.CAST self.assertTrue(subject.append(first_ballot)) # try to append a spoiled ballot first_ballot.state = BallotBoxState.SPOILED self.assertFalse(subject.append(first_ballot)) # Verify accumulation fails if the selection collection is empty if first_ballot.state == BallotBoxState.CAST: self.assertFalse( subject.cast[first_ballot.object_id].elgamal_accumulate([])) # pop the cast ballot subject._cast_ballot_ids.pop() # reset to cast first_ballot.state = BallotBoxState.CAST self.assertTrue( self._cannot_erroneously_mutate_state(subject, first_ballot, BallotBoxState.CAST)) self.assertTrue( self._cannot_erroneously_mutate_state(subject, first_ballot, BallotBoxState.SPOILED)) self.assertTrue( self._cannot_erroneously_mutate_state(subject, first_ballot, BallotBoxState.UNKNOWN)) # verify a spoiled ballot cannot be added twice first_ballot.state = BallotBoxState.SPOILED self.assertTrue(subject.append(first_ballot)) self.assertFalse(subject.append(first_ballot)) # verify an already spoiled ballot cannot be cast first_ballot.state = BallotBoxState.CAST self.assertFalse(subject.append(first_ballot)) # pop the spoiled ballot subject.spoiled_ballots.pop(first_ballot.object_id) # verify a cast ballot cannot be added twice first_ballot.state = BallotBoxState.CAST self.assertTrue(subject.append(first_ballot)) self.assertFalse(subject.append(first_ballot)) # verify an already cast ballot cannot be spoiled first_ballot.state = BallotBoxState.SPOILED self.assertFalse(subject.append(first_ballot))