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)
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 )