From e9eaedad31cae110c59416b3c1482d754937e2c2 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Fri, 22 Apr 2022 14:16:01 -0400 Subject: [PATCH 01/13] Ability to instantiate guardians from private records --- src/electionguard/guardian.py | 50 +++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index 6ca57525..7aa69a04 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -154,6 +154,13 @@ def __init__( sequence_order: int, number_of_guardians: int, quorum: int, + election_keys: ElectionKeyPair = None, + backups_to_share: Dict[GuardianId, ElectionPartialKeyBackup] = None, + election_public_keys: Dict[GuardianId, ElectionPublicKey] = None, + partial_key_backups: Dict[GuardianId, ElectionPartialKeyBackup] = None, + election_partial_key_verifications: Dict[ + GuardianId, ElectionPartialKeyVerification + ] = None, nonce_seed: Optional[ElementModQ] = None, ) -> None: """ @@ -169,12 +176,25 @@ def __init__( self.id = id self.sequence_order = sequence_order self.set_ceremony_details(number_of_guardians, quorum) - self._backups_to_share = {} - self._guardian_election_public_keys = {} - self._guardian_election_partial_key_backups = {} - self._guardian_election_partial_key_verifications = {} + self._backups_to_share = {} if backups_to_share is None else backups_to_share + self._guardian_election_public_keys = ( + {} if election_public_keys is None else election_public_keys + ) + self._guardian_election_partial_key_backups = ( + {} if partial_key_backups is None else partial_key_backups + ) + self._guardian_election_partial_key_verifications = ( + {} + if election_partial_key_verifications is None + else election_partial_key_verifications + ) - self.generate_election_key_pair(nonce_seed if nonce_seed is not None else None) + if election_keys is None: + self.generate_election_key_pair( + nonce_seed if nonce_seed is not None else None + ) + else: + self._election_keys = election_keys def reset(self, number_of_guardians: int, quorum: int) -> None: """ @@ -205,6 +225,26 @@ def export_private_data(self) -> PrivateGuardianRecord: self._guardian_election_partial_key_verifications, ) + @staticmethod + def from_private_record( + private_guardian_record: PrivateGuardianRecord, + number_of_guardians: int, + quorum: int, + ) -> "Guardian": + guardian = Guardian( + private_guardian_record.guardian_id, + private_guardian_record.election_keys.sequence_order, + number_of_guardians, + quorum, + private_guardian_record.election_keys, + private_guardian_record.backups_to_share, + private_guardian_record.guardian_election_public_keys, + private_guardian_record.guardian_election_partial_key_backups, + private_guardian_record.guardian_election_partial_key_verifications, + ) + + return guardian + def set_ceremony_details(self, number_of_guardians: int, quorum: int) -> None: """ Set ceremony details for election. From 2b6545a78eae076bb30f2cc1de48ded4a2b2c8c8 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Fri, 22 Apr 2022 14:20:57 -0400 Subject: [PATCH 02/13] Remove guardian_election_partial_key_verifications and backups_to_share per PR --- src/electionguard/guardian.py | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index 7aa69a04..97521898 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -95,20 +95,12 @@ class PrivateGuardianRecord: election_keys: ElectionKeyPair """Private election Key pair of this guardian""" - backups_to_share: Dict[GuardianId, ElectionPartialKeyBackup] - """This guardian's partial key backups that will be shared to other guardians""" - guardian_election_public_keys: Dict[GuardianId, ElectionPublicKey] """Received election public keys that are shared with this guardian""" guardian_election_partial_key_backups: Dict[GuardianId, ElectionPartialKeyBackup] """Received partial key backups that are shared with this guardian""" - guardian_election_partial_key_verifications: Dict[ - GuardianId, ElectionPartialKeyVerification - ] - """Verifications of other guardian's backups""" - # pylint: disable=too-many-instance-attributes class Guardian: @@ -155,12 +147,8 @@ def __init__( number_of_guardians: int, quorum: int, election_keys: ElectionKeyPair = None, - backups_to_share: Dict[GuardianId, ElectionPartialKeyBackup] = None, election_public_keys: Dict[GuardianId, ElectionPublicKey] = None, partial_key_backups: Dict[GuardianId, ElectionPartialKeyBackup] = None, - election_partial_key_verifications: Dict[ - GuardianId, ElectionPartialKeyVerification - ] = None, nonce_seed: Optional[ElementModQ] = None, ) -> None: """ @@ -170,24 +158,22 @@ def __init__( :param sequence_order: a unique number in [1, 256) that identifies this guardian :param number_of_guardians: the total number of guardians that will participate in the election :param quorum: the count of guardians necessary to decrypt + :param election_keys the private keys the guardian generated during a key ceremony + :param election_public_keys the public keys the guardian generated during a key ceremony + :param partial_key_backups the partial key backups the guardian generated during a key ceremony :param nonce_seed: an optional `ElementModQ` value that can be used to generate the `ElectionKeyPair`. - It is recommended to only use this field for testing. + This parameter is mutually exclusive with election_keys, election_public_keys, and + partial_key_backups. It is recommended to only use this field for testing. """ self.id = id self.sequence_order = sequence_order self.set_ceremony_details(number_of_guardians, quorum) - self._backups_to_share = {} if backups_to_share is None else backups_to_share self._guardian_election_public_keys = ( {} if election_public_keys is None else election_public_keys ) self._guardian_election_partial_key_backups = ( {} if partial_key_backups is None else partial_key_backups ) - self._guardian_election_partial_key_verifications = ( - {} - if election_partial_key_verifications is None - else election_partial_key_verifications - ) if election_keys is None: self.generate_election_key_pair( @@ -219,10 +205,8 @@ def export_private_data(self) -> PrivateGuardianRecord: return PrivateGuardianRecord( self.id, self._election_keys, - self._backups_to_share, self._guardian_election_public_keys, self._guardian_election_partial_key_backups, - self._guardian_election_partial_key_verifications, ) @staticmethod @@ -237,10 +221,8 @@ def from_private_record( number_of_guardians, quorum, private_guardian_record.election_keys, - private_guardian_record.backups_to_share, private_guardian_record.guardian_election_public_keys, private_guardian_record.guardian_election_partial_key_backups, - private_guardian_record.guardian_election_partial_key_verifications, ) return guardian From 990535340c05cc223957abd40faed4c26a1f6913 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Fri, 22 Apr 2022 14:51:21 -0400 Subject: [PATCH 03/13] Unit test guardian from private record construction --- tests/unit/test_guardian.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/unit/test_guardian.py b/tests/unit/test_guardian.py index c7d366cf..1c5b4100 100644 --- a/tests/unit/test_guardian.py +++ b/tests/unit/test_guardian.py @@ -23,21 +23,30 @@ class TestGuardian(BaseTestCase): """Guardian tests""" - def test_reset(self) -> None: - guardian = Guardian( + def test_import_from_guardian_private_record(self) -> None: + # Arrange + guardian_expected = Guardian( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - expected_number_of_guardians = 10 - expected_quorum = 4 + private_guardian_record = guardian_expected.export_private_data() # Act - guardian.reset(expected_number_of_guardians, expected_quorum) + guardian_actual = Guardian.from_private_record( + private_guardian_record, NUMBER_OF_GUARDIANS, QUORUM + ) # Assert self.assertEqual( - expected_number_of_guardians, guardian.ceremony_details.number_of_guardians + guardian_actual._election_keys, guardian_expected._election_keys + ) + self.assertEqual( + guardian_actual._guardian_election_public_keys, + guardian_expected._guardian_election_public_keys, + ) + self.assertEqual( + guardian_actual._guardian_election_partial_key_backups, + guardian_expected._guardian_election_partial_key_backups, ) - self.assertEqual(expected_quorum, guardian.ceremony_details.quorum) def test_set_ceremony_details(self) -> None: # Arrange From c69fc650c87c678bc75b6d2567530e64a150b990 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Fri, 22 Apr 2022 15:16:18 -0400 Subject: [PATCH 04/13] Fix tests and linting --- src/electionguard/guardian.py | 2 ++ tests/unit/test_guardian.py | 1 + 2 files changed, 3 insertions(+) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index 97521898..bdb36155 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -168,12 +168,14 @@ def __init__( self.id = id self.sequence_order = sequence_order self.set_ceremony_details(number_of_guardians, quorum) + self._backups_to_share = {} self._guardian_election_public_keys = ( {} if election_public_keys is None else election_public_keys ) self._guardian_election_partial_key_backups = ( {} if partial_key_backups is None else partial_key_backups ) + self._guardian_election_partial_key_verifications = {} if election_keys is None: self.generate_election_key_pair( diff --git a/tests/unit/test_guardian.py b/tests/unit/test_guardian.py index 1c5b4100..b8cda4b0 100644 --- a/tests/unit/test_guardian.py +++ b/tests/unit/test_guardian.py @@ -36,6 +36,7 @@ def test_import_from_guardian_private_record(self) -> None: ) # Assert + # pylint: disable=protected-access self.assertEqual( guardian_actual._election_keys, guardian_expected._election_keys ) From f8cb5d0b82a44b04b0501274460aaaf92b3c0edb Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Mon, 25 Apr 2022 09:19:03 -0400 Subject: [PATCH 05/13] Make a from_nonce per PR --- src/electionguard/guardian.py | 36 ++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index bdb36155..c38347bf 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -3,6 +3,8 @@ from dataclasses import dataclass from typing import Dict, List, Optional, TypeVar +from electionguard.utils import get_optional + from .ballot import SubmittedBallot from .decryption import ( compute_compensated_decryption_share, @@ -149,7 +151,7 @@ def __init__( election_keys: ElectionKeyPair = None, election_public_keys: Dict[GuardianId, ElectionPublicKey] = None, partial_key_backups: Dict[GuardianId, ElectionPartialKeyBackup] = None, - nonce_seed: Optional[ElementModQ] = None, + generate_key_pair: bool = True, ) -> None: """ Initialize a guardian with the specified arguments. @@ -161,9 +163,6 @@ def __init__( :param election_keys the private keys the guardian generated during a key ceremony :param election_public_keys the public keys the guardian generated during a key ceremony :param partial_key_backups the partial key backups the guardian generated during a key ceremony - :param nonce_seed: an optional `ElementModQ` value that can be used to generate the `ElectionKeyPair`. - This parameter is mutually exclusive with election_keys, election_public_keys, and - partial_key_backups. It is recommended to only use this field for testing. """ self.id = id self.sequence_order = sequence_order @@ -177,12 +176,10 @@ def __init__( ) self._guardian_election_partial_key_verifications = {} - if election_keys is None: - self.generate_election_key_pair( - nonce_seed if nonce_seed is not None else None - ) + if election_keys is None and generate_key_pair: + self.generate_election_key_pair(None) else: - self._election_keys = election_keys + self._election_keys = get_optional(election_keys) def reset(self, number_of_guardians: int, quorum: int) -> None: """ @@ -211,6 +208,27 @@ def export_private_data(self) -> PrivateGuardianRecord: self._guardian_election_partial_key_backups, ) + @staticmethod + def from_nonce( + id: str, + sequence_order: int, + number_of_guardians: int, + quorum: int, + nonce_seed: Optional[ElementModQ] = None, + ) -> "Guardian": + """Creates a guardian with an `ElementModQ` value that will be used to generate + the `ElectionKeyPair`. This method should generally only be used for testing.""" + + guardian = Guardian( + id, + sequence_order, + number_of_guardians, + quorum, + generate_key_pair=False, + ) + guardian.generate_election_key_pair(nonce_seed) + return guardian + @staticmethod def from_private_record( private_guardian_record: PrivateGuardianRecord, From f0387fe3539caf4c2b2bea279475e2e9133471ab Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Mon, 25 Apr 2022 13:21:05 -0400 Subject: [PATCH 06/13] Refactor to classmethod per PR --- src/electionguard/guardian.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index c38347bf..e16cd1b9 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -208,8 +208,9 @@ def export_private_data(self) -> PrivateGuardianRecord: self._guardian_election_partial_key_backups, ) - @staticmethod + @classmethod def from_nonce( + cls, id: str, sequence_order: int, number_of_guardians: int, @@ -219,7 +220,7 @@ def from_nonce( """Creates a guardian with an `ElementModQ` value that will be used to generate the `ElectionKeyPair`. This method should generally only be used for testing.""" - guardian = Guardian( + guardian = cls( id, sequence_order, number_of_guardians, @@ -229,13 +230,14 @@ def from_nonce( guardian.generate_election_key_pair(nonce_seed) return guardian - @staticmethod + @classmethod def from_private_record( + cls, private_guardian_record: PrivateGuardianRecord, number_of_guardians: int, quorum: int, ) -> "Guardian": - guardian = Guardian( + guardian = cls( private_guardian_record.guardian_id, private_guardian_record.election_keys.sequence_order, number_of_guardians, From a6aa754d19dfa197520c70aacf3cef632df030f4 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Mon, 25 Apr 2022 13:36:54 -0400 Subject: [PATCH 07/13] Refactor move the constructors next to each other --- src/electionguard/guardian.py | 54 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index e16cd1b9..9538266a 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -181,33 +181,6 @@ def __init__( else: self._election_keys = get_optional(election_keys) - def reset(self, number_of_guardians: int, quorum: int) -> None: - """ - Reset guardian to initial state. - - :param number_of_guardians: Number of guardians in election - :param quorum: Quorum of guardians required to decrypt - """ - self._backups_to_share.clear() - self._guardian_election_public_keys.clear() - self._guardian_election_partial_key_backups.clear() - self._guardian_election_partial_key_verifications.clear() - self.set_ceremony_details(number_of_guardians, quorum) - self.generate_election_key_pair() - - def publish(self) -> GuardianRecord: - """Publish record of guardian with all required information.""" - return publish_guardian_record(self._election_keys.share()) - - def export_private_data(self) -> PrivateGuardianRecord: - """Export private data of guardian. Warning cannot be published.""" - return PrivateGuardianRecord( - self.id, - self._election_keys, - self._guardian_election_public_keys, - self._guardian_election_partial_key_backups, - ) - @classmethod def from_nonce( cls, @@ -249,6 +222,33 @@ def from_private_record( return guardian + def reset(self, number_of_guardians: int, quorum: int) -> None: + """ + Reset guardian to initial state. + + :param number_of_guardians: Number of guardians in election + :param quorum: Quorum of guardians required to decrypt + """ + self._backups_to_share.clear() + self._guardian_election_public_keys.clear() + self._guardian_election_partial_key_backups.clear() + self._guardian_election_partial_key_verifications.clear() + self.set_ceremony_details(number_of_guardians, quorum) + self.generate_election_key_pair() + + def publish(self) -> GuardianRecord: + """Publish record of guardian with all required information.""" + return publish_guardian_record(self._election_keys.share()) + + def export_private_data(self) -> PrivateGuardianRecord: + """Export private data of guardian. Warning cannot be published.""" + return PrivateGuardianRecord( + self.id, + self._election_keys, + self._guardian_election_public_keys, + self._guardian_election_partial_key_backups, + ) + def set_ceremony_details(self, number_of_guardians: int, quorum: int) -> None: """ Set ceremony details for election. From c08e8411c3178797760d0906fdbf32fb817ade54 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Mon, 25 Apr 2022 14:22:24 -0400 Subject: [PATCH 08/13] Move generate_election_key_pair into dedicated ctor per PR --- docs/1_Key_Ceremony.md | 17 +++--- src/electionguard/guardian.py | 22 +++++--- .../e2e_steps/input_retrieval_step.py | 2 +- .../helpers/key_ceremony_orchestrator.py | 2 +- tests/integration/test_end_to_end_election.py | 2 +- tests/unit/test_guardian.py | 54 +++++++++---------- tests/unit/test_key_ceremony_mediator.py | 8 ++- 7 files changed, 60 insertions(+), 47 deletions(-) diff --git a/docs/1_Key_Ceremony.md b/docs/1_Key_Ceremony.md index 9e18b73a..6012b0dc 100644 --- a/docs/1_Key_Ceremony.md +++ b/docs/1_Key_Ceremony.md @@ -1,21 +1,24 @@ # Key Ceremony -The ElectionGuard Key Ceremony is the process used by Election Officials to share encryption keys for an election. Before an election, a fixed number of Guardians are selection to hold the private keys needed to decrypt the election results. A Quorum count of Guardians can also be specified to compensate for guardians who may be missing at the time of Decryption. For instance, 5 Guardians may be selected to hold the keys, but only 3 of them are required to decrypt the election results. +The ElectionGuard Key Ceremony is the process used by Election Officials to share encryption keys for an election. Before an election, a fixed number of Guardians are selection to hold the private keys needed to decrypt the election results. A Quorum count of Guardians can also be specified to compensate for guardians who may be missing at the time of Decryption. For instance, 5 Guardians may be selected to hold the keys, but only 3 of them are required to decrypt the election results. Guardians are typically Election Officials, Trustees Canvass Board Members, Government Officials or other trusted authorities who are responsible and accountable for conducting the election. ## Summary -The Key Ceremony is broken into several high-level steps. Each Guardian must _announce_ their _attendance_ in the key ceremony, generate their own public-private key pairs, and then _share_ those key pairs with the Quorum. Then the data that is shared is mathematically verified using Non-Interactive Zero Knowledge Proofs, and finally a _joint public key_ is created to encrypt ballots in the election. +The Key Ceremony is broken into several high-level steps. Each Guardian must _announce_ their _attendance_ in the key ceremony, generate their own public-private key pairs, and then _share_ those key pairs with the Quorum. Then the data that is shared is mathematically verified using Non-Interactive Zero Knowledge Proofs, and finally a _joint public key_ is created to encrypt ballots in the election. ### Attendance + Guardians exchange all public keys and ensure each fellow guardian has received an election public key ensuring at all guardians are in attendance. ### Key Sharing -Guardians generate a partial key backup for each guardian and share with that designated key with that guardian. Then each designated guardian sends a verification back to the sender. The sender then publishes to the group when all verifications are received. + +Guardians generate a partial key backup for each guardian and share with that designated key with that guardian. Then each designated guardian sends a verification back to the sender. The sender then publishes to the group when all verifications are received. ### Joint Key -The final step is to publish the joint election key after all keys and backups have been shared. + +The final step is to publish the joint election key after all keys and backups have been shared. ## Glossary @@ -36,7 +39,7 @@ This is a detailed description of the entire Key Ceremony Process 3. Each guardian must generate their `election key pair` _(ElGamal key pair)_. This will generate a corresponding Schnorr `proof` and `polynomial` used for generating `election partial key backups` for sharing. 4. Each guardian must give the other guardians their `election public key` directly or through a mediator. 5. Each guardian must check if all `election public keys` are received. -6. Each guardian must generate `election partial key backup` for each other guardian. The guardian will use their `polynomial` and the designated guardian's `sequence_order` to create the value. +6. Each guardian must generate `election partial key backup` for each other guardian. The guardian will use their `polynomial` and the designated guardian's `sequence_order` to create the value. 7. Each guardian must send each encrypted `election partial key backup` to the designated guardian directly or through a `mediator`. 8. Each guardian checks if all encrypted `election partial key backups` have been received by their recipient guardian directly or through a mediator. 9. Each recipient guardian decrypts each received encrypted `election partial key backup` @@ -70,7 +73,7 @@ guardians: List[Guardian] # Setup Guardians for i in range(NUMBER_OF_GUARDIANS): guardians.append( - Guardian(f"some_guardian_id_{str(i)}", i, NUMBER_OF_GUARDIANS, QUORUM) + Guardian.from_context_info(f"some_guardian_id_{str(i)}", i, NUMBER_OF_GUARDIANS, QUORUM) ) mediator = KeyCeremonyMediator(details) @@ -92,4 +95,4 @@ joint_public_key = mediator.publish_joint_key() ## Implementation Considerations -ElectionGuard can be run without the key ceremony. The key ceremony is the recommended process to generate keys for live end-to-end verifiable elections, however this process may not be necessary for other use cases such as privacy preserving risk limiting audits. +ElectionGuard can be run without the key ceremony. The key ceremony is the recommended process to generate keys for live end-to-end verifiable elections, however this process may not be necessary for other use cases such as privacy preserving risk limiting audits. diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index 9538266a..cdec30df 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -3,8 +3,6 @@ from dataclasses import dataclass from typing import Dict, List, Optional, TypeVar -from electionguard.utils import get_optional - from .ballot import SubmittedBallot from .decryption import ( compute_compensated_decryption_share, @@ -151,7 +149,6 @@ def __init__( election_keys: ElectionKeyPair = None, election_public_keys: Dict[GuardianId, ElectionPublicKey] = None, partial_key_backups: Dict[GuardianId, ElectionPartialKeyBackup] = None, - generate_key_pair: bool = True, ) -> None: """ Initialize a guardian with the specified arguments. @@ -176,10 +173,20 @@ def __init__( ) self._guardian_election_partial_key_verifications = {} - if election_keys is None and generate_key_pair: - self.generate_election_key_pair(None) - else: - self._election_keys = get_optional(election_keys) + if not election_keys is None: + self._election_keys = election_keys + + @classmethod + def from_context_info( + cls, + id: str, + sequence_order: int, + number_of_guardians: int, + quorum: int, + ) -> "Guardian": + guardian = cls(id, sequence_order, number_of_guardians, quorum) + guardian.generate_election_key_pair(None) + return guardian @classmethod def from_nonce( @@ -198,7 +205,6 @@ def from_nonce( sequence_order, number_of_guardians, quorum, - generate_key_pair=False, ) guardian.generate_election_key_pair(nonce_seed) return guardian diff --git a/src/electionguard_cli/e2e_steps/input_retrieval_step.py b/src/electionguard_cli/e2e_steps/input_retrieval_step.py index 53dabc97..bafd56a9 100644 --- a/src/electionguard_cli/e2e_steps/input_retrieval_step.py +++ b/src/electionguard_cli/e2e_steps/input_retrieval_step.py @@ -65,7 +65,7 @@ def _get_guardians(number_of_guardians: int, quorum: int) -> List[Guardian]: guardians: List[Guardian] = [] for i in range(number_of_guardians): guardians.append( - Guardian( + Guardian.from_context_info( str(i + 1), i + 1, number_of_guardians, diff --git a/src/electionguard_tools/helpers/key_ceremony_orchestrator.py b/src/electionguard_tools/helpers/key_ceremony_orchestrator.py index 9c9a2342..f50c9efb 100644 --- a/src/electionguard_tools/helpers/key_ceremony_orchestrator.py +++ b/src/electionguard_tools/helpers/key_ceremony_orchestrator.py @@ -11,7 +11,7 @@ class KeyCeremonyOrchestrator: @staticmethod def create_guardians(ceremony_details: CeremonyDetails) -> List[Guardian]: return [ - Guardian( + Guardian.from_context_info( str(i + 1), i + 1, ceremony_details.number_of_guardians, diff --git a/tests/integration/test_end_to_end_election.py b/tests/integration/test_end_to_end_election.py index 372124e6..33ca1829 100644 --- a/tests/integration/test_end_to_end_election.py +++ b/tests/integration/test_end_to_end_election.py @@ -184,7 +184,7 @@ def step_1_key_ceremony(self) -> None: # Setup Guardians for i in range(self.NUMBER_OF_GUARDIANS): self.guardians.append( - Guardian( + Guardian.from_context_info( str(i + 1), i + 1, self.NUMBER_OF_GUARDIANS, diff --git a/tests/unit/test_guardian.py b/tests/unit/test_guardian.py index b8cda4b0..f791aff2 100644 --- a/tests/unit/test_guardian.py +++ b/tests/unit/test_guardian.py @@ -25,7 +25,7 @@ class TestGuardian(BaseTestCase): def test_import_from_guardian_private_record(self) -> None: # Arrange - guardian_expected = Guardian( + guardian_expected = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) private_guardian_record = guardian_expected.export_private_data() @@ -51,7 +51,7 @@ def test_import_from_guardian_private_record(self) -> None: def test_set_ceremony_details(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) expected_number_of_guardians = 10 @@ -68,7 +68,7 @@ def test_set_ceremony_details(self) -> None: def test_share_key(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) @@ -84,10 +84,10 @@ def test_share_key(self) -> None: def test_save_guardian_key(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) key = other_guardian.share_key() @@ -100,10 +100,10 @@ def test_save_guardian_key(self) -> None: def test_all_guardian_keys_received(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) key = other_guardian.share_key() @@ -117,7 +117,7 @@ def test_all_guardian_keys_received(self) -> None: def test_generate_election_key_pair(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) first_public_key = guardian.share_key() @@ -133,10 +133,10 @@ def test_generate_election_key_pair(self) -> None: def test_share_backups(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -159,10 +159,10 @@ def test_share_backups(self) -> None: def test_save_election_partial_key_backup(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -178,10 +178,10 @@ def test_save_election_partial_key_backup(self) -> None: def test_all_election_partial_key_backups_received(self) -> None: # Arrange # Round 1 - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -199,10 +199,10 @@ def test_all_election_partial_key_backups_received(self) -> None: def test_verify_election_partial_key_backup(self) -> None: # Arrange # Round 1 - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -227,13 +227,13 @@ def test_verify_election_partial_key_backup(self) -> None: def test_verify_election_partial_key_challenge(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - alternate_verifier = Guardian( + alternate_verifier = Guardian.from_context_info( ALTERNATE_VERIFIER_ID, ALTERNATE_VERIFIER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, @@ -257,10 +257,10 @@ def test_verify_election_partial_key_challenge(self) -> None: def test_publish_election_backup_challenge(self) -> None: # Arrange - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -282,10 +282,10 @@ def test_publish_election_backup_challenge(self) -> None: def test_save_election_partial_key_verification(self) -> None: # Arrange # Round 1 - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -308,10 +308,10 @@ def test_save_election_partial_key_verification(self) -> None: def test_all_election_partial_key_backups_verified(self) -> None: # Arrange # Round 1 - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -335,10 +335,10 @@ def test_all_election_partial_key_backups_verified(self) -> None: def test_publish_joint_key(self) -> None: # Arrange # Round 1 - guardian = Guardian( + guardian = Guardian.from_context_info( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian( + other_guardian = Guardian.from_context_info( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) diff --git a/tests/unit/test_key_ceremony_mediator.py b/tests/unit/test_key_ceremony_mediator.py index 7fd08028..6f04cc32 100644 --- a/tests/unit/test_key_ceremony_mediator.py +++ b/tests/unit/test_key_ceremony_mediator.py @@ -27,8 +27,12 @@ class TestKeyCeremonyMediator(BaseTestCase): def setUp(self) -> None: super().setUp() - self.GUARDIAN_1 = Guardian(GUARDIAN_1_ID, 1, NUMBER_OF_GUARDIANS, QUORUM) - self.GUARDIAN_2 = Guardian(GUARDIAN_2_ID, 2, NUMBER_OF_GUARDIANS, QUORUM) + self.GUARDIAN_1 = Guardian.from_context_info( + GUARDIAN_1_ID, 1, NUMBER_OF_GUARDIANS, QUORUM + ) + self.GUARDIAN_2 = Guardian.from_context_info( + GUARDIAN_2_ID, 2, NUMBER_OF_GUARDIANS, QUORUM + ) self.GUARDIANS = [self.GUARDIAN_1, self.GUARDIAN_2] def test_reset(self) -> None: From d4bc0e4dd3229d2c58056782e2b9e43d40dc4cee Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Mon, 25 Apr 2022 14:40:04 -0400 Subject: [PATCH 09/13] Restore missing PrivateGuardianRecord fields aka backups_to_share and guardian_election_partial_key_verifications --- src/electionguard/guardian.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index cdec30df..c6c24df2 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -95,12 +95,20 @@ class PrivateGuardianRecord: election_keys: ElectionKeyPair """Private election Key pair of this guardian""" + backups_to_share: Dict[GuardianId, ElectionPartialKeyBackup] + """This guardian's partial key backups that will be shared to other guardians""" + guardian_election_public_keys: Dict[GuardianId, ElectionPublicKey] """Received election public keys that are shared with this guardian""" guardian_election_partial_key_backups: Dict[GuardianId, ElectionPartialKeyBackup] """Received partial key backups that are shared with this guardian""" + guardian_election_partial_key_verifications: Dict[ + GuardianId, ElectionPartialKeyVerification + ] + """Verifications of other guardian's backups""" + # pylint: disable=too-many-instance-attributes class Guardian: @@ -149,6 +157,10 @@ def __init__( election_keys: ElectionKeyPair = None, election_public_keys: Dict[GuardianId, ElectionPublicKey] = None, partial_key_backups: Dict[GuardianId, ElectionPartialKeyBackup] = None, + backups_to_share: Dict[GuardianId, ElectionPartialKeyBackup] = None, + guardian_election_partial_key_verifications: Dict[ + GuardianId, ElectionPartialKeyVerification + ] = None, ) -> None: """ Initialize a guardian with the specified arguments. @@ -164,14 +176,18 @@ def __init__( self.id = id self.sequence_order = sequence_order self.set_ceremony_details(number_of_guardians, quorum) - self._backups_to_share = {} + self._backups_to_share = {} if backups_to_share is None else backups_to_share self._guardian_election_public_keys = ( {} if election_public_keys is None else election_public_keys ) self._guardian_election_partial_key_backups = ( {} if partial_key_backups is None else partial_key_backups ) - self._guardian_election_partial_key_verifications = {} + self._guardian_election_partial_key_verifications = ( + {} + if guardian_election_partial_key_verifications is None + else guardian_election_partial_key_verifications + ) if not election_keys is None: self._election_keys = election_keys @@ -224,6 +240,8 @@ def from_private_record( private_guardian_record.election_keys, private_guardian_record.guardian_election_public_keys, private_guardian_record.guardian_election_partial_key_backups, + private_guardian_record.backups_to_share, + private_guardian_record.guardian_election_partial_key_verifications, ) return guardian @@ -251,8 +269,10 @@ def export_private_data(self) -> PrivateGuardianRecord: return PrivateGuardianRecord( self.id, self._election_keys, + self._backups_to_share, self._guardian_election_public_keys, self._guardian_election_partial_key_backups, + self._guardian_election_partial_key_verifications, ) def set_ceremony_details(self, number_of_guardians: int, quorum: int) -> None: From 9e90736d2def9b72bb4f30fe7f28a94f36392751 Mon Sep 17 00:00:00 2001 From: Keith Fung Date: Mon, 25 Apr 2022 22:08:51 -0400 Subject: [PATCH 10/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20around=20?= =?UTF-8?q?ElectionKeyPair?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/electionguard/guardian.py | 92 +++++-------------- .../e2e_steps/input_retrieval_step.py | 2 +- .../helpers/key_ceremony_orchestrator.py | 2 +- tests/integration/test_end_to_end_election.py | 2 +- tests/unit/test_guardian.py | 68 ++++++-------- tests/unit/test_key_ceremony_mediator.py | 4 +- 6 files changed, 56 insertions(+), 114 deletions(-) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index c6c24df2..0289e205 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -110,7 +110,6 @@ class PrivateGuardianRecord: """Verifications of other guardian's backups""" -# pylint: disable=too-many-instance-attributes class Guardian: """ Guardian of election responsible for safeguarding information and decrypting results. @@ -119,11 +118,8 @@ class Guardian: The second half relates to the decryption process. """ - id: str - sequence_order: int # Cannot be zero - ceremony_details: CeremonyDetails - _election_keys: ElectionKeyPair + ceremony_details: CeremonyDetails _backups_to_share: Dict[GuardianId, ElectionPartialKeyBackup] """ @@ -150,11 +146,8 @@ class Guardian: def __init__( self, - id: str, - sequence_order: int, - number_of_guardians: int, - quorum: int, - election_keys: ElectionKeyPair = None, + key_pair: ElectionKeyPair, + ceremony_details: CeremonyDetails, election_public_keys: Dict[GuardianId, ElectionPublicKey] = None, partial_key_backups: Dict[GuardianId, ElectionPartialKeyBackup] = None, backups_to_share: Dict[GuardianId, ElectionPartialKeyBackup] = None, @@ -165,17 +158,16 @@ def __init__( """ Initialize a guardian with the specified arguments. - :param id: the unique identifier for the guardian - :param sequence_order: a unique number in [1, 256) that identifies this guardian - :param number_of_guardians: the total number of guardians that will participate in the election - :param quorum: the count of guardians necessary to decrypt - :param election_keys the private keys the guardian generated during a key ceremony + :param key_pair The key pair the guardian generated during a key ceremony + :param ceremony_details The details of the key ceremony :param election_public_keys the public keys the guardian generated during a key ceremony :param partial_key_backups the partial key backups the guardian generated during a key ceremony """ - self.id = id - self.sequence_order = sequence_order - self.set_ceremony_details(number_of_guardians, quorum) + + self._election_keys = key_pair + self.ceremony_details = ceremony_details + + # Reduce this ⬇️ self._backups_to_share = {} if backups_to_share is None else backups_to_share self._guardian_election_public_keys = ( {} if election_public_keys is None else election_public_keys @@ -189,20 +181,15 @@ def __init__( else guardian_election_partial_key_verifications ) - if not election_keys is None: - self._election_keys = election_keys + self.save_guardian_key(key_pair.share()) - @classmethod - def from_context_info( - cls, - id: str, - sequence_order: int, - number_of_guardians: int, - quorum: int, - ) -> "Guardian": - guardian = cls(id, sequence_order, number_of_guardians, quorum) - guardian.generate_election_key_pair(None) - return guardian + @property + def id(self) -> GuardianId: + return self._election_keys.owner_id + + @property + def sequence_order(self) -> int: + return self._election_keys.sequence_order @classmethod def from_nonce( @@ -211,19 +198,14 @@ def from_nonce( sequence_order: int, number_of_guardians: int, quorum: int, - nonce_seed: Optional[ElementModQ] = None, + nonce: ElementModQ = None, ) -> "Guardian": """Creates a guardian with an `ElementModQ` value that will be used to generate - the `ElectionKeyPair`. This method should generally only be used for testing.""" - - guardian = cls( - id, - sequence_order, - number_of_guardians, - quorum, - ) - guardian.generate_election_key_pair(nonce_seed) - return guardian + the `ElectionKeyPair`. If no nonce provided, this will be generated automatically. + This method should generally only be used for testing.""" + key_pair = generate_election_key_pair(id, sequence_order, quorum, nonce) + ceremony_details = CeremonyDetails(number_of_guardians, quorum) + return cls(key_pair, ceremony_details) @classmethod def from_private_record( @@ -233,11 +215,8 @@ def from_private_record( quorum: int, ) -> "Guardian": guardian = cls( - private_guardian_record.guardian_id, - private_guardian_record.election_keys.sequence_order, - number_of_guardians, - quorum, private_guardian_record.election_keys, + CeremonyDetails(number_of_guardians, quorum), private_guardian_record.guardian_election_public_keys, private_guardian_record.guardian_election_partial_key_backups, private_guardian_record.backups_to_share, @@ -246,20 +225,6 @@ def from_private_record( return guardian - def reset(self, number_of_guardians: int, quorum: int) -> None: - """ - Reset guardian to initial state. - - :param number_of_guardians: Number of guardians in election - :param quorum: Quorum of guardians required to decrypt - """ - self._backups_to_share.clear() - self._guardian_election_public_keys.clear() - self._guardian_election_partial_key_backups.clear() - self._guardian_election_partial_key_verifications.clear() - self.set_ceremony_details(number_of_guardians, quorum) - self.generate_election_key_pair() - def publish(self) -> GuardianRecord: """Publish record of guardian with all required information.""" return publish_guardian_record(self._election_keys.share()) @@ -285,13 +250,6 @@ def set_ceremony_details(self, number_of_guardians: int, quorum: int) -> None: self.ceremony_details = CeremonyDetails(number_of_guardians, quorum) # Public Keys - def generate_election_key_pair(self, nonce: ElementModQ = None) -> None: - """Generate election key pair for encrypting/decrypting election.""" - self._election_keys = generate_election_key_pair( - self.id, self.sequence_order, self.ceremony_details.quorum, nonce - ) - self.save_guardian_key(self.share_key()) - def share_key(self) -> ElectionPublicKey: """ Share election public key with another guardian. diff --git a/src/electionguard_cli/e2e_steps/input_retrieval_step.py b/src/electionguard_cli/e2e_steps/input_retrieval_step.py index bafd56a9..b238f0e0 100644 --- a/src/electionguard_cli/e2e_steps/input_retrieval_step.py +++ b/src/electionguard_cli/e2e_steps/input_retrieval_step.py @@ -65,7 +65,7 @@ def _get_guardians(number_of_guardians: int, quorum: int) -> List[Guardian]: guardians: List[Guardian] = [] for i in range(number_of_guardians): guardians.append( - Guardian.from_context_info( + Guardian.from_nonce( str(i + 1), i + 1, number_of_guardians, diff --git a/src/electionguard_tools/helpers/key_ceremony_orchestrator.py b/src/electionguard_tools/helpers/key_ceremony_orchestrator.py index f50c9efb..32ceca30 100644 --- a/src/electionguard_tools/helpers/key_ceremony_orchestrator.py +++ b/src/electionguard_tools/helpers/key_ceremony_orchestrator.py @@ -11,7 +11,7 @@ class KeyCeremonyOrchestrator: @staticmethod def create_guardians(ceremony_details: CeremonyDetails) -> List[Guardian]: return [ - Guardian.from_context_info( + Guardian.from_nonce( str(i + 1), i + 1, ceremony_details.number_of_guardians, diff --git a/tests/integration/test_end_to_end_election.py b/tests/integration/test_end_to_end_election.py index 33ca1829..6c6280d0 100644 --- a/tests/integration/test_end_to_end_election.py +++ b/tests/integration/test_end_to_end_election.py @@ -184,7 +184,7 @@ def step_1_key_ceremony(self) -> None: # Setup Guardians for i in range(self.NUMBER_OF_GUARDIANS): self.guardians.append( - Guardian.from_context_info( + Guardian.from_nonce( str(i + 1), i + 1, self.NUMBER_OF_GUARDIANS, diff --git a/tests/unit/test_guardian.py b/tests/unit/test_guardian.py index f791aff2..01c0a636 100644 --- a/tests/unit/test_guardian.py +++ b/tests/unit/test_guardian.py @@ -25,7 +25,7 @@ class TestGuardian(BaseTestCase): def test_import_from_guardian_private_record(self) -> None: # Arrange - guardian_expected = Guardian.from_context_info( + guardian_expected = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) private_guardian_record = guardian_expected.export_private_data() @@ -51,7 +51,7 @@ def test_import_from_guardian_private_record(self) -> None: def test_set_ceremony_details(self) -> None: # Arrange - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) expected_number_of_guardians = 10 @@ -68,7 +68,7 @@ def test_set_ceremony_details(self) -> None: def test_share_key(self) -> None: # Arrange - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) @@ -84,10 +84,10 @@ def test_share_key(self) -> None: def test_save_guardian_key(self) -> None: # Arrange - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) key = other_guardian.share_key() @@ -100,10 +100,10 @@ def test_save_guardian_key(self) -> None: def test_all_guardian_keys_received(self) -> None: # Arrange - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) key = other_guardian.share_key() @@ -115,28 +115,12 @@ def test_all_guardian_keys_received(self) -> None: # Assert self.assertTrue(guardian.all_guardian_keys_received()) - def test_generate_election_key_pair(self) -> None: - # Arrange - guardian = Guardian.from_context_info( - SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM - ) - first_public_key = guardian.share_key() - - # Act - guardian.generate_election_key_pair() - second_public_key = guardian.share_key() - - # Assert - self.assertIsNotNone(second_public_key) - self.assertIsNotNone(second_public_key.key) - self.assertNotEqual(first_public_key.key, second_public_key.key) - def test_share_backups(self) -> None: # Arrange - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -159,10 +143,10 @@ def test_share_backups(self) -> None: def test_save_election_partial_key_backup(self) -> None: # Arrange - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -178,10 +162,10 @@ def test_save_election_partial_key_backup(self) -> None: def test_all_election_partial_key_backups_received(self) -> None: # Arrange # Round 1 - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -199,10 +183,10 @@ def test_all_election_partial_key_backups_received(self) -> None: def test_verify_election_partial_key_backup(self) -> None: # Arrange # Round 1 - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -227,13 +211,13 @@ def test_verify_election_partial_key_backup(self) -> None: def test_verify_election_partial_key_challenge(self) -> None: # Arrange - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - alternate_verifier = Guardian.from_context_info( + alternate_verifier = Guardian.from_nonce( ALTERNATE_VERIFIER_ID, ALTERNATE_VERIFIER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, @@ -257,10 +241,10 @@ def test_verify_election_partial_key_challenge(self) -> None: def test_publish_election_backup_challenge(self) -> None: # Arrange - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -282,10 +266,10 @@ def test_publish_election_backup_challenge(self) -> None: def test_save_election_partial_key_verification(self) -> None: # Arrange # Round 1 - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -308,10 +292,10 @@ def test_save_election_partial_key_verification(self) -> None: def test_all_election_partial_key_backups_verified(self) -> None: # Arrange # Round 1 - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) @@ -335,10 +319,10 @@ def test_all_election_partial_key_backups_verified(self) -> None: def test_publish_joint_key(self) -> None: # Arrange # Round 1 - guardian = Guardian.from_context_info( + guardian = Guardian.from_nonce( SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) - other_guardian = Guardian.from_context_info( + other_guardian = Guardian.from_nonce( RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM ) guardian.save_guardian_key(other_guardian.share_key()) diff --git a/tests/unit/test_key_ceremony_mediator.py b/tests/unit/test_key_ceremony_mediator.py index 6f04cc32..8b1e157a 100644 --- a/tests/unit/test_key_ceremony_mediator.py +++ b/tests/unit/test_key_ceremony_mediator.py @@ -27,10 +27,10 @@ class TestKeyCeremonyMediator(BaseTestCase): def setUp(self) -> None: super().setUp() - self.GUARDIAN_1 = Guardian.from_context_info( + self.GUARDIAN_1 = Guardian.from_nonce( GUARDIAN_1_ID, 1, NUMBER_OF_GUARDIANS, QUORUM ) - self.GUARDIAN_2 = Guardian.from_context_info( + self.GUARDIAN_2 = Guardian.from_nonce( GUARDIAN_2_ID, 2, NUMBER_OF_GUARDIANS, QUORUM ) self.GUARDIANS = [self.GUARDIAN_1, self.GUARDIAN_2] From 92a8587d022420f8371c7104415efbe335c64b5f Mon Sep 17 00:00:00 2001 From: Keith Fung Date: Mon, 25 Apr 2022 22:11:18 -0400 Subject: [PATCH 11/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Remove=20lint=20skip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/electionguard/guardian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/electionguard/guardian.py b/src/electionguard/guardian.py index 0289e205..b1cadbb2 100644 --- a/src/electionguard/guardian.py +++ b/src/electionguard/guardian.py @@ -1,5 +1,5 @@ # pylint: disable=too-many-public-methods -# pylint: disable=too-many-instance-attributes + from dataclasses import dataclass from typing import Dict, List, Optional, TypeVar From 7b3bed2628eb7d3e421b60eec06155f17f5bc71d Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Tue, 26 Apr 2022 12:06:58 -0400 Subject: [PATCH 12/13] Final fixup --- docs/1_Key_Ceremony.md | 2 +- .../e2e/e2e_input_retrieval_step.py | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/electionguard_cli/e2e/e2e_input_retrieval_step.py diff --git a/docs/1_Key_Ceremony.md b/docs/1_Key_Ceremony.md index 6012b0dc..b94606aa 100644 --- a/docs/1_Key_Ceremony.md +++ b/docs/1_Key_Ceremony.md @@ -73,7 +73,7 @@ guardians: List[Guardian] # Setup Guardians for i in range(NUMBER_OF_GUARDIANS): guardians.append( - Guardian.from_context_info(f"some_guardian_id_{str(i)}", i, NUMBER_OF_GUARDIANS, QUORUM) + Guardian.from_nonce(f"some_guardian_id_{str(i)}", i, NUMBER_OF_GUARDIANS, QUORUM) ) mediator = KeyCeremonyMediator(details) diff --git a/src/electionguard_cli/e2e/e2e_input_retrieval_step.py b/src/electionguard_cli/e2e/e2e_input_retrieval_step.py new file mode 100644 index 00000000..8dc7400b --- /dev/null +++ b/src/electionguard_cli/e2e/e2e_input_retrieval_step.py @@ -0,0 +1,61 @@ +from io import TextIOWrapper +from typing import List + +from electionguard.ballot import PlaintextBallot +from electionguard.guardian import Guardian +from electionguard.manifest import Manifest +from electionguard.serialize import ( + from_list_in_file_wrapper, +) + +from ..cli_steps import ( + InputRetrievalStepBase, +) +from .e2e_inputs import E2eInputs + + +class E2eInputRetrievalStep(InputRetrievalStepBase): + """Responsible for retrieving and parsing user provided inputs for the CLI's e2e command.""" + + def get_inputs( + self, + guardian_count: int, + quorum: int, + manifest_file: TextIOWrapper, + ballots_file: TextIOWrapper, + spoil_id: str, + output_record: str, + output_keys: str, + ) -> E2eInputs: + self.print_header("Retrieving Inputs") + guardians = E2eInputRetrievalStep._get_guardians(guardian_count, quorum) + manifest: Manifest = self._get_manifest(manifest_file) + ballots = E2eInputRetrievalStep._get_ballots(ballots_file) + self.print_value("Guardians", guardian_count) + self.print_value("Quorum", quorum) + return E2eInputs( + guardian_count, + quorum, + guardians, + manifest, + ballots, + spoil_id, + output_record, + output_keys, + ) + + @staticmethod + def _get_ballots(ballots_file: TextIOWrapper) -> List[PlaintextBallot]: + ballots: List[PlaintextBallot] = from_list_in_file_wrapper( + PlaintextBallot, ballots_file + ) + return ballots + + @staticmethod + def _get_guardians(number_of_guardians: int, quorum: int) -> List[Guardian]: + guardians: List[Guardian] = [] + for i in range(number_of_guardians): + guardians.append( + Guardian.from_nonce(str(i + 1), i + 1, number_of_guardians, quorum) + ) + return guardians From 860cd365fe5b6cc8d1522e016572d0194f139330 Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Tue, 26 Apr 2022 12:16:21 -0400 Subject: [PATCH 13/13] Remove accidentally added file --- .../e2e/e2e_input_retrieval_step.py | 61 ------------------- 1 file changed, 61 deletions(-) delete mode 100644 src/electionguard_cli/e2e/e2e_input_retrieval_step.py diff --git a/src/electionguard_cli/e2e/e2e_input_retrieval_step.py b/src/electionguard_cli/e2e/e2e_input_retrieval_step.py deleted file mode 100644 index 8dc7400b..00000000 --- a/src/electionguard_cli/e2e/e2e_input_retrieval_step.py +++ /dev/null @@ -1,61 +0,0 @@ -from io import TextIOWrapper -from typing import List - -from electionguard.ballot import PlaintextBallot -from electionguard.guardian import Guardian -from electionguard.manifest import Manifest -from electionguard.serialize import ( - from_list_in_file_wrapper, -) - -from ..cli_steps import ( - InputRetrievalStepBase, -) -from .e2e_inputs import E2eInputs - - -class E2eInputRetrievalStep(InputRetrievalStepBase): - """Responsible for retrieving and parsing user provided inputs for the CLI's e2e command.""" - - def get_inputs( - self, - guardian_count: int, - quorum: int, - manifest_file: TextIOWrapper, - ballots_file: TextIOWrapper, - spoil_id: str, - output_record: str, - output_keys: str, - ) -> E2eInputs: - self.print_header("Retrieving Inputs") - guardians = E2eInputRetrievalStep._get_guardians(guardian_count, quorum) - manifest: Manifest = self._get_manifest(manifest_file) - ballots = E2eInputRetrievalStep._get_ballots(ballots_file) - self.print_value("Guardians", guardian_count) - self.print_value("Quorum", quorum) - return E2eInputs( - guardian_count, - quorum, - guardians, - manifest, - ballots, - spoil_id, - output_record, - output_keys, - ) - - @staticmethod - def _get_ballots(ballots_file: TextIOWrapper) -> List[PlaintextBallot]: - ballots: List[PlaintextBallot] = from_list_in_file_wrapper( - PlaintextBallot, ballots_file - ) - return ballots - - @staticmethod - def _get_guardians(number_of_guardians: int, quorum: int) -> List[Guardian]: - guardians: List[Guardian] = [] - for i in range(number_of_guardians): - guardians.append( - Guardian.from_nonce(str(i + 1), i + 1, number_of_guardians, quorum) - ) - return guardians