コード例 #1
0
    def test_get_plaintext_tally_all_guardians_present(self, data,
                                                       parties: int,
                                                       contests: int):
        # Arrange
        description = data.draw(election_descriptions(parties, contests))
        builder = ElectionBuilder(self.NUMBER_OF_GUARDIANS, self.QUORUM,
                                  description)
        metadata, context = builder.set_public_key(
            self.joint_public_key).build()

        plaintext_ballots: List[PlaintextBallot] = data.draw(
            plaintext_voted_ballots(metadata, randrange(3, 6)))
        plaintext_tallies = accumulate_plaintext_ballots(plaintext_ballots)

        encrypted_tally = self._generate_encrypted_tally(
            metadata, context, plaintext_ballots)

        subject = DecryptionMediator(metadata, context, encrypted_tally)

        # act
        for guardian in self.guardians:
            self.assertIsNotNone(subject.announce(guardian))

        decrypted_tallies = subject.get_plaintext_tally()
        result = self._convert_to_selections(decrypted_tallies)

        # assert
        self.assertIsNotNone(result)
        self.assertEqual(plaintext_tallies, result)
コード例 #2
0
    def test_get_plaintext_tally_with_all_guardians_present(
            self, values, parties: int, contests: int):
        # Arrange
        description = values.draw(election_descriptions(parties, contests))
        builder = ElectionBuilder(self.NUMBER_OF_GUARDIANS, self.QUORUM,
                                  description)
        internal_manifest, context = (builder.set_public_key(
            self.joint_public_key.joint_public_key).set_commitment_hash(
                self.joint_public_key.commitment_hash).build())

        plaintext_ballots: List[PlaintextBallot] = values.draw(
            plaintext_voted_ballots(internal_manifest, randrange(3, 6)))
        expected_plaintext_tally = accumulate_plaintext_ballots(
            plaintext_ballots)

        encrypted_tally = self._generate_encrypted_tally(
            internal_manifest, context, plaintext_ballots)

        mediator = DecryptionMediator(self.decryption_mediator_id, context)
        available_guardians = self.guardians
        DecryptionHelper.perform_decryption_setup(available_guardians,
                                                  mediator, context,
                                                  encrypted_tally, [])

        # Act
        plaintext_tally = mediator.get_plaintext_tally(encrypted_tally)
        selections = _convert_to_selections(plaintext_tally)

        # Assert
        self.assertIsNotNone(plaintext_tally)
        self.assertIsNotNone(selections)
        self.assertEqual(expected_plaintext_tally, selections)
コード例 #3
0
class TestElections(unittest.TestCase):
    @settings(
        deadline=timedelta(milliseconds=2000),
        suppress_health_check=[HealthCheck.too_slow],
        max_examples=10,
    )
    @given(election_descriptions())
    def test_generators_yield_valid_output(self, ed: ElectionDescription):
        """
        Tests that our Hypothesis election strategies generate "valid" output, also exercises the full stack
        of `is_valid` methods.
        """

        self.assertTrue(ed.is_valid())

    @settings(
        deadline=timedelta(milliseconds=10000),
        suppress_health_check=[HealthCheck.too_slow],
        max_examples=5,
        # disabling the "shrink" phase, because it runs very slowly
        phases=[Phase.explicit, Phase.reuse, Phase.generate, Phase.target],
    )
    @given(
        integers(1, 3).flatmap(lambda n: elections_and_ballots(n)),
        elements_mod_q(),
    )
    def test_accumulation_encryption_decryption(
        self,
        everything: ELECTIONS_AND_BALLOTS_TUPLE_TYPE,
        nonce: ElementModQ,
    ):
        """
        Tests that decryption is the inverse of encryption over arbitrarily generated elections and ballots.

        This test uses an abitrarily generated dataset with a single public-private keypair for the election
        encryption context.  It also manually verifies that homomorphic accumulation works as expected.
        """
        # Arrange
        election_description, metadata, ballots, secret_key, context = everything

        # Tally the plaintext ballots for comparison later
        plaintext_tallies = accumulate_plaintext_ballots(ballots)
        num_ballots = len(ballots)
        num_contests = len(metadata.contests)
        zero_nonce, *nonces = Nonces(nonce)[:num_ballots + 1]
        self.assertEqual(len(nonces), num_ballots)
        self.assertTrue(len(metadata.contests) > 0)

        # Generatea valid encryption of zero
        encrypted_zero = elgamal_encrypt(0, zero_nonce,
                                         context.elgamal_public_key)

        # Act
        encrypted_ballots = []

        # encrypt each ballot
        for i in range(num_ballots):
            encrypted_ballot = encrypt_ballot(ballots[i], metadata, context,
                                              SEED_HASH, nonces[i])
            encrypted_ballots.append(encrypted_ballot)

            # sanity check the encryption
            self.assertIsNotNone(encrypted_ballot)
            self.assertEqual(num_contests, len(encrypted_ballot.contests))

            # decrypt the ballot with secret and verify it matches the plaintext
            decrypted_ballot = decrypt_ballot_with_secret(
                ballot=encrypted_ballot,
                election_metadata=metadata,
                crypto_extended_base_hash=context.crypto_extended_base_hash,
                public_key=context.elgamal_public_key,
                secret_key=secret_key,
                remove_placeholders=True,
            )
            self.assertEqual(ballots[i], decrypted_ballot)

        # homomorphically accumualte the encrypted ballot representations
        encrypted_tallies = _accumulate_encrypted_ballots(
            encrypted_zero, encrypted_ballots)

        decrypted_tallies = {}
        for object_id in encrypted_tallies.keys():
            decrypted_tallies[object_id] = encrypted_tallies[
                object_id].decrypt(secret_key)

        # loop through the contest descriptions and verify
        # the decrypted tallies match the plaintext tallies
        for contest in metadata.contests:
            # Sanity check the generated data
            self.assertTrue(len(contest.ballot_selections) > 0)
            self.assertTrue(len(contest.placeholder_selections) > 0)

            decrypted_selection_tallies = [
                decrypted_tallies[selection.object_id]
                for selection in contest.ballot_selections
            ]
            decrypted_placeholder_tallies = [
                decrypted_tallies[placeholder.object_id]
                for placeholder in contest.placeholder_selections
            ]
            plaintext_tally_values = [
                plaintext_tallies[selection.object_id]
                for selection in contest.ballot_selections
            ]

            # verify the plaintext tallies match the decrypted tallies
            self.assertEqual(decrypted_selection_tallies,
                             plaintext_tally_values)

            # validate the right number of selections including placeholders across all ballots
            self.assertEqual(
                contest.number_elected * num_ballots,
                sum(decrypted_selection_tallies) +
                sum(decrypted_placeholder_tallies),
            )