Exemplo n.º 1
0
    def test_publish(self) -> None:
        # Arrange
        now = datetime.now(timezone.utc)
        manifest = Manifest("", ElectionType.unknown, now, now, [], [], [], [], [], [])
        context = make_ciphertext_election_context(
            1, 1, ONE_MOD_P, ONE_MOD_Q, ONE_MOD_Q
        )
        constants = ElectionConstants()
        devices = []
        guardian_records = [GuardianRecord("", "", ONE_MOD_Q, [], [])]
        encrypted_ballots = []
        spoiled_ballots = []
        plaintext_tally = PlaintextTally("", [])
        ciphertext_tally = CiphertextTally("", manifest, context)

        # Act
        publish(
            manifest,
            context,
            constants,
            devices,
            encrypted_ballots,
            spoiled_ballots,
            ciphertext_tally.publish(),
            plaintext_tally,
            guardian_records,
        )

        # Assert
        self.assertTrue(path.exists(RESULTS_DIR))

        # Cleanup
        rmtree(RESULTS_DIR)
Exemplo n.º 2
0
    def test_publish(self) -> None:
        # Arrange
        now = datetime.now(timezone.utc)
        description = ElectionDescription(
            "", ElectionType.unknown, now, now, [], [], [], [], [], []
        )
        context = CiphertextElectionContext(1, 1, ONE_MOD_P, ONE_MOD_Q)
        constants = ElectionConstants()
        devices = []
        coefficients = [CoefficientValidationSet("", [], [])]
        encrypted_ballots = []
        tally = PlaintextTally("", [], [])

        # Act
        publish(
            description,
            context,
            constants,
            devices,
            encrypted_ballots,
            CiphertextTally("", description, context),
            tally,
            coefficients,
        )

        # Assert
        self.assertTrue(path.exists(RESULTS_DIR))

        # Cleanup
        rmtree(RESULTS_DIR)
Exemplo n.º 3
0
def start_tally(
        request: StartTallyRequest = Body(...),
        scheduler: Scheduler = Depends(get_scheduler),
) -> Any:
    """
    Start a new tally of a collection of ballots
    """

    ballots, description, context = _parse_tally_request(request)
    tally = CiphertextTally("election-results", description, context)

    return _tally_ballots(tally, ballots, scheduler)
Exemplo n.º 4
0
    def process_message(
        self,
        message_type: Literal["tally.cast"],
        message: Content,
        context: TrusteeContext,
    ) -> Tuple[List[Content], ElectionStep[TrusteeContext]]:
        contests: Dict[CONTEST_ID, CiphertextDecryptionContest] = {}

        tally_cast: Dict[CONTEST_ID, CiphertextTallyContest] = deserialize(
            message["content"], Dict[CONTEST_ID, CiphertextTallyContest])

        # save for when compensating
        context.tally = CiphertextTally("election-results",
                                        context.election_metadata,
                                        context.election_context)
        context.tally.contests = tally_cast

        for contest in tally_cast.values():
            selections: Dict[
                SELECTION_ID, CiphertextDecryptionSelection] = dict(
                    pair_with_object_id(
                        unwrap(
                            compute_decryption_share_for_selection(
                                context.guardian._election_keys,
                                selection,
                                context.election_context,
                            )))
                    for (_, selection) in contest.selections.items())

            contests[contest.object_id] = CiphertextDecryptionContest(
                contest.object_id,
                context.guardian_id,
                contest.description_hash,
                selections,
            )

        tally_share = DecryptionShare(
            context.tally.object_id,
            guardian_id=context.guardian_id,
            public_key=context.guardian.share_election_public_key().key,
            contests=contests,
        )

        return [{
            "message_type": "tally.trustee_share",
            "content": serialize(tally_share)
        }], ProcessEndTally()
    def process_message(
        self,
        message_type: Literal["vote.cast", "end_vote"],
        message: Union[Content, dict],
        context: BulletinBoardContext,
    ) -> Union[NoReturn, Tuple[List[Content], Optional[ElectionStep]]]:
        if message_type == "end_vote":
            context.tally = CiphertextTally("election-results",
                                            context.election_metadata,
                                            context.election_context)
            return [], ProcessStartTally()

        ballot = deserialize(message["content"], CiphertextBallot)
        if not ballot_is_valid_for_election(ballot, context.election_metadata,
                                            context.election_context):
            raise InvalidBallot()
        else:
            return [], None
Exemplo n.º 6
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))
Exemplo n.º 7
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))
 def close_ballot_box(self):
     self.context.tally = CiphertextTally('election-results',
                                          self.context.election_metadata,
                                          self.context.election_context)
     self.step = ProcessTrusteeShare()