コード例 #1
0
ファイル: tally.py プロジェクト: nealmcb/arlo-e2e
def ciphertext_ballot_to_accepted(
    ballot: CiphertextBallot, ) -> CiphertextAcceptedBallot:
    """
    Given a CiphertextBallot, returns a CiphertextAcceptedBallot (and thus, with
    with nonces removed).
    """
    return from_ciphertext_ballot(ballot, BallotBoxState.CAST)
コード例 #2
0
    def test_tally_cast_ballots_accumulates_valid_tally(
            self, everything: ELECTIONS_AND_BALLOTS_TUPLE_TYPE):
        # Arrange
        (
            _election_description,
            internal_manifest,
            ballots,
            secret_key,
            context,
        ) = everything
        # Tally the plaintext ballots for comparison later
        plaintext_tallies = accumulate_plaintext_ballots(ballots)

        # encrypt each ballot
        store = DataStore()
        encryption_seed = ElectionFactory.get_encryption_device().get_hash()
        for ballot in ballots:
            encrypted_ballot = encrypt_ballot(ballot, internal_manifest,
                                              context, encryption_seed)
            encryption_seed = encrypted_ballot.code
            self.assertIsNotNone(encrypted_ballot)
            # add to the ballot store
            store.set(
                encrypted_ballot.object_id,
                from_ciphertext_ballot(encrypted_ballot, BallotBoxState.CAST),
            )

        # act
        result = tally_ballots(store, internal_manifest, context)
        self.assertIsNotNone(result)

        # Assert
        decrypted_tallies = self._decrypt_with_secret(result, secret_key)
        self.assertEqual(plaintext_tallies, decrypted_tallies)
コード例 #3
0
    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))
コード例 #4
0
 def add_ballot(self, ballot: str):
     ciphertext_ballot = deserialize(ballot, CiphertextBallot)
     submitted_ballot = from_ciphertext_ballot(ciphertext_ballot,
                                               BallotBoxState.CAST)
     self.context.tally.append(
         submitted_ballot,
         DummyScheduler(),  # type: ignore
     )
コード例 #5
0
    def add_ballots(self, ballots: List[str]):
        submitted_ballots: List[Tuple[None, SubmittedBallot]] = []
        for ballot in ballots:
            ciphertext_ballot = deserialize(ballot, CiphertextBallot)
            submitted_ballots.append(
                (None,
                 from_ciphertext_ballot(ciphertext_ballot,
                                        BallotBoxState.CAST)))

        self.context.tally.batch_append(submitted_ballots,
                                        DummyScheduler())  # type: ignore
コード例 #6
0
    def setUp(self) -> None:
        # Election setup
        election_factory = ElectionFactory()
        keypair = elgamal_keypair_from_secret(int_to_q(2))
        manifest = election_factory.get_fake_manifest()
        (
            self.internal_manifest,
            self.context,
        ) = election_factory.get_fake_ciphertext_election(
            manifest, keypair.public_key)
        device_hash = ElectionFactory.get_encryption_device().get_hash()

        # Arrange ballots
        self.plaintext_ballot = election_factory.get_fake_ballot(
            self.internal_manifest)
        ciphertext_ballot = encrypt_ballot(self.plaintext_ballot,
                                           self.internal_manifest,
                                           self.context, device_hash)
        self.ballot_nonce = ciphertext_ballot.nonce
        self.submitted_ballot = from_ciphertext_ballot(ciphertext_ballot,
                                                       BallotBoxState.CAST)
コード例 #7
0
    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)
コード例 #8
0
    def test_tally_ballot_invalid_input_fails(
            self, everything: ELECTIONS_AND_BALLOTS_TUPLE_TYPE):

        # Arrange
        (
            _election_description,
            internal_manifest,
            ballots,
            _secret_key,
            context,
        ) = everything

        # encrypt each ballot
        store = DataStore()
        encryption_seed = ElectionFactory.get_encryption_device().get_hash()
        for ballot in ballots:
            encrypted_ballot = encrypt_ballot(ballot, internal_manifest,
                                              context, encryption_seed)
            encryption_seed = encrypted_ballot.code
            self.assertIsNotNone(encrypted_ballot)
            # add to the ballot store
            store.set(
                encrypted_ballot.object_id,
                from_ciphertext_ballot(encrypted_ballot, BallotBoxState.CAST),
            )

        tally = CiphertextTally("my-tally", internal_manifest, 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, tally))
        self.assertFalse(tally.append(first_ballot))

        # cast a ballot
        first_ballot.state = BallotBoxState.CAST
        self.assertTrue(tally.append(first_ballot))

        # try to append a spoiled ballot
        first_ballot.state = BallotBoxState.SPOILED
        self.assertFalse(tally.append(first_ballot))

        # Verify accumulation fails if the selection collection is empty
        if first_ballot.state == BallotBoxState.CAST:
            self.assertFalse(
                tally.contests[first_ballot.object_id].accumulate_contest([]))

        # pylint: disable=protected-access
        # pop the cast ballot
        tally._cast_ballot_ids.pop()

        # reset to cast
        first_ballot.state = BallotBoxState.CAST

        self.assertTrue(
            self._cannot_erroneously_mutate_state(tally, first_ballot,
                                                  BallotBoxState.CAST))

        self.assertTrue(
            self._cannot_erroneously_mutate_state(tally, first_ballot,
                                                  BallotBoxState.SPOILED))

        self.assertTrue(
            self._cannot_erroneously_mutate_state(tally, first_ballot,
                                                  BallotBoxState.UNKNOWN))

        # verify a cast ballot cannot be added twice
        first_ballot.state = BallotBoxState.CAST
        self.assertTrue(tally.append(first_ballot))
        self.assertFalse(tally.append(first_ballot))

        # verify an already submitted ballot cannot be changed or readded
        first_ballot.state = BallotBoxState.SPOILED
        self.assertFalse(tally.append(first_ballot))
コード例 #9
0
    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))
コード例 #10
0
 def add_ballot(self, ballot: dict):
     ciphertext_ballot = deserialize(ballot, CiphertextBallot)
     # TODO: remove the dependency of multiprocessing
     tally_ballot(
         from_ciphertext_ballot(ciphertext_ballot, BallotBoxState.CAST),
         self.context.tally)