Example #1
0
def _candidate_to_selection_description(
        candidate: Candidate, sequence_order: int) -> SelectionDescription:
    """
    Given a `Candidate` and its position in a list of candidates, returns an equivalent
    `SelectionDescription`. The selection's `object_id` will contain the candidates's
    `object_id` within, but will have a "c-" prefix attached, so you'll be able to
    tell that they're related.
    """
    return SelectionDescription(f"c-{candidate.object_id}",
                                candidate.get_candidate_id(), sequence_order)
    def test_encrypt_simple_selection_succeeds(self):

        # Arrange
        keypair = elgamal_keypair_from_secret(int_to_q(2))
        nonce = randbelow(Q)
        metadata = SelectionDescription("some-selection-object-id",
                                        "some-candidate-id", 1)
        hash_context = metadata.crypto_hash()

        subject = selection_from(metadata)
        self.assertTrue(subject.is_valid(metadata.object_id))

        # Act
        result = encrypt_selection(subject, metadata, keypair.public_key,
                                   nonce)

        # Assert
        self.assertIsNotNone(result)
        self.assertIsNotNone(result.message)
        self.assertTrue(
            result.is_valid_encryption(hash_context, keypair.public_key))
Example #3
0
    def test_encrypt_simple_selection_malformed_data_fails(self):

        # Arrange
        keypair = elgamal_keypair_from_secret(int_to_q(2))
        nonce = randbelow(Q)
        metadata = SelectionDescription(
            "some-selection-object-id", "some-candidate-id", 1
        )
        hash_context = metadata.crypto_hash()

        subject = selection_from(metadata)
        self.assertTrue(subject.is_valid(metadata.object_id))

        # Act
        result = encrypt_selection(
            subject, metadata, keypair.public_key, ONE_MOD_Q, nonce
        )

        # tamper with the description_hash
        malformed_description_hash = deepcopy(result)
        malformed_description_hash.description_hash = TWO_MOD_Q

        # remove the proof
        missing_proof = deepcopy(result)
        missing_proof.proof = None

        # Assert
        self.assertFalse(
            malformed_description_hash.is_valid_encryption(
                hash_context, keypair.public_key, ONE_MOD_Q
            )
        )
        self.assertFalse(
            missing_proof.is_valid_encryption(
                hash_context, keypair.public_key, ONE_MOD_Q
            )
        )
def get_selection_description_well_formed(
    draw: _DrawType,
    ints=integers(1, 20),
    emails=emails(),
    candidate_id: Optional[str] = None,
    sequence_order: Optional[int] = None,
) -> Tuple[str, SelectionDescription]:
    if candidate_id is None:
        candidate_id = draw(emails)

    object_id = f"{candidate_id}-selection"

    if sequence_order is None:
        sequence_order = draw(ints)

    return (object_id,
            SelectionDescription(object_id, candidate_id, sequence_order))
    def get_fake_election(self) -> ElectionDescription:
        """
        Get a single Fake Election object that is manually constructed with default values
        """

        fake_ballot_style = BallotStyle("some-ballot-style-id")
        fake_ballot_style.geopolitical_unit_ids = ["some-geopoltical-unit-id"]

        fake_referendum_ballot_selections = [
            # Referendum selections are simply a special case of `candidate` in the object model
            SelectionDescription("some-object-id-affirmative",
                                 "some-candidate-id-1", 0),
            SelectionDescription("some-object-id-negative",
                                 "some-candidate-id-2", 1),
        ]

        sequence_order = 0
        number_elected = 1
        votes_allowed = 1
        fake_referendum_contest = ReferendumContestDescription(
            "some-referendum-contest-object-id",
            "some-geopoltical-unit-id",
            sequence_order,
            VoteVariationType.one_of_m,
            number_elected,
            votes_allowed,
            "some-referendum-contest-name",
            fake_referendum_ballot_selections,
        )

        fake_candidate_ballot_selections = [
            SelectionDescription("some-object-id-candidate-1",
                                 "some-candidate-id-1", 0),
            SelectionDescription("some-object-id-candidate-2",
                                 "some-candidate-id-2", 1),
            SelectionDescription("some-object-id-candidate-3",
                                 "some-candidate-id-3", 2),
        ]

        sequence_order_2 = 1
        number_elected_2 = 2
        votes_allowed_2 = 2
        fake_candidate_contest = CandidateContestDescription(
            "some-candidate-contest-object-id",
            "some-geopoltical-unit-id",
            sequence_order_2,
            VoteVariationType.one_of_m,
            number_elected_2,
            votes_allowed_2,
            "some-candidate-contest-name",
            fake_candidate_ballot_selections,
        )

        fake_election = ElectionDescription(
            election_scope_id="some-scope-id",
            type=ElectionType.unknown,
            start_date=datetime.now(),
            end_date=datetime.now(),
            geopolitical_units=[
                GeopoliticalUnit(
                    "some-geopoltical-unit-id",
                    "some-gp-unit-name",
                    ReportingUnitType.unknown,
                )
            ],
            parties=[Party("some-party-id-1"),
                     Party("some-party-id-2")],
            candidates=[
                Candidate("some-candidate-id-1"),
                Candidate("some-candidate-id-2"),
                Candidate("some-candidate-id-3"),
            ],
            contests=[fake_referendum_contest, fake_candidate_contest],
            ballot_styles=[fake_ballot_style],
        )

        return fake_election
Example #6
0
    def _contest_name_to_description(
        self,
        name: str,
        contest_uid_maker: UidMaker,
        gp_uid_maker: UidMaker,
    ) -> Tuple[ContestDescription, List[Candidate], GeopoliticalUnit, Dict[
            str, str]]:

        selections: List[SelectionDescription] = []
        candidates: List[Candidate] = []

        # We're collecting the candidates at the same time that we're collecting the selections
        # to make sure that the object_id's are properly synchronized. Consider the case where
        # we ended up with two candidates with identical names. In the Dominion CSV, we'd have
        # no way to tell if they were actually the same person running in two separate contests,
        # or whether they were distinct. This solution ensures that we create a fresh Candidate
        # one-to-one with every SelectionDescription.

        # This method will be called separately for every contest name, and the resulting lists
        # of candidates should be merged to make the complete list that goes into an
        # ElectionDescription.

        candidate_to_column: Dict[str, str] = {}

        for c in self.metadata.contest_map[name]:
            id_str = c.object_id
            id_number = int(
                id_str[1:])  # total hack: stripping off the first char

            candidate = Candidate(
                object_id=id_str,
                ballot_name=_str_to_internationalized_text_en(c.choice_name),
                party_id=c.party_name if c.party_name != "" else None,
                image_uri=None,
            )

            # To make our lives easier, we're going to use identical object_ids for selections
            # and candidates, and hopefully this won't break anything.
            candidates.append(candidate)
            selections.append(
                SelectionDescription(
                    object_id=candidate.object_id,
                    candidate_id=candidate.object_id,
                    sequence_order=id_number,
                ))

            candidate_to_column[candidate.object_id] = c.to_string()

        gp = GeopoliticalUnit(
            object_id=gp_uid_maker.next(),
            name=name,
            type=ReportingUnitType.unknown,
        )

        id_number, id_str = contest_uid_maker.next_int()
        number_elected = self.metadata.max_votes_for_map[name]
        vote_variation_type = (VoteVariationType.one_of_m if number_elected
                               == 1 else VoteVariationType.n_of_m)
        vote_allowed = None if number_elected < 2 else number_elected

        return (
            ContestDescription(
                object_id=id_str,
                electoral_district_id=gp.object_id,
                sequence_order=id_number,
                vote_variation=vote_variation_type,
                number_elected=number_elected,
                votes_allowed=vote_allowed,
                name=name,
                ballot_selections=selections,
                ballot_title=_str_to_internationalized_text_en(name),
            ),
            candidates,
            gp,
            candidate_to_column,
        )