Esempio n. 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
            )
    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)
Esempio n. 3
0
def encrypt_ballots(request: EncryptBallotsRequest = Body(...)) -> Any:
    """
    Encrypt one or more ballots
    """
    ballots = [
        PlaintextBallot.from_json_object(ballot) for ballot in request.ballots
    ]
    description = InternalElectionDescription(
        ElectionDescription.from_json_object(request.description))
    context = CiphertextElectionContext.from_json_object(request.context)
    seed_hash = read_json_object(request.seed_hash, ElementModQ)
    nonce: Optional[ElementModQ] = (read_json_object(
        request.nonce, ElementModQ) if request.nonce else None)

    encrypted_ballots = []
    current_hash = seed_hash

    for ballot in ballots:
        encrypted_ballot = encrypt_ballot(ballot, description, context,
                                          current_hash, nonce)
        if not encrypted_ballot:
            raise HTTPException(status_code=500,
                                detail="Ballot failed to encrypt")
        encrypted_ballots.append(encrypted_ballot)
        current_hash = get_optional(encrypted_ballot.tracking_hash)

    response = EncryptBallotsResponse(
        encrypted_ballots=[
            ballot.to_json_object() for ballot in encrypted_ballots
        ],
        next_seed_hash=write_json_object(current_hash),
    )
    return response
Esempio n. 4
0
def handle_ballot(request: AcceptBallotRequest, state: BallotBoxState) -> Any:
    ballot = CiphertextBallot.from_json_object(request.ballot)
    description = ElectionDescription.from_json_object(request.description)
    internal_description = InternalElectionDescription(description)
    context = CiphertextElectionContext.from_json_object(request.context)

    accepted_ballot = accept_ballot(
        ballot,
        state,
        internal_description,
        context,
        BallotStore(),
    )

    return accepted_ballot
Esempio n. 5
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)
    def verify_results(self) -> None:
        """Verify results of election"""

        # Deserialize
        manifest_from_file = Manifest.from_json_file(MANIFEST_FILE_NAME,
                                                     RESULTS_DIR)
        self.assertEqual(self.manifest, manifest_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)

        for ballot in self.ballot_store.all():
            name = BALLOT_PREFIX + ballot.object_id
            ballot_from_file = SubmittedBallot.from_json_file(
                name, BALLOTS_DIR)
            self.assertEqual(ballot, ballot_from_file)

        for spoiled_ballot in self.plaintext_spoiled_ballots.values():
            name = BALLOT_PREFIX + spoiled_ballot.object_id
            spoiled_ballot_from_file = PlaintextTally.from_json_file(
                name, SPOILED_DIR)
            self.assertEqual(spoiled_ballot, spoiled_ballot_from_file)

        published_ciphertext_tally_from_file = PublishedCiphertextTally.from_json_file(
            ENCRYPTED_TALLY_FILE_NAME, RESULTS_DIR)
        self.assertEqual(self.ciphertext_tally.publish(),
                         published_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)

        for guardian_record in self.guardian_records:
            set_name = COEFFICIENT_PREFIX + guardian_record.guardian_id
            guardian_record_from_file = GuardianRecord.from_json_file(
                set_name, GUARDIAN_DIR)
            self.assertEqual(guardian_record, guardian_record_from_file)
Esempio n. 7
0
def decrypt_share(
    request: DecryptTallyShareRequest = Body(...),
    scheduler: Scheduler = Depends(get_scheduler),
) -> Any:
    """
    Decrypt a single guardian's share of a tally
    """
    description = InternalElectionDescription(
        ElectionDescription.from_json_object(request.description)
    )
    context = CiphertextElectionContext.from_json_object(request.context)
    guardian = convert_guardian(request.guardian)
    tally = convert_tally(request.encrypted_tally, description, context)

    share = compute_decryption_share(guardian, tally, context, scheduler)

    return write_json_object(share)
Esempio n. 8
0
def ciphertext_elections(draw: _DrawType, election_description: ElectionDescription):
    """
    Generates a `CiphertextElectionContext` with a single public-private key pair as the encryption context.

    In a real election, the key ceremony would be used to generate a shared public key.

    :param draw: Hidden argument, used by Hypothesis.
    :param election_description: An `ElectionDescription` object, with which the `CiphertextElectionContext` will be associated
    :return: a tuple of a `CiphertextElectionContext` and the secret key associated with it
    """
    secret_key, public_key = draw(elgamal_keypairs())
    ciphertext_election_with_secret: CIPHERTEXT_ELECTIONS_TUPLE_TYPE = (
        secret_key,
        CiphertextElectionContext(
            number_of_guardians=1,
            quorum=1,
            elgamal_public_key=public_key,
            description_hash=election_description.crypto_hash(),
        ),
    )
    return ciphertext_election_with_secret
Esempio n. 9
0
def decrypt_tally(request: DecryptTallyRequest = Body(...)) -> Any:
    """
    Decrypt a tally from a collection of decrypted guardian shares
    """
    description = InternalElectionDescription(
        ElectionDescription.from_json_object(request.description))
    context = CiphertextElectionContext.from_json_object(request.context)
    tally = convert_tally(request.encrypted_tally, description, context)

    shares = {
        guardian_id: read_json_object(share, TallyDecryptionShare)
        for guardian_id, share in request.shares.items()
    }

    full_plaintext_tally = decrypt(tally, shares, context)
    if not full_plaintext_tally:
        raise HTTPException(
            status_code=500,
            detail="Unable to decrypt tally",
        )
    published_plaintext_tally = publish_plaintext_tally(full_plaintext_tally)

    return published_plaintext_tally.to_json_object()
Esempio n. 10
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)
Esempio n. 11
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