diff --git a/src/electionguard/__init__.py b/src/electionguard/__init__.py index d1273809..04cffe26 100644 --- a/src/electionguard/__init__.py +++ b/src/electionguard/__init__.py @@ -185,6 +185,7 @@ sequence_order_sort, ) from electionguard.election_polynomial import ( + Coefficient, ElectionPolynomial, LagrangeCoefficientsRecord, PublicCommitment, @@ -409,6 +410,7 @@ "CiphertextTally", "CiphertextTallyContest", "CiphertextTallySelection", + "Coefficient", "CompactPlaintextBallot", "CompactSubmittedBallot", "CompensatedDecryptionShare", diff --git a/src/electionguard/election_polynomial.py b/src/electionguard/election_polynomial.py index 440dd1f2..011f66e5 100644 --- a/src/electionguard/election_polynomial.py +++ b/src/electionguard/election_polynomial.py @@ -22,6 +22,22 @@ PublicCommitment = ElementModP # Public commitment of election polynomial +@dataclass +class Coefficient: + """ + A coefficient of an Election Polynomial + """ + + value: SecretCoefficient + """The secret coefficient `a_ij` """ + + commitment: PublicCommitment + """The public key `K_ij` generated from secret coefficient""" + + proof: SchnorrProof + """A proof of possession of the private key for the secret coefficient""" + + @dataclass class ElectionPolynomial: """ @@ -31,14 +47,16 @@ class ElectionPolynomial: be discovered by a quorum of n guardians corresponding to n coefficients. """ - coefficients: List[SecretCoefficient] - """The secret coefficients `a_ij` """ + coefficients: List[Coefficient] + """List of coefficient value, commitments and proofs""" - coefficient_commitments: List[PublicCommitment] - """The public keys `K_ij`generated from secret coefficients""" + def get_commitments(self) -> List[PublicCommitment]: + """Access the list of public keys generated from secret coefficient""" + return [coefficient.commitment for coefficient in self.coefficients] - coefficient_proofs: List[SchnorrProof] - """A proof of posession of the private key for each secret coefficient""" + def get_proofs(self) -> List[SchnorrProof]: + """Access the list of proof of possesion of the private key for the secret coefficient""" + return [coefficient.proof for coefficient in self.coefficients] def generate_polynomial( @@ -51,23 +69,19 @@ def generate_polynomial( :param nonce: an optional nonce parameter that may be provided (useful for testing) :return: Polynomial used to share election keys """ - coefficients: List[SecretCoefficient] = [] - commitments: List[PublicCommitment] = [] - proofs: List[SchnorrProof] = [] + coefficients: List[Coefficient] = [] for i in range(number_of_coefficients): - # Note: the nonce value is not safe. it is designed for testing only. + # Note: the nonce value is not safe. it is designed for testing only. # this method should be called without the nonce in production. - coefficient = add_q(nonce, i) if nonce is not None else rand_q() - commitment = g_pow_p(coefficient) + value = add_q(nonce, i) if nonce is not None else rand_q() + commitment = g_pow_p(value) proof = make_schnorr_proof( - ElGamalKeyPair(coefficient, commitment), rand_q() + ElGamalKeyPair(value, commitment), rand_q() ) # TODO Alternate schnoor proof method that doesn't need KeyPair - + coefficient = Coefficient(value, commitment, proof) coefficients.append(coefficient) - commitments.append(commitment) - proofs.append(proof) - return ElectionPolynomial(coefficients, commitments, proofs) + return ElectionPolynomial(coefficients) def compute_polynomial_coordinate( @@ -86,7 +100,7 @@ def compute_polynomial_coordinate( computed_value = ZERO_MOD_Q for (i, coefficient) in enumerate(polynomial.coefficients): exponent = pow_q(exponent_modifier, i) - factor = mult_q(coefficient, exponent) + factor = mult_q(coefficient.value, exponent) computed_value = add_q(computed_value, factor) return computed_value diff --git a/src/electionguard/key_ceremony.py b/src/electionguard/key_ceremony.py index 249af39f..6ffcdd98 100644 --- a/src/electionguard/key_ceremony.py +++ b/src/electionguard/key_ceremony.py @@ -91,8 +91,8 @@ def share(self) -> ElectionPublicKey: self.owner_id, self.sequence_order, self.key_pair.public_key, - self.polynomial.coefficient_commitments, - self.polynomial.coefficient_proofs, + self.polynomial.get_commitments(), + self.polynomial.get_proofs(), ) @@ -217,7 +217,7 @@ def generate_election_key_pair( """ polynomial = generate_polynomial(quorum, nonce) key_pair = ElGamalKeyPair( - polynomial.coefficients[0], polynomial.coefficient_commitments[0] + polynomial.coefficients[0].value, polynomial.coefficients[0].commitment ) return ElectionKeyPair(guardian_id, sequence_order, key_pair, polynomial) @@ -301,8 +301,8 @@ def generate_election_partial_key_challenge( backup.designated_id, backup.designated_sequence_order, compute_polynomial_coordinate(backup.designated_sequence_order, polynomial), - polynomial.coefficient_commitments, - polynomial.coefficient_proofs, + polynomial.get_commitments(), + polynomial.get_proofs(), ) diff --git a/tests/unit/test_election_polynomial.py b/tests/unit/test_election_polynomial.py index a973b78d..428fc0fe 100644 --- a/tests/unit/test_election_polynomial.py +++ b/tests/unit/test_election_polynomial.py @@ -1,6 +1,9 @@ from tests.base_test_case import BaseTestCase - +from electionguard.schnorr import make_schnorr_proof +from electionguard.elgamal import ElGamalKeyPair +from electionguard.group import rand_q from electionguard.election_polynomial import ( + Coefficient, compute_polynomial_coordinate, ElectionPolynomial, generate_polynomial, @@ -24,13 +27,17 @@ def test_generate_polynomial(self): self.assertIsNotNone(polynomial) def test_compute_polynomial_coordinate(self): + # create proofs + proof_one = make_schnorr_proof(ElGamalKeyPair(ONE_MOD_Q, ONE_MOD_P), rand_q()) + proof_two = make_schnorr_proof(ElGamalKeyPair(TWO_MOD_Q, TWO_MOD_P), rand_q()) + # Arrange polynomial = ElectionPolynomial( - [ONE_MOD_Q, TWO_MOD_Q], - [ONE_MOD_P, TWO_MOD_P], - [], + [ + Coefficient(ONE_MOD_Q, ONE_MOD_P, proof_one), + Coefficient(TWO_MOD_Q, TWO_MOD_P, proof_two), + ] ) - # Act value = compute_polynomial_coordinate(TEST_EXPONENT_MODIFIER, polynomial) @@ -47,6 +54,6 @@ def test_verify_polynomial_coordinate(self): # Assert self.assertTrue( verify_polynomial_coordinate( - value, TEST_EXPONENT_MODIFIER, polynomial.coefficient_commitments + value, TEST_EXPONENT_MODIFIER, polynomial.get_commitments() ) ) diff --git a/tests/unit/test_key_ceremony.py b/tests/unit/test_key_ceremony.py index 839c911e..8be1e576 100644 --- a/tests/unit/test_key_ceremony.py +++ b/tests/unit/test_key_ceremony.py @@ -50,12 +50,10 @@ def test_generate_election_key_pair(self): self.assertIsNotNone(election_key_pair.key_pair.public_key) self.assertIsNotNone(election_key_pair.key_pair.secret_key) self.assertIsNotNone(election_key_pair.polynomial) - self.assertEqual( - len(election_key_pair.polynomial.coefficient_commitments), QUORUM - ) - self.assertEqual(len(election_key_pair.polynomial.coefficient_proofs), QUORUM) - for proof in election_key_pair.polynomial.coefficient_proofs: - self.assertTrue(proof.is_valid()) + + self.assertEqual(len(election_key_pair.polynomial.coefficients), QUORUM) + for coefficient in election_key_pair.polynomial.coefficients: + self.assertTrue(coefficient.proof.is_valid()) def test_generate_election_partial_key_backup(self): # Arrange