def partially_decrypt( guardian_keys: ElectionKeyPair, elgamal: ElGamalCiphertext, extended_base_hash: ElementModQ, nonce_seed: ElementModQ = None, ) -> Tuple[ElementModP, ChaumPedersenProof]: """ Compute a partial decryption of an elgamal encryption :param elgamal: the `ElGamalCiphertext` that will be partially decrypted :param extended_base_hash: the extended base hash of the election that was used to generate t he ElGamal Ciphertext :param nonce_seed: an optional value used to generate the `ChaumPedersenProof` if no value is provided, a random number will be used. :return: a `Tuple[ElementModP, ChaumPedersenProof]` of the decryption and its proof """ if nonce_seed is None: nonce_seed = rand_q() # TODO: ISSUE #47: Decrypt the election secret key # 𝑀_i = 𝐴^𝑠𝑖 mod 𝑝 partial_decryption = elgamal.partial_decrypt( guardian_keys.key_pair.secret_key) # 𝑀_i = 𝐴^𝑠𝑖 mod 𝑝 and 𝐾𝑖 = 𝑔^𝑠𝑖 mod 𝑝 proof = make_chaum_pedersen( message=elgamal, s=guardian_keys.key_pair.secret_key, m=partial_decryption, seed=nonce_seed, hash_header=extended_base_hash, ) return (partial_decryption, proof)
def compensate_decrypt( guardian_auxiliary_keys: AuxiliaryKeyPair, missing_guardian_backup: ElectionPartialKeyBackup, ciphertext: ElGamalCiphertext, extended_base_hash: ElementModQ, nonce_seed: ElementModQ = None, decrypt: AuxiliaryDecrypt = rsa_decrypt, ) -> Optional[Tuple[ElementModP, ChaumPedersenProof]]: """ Compute a compensated partial decryption of an elgamal encryption on behalf of the missing guardian :param guardian_auxiliary_keys: Auxiliary key pair for guardian decrypting :param missing_guardian_backup: Missing guardians backup :param ciphertext: the `ElGamalCiphertext` that will be partially decrypted :param extended_base_hash: the extended base hash of the election that was used to generate t he ElGamal Ciphertext :param nonce_seed: an optional value used to generate the `ChaumPedersenProof` if no value is provided, a random number will be used. :param decrypt: an `AuxiliaryDecrypt` function to decrypt the missing guardina private key backup :return: a `Tuple[ElementModP, ChaumPedersenProof]` of the decryption and its proof """ if nonce_seed is None: nonce_seed = rand_q() decrypted_value = decrypt(missing_guardian_backup.encrypted_value, guardian_auxiliary_keys.secret_key) if decrypted_value is None: log_warning( (f"compensate decrypt guardian {guardian_auxiliary_keys.owner_id}" f" failed decryption for {missing_guardian_backup.owner_id}")) return None partial_secret_key = get_optional(hex_to_q(decrypted_value)) # 𝑀_{𝑖,l} = 𝐴^P𝑖_{l} partial_decryption = ciphertext.partial_decrypt(partial_secret_key) # 𝑀_{𝑖,l} = 𝐴^𝑠𝑖 mod 𝑝 and 𝐾𝑖 = 𝑔^𝑠𝑖 mod 𝑝 proof = make_chaum_pedersen( ciphertext, partial_secret_key, partial_decryption, nonce_seed, extended_base_hash, ) return (partial_decryption, proof)
def test_decrypt_contest_invalid_input_fails( self, contest_description: Tuple[str, ContestDescription], keypair: ElGamalKeyPair, nonce_seed: ElementModQ, random_seed: int, ): # Arrange _, description = contest_description random = Random(random_seed) data = ballot_factory.get_random_contest_from(description, random) placeholders = generate_placeholder_selections_from( description, description.number_elected ) description_with_placeholders = contest_description_with_placeholders_from( description, placeholders ) self.assertTrue(description_with_placeholders.is_valid()) # Act subject = encrypt_contest( data, description_with_placeholders, keypair.public_key, nonce_seed ) self.assertIsNotNone(subject) # tamper with the nonce subject.nonce = int_to_q_unchecked(1) result_from_nonce = decrypt_contest_with_nonce( subject, description_with_placeholders, keypair.public_key, remove_placeholders=False, ) result_from_nonce_seed = decrypt_contest_with_nonce( subject, description_with_placeholders, keypair.public_key, nonce_seed, remove_placeholders=False, ) # Assert self.assertIsNone(result_from_nonce) self.assertIsNone(result_from_nonce_seed) # Tamper with the encryption subject.ballot_selections[0].message = ElGamalCiphertext(TWO_MOD_P, TWO_MOD_P) result_from_key_tampered = decrypt_contest_with_secret( subject, description_with_placeholders, keypair.public_key, keypair.secret_key, remove_placeholders=False, ) result_from_nonce_tampered = decrypt_contest_with_nonce( subject, description_with_placeholders, keypair.public_key, remove_placeholders=False, ) result_from_nonce_seed_tampered = decrypt_contest_with_nonce( subject, description_with_placeholders, keypair.public_key, nonce_seed, remove_placeholders=False, ) # Assert self.assertIsNone(result_from_key_tampered) self.assertIsNone(result_from_nonce_tampered) self.assertIsNone(result_from_nonce_seed_tampered)