Repository: microsoft/electionguard-python

Issue:
🚧  Reconstruction of Guardian
## Original Issue

**Description**
The stateful `Guardian` class after the latest refactor is used to both run the Key Ceremony and create their decryption shares. Since these two pieces may exist over vastly different times this brings together the question of reconstructing the `Guardian`. The best way to proceed forward with this seems to allow a constructor or a factory method to reconstruct the guardians. 

~~This would need specifically to include most of the information in the `PrivateGuardianRecord`.~~

~~So two options to move forward on this:~~
~~1. Create a constructor with optionals to allow a guardian to be constructed with ElectionKeyPair, AuxiliaryKeyPair, sent and received ElectionPartialKeyBackups~~
~~2. Use a factory method to build a guardian and utilize the save and receive to consume a PrivateGuardianRecord to rebuild that guardian with the necessary information~~

------------------------------------------------------

## Updated Information 

When reconstructing the guardian it can be tempting to use the nonce, instead the correct methodology should be having the `ElectionKeyPair` as a parameter the `Guardian` instead of the nonce.

The nonce can be seen here:
https://github.com/microsoft/electionguard-python/blob/03698c683ab35f608e35dc9b66217caa1d40f7eb/src/electionguard/guardian.py#L157

But here we can notice that it is only used to create an `ElectionKeyPair`
https://github.com/microsoft/electionguard-python/blob/03698c683ab35f608e35dc9b66217caa1d40f7eb/src/electionguard/guardian.py#L177

### Implementation

There is a PrivateGuardianRecord that holds this info, but the simplest way is:
- [ ] Add a get_key_pair() to the Guardian and saving that file. 
- [ ] Remove the nonce seed from the init and replace with mandatory ElectionGuardKeyPair
- [ ] Use @classmethod to implement the nonce and other constructors.

The `get_key_pair` could eventually be changed to a `export_secret_key` if necessary to get the math value for saving on items like Yubikeys.

```py
class Guardian

    def __init__(
        self,
        id: str,
        sequence_order: int,
        number_of_guardians: int,
        quorum: int,
        key_pair: ElectionKeyPair,
    ) -> None:
        ...

    @classmethod
    def from_nonce(..., nonce: ElementModQ = None)
         """An optional `ElementModQ` value for nonce that can be used to generate the `ElectionKeyPair`. It is recommended to only use this field for testing"""
        generate_election_key_pair(id, sequence_order, quorum, nonce)
         return cls(..., key_pair)

    def get_key_pair(self) -> ElectionKeyPair:
          pass
 ```

Discussion:

Patch:
diff --git a/docs/1_Key_Ceremony.md b/docs/1_Key_Ceremony.md
index 9e18b73..b94606a 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_nonce(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 6ca5752..b1cadbb 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
 
@@ -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,45 +146,84 @@ class Guardian:
 
     def __init__(
         self,
-        id: str,
-        sequence_order: int,
-        number_of_guardians: int,
-        quorum: int,
-        nonce_seed: Optional[ElementModQ] = 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,
+        guardian_election_partial_key_verifications: Dict[
+            GuardianId, ElectionPartialKeyVerification
+        ] = None,
     ) -> None:
         """
         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 nonce_seed: an optional `ElementModQ` value that can be used to generate the `ElectionKeyPair`.
-                           It is recommended to only use this field for testing.
+        :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._backups_to_share = {}
-        self._guardian_election_public_keys = {}
-        self._guardian_election_partial_key_backups = {}
-        self._guardian_election_partial_key_verifications = {}
 
-        self.generate_election_key_pair(nonce_seed if nonce_seed is not None else None)
+        self._election_keys = key_pair
+        self.ceremony_details = ceremony_details
 
-    def reset(self, number_of_guardians: int, quorum: int) -> None:
-        """
-        Reset guardian to initial state.
+        # 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
+        )
+        self._guardian_election_partial_key_backups = (
+            {} if partial_key_backups is None else partial_key_backups
+        )
+        self._guardian_election_partial_key_verifications = (
+            {}
+            if guardian_election_partial_key_verifications is None
+            else guardian_election_partial_key_verifications
+        )
 
-        :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()
+        self.save_guardian_key(key_pair.share())
+
+    @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(
+        cls,
+        id: str,
+        sequence_order: int,
+        number_of_guardians: int,
+        quorum: int,
+        nonce: ElementModQ = None,
+    ) -> "Guardian":
+        """Creates a guardian with an `ElementModQ` value that will be used to generate
+        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(
+        cls,
+        private_guardian_record: PrivateGuardianRecord,
+        number_of_guardians: int,
+        quorum: int,
+    ) -> "Guardian":
+        guardian = cls(
+            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,
+            private_guardian_record.guardian_election_partial_key_verifications,
+        )
+
+        return guardian
 
     def publish(self) -> GuardianRecord:
         """Publish record of guardian with all required information."""
@@ -215,13 +250,6 @@ class Guardian:
         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_tools/helpers/key_ceremony_orchestrator.py b/src/electionguard_tools/helpers/key_ceremony_orchestrator.py
index 9c9a234..32ceca3 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_nonce(
                 str(i + 1),
                 i + 1,
                 ceremony_details.number_of_guardians,


Test patch:
diff --git a/src/electionguard_cli/e2e_steps/input_retrieval_step.py b/src/electionguard_cli/e2e_steps/input_retrieval_step.py
index 53dabc9..b238f0e 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 @@ class InputRetrievalStep(E2eStepBase):
         guardians: List[Guardian] = []
         for i in range(number_of_guardians):
             guardians.append(
-                Guardian(
+                Guardian.from_nonce(
                     str(i + 1),
                     i + 1,
                     number_of_guardians,
diff --git a/tests/integration/test_end_to_end_election.py b/tests/integration/test_end_to_end_election.py
index 372124e..6c6280d 100644
--- a/tests/integration/test_end_to_end_election.py
+++ b/tests/integration/test_end_to_end_election.py
@@ -184,7 +184,7 @@ class TestEndToEndElection(BaseTestCase):
         # Setup Guardians
         for i in range(self.NUMBER_OF_GUARDIANS):
             self.guardians.append(
-                Guardian(
+                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 c7d366c..01c0a63 100644
--- a/tests/unit/test_guardian.py
+++ b/tests/unit/test_guardian.py
@@ -23,25 +23,35 @@ ELECTION_PUBLIC_KEY = ""
 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.from_nonce(
             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
+        # pylint: disable=protected-access
         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
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         expected_number_of_guardians = 10
@@ -58,7 +68,7 @@ class TestGuardian(BaseTestCase):
 
     def test_share_key(self) -> None:
         # Arrange
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
 
@@ -74,10 +84,10 @@ class TestGuardian(BaseTestCase):
 
     def test_save_guardian_key(self) -> None:
         # Arrange
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         key = other_guardian.share_key()
@@ -90,10 +100,10 @@ class TestGuardian(BaseTestCase):
 
     def test_all_guardian_keys_received(self) -> None:
         # Arrange
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         key = other_guardian.share_key()
@@ -105,28 +115,12 @@ class TestGuardian(BaseTestCase):
         # Assert
         self.assertTrue(guardian.all_guardian_keys_received())
 
-    def test_generate_election_key_pair(self) -> None:
-        # Arrange
-        guardian = Guardian(
-            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(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         guardian.save_guardian_key(other_guardian.share_key())
@@ -149,10 +143,10 @@ class TestGuardian(BaseTestCase):
 
     def test_save_election_partial_key_backup(self) -> None:
         # Arrange
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         guardian.save_guardian_key(other_guardian.share_key())
@@ -168,10 +162,10 @@ class TestGuardian(BaseTestCase):
     def test_all_election_partial_key_backups_received(self) -> None:
         # Arrange
         # Round 1
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         guardian.save_guardian_key(other_guardian.share_key())
@@ -189,10 +183,10 @@ class TestGuardian(BaseTestCase):
     def test_verify_election_partial_key_backup(self) -> None:
         # Arrange
         # Round 1
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         guardian.save_guardian_key(other_guardian.share_key())
@@ -217,13 +211,13 @@ class TestGuardian(BaseTestCase):
 
     def test_verify_election_partial_key_challenge(self) -> None:
         # Arrange
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        alternate_verifier = Guardian(
+        alternate_verifier = Guardian.from_nonce(
             ALTERNATE_VERIFIER_ID,
             ALTERNATE_VERIFIER_SEQUENCE_ORDER,
             NUMBER_OF_GUARDIANS,
@@ -247,10 +241,10 @@ class TestGuardian(BaseTestCase):
 
     def test_publish_election_backup_challenge(self) -> None:
         # Arrange
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         guardian.save_guardian_key(other_guardian.share_key())
@@ -272,10 +266,10 @@ class TestGuardian(BaseTestCase):
     def test_save_election_partial_key_verification(self) -> None:
         # Arrange
         # Round 1
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         guardian.save_guardian_key(other_guardian.share_key())
@@ -298,10 +292,10 @@ class TestGuardian(BaseTestCase):
     def test_all_election_partial_key_backups_verified(self) -> None:
         # Arrange
         # Round 1
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        other_guardian = Guardian.from_nonce(
             RECIPIENT_GUARDIAN_ID, RECIPIENT_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
         guardian.save_guardian_key(other_guardian.share_key())
@@ -325,10 +319,10 @@ class TestGuardian(BaseTestCase):
     def test_publish_joint_key(self) -> None:
         # Arrange
         # Round 1
-        guardian = Guardian(
+        guardian = Guardian.from_nonce(
             SENDER_GUARDIAN_ID, SENDER_SEQUENCE_ORDER, NUMBER_OF_GUARDIANS, QUORUM
         )
-        other_guardian = Guardian(
+        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 7fd0802..8b1e157 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_nonce(
+            GUARDIAN_1_ID, 1, NUMBER_OF_GUARDIANS, QUORUM
+        )
+        self.GUARDIAN_2 = Guardian.from_nonce(
+            GUARDIAN_2_ID, 2, NUMBER_OF_GUARDIANS, QUORUM
+        )
         self.GUARDIANS = [self.GUARDIAN_1, self.GUARDIAN_2]
 
     def test_reset(self) -> None: