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)
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) manifest = Manifest("", ElectionType.unknown, now, now, [], [], [], [], [], []) context = make_ciphertext_election_context( 1, 1, ONE_MOD_P, ONE_MOD_Q, ONE_MOD_Q ) constants = ElectionConstants() devices = [] guardian_records = [GuardianRecord("", "", ONE_MOD_Q, [], [])] encrypted_ballots = [] spoiled_ballots = [] plaintext_tally = PlaintextTally("", []) ciphertext_tally = CiphertextTally("", manifest, context) # Act publish( manifest, context, constants, devices, encrypted_ballots, spoiled_ballots, ciphertext_tally.publish(), plaintext_tally, guardian_records, ) # Assert self.assertTrue(path.exists(RESULTS_DIR)) # Cleanup rmtree(RESULTS_DIR)
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)
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 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()
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 make_fresh_election_admin() -> ElectionAdmin: return ElectionAdmin(elgamal_keypair_random(), ElectionConstants())
def step_1_key_ceremony(self) -> None: """ Using the NUMBER_OF_GUARDIANS, generate public-private keypairs and share representations of those keys with QUORUM of other Guardians. Then, combine the public election keys to make a joint election key that is used to encrypt ballots """ # Setup Guardians for i in range(self.NUMBER_OF_GUARDIANS): self.guardians.append( Guardian( "guardian_" + str(i + 1), i + 1, self.NUMBER_OF_GUARDIANS, self.QUORUM, )) # Setup Mediator self.mediator = KeyCeremonyMediator("mediator_1", self.guardians[0].ceremony_details) # ROUND 1: Public Key Sharing # Announce for guardian in self.guardians: self.mediator.announce(guardian.share_public_keys()) # Share Keys for guardian in self.guardians: announced_keys = self.mediator.share_announced() for key_set in announced_keys: if guardian.id is not key_set.election.owner_id: guardian.save_guardian_public_keys(key_set) self._assert_message( KeyCeremonyMediator.all_guardians_announced.__qualname__, "Confirms all guardians have shared their public keys", self.mediator.all_guardians_announced(), ) # ROUND 2: Election Partial Key Backup Sharing # Share Backups for sending_guardian in self.guardians: sending_guardian.generate_election_partial_key_backups( identity_auxiliary_encrypt) backups = [] for designated_guardian in self.guardians: if designated_guardian.id != sending_guardian.id: backups.append( sending_guardian.share_election_partial_key_backup( designated_guardian.id)) self.mediator.receive_backups(backups) self._assert_message( KeyCeremonyMediator.receive_backups.__qualname__, "Receive election partial key backups from key owning guardian", len(backups) == NUMBER_OF_GUARDIANS - 1, ) self._assert_message( KeyCeremonyMediator.all_backups_available.__qualname__, "Confirm all guardians have shared their election partial key backups", self.mediator.all_backups_available(), ) # Receive Backups for designated_guardian in self.guardians: backups = self.mediator.share_backups(designated_guardian.id) self._assert_message( KeyCeremonyMediator.share_backups.__qualname__, "Share election partial key backups for the designated guardian", len(backups) == NUMBER_OF_GUARDIANS - 1, ) for backup in backups: designated_guardian.save_election_partial_key_backup(backup) # ROUND 3: Verification of Backups # Verify Backups for designated_guardian in self.guardians: verifications = [] for backup_owner in self.guardians: if designated_guardian.id is not backup_owner.id: verification = ( designated_guardian.verify_election_partial_key_backup( backup_owner.id, identity_auxiliary_encrypt)) verifications.append(verification) self.mediator.receive_backup_verifications(verifications) self._assert_message( KeyCeremonyMediator.all_backups_verified.__qualname__, "Confirms all guardians have verified the backups of all other guardians", self.mediator.all_backups_verified(), ) # FINAL: Publish Joint Key joint_key = self.mediator.publish_joint_key() self._assert_message( KeyCeremonyMediator.publish_joint_key.__qualname__, "Publishes the Joint Election Key", joint_key is not None, ) # Build the Election self.election_builder.set_public_key( get_optional(joint_key).joint_public_key) self.election_builder.set_commitment_hash( get_optional(joint_key).commitment_hash) self.internal_manifest, self.context = get_optional( self.election_builder.build()) self.constants = ElectionConstants()
def get_election_constants() -> Any: """ Return the constants defined for an election """ constants = ElectionConstants() return constants.to_json_object()
def step_1_key_ceremony(self) -> None: """ Using the NUMBER_OF_GUARDIANS, generate public-private keypairs and share representations of those keys with QUORUM of other Guardians. Then, combine the public election keys to make a joint election key that is used to encrypt ballots """ # Setup Guardians for i in range(self.NUMBER_OF_GUARDIANS): self.guardians.append( Guardian("guardian_" + str(i), i, self.NUMBER_OF_GUARDIANS, self.QUORUM)) # Setup Mediator self.mediator = KeyCeremonyMediator(self.guardians[0].ceremony_details) # Attendance (Public Key Share) for guardian in self.guardians: self.mediator.announce(guardian) self._assert_message( KeyCeremonyMediator.all_guardians_in_attendance.__qualname__, "Confirms all guardians have shared their public keys", self.mediator.all_guardians_in_attendance(), ) # Run the Key Ceremony process, # Which shares the keys among the guardians orchestrated = self.mediator.orchestrate() self._assert_message( KeyCeremonyMediator.orchestrate.__qualname__, "Executes the key exchange between guardians", orchestrated is not None, ) self._assert_message( KeyCeremonyMediator.all_election_partial_key_backups_available. __qualname__, "Confirm sall guardians have shared their partial key backups", self.mediator.all_election_partial_key_backups_available(), ) # Verification verified = self.mediator.verify() self._assert_message( KeyCeremonyMediator.verify.__qualname__, "Confirms all guardians truthfully executed the ceremony", verified, ) self._assert_message( KeyCeremonyMediator. all_election_partial_key_verifications_received.__qualname__, "Confirms all guardians have submitted a verification of the backups of all other guardians", self.mediator.all_election_partial_key_verifications_received(), ) self._assert_message( KeyCeremonyMediator.all_election_partial_key_backups_verified. __qualname__, "Confirms all guardians have verified the backups of all other guardians", self.mediator.all_election_partial_key_backups_verified(), ) # Joint Key joint_key = self.mediator.publish_joint_key() self._assert_message( KeyCeremonyMediator.publish_joint_key.__qualname__, "Publishes the Joint Election Key", joint_key is not None, ) # Save Validation Keys for guardian in self.guardians: self.coefficient_validation_sets.append( guardian.share_coefficient_validation_set()) # Build the Election self.election_builder.set_public_key(get_optional(joint_key)) self.metadata, self.context = get_optional( self.election_builder.build()) self.constants = ElectionConstants()