def test_in_bounds_q(self, q: ElementModQ): self.assertTrue(q.is_in_bounds()) too_big = q.to_int() + Q too_small = q.to_int() - Q self.assertFalse(int_to_q_unchecked(too_big).is_in_bounds()) self.assertFalse(int_to_q_unchecked(too_small).is_in_bounds()) self.assertEqual(None, int_to_q(too_big)) self.assertEqual(None, int_to_q(too_small))
def test_encrypt_ballot_with_stateful_composer_succeeds(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) manifest = election_factory.get_fake_manifest() internal_manifest, context = election_factory.get_fake_ciphertext_election( manifest, keypair.public_key) data = election_factory.get_fake_ballot(internal_manifest) self.assertTrue( data.is_valid(internal_manifest.ballot_styles[0].object_id)) device = election_factory.get_encryption_device() subject = EncryptionMediator(internal_manifest, context, device) # Act result = subject.encrypt(data) # Assert self.assertIsNotNone(result) self.assertTrue( result.is_valid_encryption( internal_manifest.manifest_hash, keypair.public_key, context.crypto_extended_base_hash, ))
def test_encrypt_simple_ballot_from_files_succeeds(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) election = election_factory.get_simple_election_from_file() metadata, context = election_factory.get_fake_ciphertext_election( election, keypair.public_key ) data = ballot_factory.get_simple_ballot_from_file() self.assertTrue(data.is_valid(metadata.ballot_styles[0].object_id)) device = EncryptionDevice("Location") subject = EncryptionMediator(metadata, context, device) # Act result = subject.encrypt(data) # Assert self.assertIsNotNone(result) self.assertEqual(data.object_id, result.object_id) self.assertTrue( result.is_valid_encryption( metadata.description_hash, keypair.public_key, context.crypto_extended_base_hash, ) )
def test_encrypt_ballot_simple_succeeds(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) manifest = election_factory.get_fake_manifest() internal_manifest, context = election_factory.get_fake_ciphertext_election( manifest, keypair.public_key) nonce_seed = TWO_MOD_Q # TODO: Ballot Factory subject = election_factory.get_fake_ballot(internal_manifest) self.assertTrue( subject.is_valid(internal_manifest.ballot_styles[0].object_id)) # Act result = encrypt_ballot(subject, internal_manifest, context, SEED) result_from_seed = encrypt_ballot(subject, internal_manifest, context, SEED, nonce_seed) # Assert self.assertIsNotNone(result) self.assertIsNotNone(result.code) self.assertIsNotNone(result_from_seed) self.assertTrue( result.is_valid_encryption( internal_manifest.manifest_hash, keypair.public_key, context.crypto_extended_base_hash, )) self.assertTrue( result_from_seed.is_valid_encryption( internal_manifest.manifest_hash, keypair.public_key, context.crypto_extended_base_hash, ))
def test_cast_ballot(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) election = election_factory.get_fake_election() metadata, context = election_factory.get_fake_ciphertext_election( election, keypair.public_key ) store = BallotStore() source = election_factory.get_fake_ballot(metadata) self.assertTrue(source.is_valid(metadata.ballot_styles[0].object_id)) # Act data = encrypt_ballot(source, metadata, context, SEED_HASH) result = accept_ballot(data, BallotBoxState.CAST, metadata, context, store) # Assert expected = store.get(source.object_id) self.assertEqual(expected.state, BallotBoxState.CAST) self.assertEqual(result.state, BallotBoxState.CAST) self.assertEqual(expected.object_id, result.object_id) # Test failure modes self.assertIsNone( accept_ballot(data, BallotBoxState.CAST, metadata, context, store) ) # cannot cast again self.assertIsNone( accept_ballot(data, BallotBoxState.SPOILED, metadata, context, store) ) # cannot cspoil a ballot already cast
def test_electionguard_basics(self) -> None: plaintexts = range(0, 1000) nonces = Nonces(int_to_q(3)) keypair = elgamal_keypair_random() r_public_key = ray.put(keypair.public_key) start = timer() serial_ciphertexts: List[ElGamalCiphertext] = [ elgamal_encrypt(p, n, keypair.public_key) for p, n in zip(plaintexts, nonces) ] serial_time = timer() # List[ObjectRef[ElGamalCiphertext] parallel_ciphertext_objects: List[ObjectRef] = [ r_encrypt.remote(p, n, r_public_key) for p, n in zip(plaintexts, nonces) ] parallel_ciphertexts: List[ElGamalCiphertext] = ray.get( parallel_ciphertext_objects ) parallel_time = timer() self.assertEqual(serial_ciphertexts, parallel_ciphertexts) print( f"Parallel speedup: {(serial_time - start) / (parallel_time - serial_time):.3f}x" )
def test_reduce_with_ray_wait_with_progress( self, counters: List[int], keypair: ElGamalKeyPair) -> None: nonces = Nonces(int_to_q(3))[0:len(counters)] pbar = ProgressBar({ "Ballots": len(counters), "Tallies": len(counters), "Iterations": 0 }) ciphertexts: List[ObjectRef] = [ r_encrypt.remote(pbar.actor, p, n, keypair.public_key) for p, n in zip(counters, nonces) ] # compute in parallel ptotal = ray.get( ray_reduce_with_ray_wait( inputs=ciphertexts, shard_size=3, reducer_first_arg=pbar.actor, reducer=r_elgamal_add.remote, progressbar=pbar, progressbar_key="Tallies", timeout=None, verbose=False, )) # recompute serially stotal = elgamal_add(*ray.get(ciphertexts)) self.assertEqual(stotal, ptotal)
def test_encrypt_ballot_simple_succeeds(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) election = election_factory.get_fake_election() metadata, context = election_factory.get_fake_ciphertext_election( election, keypair.public_key) nonce_seed = TWO_MOD_Q # TODO: Ballot Factory subject = election_factory.get_fake_ballot(metadata) self.assertTrue(subject.is_valid(metadata.ballot_styles[0].object_id)) # Act result = encrypt_ballot(subject, metadata, context, SEED_HASH) tracker_code = result.get_tracker_code() result_from_seed = encrypt_ballot(subject, metadata, context, SEED_HASH, nonce_seed) # Assert self.assertIsNotNone(result) self.assertIsNotNone(result.tracking_id) self.assertIsNotNone(tracker_code) self.assertIsNotNone(result_from_seed) self.assertTrue( result.is_valid_encryption(context.crypto_extended_base_hash, keypair.public_key)) self.assertTrue( result_from_seed.is_valid_encryption( context.crypto_extended_base_hash, keypair.public_key))
def test_encrypt_simple_selection_malformed_data_fails(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) nonce = randbelow(Q) metadata = SelectionDescription("some-selection-object-id", "some-candidate-id", 1) hash_context = metadata.crypto_hash() subject = selection_from(metadata) self.assertTrue(subject.is_valid(metadata.object_id)) # Act result = encrypt_selection(subject, metadata, keypair.public_key, nonce) # tamper with the description_hash malformed_description_hash = deepcopy(result) malformed_description_hash.description_hash = TWO_MOD_Q # remove the proof missing_proof = deepcopy(result) missing_proof.proof = None # Assert self.assertFalse( malformed_description_hash.is_valid_encryption( hash_context, keypair.public_key)) self.assertFalse( missing_proof.is_valid_encryption(hash_context, keypair.public_key))
def test_ballot_box_spoil_ballot(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) manifest = election_factory.get_fake_manifest() internal_manifest, context = election_factory.get_fake_ciphertext_election( manifest, keypair.public_key) store = DataStore() source = election_factory.get_fake_ballot(internal_manifest) self.assertTrue( source.is_valid(internal_manifest.ballot_styles[0].object_id)) # Act data = encrypt_ballot(source, internal_manifest, context, SEED) subject = BallotBox(internal_manifest, context, store) result = subject.spoil(data) # Assert expected = store.get(source.object_id) self.assertEqual(expected.state, BallotBoxState.SPOILED) self.assertEqual(result.state, BallotBoxState.SPOILED) self.assertEqual(expected.object_id, result.object_id) # Test failure modes self.assertIsNone(subject.spoil(data)) # cannot spoil again self.assertIsNone( subject.cast(data)) # cannot cast a ballot alraedy spoiled
def test_encrypt_simple_contest_referendum_succeeds(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) nonce = randbelow(Q) ballot_selections = [ SelectionDescription( "some-object-id-affirmative", "some-candidate-id-affirmative", 0 ), SelectionDescription( "some-object-id-negative", "some-candidate-id-negative", 1 ), ] placeholder_selections = [ SelectionDescription( "some-object-id-placeholder", "some-candidate-id-placeholder", 2 ) ] metadata = ContestDescriptionWithPlaceholders( "some-contest-object-id", "some-electoral-district-id", 0, VoteVariationType.one_of_m, 1, 1, "some-referendum-contest-name", ballot_selections, None, None, placeholder_selections, ) hash_context = metadata.crypto_hash() subject = contest_from(metadata) self.assertTrue( subject.is_valid( metadata.object_id, len(metadata.ballot_selections), metadata.number_elected, metadata.votes_allowed, ) ) # Act result = encrypt_contest( subject, metadata, keypair.public_key, ONE_MOD_Q, nonce ) # Assert self.assertIsNotNone(result) self.assertTrue( result.is_valid_encryption(hash_context, keypair.public_key, ONE_MOD_Q) )
def test_encrypt_simple_selection_succeeds(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) nonce = randbelow(Q) metadata = SelectionDescription("some-selection-object-id", "some-candidate-id", 1) hash_context = metadata.crypto_hash() subject = selection_from(metadata) self.assertTrue(subject.is_valid(metadata.object_id)) # Act result = encrypt_selection(subject, metadata, keypair.public_key, nonce) # Assert self.assertIsNotNone(result) self.assertIsNotNone(result.message) self.assertTrue( result.is_valid_encryption(hash_context, keypair.public_key))
def setUp(self) -> None: # Election setup election_factory = ElectionFactory() keypair = elgamal_keypair_from_secret(int_to_q(2)) manifest = election_factory.get_fake_manifest() ( self.internal_manifest, self.context, ) = election_factory.get_fake_ciphertext_election( manifest, keypair.public_key) device_hash = ElectionFactory.get_encryption_device().get_hash() # Arrange ballots self.plaintext_ballot = election_factory.get_fake_ballot( self.internal_manifest) ciphertext_ballot = encrypt_ballot(self.plaintext_ballot, self.internal_manifest, self.context, device_hash) self.ballot_nonce = ciphertext_ballot.nonce self.submitted_ballot = from_ciphertext_ballot(ciphertext_ballot, BallotBoxState.CAST)
def test_reduce_with_rounds_without_progress( self, counters: List[int], keypair: ElGamalKeyPair) -> None: nonces = Nonces(int_to_q(3))[0:len(counters)] ciphertexts: List[ObjectRef] = [ r_encrypt.remote(None, p, n, keypair.public_key) for p, n in zip(counters, nonces) ] # compute in parallel ptotal = ray.get( ray_reduce_with_rounds( inputs=ciphertexts, shard_size=3, reducer_first_arg=None, reducer=r_elgamal_add.remote, progressbar=None, verbose=True, )) # recompute serially stotal = elgamal_add(*ray.get(ciphertexts)) self.assertEqual(stotal, ptotal)
def test_large_values_rejected_by_int_to_q(self, q: ElementModQ): oversize = q.to_int() + Q self.assertEqual(None, int_to_q(oversize))
def test_ballot_store(self): # Arrange keypair = elgamal_keypair_from_secret(int_to_q(2)) election = election_factory.get_fake_election() metadata, context = election_factory.get_fake_ciphertext_election( election, keypair.public_key) # get an encrypted fake ballot to work with fake_ballot = election_factory.get_fake_ballot(metadata) encrypted_ballot = encrypt_ballot(fake_ballot, metadata, context, SEED_HASH) # Set up the ballot store subject = BallotStore() data_cast = CiphertextAcceptedBallot( encrypted_ballot.object_id, encrypted_ballot.ballot_style, encrypted_ballot.description_hash, encrypted_ballot.previous_tracking_hash, encrypted_ballot.contests, encrypted_ballot.tracking_hash, encrypted_ballot.timestamp, ) data_cast.state = BallotBoxState.CAST data_spoiled = CiphertextAcceptedBallot( encrypted_ballot.object_id, encrypted_ballot.ballot_style, encrypted_ballot.description_hash, encrypted_ballot.previous_tracking_hash, encrypted_ballot.contests, encrypted_ballot.tracking_hash, encrypted_ballot.timestamp, ) data_spoiled.state = BallotBoxState.SPOILED self.assertIsNone(subject.get("cast")) self.assertIsNone(subject.get("spoiled")) # try to set a ballot with an unknown state self.assertFalse( subject.set( "unknown", CiphertextAcceptedBallot( encrypted_ballot.object_id, encrypted_ballot.ballot_style, encrypted_ballot.description_hash, encrypted_ballot.previous_tracking_hash, encrypted_ballot.contests, encrypted_ballot.tracking_hash, encrypted_ballot.timestamp, ), )) # Act self.assertTrue(subject.set("cast", data_cast)) self.assertTrue(subject.set("spoiled", data_spoiled)) self.assertEqual(subject.get("cast"), data_cast) self.assertEqual(subject.get("spoiled"), data_spoiled) self.assertEqual(subject.exists("cast"), (True, data_cast)) self.assertEqual(subject.exists("spoiled"), (True, data_spoiled)) # test mutate state data_cast.state = BallotBoxState.UNKNOWN self.assertEqual(subject.exists("cast"), (False, data_cast)) # test remove self.assertTrue(subject.set("cast", None)) self.assertEqual(subject.exists("cast"), (False, None))
def test_cached(self, exp: int): plaintext = get_optional(int_to_q(exp)) exp_plaintext = g_pow_p(plaintext) plaintext_again = discrete_log(exp_plaintext) self.assertEqual(exp, plaintext_again)