@@ -203,7 +203,10 @@ class CiphertextBallotSelection(
203203 """encrypted representation of the extended_data field"""
204204
205205 def is_valid_encryption (
206- self , seed_hash : ElementModQ , elgamal_public_key : ElementModP
206+ self ,
207+ seed_hash : ElementModQ ,
208+ elgamal_public_key : ElementModP ,
209+ crypto_extended_base_hash : ElementModQ ,
207210 ) -> bool :
208211 """
209212 Given an encrypted BallotSelection, validates the encryption state against a specific seed hash and public key.
@@ -233,7 +236,9 @@ def is_valid_encryption(
233236 log_warning (f"no proof exists for: { self .object_id } " )
234237 return False
235238
236- return self .proof .is_valid (self .ciphertext , elgamal_public_key )
239+ return self .proof .is_valid (
240+ self .ciphertext , elgamal_public_key , crypto_extended_base_hash
241+ )
237242
238243 def crypto_hash_with (self , seed_hash : ElementModQ ) -> ElementModQ :
239244 """
@@ -262,6 +267,7 @@ def make_ciphertext_ballot_selection(
262267 description_hash : ElementModQ ,
263268 ciphertext : ElGamalCiphertext ,
264269 elgamal_public_key : ElementModP ,
270+ crypto_extended_base_hash : ElementModQ ,
265271 proof_seed : ElementModQ ,
266272 selection_representation : int ,
267273 is_placeholder_selection : bool = False ,
@@ -285,7 +291,12 @@ def make_ciphertext_ballot_selection(
285291 proof = flatmap_optional (
286292 nonce ,
287293 lambda n : make_disjunctive_chaum_pedersen (
288- ciphertext , n , elgamal_public_key , proof_seed , selection_representation
294+ ciphertext ,
295+ n ,
296+ elgamal_public_key ,
297+ crypto_extended_base_hash ,
298+ proof_seed ,
299+ selection_representation ,
289300 ),
290301 )
291302
@@ -443,7 +454,10 @@ def elgamal_accumulate(self) -> ElGamalCiphertext:
443454 return _ciphertext_ballot_elgamal_accumulate (self .ballot_selections )
444455
445456 def is_valid_encryption (
446- self , seed_hash : ElementModQ , elgamal_public_key : ElementModP
457+ self ,
458+ seed_hash : ElementModQ ,
459+ elgamal_public_key : ElementModP ,
460+ crypto_extended_base_hash : ElementModQ ,
447461 ) -> bool :
448462 """
449463 Given an encrypted BallotContest, validates the encryption state against a specific seed hash and public key
@@ -475,7 +489,9 @@ def is_valid_encryption(
475489
476490 # Verify the sum of the selections matches the proof
477491 elgamal_accumulation = self .elgamal_accumulate ()
478- return self .proof .is_valid (elgamal_accumulation , elgamal_public_key )
492+ return self .proof .is_valid (
493+ elgamal_accumulation , elgamal_public_key , crypto_extended_base_hash
494+ )
479495
480496
481497def _ciphertext_ballot_elgamal_accumulate (
@@ -521,6 +537,7 @@ def make_ciphertext_ballot_contest(
521537 description_hash : ElementModQ ,
522538 ballot_selections : List [CiphertextBallotSelection ],
523539 elgamal_public_key : ElementModP ,
540+ crypto_extended_base_hash : ElementModQ ,
524541 proof_seed : ElementModQ ,
525542 number_elected : int ,
526543 crypto_hash : Optional [ElementModQ ] = None ,
@@ -548,6 +565,7 @@ def make_ciphertext_ballot_contest(
548565 r = ag ,
549566 k = elgamal_public_key ,
550567 seed = proof_seed ,
568+ hash_header = crypto_extended_base_hash ,
551569 ),
552570 )
553571 return CiphertextBallotContest (
@@ -606,7 +624,7 @@ class CiphertextBallot(ElectionObjectBase, CryptoHashCheckable):
606624 When a ballot is in it's complete, encrypted state, the `nonce` is the master nonce
607625 from which all other nonces can be derived to encrypt the ballot. Allong with the `nonce`
608626 fields on `Ballotcontest` and `BallotSelection`, this value is sensitive.
609- :field object_id: A unique Ballot ID that is relevant to the external system
627+ :field object_id: A unique Ballot ID that is relevant to the external system
610628 """
611629
612630 ballot_style : str
@@ -638,7 +656,16 @@ def __post_init__(self) -> None:
638656 self .timestamp = to_ticks (datetime .utcnow ())
639657 self .generate_tracking (self .previous_tracking_hash )
640658
641- @property
659+ @staticmethod
660+ def nonce_seed (
661+ description_hash : ElementModQ , object_id : str , nonce : ElementModQ
662+ ) -> ElementModQ :
663+ """
664+ :return: a representation of the election and the external Id in the nonce's used
665+ to derive other nonce values on the ballot
666+ """
667+ return hash_elems (description_hash , object_id , nonce )
668+
642669 def hashed_ballot_nonce (self ) -> Optional [ElementModQ ]:
643670 """
644671 :return: a hash value derived from the description hash, the object id, and the nonce value
@@ -651,7 +678,7 @@ def hashed_ballot_nonce(self) -> Optional[ElementModQ]:
651678 )
652679 return None
653680
654- return hash_elems (self .description_hash , self .object_id , self .nonce )
681+ return self . nonce_seed (self .description_hash , self .object_id , self .nonce )
655682
656683 def generate_tracking (self , seed_hash : ElementModQ ) -> None :
657684 """
@@ -691,7 +718,10 @@ def crypto_hash_with(self, seed_hash: ElementModQ) -> ElementModQ:
691718 return hash_elems (self .object_id , seed_hash , * contest_hashes )
692719
693720 def is_valid_encryption (
694- self , seed_hash : ElementModQ , elgamal_public_key : ElementModP
721+ self ,
722+ seed_hash : ElementModQ ,
723+ elgamal_public_key : ElementModP ,
724+ crypto_extended_base_hash : ElementModQ ,
695725 ) -> bool :
696726 """
697727 Given an encrypted Ballot, validates the encryption state against a specific seed hash and public key
@@ -723,12 +753,16 @@ def is_valid_encryption(
723753 for selection in contest .ballot_selections :
724754 valid_proofs .append (
725755 selection .is_valid_encryption (
726- selection .description_hash , elgamal_public_key
756+ selection .description_hash ,
757+ elgamal_public_key ,
758+ crypto_extended_base_hash ,
727759 )
728760 )
729761 valid_proofs .append (
730762 contest .is_valid_encryption (
731- contest .description_hash , elgamal_public_key
763+ contest .description_hash ,
764+ elgamal_public_key ,
765+ crypto_extended_base_hash ,
732766 )
733767 )
734768 return all (valid_proofs )
0 commit comments