def test_get_plaintext_tally_all_guardians_present(self, data, parties: int, contests: int): # Arrange description = data.draw(election_descriptions(parties, contests)) builder = ElectionBuilder(self.NUMBER_OF_GUARDIANS, self.QUORUM, description) metadata, context = builder.set_public_key( self.joint_public_key).build() plaintext_ballots: List[PlaintextBallot] = data.draw( plaintext_voted_ballots(metadata, randrange(3, 6))) plaintext_tallies = accumulate_plaintext_ballots(plaintext_ballots) encrypted_tally = self._generate_encrypted_tally( metadata, context, plaintext_ballots) subject = DecryptionMediator(metadata, context, encrypted_tally) # act for guardian in self.guardians: self.assertIsNotNone(subject.announce(guardian)) decrypted_tallies = subject.get_plaintext_tally() result = self._convert_to_selections(decrypted_tallies) # assert self.assertIsNotNone(result) self.assertEqual(plaintext_tallies, result)
def step_0_configure_election(self) -> None: """ To conduct an election, load an `Manifest` file """ # Load a pre-configured Election Description # TODO: replace with complex election self.manifest = ElectionFactory().get_simple_manifest_from_file() print(f""" {'-'*40}\n # Election Summary: # Scope: {self.manifest.election_scope_id} # Geopolitical Units: {len(self.manifest.geopolitical_units)} # Parties: {len(self.manifest.parties)} # Candidates: {len(self.manifest.candidates)} # Contests: {len(self.manifest.contests)} # Ballot Styles: {len(self.manifest.ballot_styles)}\n {'-'*40}\n """) self._assert_message( Manifest.is_valid.__qualname__, "Verify that the input election meta-data is well-formed", self.manifest.is_valid(), ) # Create an Election Builder self.election_builder = ElectionBuilder(self.NUMBER_OF_GUARDIANS, self.QUORUM, self.manifest) self._assert_message( ElectionBuilder.__qualname__, f"Created with number_of_guardians: {self.NUMBER_OF_GUARDIANS} quorum: {self.QUORUM}", )
def test_get_plaintext_tally_with_all_guardians_present( self, values, parties: int, contests: int): # Arrange description = values.draw(election_descriptions(parties, contests)) builder = ElectionBuilder(self.NUMBER_OF_GUARDIANS, self.QUORUM, description) internal_manifest, context = (builder.set_public_key( self.joint_public_key.joint_public_key).set_commitment_hash( self.joint_public_key.commitment_hash).build()) plaintext_ballots: List[PlaintextBallot] = values.draw( plaintext_voted_ballots(internal_manifest, randrange(3, 6))) expected_plaintext_tally = accumulate_plaintext_ballots( plaintext_ballots) encrypted_tally = self._generate_encrypted_tally( internal_manifest, context, plaintext_ballots) mediator = DecryptionMediator(self.decryption_mediator_id, context) available_guardians = self.guardians DecryptionHelper.perform_decryption_setup(available_guardians, mediator, context, encrypted_tally, []) # Act plaintext_tally = mediator.get_plaintext_tally(encrypted_tally) selections = _convert_to_selections(plaintext_tally) # Assert self.assertIsNotNone(plaintext_tally) self.assertIsNotNone(selections) self.assertEqual(expected_plaintext_tally, selections)
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
def build_election(self, election_creation: dict): self.election = parse_description(election_creation["description"]) if not self.election.is_valid(): raise InvalidElectionDescription() self.number_of_guardians = len(election_creation["trustees"]) self.quorum = election_creation["scheme"]["quorum"] self.election_builder = ElectionBuilder(self.number_of_guardians, self.quorum, self.election)
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 build_election(self, election_creation: dict): self.election = ElectionDescription.from_json_object( complete_election_description(election_creation['description'])) if not self.election.is_valid(): raise InvalidElectionDescription() self.number_of_guardians = len(election_creation['trustees']) self.quorum = election_creation['scheme']['parameters']['quorum'] self.election_builder = ElectionBuilder(self.number_of_guardians, self.quorum, self.election)
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), )
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
def create() -> Tuple: """ An election with only one guardian and random keys gets generated. More configuration options and the ability to hold a key ceremony should be added later. """ # Open an election manifest file with open(os.path.join(ELECTION_MANIFEST), "r") as manifest: string_representation = manifest.read() election_description = ElectionDescription.from_json( string_representation) # 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. # TODO: Allow real key ceremony builder = ElectionBuilder( number_of_guardians= 1, # since we will generate a single public-private keypair, we set this to 1 quorum= 1, # since we will generate a single public-private keypair, we set this to 1 description=election_description) # We simply generate a random keypair. For a real election this step should # be replaced by the key ceremony keypair = elgamal_keypair_random() builder.set_public_key(keypair.public_key) # get an `InternalElectionDescription` and `CiphertextElectionContext` # that are used for the remainder of the election. (metadata, context) = builder.build() # Configure an encryption device # In the docs the encrypter device gets defined when encrypting a ballot. # I think for our usecase it makes more sense to define one encrypter and use for the whole election device = EncryptionDevice("polling-place-one") encrypter = EncryptionMediator(metadata, context, device) store = BallotStore() ballot_box = BallotBox(metadata, context, store) return metadata, context, encrypter, ballot_box, store, keypair
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), )
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)
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)