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.internal_manifest,
                          self.context))
        self.ciphertext_ballots = get_ballots(self.ballot_store,
                                              BallotBoxState.SPOILED)
        self._assert_message(
            tally_ballots.__qualname__,
            f"""
            - cast: {self.ciphertext_tally.cast()}
            - spoiled: {self.ciphertext_tally.spoiled()}
            Total: {len(self.ciphertext_tally)}
            """,
            self.ciphertext_tally is not None,
        )

        # Configure the Decryption
        ciphertext_ballots = list(self.ciphertext_ballots.values())
        self.decryption_mediator = DecryptionMediator(
            "decryption-mediator",
            self.context,
        )

        # Announce each guardian as present
        count = 0
        for guardian in self.guardians:
            guardian_key = guardian.share_election_public_key()
            tally_share = guardian.compute_tally_share(self.ciphertext_tally,
                                                       self.context)
            ballot_shares = guardian.compute_ballot_shares(
                ciphertext_ballots, self.context)
            self.decryption_mediator.announce(guardian_key, tally_share,
                                              ballot_shares)
            count += 1
            self._assert_message(
                DecryptionMediator.announce.__qualname__,
                f"Guardian Present: {guardian.id}",
                len(self.decryption_mediator.get_available_guardians()) ==
                count,
            )

        # Get the plaintext Tally
        self.plaintext_tally = get_optional(
            self.decryption_mediator.get_plaintext_tally(
                self.ciphertext_tally))
        self._assert_message(
            DecryptionMediator.get_plaintext_tally.__qualname__,
            "Tally Decrypted",
            self.plaintext_tally is not None,
        )

        # Get the plaintext Spoiled Ballots
        self.plaintext_spoiled_ballots = get_optional(
            self.decryption_mediator.get_plaintext_ballots(ciphertext_ballots))
        self._assert_message(
            DecryptionMediator.get_plaintext_ballots.__qualname__,
            "Spoiled Ballots Decrypted",
            self.plaintext_tally is not None,
        )

        # Now, compare the results
        self.compare_results()
    def setUp(self):

        # Key Ceremony
        self.key_ceremony_mediator = KeyCeremonyMediator(
            "key_ceremony_mediator_mediator", self.CEREMONY_DETAILS)
        self.guardians: List[Guardian] = KeyCeremonyHelper.create_guardians(
            self.CEREMONY_DETAILS)
        KeyCeremonyHelper.perform_full_ceremony(self.guardians,
                                                self.key_ceremony_mediator)
        self.joint_public_key = self.key_ceremony_mediator.publish_joint_key()

        # Setup the election
        manifest = election_factory.get_fake_manifest()
        builder = ElectionBuilder(self.NUMBER_OF_GUARDIANS, self.QUORUM,
                                  manifest)
        builder.set_public_key(self.joint_public_key.joint_public_key)
        builder.set_commitment_hash(self.joint_public_key.commitment_hash)
        self.internal_manifest, self.context = get_optional(builder.build())

        self.encryption_device = election_factory.get_encryption_device()
        self.ballot_marking_device = EncryptionMediator(
            self.internal_manifest, self.context, self.encryption_device)

        # get some fake ballots
        self.fake_cast_ballot = ballot_factory.get_fake_ballot(
            self.internal_manifest, "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.internal_manifest, f"some-unique-ballot-id-cast{i}"))
        self.fake_spoiled_ballot = ballot_factory.get_fake_ballot(
            self.internal_manifest, "some-unique-ballot-id-spoiled")
        self.more_fake_spoiled_ballots = []
        for i in range(2):
            self.more_fake_spoiled_ballots.append(
                ballot_factory.get_fake_ballot(
                    self.internal_manifest,
                    f"some-unique-ballot-id-spoiled{i}"))
        self.assertTrue(
            self.fake_cast_ballot.is_valid(
                self.internal_manifest.ballot_styles[0].object_id))
        self.assertTrue(
            self.fake_spoiled_ballot.is_valid(
                self.internal_manifest.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 = {
            selection.object_id
            for contest in self.internal_manifest.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
        self.encrypted_fake_cast_ballot = self.ballot_marking_device.encrypt(
            self.fake_cast_ballot)
        self.encrypted_fake_spoiled_ballot = self.ballot_marking_device.encrypt(
            self.fake_spoiled_ballot)

        # 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))
        # encrypt some more fake ballots
        self.more_fake_encrypted_spoiled_ballots = []
        for fake_ballot in self.more_fake_spoiled_ballots:
            self.more_fake_encrypted_spoiled_ballots.append(
                self.ballot_marking_device.encrypt(fake_ballot))

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

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

        # generate encrypted tally
        self.ciphertext_tally = tally_ballots(ballot_store,
                                              self.internal_manifest,
                                              self.context)
        self.ciphertext_ballots = get_ballots(ballot_store,
                                              BallotBoxState.SPOILED)
Example #3
0
    def generate(
        self,
        number_of_ballots: int = DEFAULT_NUMBER_OF_BALLOTS,
        spoil_rate: int = DEFAULT_SPOIL_RATE,
        use_all_guardians: bool = DEFAULT_USE_ALL_GUARDIANS,
    ):
        """
        Generate the sample data set
        """

        # Clear the results directory
        rmtree(RESULTS_DIR, ignore_errors=True)

        # Configure the election
        (
            manifest,
            private_data,
        ) = self.election_factory.get_hamilton_manifest_with_encryption_context()
        plaintext_ballots = (
            self.ballot_factory.generate_fake_plaintext_ballots_for_election(
                manifest.internal_manifest, number_of_ballots
            )
        )
        self.encrypter = EncryptionMediator(
            manifest.internal_manifest, manifest.context, self.encryption_device
        )

        # Encrypt some ballots
        ciphertext_ballots: List[CiphertextBallot] = []
        for plaintext_ballot in plaintext_ballots:
            ciphertext_ballots.append(
                get_optional(self.encrypter.encrypt(plaintext_ballot))
            )

        ballot_store = DataStore()
        ballot_box = BallotBox(
            manifest.internal_manifest, manifest.context, ballot_store
        )

        # Randomly cast/spoil the ballots
        submitted_ballots: List[SubmittedBallot] = []
        for ballot in ciphertext_ballots:
            if randint(0, 100) < spoil_rate:
                submitted_ballots.append(ballot_box.spoil(ballot))
            else:
                submitted_ballots.append(ballot_box.cast(ballot))

        # Tally
        spoiled_ciphertext_ballots = get_ballots(ballot_store, BallotBoxState.SPOILED)
        ciphertext_tally = get_optional(
            tally_ballots(ballot_store, manifest.internal_manifest, manifest.context)
        )

        # Decrypt
        mediator = DecryptionMediator("sample-manifest-decrypter", manifest.context)
        available_guardians = (
            private_data.guardians
            if use_all_guardians
            else private_data.guardians[0:QUORUM]
        )
        DecryptionHelper.perform_decryption_setup(
            available_guardians,
            mediator,
            manifest.context,
            ciphertext_tally,
            spoiled_ciphertext_ballots,
        )

        plaintext_tally = get_optional(mediator.get_plaintext_tally(ciphertext_tally))
        plaintext_spoiled_ballots = get_optional(
            mediator.get_plaintext_ballots(spoiled_ciphertext_ballots)
        )

        # Publish
        publish(
            manifest.manifest,
            manifest.context,
            manifest.constants,
            [self.encryption_device],
            submitted_ballots,
            plaintext_spoiled_ballots.values(),
            ciphertext_tally.publish(),
            plaintext_tally,
            manifest.guardians,
        )

        publish_private_data(
            plaintext_ballots, ciphertext_ballots, private_data.guardians
        )