Пример #1
0
    def compare_results(self) -> None:
        """
        Compare the results to ensure the decryption was done correctly
        """
        print(f"""
            {'-'*40}
            # Election Results:

            """)

        # Create a representation of each contest's tally
        selection_ids = [
            selection.object_id for contest in self.metadata.contests
            for selection in contest.ballot_selections
        ]
        expected_plaintext_tally: Dict[str, int] = {
            key: 0
            for key in selection_ids
        }

        # Tally the expected values from the loaded ballots
        for ballot in self.plaintext_ballots:
            if (get_optional(self.ballot_store.get(
                    ballot.object_id)).state == BallotBoxState.CAST):
                for contest in ballot.contests:
                    for selection in contest.ballot_selections:
                        expected_plaintext_tally[
                            selection.object_id] += selection.to_int()

        # Compare the expected tally to the decrypted tally
        for tally_contest in self.plaintext_tally.contests.values():
            print(f" Contest: {tally_contest.object_id}")
            for tally_selection in tally_contest.selections.values():
                expected = expected_plaintext_tally[tally_selection.object_id]
                self._assert_message(
                    f"   - Selection: {tally_selection.object_id}",
                    f"expected: {expected}, actual: {tally_selection.plaintext}",
                    expected == tally_selection.plaintext,
                )
        print(f"\n{'-'*40}\n")

        # Compare the expected values for each spoiled ballot
        for ballot_id, accepted_ballot in self.ciphertext_tally.spoiled_ballots.items(
        ):
            if accepted_ballot.state == BallotBoxState.SPOILED:
                for plaintext_ballot in self.plaintext_ballots:
                    if ballot_id == plaintext_ballot.object_id:
                        print(f"\nSpoiled Ballot: {ballot_id}")
                        for contest in plaintext_ballot.contests:
                            print(f"\n Contest: {contest.object_id}")
                            for selection in contest.ballot_selections:
                                expected = selection.to_int()
                                decrypted_selection = self.plaintext_tally.spoiled_ballots[
                                    ballot_id][contest.object_id].selections[
                                        selection.object_id]
                                self._assert_message(
                                    f"   - Selection: {selection.object_id}",
                                    f"expected: {expected}, actual: {decrypted_selection.plaintext}",
                                    expected == decrypted_selection.plaintext,
                                )
Пример #2
0
    def test_flatmap(self):
        good: Optional[int] = 3
        bad: Optional[int] = None

        self.assertEqual(5,
                         get_optional(flatmap_optional(good, lambda x: x + 2)))
        self.assertIsNone(flatmap_optional(bad, lambda x: x + 2))
Пример #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
Пример #4
0
    def test_djcp_proof_broken(self, keypair: ElGamalKeyPair,
                               nonce: ElementModQ, seed: ElementModQ):
        # verify two different ways to generate an invalid C-P proof.
        message = get_optional(elgamal_encrypt(0, nonce, keypair.public_key))
        message_bad = get_optional(
            elgamal_encrypt(2, nonce, keypair.public_key))
        proof = make_disjunctive_chaum_pedersen_zero(message, nonce,
                                                     keypair.public_key,
                                                     ONE_MOD_Q, seed)
        proof_bad = make_disjunctive_chaum_pedersen_zero(
            message_bad, nonce, keypair.public_key, ONE_MOD_Q, seed)

        self.assertFalse(
            proof_bad.is_valid(message_bad, keypair.public_key, ONE_MOD_Q))
        self.assertFalse(
            proof.is_valid(message_bad, keypair.public_key, ONE_MOD_Q))
    def test_elgamal_encryption_decryption_with_known_nonce_inverses(
            self, message: int, nonce: ElementModQ, keypair: ElGamalKeyPair):
        ciphertext = get_optional(
            elgamal_encrypt(message, nonce, keypair.public_key))
        plaintext = ciphertext.decrypt_known_nonce(keypair.public_key, nonce)

        self.assertEqual(message, plaintext)
Пример #6
0
    def test_ccp_proof(
        self,
        keypair: ElGamalKeyPair,
        nonce: ElementModQ,
        seed: ElementModQ,
        constant: int,
        bad_constant: int,
    ):
        if constant == bad_constant:
            bad_constant = constant + 1

        message = get_optional(elgamal_encrypt(constant, nonce, keypair.public_key))
        decryption = message.partial_decrypt(keypair.secret_key)
        proof = make_chaum_pedersen(
            message, keypair.secret_key, decryption, seed, ONE_MOD_Q
        )
        bad_proof = make_chaum_pedersen(
            message, keypair.secret_key, int_to_p(bad_constant), seed, ONE_MOD_Q
        )
        self.assertTrue(
            proof.is_valid(message, keypair.public_key, decryption, ONE_MOD_Q)
        )
        self.assertFalse(
            bad_proof.is_valid(message, keypair.public_key, decryption, ONE_MOD_Q)
        )
    def step_2_encrypt_votes(self) -> None:
        """
        Using the `CiphertextElectionContext` encrypt ballots for the election
        """

        # Configure the Encryption Device
        self.device = ElectionFactory.get_encryption_device()
        self.encrypter = EncryptionMediator(self.internal_manifest,
                                            self.context, self.device)
        self._assert_message(
            EncryptionDevice.__qualname__,
            f"Ready to encrypt at location: {self.device.location}",
        )

        # Load some Ballots
        self.plaintext_ballots = BallotFactory().get_simple_ballots_from_file()
        self._assert_message(
            PlaintextBallot.__qualname__,
            f"Loaded ballots: {len(self.plaintext_ballots)}",
            len(self.plaintext_ballots) > 0,
        )

        # Encrypt the Ballots
        for plaintext_ballot in self.plaintext_ballots:
            encrypted_ballot = self.encrypter.encrypt(plaintext_ballot)
            self._assert_message(
                EncryptionMediator.encrypt.__qualname__,
                f"Ballot Id: {plaintext_ballot.object_id}",
                encrypted_ballot is not None,
            )
            self.ciphertext_ballots.append(get_optional(encrypted_ballot))
Пример #8
0
    def step_4_decrypt_tally(self) -> None:
        """
        Homomorphically combine the selections made on all of the cast ballots
        and use the Available Guardians to decrypt the combined tally.
        In this way, no individual voter's cast ballot is ever decrypted drectly.
        """

        # Generate a Homomorphically Accumulated Tally of the ballots
        self.ciphertext_tally = get_optional(
            tally_ballots(self.ballot_store, self.metadata, self.context)
        )
        self._assert_message(
            tally_ballots.__qualname__,
            f"""
            - cast: {self.ciphertext_tally.count()} 
            - spoiled: {len(self.ciphertext_tally.spoiled_ballots)}
            Total: {len(self.ciphertext_tally)}
            """,
            self.ciphertext_tally is not None,
        )

        # Configure the Decryption
        self.decrypter = DecryptionMediator(
            self.metadata, self.context, self.ciphertext_tally
        )

        # Announce each guardian as present
        for guardian in self.guardians:
            decryption_share = self.decrypter.announce(guardian)
            self._assert_message(
                DecryptionMediator.announce.__qualname__,
                f"Guardian Present: {guardian.object_id}",
                decryption_share is not None,
            )

        # Get the Plain Text Tally
        self.plaintext_tally = get_optional(self.decrypter.get_plaintext_tally())
        self._assert_message(
            DecryptionMediator.get_plaintext_tally.__qualname__,
            "Tally Decrypted",
            self.plaintext_tally is not None,
        )

        # Now, compare the results
        self.compare_results()
 def test_djcp_proof_one(self, keypair: ElGamalKeyPair, nonce: ElementModQ,
                         seed: ElementModQ):
     message = get_optional(elgamal_encrypt(1, nonce, keypair.public_key))
     proof = make_disjunctive_chaum_pedersen_one(message, nonce,
                                                 keypair.public_key, seed)
     proof_bad = make_disjunctive_chaum_pedersen_zero(
         message, nonce, keypair.public_key, seed)
     self.assertTrue(proof.is_valid(message, keypair.public_key))
     self.assertFalse(proof_bad.is_valid(message, keypair.public_key))
 def get_fake_ciphertext_election(
     self, description: ElectionDescription, elgamal_public_key: ElementModP
 ) -> Tuple[InternalElectionDescription, CiphertextElectionContext]:
     builder = ElectionBuilder(number_of_guardians=1,
                               quorum=1,
                               description=description)
     builder.set_public_key(elgamal_public_key)
     metadata, election = get_optional(builder.build())
     return metadata, election
Пример #11
0
    def get_hamilton_election_with_encryption_context(
        self, ) -> Tuple[AllPublicElectionData, AllPrivateElectionData]:
        guardians: List[Guardian] = []
        coefficient_validation_sets: List[CoefficientValidationSet] = []

        # Configure the election builder
        description = self.get_hamilton_election_from_file()
        builder = ElectionBuilder(NUMBER_OF_GUARDIANS, QUORUM, description)

        # Setup Guardians
        for i in range(NUMBER_OF_GUARDIANS):
            guardians.append(
                Guardian(
                    "hamilton-county-canvass-board-member-" + str(i),
                    i,
                    NUMBER_OF_GUARDIANS,
                    QUORUM,
                ))

        # Run the key ceremony
        mediator = KeyCeremonyMediator(guardians[0].ceremony_details)
        for guardian in guardians:
            mediator.announce(guardian)
        mediator.orchestrate()
        mediator.verify()

        # Joint Key
        joint_key = mediator.publish_joint_key()

        # Save Validation Keys
        for guardian in guardians:
            coefficient_validation_sets.append(
                guardian.share_coefficient_validation_set())

        builder.set_public_key(get_optional(joint_key).joint_public_key)
        builder.set_commitment_hash(get_optional(joint_key).commitment_hash)
        metadata, context = get_optional(builder.build())
        constants = ElectionConstants()

        return (
            AllPublicElectionData(description, metadata, context, constants,
                                  coefficient_validation_sets),
            AllPrivateElectionData(guardians),
        )
Пример #12
0
def _equivalent_decrypt_helper(
    ied: InternalElectionDescription,
    base_hash: ElementModQ,
    public_key: ElementModP,
    secret_key: ElementModQ,
    cballot: CiphertextAcceptedBallot,
) -> PlaintextBallot:  # pragma: no cover
    return get_optional(
        decrypt_ballot_with_secret(cballot, ied, base_hash, public_key,
                                   secret_key, True, True))
Пример #13
0
 def get_fake_ciphertext_election(
     manifest: Manifest, elgamal_public_key: ElementModP
 ) -> Tuple[InternalManifest, CiphertextElectionContext]:
     builder = ElectionBuilder(number_of_guardians=1,
                               quorum=1,
                               manifest=manifest)
     builder.set_public_key(elgamal_public_key)
     builder.set_commitment_hash(TWO_MOD_Q)
     internal_manifest, context = get_optional(builder.build())
     return internal_manifest, context
 def test_ccp_proofs_simple_encryption_of_one(self):
     keypair = elgamal_keypair_from_secret(TWO_MOD_Q)
     nonce = ONE_MOD_Q
     seed = TWO_MOD_Q
     message = get_optional(elgamal_encrypt(1, nonce, keypair.public_key))
     proof = make_constant_chaum_pedersen(message, 1, nonce,
                                          keypair.public_key, seed)
     bad_proof = make_constant_chaum_pedersen(message, 0, nonce,
                                              keypair.public_key, seed)
     self.assertTrue(proof.is_valid(message, keypair.public_key))
     self.assertFalse(bad_proof.is_valid(message, keypair.public_key))
Пример #15
0
 def process_message(
     self,
     message_type: Literal["end_key_ceremony"],
     message: Content,
     context: TrusteeContext,
 ) -> Tuple[List[Content], ElectionStep]:
     key_ceremony_results = deserialize(message["content"],
                                        KeyCeremonyResults)
     context.election_builder.set_public_key(
         get_optional(
             key_ceremony_results.election_joint_key.joint_public_key))
     context.election_builder.set_commitment_hash(
         get_optional(
             key_ceremony_results.election_joint_key.commitment_hash))
     context.election_metadata, context.election_context = get_optional(
         context.election_builder.build())
     # TODO: coefficient validation keys???
     # TODO: check joint key, without using private variables if possible
     #         serialize(elgamal_combine_public_keys(context.guardian._guardian_election_public_keys.values()))
     return [], ProcessTallyCast()
    def test_djcp_proofs_simple(self):
        # doesn't get any simpler than this
        keypair = elgamal_keypair_from_secret(TWO_MOD_Q)
        nonce = ONE_MOD_Q
        seed = TWO_MOD_Q
        message0 = get_optional(elgamal_encrypt(0, nonce, keypair.public_key))
        proof0 = make_disjunctive_chaum_pedersen_zero(message0, nonce,
                                                      keypair.public_key, seed)
        proof0bad = make_disjunctive_chaum_pedersen_one(
            message0, nonce, keypair.public_key, seed)
        self.assertTrue(proof0.is_valid(message0, keypair.public_key))
        self.assertFalse(proof0bad.is_valid(message0, keypair.public_key))

        message1 = get_optional(elgamal_encrypt(1, nonce, keypair.public_key))
        proof1 = make_disjunctive_chaum_pedersen_one(message1, nonce,
                                                     keypair.public_key, seed)
        proof1bad = make_disjunctive_chaum_pedersen_zero(
            message1, nonce, keypair.public_key, seed)
        self.assertTrue(proof1.is_valid(message1, keypair.public_key))
        self.assertFalse(proof1bad.is_valid(message1, keypair.public_key))
Пример #17
0
    def test_ccp_proof(
        self,
        keypair: ElGamalKeyPair,
        nonce: ElementModQ,
        seed: ElementModQ,
        constant: int,
        bad_constant: int,
    ):
        # assume() slows down the test-case generation
        # so assume(constant != bad_constant)
        if constant == bad_constant:
            bad_constant = constant + 1

        message = get_optional(
            elgamal_encrypt(constant, nonce, keypair.public_key))
        message_bad = get_optional(
            elgamal_encrypt(bad_constant, nonce, keypair.public_key))

        proof = make_constant_chaum_pedersen(message, constant, nonce,
                                             keypair.public_key, seed,
                                             ONE_MOD_Q)
        self.assertTrue(proof.is_valid(message, keypair.public_key, ONE_MOD_Q))

        proof_bad1 = make_constant_chaum_pedersen(message_bad, constant, nonce,
                                                  keypair.public_key, seed,
                                                  ONE_MOD_Q)
        self.assertFalse(
            proof_bad1.is_valid(message_bad, keypair.public_key, ONE_MOD_Q))

        proof_bad2 = make_constant_chaum_pedersen(message, bad_constant, nonce,
                                                  keypair.public_key, seed,
                                                  ONE_MOD_Q)
        self.assertFalse(
            proof_bad2.is_valid(message, keypair.public_key, ONE_MOD_Q))

        proof_bad3 = ConstantChaumPedersenProof(proof.pad, proof.data,
                                                proof.challenge,
                                                proof.response, -1)
        self.assertFalse(
            proof_bad3.is_valid(message, keypair.public_key, ONE_MOD_Q))
Пример #18
0
    def is_valid(self) -> bool:
        if self.constants != ElectionConstants():
            log_error("Mismatching election constants!")
            return False

        # super-cheesy unit test to make sure keypair works

        m1 = randbelow(5)
        m2 = randbelow(5)
        nonce1 = rand_q()
        nonce2 = rand_q()

        c1 = get_optional(elgamal_encrypt(m1, nonce1, self.keypair.public_key))
        c2 = get_optional(elgamal_encrypt(m2, nonce2, self.keypair.public_key))
        csum = elgamal_add(c1, c2)

        psum = csum.decrypt(self.keypair.secret_key)

        if psum != m1 + m2:
            log_error("The given keypair didn't work for basic ElGamal math")
            return False

        return True
    def process_message(
        self,
        message_type: Literal["key_ceremony.trustee_verification"],
        message: Content,
        context: BulletinBoardContext,
    ) -> Tuple[List[Content], Optional[ElectionStep]]:
        content = deserialize(message["content"], TrusteeVerification)
        self.verifications_received.add(content.guardian_id)
        # TO-DO: check verifications?

        if len(self.verifications_received) < context.number_of_guardians:
            return [], None

        sorted_trustee_elections_keys = sorted(
            context.trustee_election_keys.items())

        election_joint_key = combine_election_public_keys(
            trustee_election_key.public_key_set.election for guardian_id,
            trustee_election_key in sorted_trustee_elections_keys)
        context.election_builder.set_public_key(
            get_optional(election_joint_key.joint_public_key))
        context.election_builder.set_commitment_hash(
            get_optional(election_joint_key.commitment_hash))
        context.election_metadata, context.election_context = get_optional(
            context.election_builder.build())

        return [{
            "message_type":
            "end_key_ceremony",
            "content":
            serialize(
                KeyCeremonyResults(
                    election_joint_key=election_joint_key,
                    constants=ElectionConstants(),
                    context=context.election_context,
                )),
        }], ProcessStartVote()
Пример #20
0
    def get_hamilton_manifest_with_encryption_context(
        self, ) -> Tuple[AllPublicElectionData, AllPrivateElectionData]:
        guardians: List[Guardian] = []
        guardian_records: List[GuardianRecord] = []

        # Configure the election builder
        manifest = self.get_hamilton_manifest_from_file()
        builder = ElectionBuilder(NUMBER_OF_GUARDIANS, QUORUM, manifest)

        # Run the Key Ceremony
        ceremony_details = CeremonyDetails(NUMBER_OF_GUARDIANS, QUORUM)
        guardians = KeyCeremonyHelper.create_guardians(ceremony_details)
        mediator = KeyCeremonyMediator("key-ceremony-mediator",
                                       ceremony_details)
        KeyCeremonyHelper.perform_full_ceremony(guardians, mediator)

        # Final: Joint Key
        joint_key = mediator.publish_joint_key()

        # Publish Guardian Records
        guardian_records = [guardian.publish() for guardian in guardians]

        builder.set_public_key(get_optional(joint_key).joint_public_key)
        builder.set_commitment_hash(get_optional(joint_key).commitment_hash)
        internal_manifest, context = get_optional(builder.build())
        constants = ElectionConstants()

        return (
            AllPublicElectionData(
                manifest,
                internal_manifest,
                context,
                constants,
                guardian_records,
            ),
            AllPrivateElectionData(guardians),
        )
Пример #21
0
 def test_djcp_proof_invalid_inputs(self):
     # this is here to push up our coverage
     keypair = elgamal_keypair_from_secret(TWO_MOD_Q)
     nonce = ONE_MOD_Q
     seed = TWO_MOD_Q
     message0 = get_optional(elgamal_encrypt(0, nonce, keypair.public_key))
     self.assertRaises(
         Exception,
         make_disjunctive_chaum_pedersen,
         message0,
         nonce,
         keypair.public_key,
         seed,
         3,
     )
 def test_cp_proofs_simple(self):
     keypair = elgamal_keypair_from_secret(TWO_MOD_Q)
     nonce = ONE_MOD_Q
     seed = TWO_MOD_Q
     message = get_optional(elgamal_encrypt(0, nonce, keypair.public_key))
     decryption = message.partial_decrypt(keypair.secret_key)
     proof = make_chaum_pedersen(message, keypair.secret_key, decryption,
                                 seed, ONE_MOD_Q)
     bad_proof = make_chaum_pedersen(message, keypair.secret_key, TWO_MOD_Q,
                                     seed, ONE_MOD_Q)
     self.assertTrue(
         proof.is_valid(message, keypair.public_key, decryption, ONE_MOD_Q))
     self.assertFalse(
         bad_proof.is_valid(message, keypair.public_key, decryption,
                            ONE_MOD_Q))
Пример #23
0
def compensate_decrypt(
    guardian_auxiliary_keys: AuxiliaryKeyPair,
    missing_guardian_backup: ElectionPartialKeyBackup,
    ciphertext: ElGamalCiphertext,
    extended_base_hash: ElementModQ,
    nonce_seed: ElementModQ = None,
    decrypt: AuxiliaryDecrypt = rsa_decrypt,
) -> Optional[Tuple[ElementModP, ChaumPedersenProof]]:
    """
    Compute a compensated partial decryption of an elgamal encryption
    on behalf of the missing guardian

    :param guardian_auxiliary_keys: Auxiliary key pair for guardian decrypting
    :param missing_guardian_backup: Missing guardians backup
    :param ciphertext: the `ElGamalCiphertext` that will be partially decrypted
    :param extended_base_hash: the extended base hash of the election that
                                was used to generate t he ElGamal Ciphertext
    :param nonce_seed: an optional value used to generate the `ChaumPedersenProof`
                        if no value is provided, a random number will be used.
    :param decrypt: an `AuxiliaryDecrypt` function to decrypt the missing guardina private key backup
    :return: a `Tuple[ElementModP, ChaumPedersenProof]` of the decryption and its proof
    """
    if nonce_seed is None:
        nonce_seed = rand_q()

    decrypted_value = decrypt(missing_guardian_backup.encrypted_value,
                              guardian_auxiliary_keys.secret_key)
    if decrypted_value is None:
        log_warning(
            (f"compensate decrypt guardian {guardian_auxiliary_keys.owner_id}"
             f" failed decryption for {missing_guardian_backup.owner_id}"))
        return None
    partial_secret_key = get_optional(hex_to_q(decrypted_value))

    # 𝑀_{𝑖,l} = 𝐴^P𝑖_{l}
    partial_decryption = ciphertext.partial_decrypt(partial_secret_key)

    # 𝑀_{𝑖,l} = 𝐴^𝑠𝑖 mod 𝑝 and 𝐾𝑖 = 𝑔^𝑠𝑖 mod 𝑝
    proof = make_chaum_pedersen(
        ciphertext,
        partial_secret_key,
        partial_decryption,
        nonce_seed,
        extended_base_hash,
    )

    return (partial_decryption, proof)
def chaum_pedersen_bench(bi: BenchInput) -> Tuple[float, float]:
    """
    Given an input (instance of the BenchInput tuple), constructs and validates
    a disjunctive Chaum-Pedersen proof, returning the time (in seconds) to do each operation.
    """
    (keypair, r, s) = bi
    ciphertext = get_optional(elgamal_encrypt(0, r, keypair.public_key))
    start1 = timer()
    proof = make_disjunctive_chaum_pedersen_zero(ciphertext, r,
                                                 keypair.public_key, s)
    end1 = timer()
    valid = proof.is_valid(ciphertext, keypair.public_key)
    end2 = timer()
    if not valid:
        raise Exception(
            "Wasn't expecting an invalid proof during a benchmark!")
    return end1 - start1, end2 - end1
Пример #25
0
def run_bench(filename: str, output_dir: Optional[str],
              use_progressbar: bool) -> None:
    start_time = timer()
    print(f"Benchmarking: {filename}")
    cvrs = read_dominion_csv(filename)
    if cvrs is None:
        print(f"Failed to read {filename}, terminating.")
        exit(1)
    rows, cols = cvrs.data.shape

    parse_time = timer()
    print(
        f"    Parse time: {parse_time - start_time: .3f} sec, {rows / (parse_time - start_time):.3f} ballots/sec"
    )

    assert rows > 0, "can't have zero ballots!"

    # doesn't matter what the key is, so long as it's consistent for both runs
    keypair = get_optional(
        elgamal_keypair_from_secret(int_to_q_unchecked(31337)))

    rtally_start = timer()
    rtally = ray_tally_everything(
        cvrs,
        secret_key=keypair.secret_key,
        verbose=True,
        root_dir=output_dir,
        use_progressbar=use_progressbar,
    )
    rtally_end = timer()

    print(f"\nOVERALL PERFORMANCE")
    print(f"    Ray time:    {rtally_end - rtally_start : .3f} sec")
    print(
        f"    Ray rate:    {rows / (rtally_end - rtally_start): .3f} ballots/sec"
    )

    if output_dir:
        print(f"\nSANITY CHECK")
        assert rtally.all_proofs_valid(
            verbose=True,
            recheck_ballots_and_tallies=False,
            use_progressbar=use_progressbar,
        ), "proof failure!"
Пример #26
0
    def setupElectionBuilder(self):
        # Create an election builder instance, and configure it for a single public-private keypair.
        # in a real election, you would configure this for a group of guardians.  See Key Ceremony for more information.
        with open(os.path.join(self.manifest_path, self.manifest_file),
                  "r") as manifest:
            string_representation = manifest.read()
            election_description = ElectionDescription.from_json(
                string_representation)

        builder = ElectionBuilder(
            number_of_guardians=self.
            NUMBER_OF_GUARDIANS,  # since we will generate a single public-private keypair, we set this to 1
            quorum=self.
            QUORUM,  # since we will generate a single public-private keypair, we set this to 1
            description=election_description)

        builder.set_public_key(self.joint_public_key)
        self.metadata, self.context = get_optional(builder.build())
        self.builder = builder
Пример #27
0
    def test_reconstruct_decryption_share_for_ballot(self):
        # Arrange
        available_guardians = self.guardians[0:2]
        available_guardians_keys = [
            guardian.share_election_public_key()
            for guardian in available_guardians
        ]
        missing_guardian = self.guardians[2]
        missing_guardian_key = missing_guardian.share_election_public_key()
        missing_guardian_backups = {
            backup.designated_id: backup
            for backup in
            missing_guardian.share_election_partial_key_backups()
        }
        ballot = self.ciphertext_ballots[self.fake_spoiled_ballot.object_id]

        # Act
        compensated_shares: Dict[GUARDIAN_ID, CompensatedDecryptionShare] = {
            available_guardian.id: get_optional(
                compute_compensated_decryption_share_for_ballot(
                    available_guardian.share_election_public_key(),
                    available_guardian._auxiliary_keys,
                    missing_guardian_key,
                    missing_guardian_backups[available_guardian.id],
                    ballot,
                    self.context,
                    identity_auxiliary_decrypt,
                ))
            for available_guardian in available_guardians
        }

        lagrange_coefficients = compute_lagrange_coefficients_for_guardians(
            available_guardians_keys)

        share = reconstruct_decryption_share_for_ballot(
            missing_guardian_key, ballot, compensated_shares,
            lagrange_coefficients)

        # Assert
        self.assertEqual(self.QUORUM, len(compensated_shares))
        self.assertEqual(self.QUORUM, len(lagrange_coefficients))
        self.assertIsNotNone(share)
    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)
Пример #29
0
def encrypt_ballot_helper(
    ied: InternalElectionDescription,
    cec: CiphertextElectionContext,
    seed_hash: ElementModQ,
    input_tuple: Tuple[PlaintextBallot, ElementModQ],
) -> CiphertextBallot:  # pragma: no cover
    """
    Given a ballot and the associated metadata, encrypt it. Note that this method
    is meant to be used with `functools.partial`, so we can create a function
    that only takes the final tuple argument while remembering all the rest.
    """
    b, n = input_tuple

    # Coverage note: you'll see a directive on this method and on the other methods
    # used for the parallel mapping. For whatever reason, the Python coverage tool
    # can't figure out that they're running, so we'll exclude them.

    # Performance note: Nearly 2x performance boost by disabling proof verification
    # here. We do verify the tally proofs at the end, so doing all this extra work
    # here is in the "would be nice if cycles were free" category, but this is the
    # inner loop of the most performance-sensitive part of our code.
    return get_optional(
        encrypt_ballot(b, ied, cec, seed_hash, n, should_verify_proofs=False))
    def setUp(self):

        self.key_ceremony = KeyCeremonyMediator(self.CEREMONY_DETAILS)

        self.guardians: List[Guardian] = []

        # Setup Guardians
        for i in range(self.NUMBER_OF_GUARDIANS):
            sequence = i + 2
            self.guardians.append(
                Guardian(
                    "guardian_" + str(sequence),
                    sequence,
                    self.NUMBER_OF_GUARDIANS,
                    self.QUORUM,
                ))

        # Attendance (Public Key Share)
        for guardian in self.guardians:
            self.key_ceremony.announce(guardian)

        self.key_ceremony.orchestrate(identity_auxiliary_encrypt)
        self.key_ceremony.verify(identity_auxiliary_decrypt)

        self.joint_public_key = self.key_ceremony.publish_joint_key()
        self.assertIsNotNone(self.joint_public_key)

        # setup the election
        self.election = election_factory.get_fake_election()
        builder = ElectionBuilder(self.NUMBER_OF_GUARDIANS, self.QUORUM,
                                  self.election)

        self.assertIsNone(
            builder.build())  # Can't build without the public key

        builder.set_public_key(self.joint_public_key)
        self.metadata, self.context = get_optional(builder.build())

        self.encryption_device = EncryptionDevice("location")
        self.ballot_marking_device = EncryptionMediator(
            self.metadata, self.context, self.encryption_device)

        # get some fake ballots
        self.fake_cast_ballot = ballot_factory.get_fake_ballot(
            self.metadata, "some-unique-ballot-id-cast")
        self.more_fake_ballots = []
        for i in range(10):
            self.more_fake_ballots.append(
                ballot_factory.get_fake_ballot(
                    self.metadata, f"some-unique-ballot-id-cast{i}"))
        self.fake_spoiled_ballot = ballot_factory.get_fake_ballot(
            self.metadata, "some-unique-ballot-id-spoiled")
        self.assertTrue(
            self.fake_cast_ballot.is_valid(
                self.metadata.ballot_styles[0].object_id))
        self.assertTrue(
            self.fake_spoiled_ballot.is_valid(
                self.metadata.ballot_styles[0].object_id))
        self.expected_plaintext_tally = accumulate_plaintext_ballots(
            [self.fake_cast_ballot] + self.more_fake_ballots)

        # Fill in the expected values with any missing selections
        # that were not made on any ballots
        selection_ids = set([
            selection.object_id for contest in self.metadata.contests
            for selection in contest.ballot_selections
        ])

        missing_selection_ids = selection_ids.difference(
            set(self.expected_plaintext_tally))

        for id in missing_selection_ids:
            self.expected_plaintext_tally[id] = 0

        # Encrypt
        encrypted_fake_cast_ballot = self.ballot_marking_device.encrypt(
            self.fake_cast_ballot)
        encrypted_fake_spoiled_ballot = self.ballot_marking_device.encrypt(
            self.fake_spoiled_ballot)
        self.assertIsNotNone(encrypted_fake_cast_ballot)
        self.assertIsNotNone(encrypted_fake_spoiled_ballot)
        self.assertTrue(
            encrypted_fake_cast_ballot.is_valid_encryption(
                self.context.crypto_extended_base_hash, self.joint_public_key))

        # encrypt some more fake ballots
        self.more_fake_encrypted_ballots = []
        for fake_ballot in self.more_fake_ballots:
            self.more_fake_encrypted_ballots.append(
                self.ballot_marking_device.encrypt(fake_ballot))

        # configure the ballot box
        ballot_store = BallotStore()
        ballot_box = BallotBox(self.metadata, self.context, ballot_store)
        ballot_box.cast(encrypted_fake_cast_ballot)
        ballot_box.spoil(encrypted_fake_spoiled_ballot)

        # Cast some more fake ballots
        for fake_ballot in self.more_fake_encrypted_ballots:
            ballot_box.cast(fake_ballot)

        # generate encrypted tally
        self.ciphertext_tally = tally_ballots(ballot_store, self.metadata,
                                              self.context)