Example #1
0
    def verify_results(self) -> None:
        """Verify results of election"""

        # Deserialize
        description_from_file = ElectionDescription.from_json_file(
            DESCRIPTION_FILE_NAME, RESULTS_DIR
        )
        self.assertEqual(self.description, description_from_file)

        context_from_file = CiphertextElectionContext.from_json_file(
            CONTEXT_FILE_NAME, RESULTS_DIR
        )
        self.assertEqual(self.context, context_from_file)

        constants_from_file = ElectionConstants.from_json_file(
            CONSTANTS_FILE_NAME, RESULTS_DIR
        )
        self.assertEqual(self.constants, constants_from_file)

        device_name = DEVICE_PREFIX + str(self.device.uuid)
        device_from_file = EncryptionDevice.from_json_file(device_name, DEVICES_DIR)
        self.assertEqual(self.device, device_from_file)

        ciphertext_ballots: List[CiphertextAcceptedBallot] = []
        for ballot in self.ballot_store.all():
            ballot_name = BALLOT_PREFIX + ballot.object_id
            ballot_from_file = CiphertextAcceptedBallot.from_json_file(
                ballot_name, BALLOTS_DIR
            )
            self.assertEqual(ballot, ballot_from_file)

        spoiled_ballots: List[CiphertextAcceptedBallot] = []
        for spoiled_ballot in self.ciphertext_tally.spoiled_ballots.values():
            ballot_name = BALLOT_PREFIX + spoiled_ballot.object_id
            spoiled_ballot_from_file = CiphertextAcceptedBallot.from_json_file(
                ballot_name, SPOILED_DIR
            )
            self.assertEqual(spoiled_ballot, spoiled_ballot_from_file)

        ciphertext_tally_from_file = PublishedCiphertextTally.from_json_file(
            ENCRYPTED_TALLY_FILE_NAME, RESULTS_DIR
        )
        self.assertEqual(
            publish_ciphertext_tally(self.ciphertext_tally), ciphertext_tally_from_file
        )

        plainttext_tally_from_file = PlaintextTally.from_json_file(
            TALLY_FILE_NAME, RESULTS_DIR
        )
        self.assertEqual(self.plaintext_tally, plainttext_tally_from_file)

        coefficient_validation_sets: List[CoefficientValidationSet] = []
        for coefficient_validation_set in self.coefficient_validation_sets:
            set_name = COEFFICIENT_PREFIX + coefficient_validation_set.owner_id
            coefficient_validation_set_from_file = CoefficientValidationSet.from_json_file(
                set_name, COEFFICIENTS_DIR
            )
            self.assertEqual(
                coefficient_validation_set, coefficient_validation_set_from_file
            )
Example #2
0
def verify_ballot_proof(
        cec: CiphertextElectionContext,
        ballot: CiphertextAcceptedBallot) -> bool:  # pragma: no cover
    """
    Given a ballot, verify its Chaum-Pedersen proofs.
    """
    return ballot.is_valid_encryption(ballot.description_hash,
                                      cec.elgamal_public_key,
                                      cec.crypto_extended_base_hash)
Example #3
0
def _parse_tally_request(
    request: StartTallyRequest,
) -> Tuple[List[CiphertextAcceptedBallot], InternalElectionDescription,
           CiphertextElectionContext, ]:
    """
    Deserialize common tally request values
    """
    ballots = [
        CiphertextAcceptedBallot.from_json_object(ballot)
        for ballot in request.ballots
    ]
    description = ElectionDescription.from_json_object(request.description)
    internal_description = InternalElectionDescription(description)
    context = CiphertextElectionContext.from_json_object(request.context)

    return (ballots, internal_description, context)
Example #4
0
def decrypt_ballots(request: DecryptBallotsRequest = Body(...)) -> Any:
    ballots = [
        CiphertextAcceptedBallot.from_json_object(ballot)
        for ballot in request.encrypted_ballots
    ]
    context: CiphertextElectionContext = CiphertextElectionContext.from_json_object(
        request.context)

    all_shares: List[BallotDecryptionShare] = [
        read_json_object(share, BallotDecryptionShare)
        for shares in request.shares.values() for share in shares
    ]
    shares_by_ballot = index_shares_by_ballot(all_shares)

    extended_base_hash = context.crypto_extended_base_hash
    decrypted_ballots = {
        ballot.object_id:
        decrypt_ballot(ballot, shares_by_ballot[ballot.object_id],
                       extended_base_hash)
        for ballot in ballots
    }

    return write_json_object(decrypted_ballots)
Example #5
0
def decrypt_ballot_shares(
    request: DecryptBallotSharesRequest = Body(...),
    scheduler: Scheduler = Depends(get_scheduler),
) -> Any:
    """
    Decrypt this guardian's share of one or more ballots
    """
    ballots = [
        CiphertextAcceptedBallot.from_json_object(ballot)
        for ballot in request.encrypted_ballots
    ]
    context = CiphertextElectionContext.from_json_object(request.context)
    guardian = convert_guardian(request.guardian)

    shares = [
        compute_decryption_share_for_ballot(guardian, ballot, context, scheduler)
        for ballot in ballots
    ]

    response = DecryptBallotSharesResponse(
        shares=[write_json_object(share) for share in shares]
    )

    return response
Example #6
0
    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))
Example #7
0
    def _cannot_erroneously_mutate_state(
        self,
        subject: CiphertextTally,
        ballot: CiphertextAcceptedBallot,
        state_to_test: BallotBoxState,
    ) -> bool:

        input_state = ballot.state
        ballot.state = state_to_test

        # remove the first selection
        first_contest = ballot.contests[0]
        first_selection = first_contest.ballot_selections[0]
        ballot.contests[0].ballot_selections.remove(first_selection)

        self.assertIsNone(tally_ballot(ballot, subject))
        self.assertFalse(subject.append(ballot))

        # Verify accumulation fails if the selection count does not match
        if ballot.state == BallotBoxState.CAST:
            first_tally = subject.cast[first_contest.object_id]
            self.assertFalse(
                first_tally.elgamal_accumulate(
                    ballot.contests[0].ballot_selections))

            key, bad_accumulation = first_tally._accumulate_selections(
                first_selection.object_id,
                first_tally.tally_selections[first_selection.object_id],
                ballot.contests[0].ballot_selections,
            )
            self.assertIsNone(bad_accumulation)

        ballot.contests[0].ballot_selections.insert(0, first_selection)

        # modify the contest description hash
        first_contest_hash = ballot.contests[0].description_hash
        ballot.contests[0].description_hash = ONE_MOD_Q
        self.assertIsNone(tally_ballot(ballot, subject))
        self.assertFalse(subject.append(ballot))

        ballot.contests[0].description_hash = first_contest_hash

        # modify a contest object id
        first_contest_object_id = ballot.contests[0].object_id
        ballot.contests[0].object_id = "a-bad-object-id"
        self.assertIsNone(tally_ballot(ballot, subject))
        self.assertFalse(subject.append(ballot))

        ballot.contests[0].object_id = first_contest_object_id

        # modify a selection object id
        first_contest_selection_object_id = (
            ballot.contests[0].ballot_selections[0].object_id)
        ballot.contests[0].ballot_selections[
            0].object_id = "another-bad-object-id"

        self.assertIsNone(tally_ballot(ballot, subject))
        self.assertFalse(subject.append(ballot))

        # Verify accumulation fails if the selection object id does not match
        if ballot.state == BallotBoxState.CAST:
            self.assertFalse(
                subject.cast[ballot.contests[0].object_id].elgamal_accumulate(
                    ballot.contests[0].ballot_selections))

        ballot.contests[0].ballot_selections[
            0].object_id = first_contest_selection_object_id

        # modify the ballot's hash
        first_ballot_hash = ballot.description_hash
        ballot.description_hash = ONE_MOD_Q
        self.assertIsNone(tally_ballot(ballot, subject))
        self.assertFalse(subject.append(ballot))

        ballot.description_hash = first_ballot_hash
        ballot.state = input_state

        return True