def create_proof(self, commitments, challenge=None): # Challenge ( commitment_pk, commitment_token, commitment_vote1, commitment_vote2, ) = commitments if challenge: proof_challenge = challenge else: proof_challenge = compute_challenge( [commitment_pk] + [commitment_token] + commitment_vote1.tolist() + commitment_vote2.tolist() + self.reencrypted_vote.tolist() + self.token.tolist(), self.hash_reduction, ) # Response response_sk = self.hiding_sk + self.sk_tally * proof_challenge response_randomizer = self.hiding_randomizer + self.randomizer * proof_challenge return DummyVoterReencryptionProof(commitments, [response_sk, response_randomizer], challenge=proof_challenge)
def verify(self, pk_tally, pk_vote, tokens, vote): """ todo: reencryption votes should be an input of the verify function Example: >>> G = EcGroup() >>> kp_tally = elgamal.KeyPair(G) >>> pk_tally = kp_tally.pk >>> kp_vote = elgamal.KeyPair(G) >>> pk_vote = kp_vote.pk >>> token = [pk_tally.encrypt(1 * G.generator()), pk_tally.encrypt(0 * G.generator())] >>> vote = VoteVector([pk_vote.encrypt(1 * G.generator())] * 2) >>> proof = ReencryptionProof(kp_tally, pk_vote, token, vote) >>> proof.verify(pk_tally, pk_vote, token, vote) True Now we try for a token of 1 * generator, and vote of zero >>> token = [pk_tally.encrypt(1 * G.generator()), pk_tally.encrypt(1 * G.generator()), pk_tally.encrypt(1 * G.generator())] >>> vote = VoteVector([pk_vote.encrypt(0 * G.generator())] * 2) >>> proof = ReencryptionProof(kp_tally, pk_vote, token, vote) >>> proof.verify(pk_tally, pk_vote, token, vote) True """ # Challenge big proof self.main_challenge = compute_challenge( [self.proof_real.commitment_pk] + [self.proof_real.commitment_token] + self.proof_real.commitment_vote1.tolist() + self.proof_real.commitment_vote2.tolist() + [ self.proof_dummy.commitment_pk, self.proof_dummy.commitment_token ] + self.proof_dummy.commitment_vote1.tolist() + self.proof_dummy.commitment_vote2.tolist() + self.vote.tolist() + self.added_token.tolist() + [self.pk_vote.pk] + [self.pk_tally.pk], self.hash_reduction, ) verify_challenges = ( self.main_challenge == self.proof_dummy.challenge + self.proof_real.challenge) verify_real = self.proof_real.verify(pk_tally, pk_vote, self.max_tag, vote, self.reencrypted_vote) verify_dummy = self.proof_dummy.verify(pk_tally, pk_vote, self.added_token, self.reencrypted_vote, len(tokens)) return verify_challenges and verify_real and verify_dummy
def __init__(self, ciphertext, plaintext, kp): self.group = kp.group self.infinity = self.group.infinite() self.order = self.group.order() self.pk = kp.pk self.generator = self.group.generator() random_announcement = self.order.random() self.announcement = random_announcement * ciphertext.c1 challenge = compute_challenge( ciphertext.tolist() + [plaintext] + [self.announcement] + [self.order], self.order, ) self.response = random_announcement + challenge * kp.sk
def verify(self, ciphertext, plaintext): """Verify proof Example: >>> G = EcGroup() >>> kp = elgamal.KeyPair(G) >>> msg = 20 * G.generator() >>> ctxt = kp.pk.encrypt(msg) >>> msg_recovered = ctxt.decrypt(kp.sk) >>> proof = CorrectDecryption(ctxt, msg_recovered, kp) >>> proof.verify(ctxt, msg_recovered) True """ challenge = compute_challenge( ciphertext.tolist() + [plaintext] + [self.announcement] + [self.order], self.order, ) return (challenge * plaintext - self.announcement == challenge * ciphertext.c2 - self.response * ciphertext.c1)
def __init__(self, com_pk, commitment, product, committed_values, randomizer): self.n = len(committed_values) self.order = com_pk.group.order() self.sec_param_l_e = 160 self.sec_param_l_s = 80 self.bn_two = Bn.from_num(2) # Prepare announcement products = [committed_values[0]] for i in range(1, self.n): products.append( (products[i - 1] * committed_values[i]).mod(self.order)) commitment_rand_one = self.order.random() commitment_rand_two = self.order.random() commitment_rand_three = self.order.random() d_randoms = [self.order.random() for _ in range(self.n)] delta_randoms = [self.order.random() for _ in range(self.n)] delta_randoms[0] = d_randoms[0] delta_randoms[-1] = 0 value_to_commit_two = [ -delta_randoms[i] * d_randoms[i + 1] for i in range(self.n - 1) ] value_to_commit_three = [ delta_randoms[i + 1] - committed_values[i + 1] * delta_randoms[i] - products[i] * d_randoms[i + 1] for i in range(self.n - 1) ] self.announcement_one, _ = com_pk.commit(d_randoms, commitment_rand_one) self.announcement_two, _ = com_pk.commit_reduced( value_to_commit_two, self.n - 1, commitment_rand_two) self.announcement_three, _ = com_pk.commit_reduced( value_to_commit_three, self.n - 1, commitment_rand_three) # Compute challenge [Verify validity of this] self.challenge = compute_challenge( [ commitment, product, self.announcement_one, self.announcement_two, self.announcement_three, ], self.order, ) # Compute response self.response_committed_values = [ (self.challenge * committed_values[i] + d_randoms[i]).mod( self.order) for i in range(self.n) ] self.response_product = [ (self.challenge * products[i] + delta_randoms[i]).mod(self.order) for i in range(self.n) ] self.response_randomizer = (self.challenge * randomizer + commitment_rand_one).mod(self.order) self.response_randomizer_commitments = ( self.challenge * commitment_rand_three + commitment_rand_two).mod( self.order)
def __init__(self, com_pk, commitment_A, commitment_b, A, random_comm_A, random_comm_b): self.order = com_pk.group.order() self.m = len(A) self.n = len(A[0]) # Prepare announcement vectors_b = [A[0]] for i in range(1, self.m): vectors_b.append([(first * second).mod(self.order) for first, second in zip(vectors_b[i - 1], A[i]) ]) random_comm_announcement = [self.order.random() for _ in range(self.m)] self.announcement_b = [ com_pk.commit(vectors_b[i], random_comm_announcement[i])[0] for i in range(self.m) ] random_comm_announcement[0] = random_comm_A[0] random_comm_announcement[self.m - 1] = random_comm_b self.announcement_b[0] = commitment_A[0] self.announcement_b[self.m - 1] = commitment_b # Compute challenges. One challenge is used for the constant of the bilinear map. # todo: attention to the transcript. Change it self.challenge = compute_challenge(self.announcement_b, self.order) transcript_bilinear = self.announcement_b transcript_bilinear.append(self.challenge) self.challenge_bilinear = compute_challenge(transcript_bilinear, self.order) # Engage in the Zero argument proof opening_vectors_commitments_D = [[ (self.challenge.mod_pow(i, self.order) * vectors_b[i][j]).mod( self.order) for j in range(self.n) ] for i in range(self.m - 1)] random_vectors_commitments_D = [ (self.challenge.mod_pow(i, self.order) * random_comm_announcement[i]).mod(self.order) for i in range(self.m - 1) ] modified_vectors_b = [[(self.challenge.mod_pow(i, self.order) * vectors_b[i + 1][j]).mod(self.order) for j in range(self.n)] for i in range(self.m - 1)] opening_value_commitment_D = [ modular_sum(x, self.order) for x in zip(*modified_vectors_b[:self.m - 1]) ] random_value_commitment_D = modular_sum( [(self.challenge.mod_pow(i, self.order) * random_comm_announcement[i + 1]).mod(self.order) for i in range(self.m - 1)], self.order, ) zero_argument_A = A[1:] zero_argument_A.append([-1] * self.n) zero_argument_B = opening_vectors_commitments_D zero_argument_B.append(opening_value_commitment_D) zero_argument_random_A = random_comm_A[1:] zero_argument_random_A.append(0) zero_argument_random_B = random_vectors_commitments_D zero_argument_random_B.append(random_value_commitment_D) self.zero_argument_proof = ZeroArgument( com_pk, zero_argument_A, zero_argument_B, zero_argument_random_A, zero_argument_random_B, self.challenge_bilinear, )
def __init__( self, com_pk, A, B, random_comm_A, random_comm_B, bilinear_const=Bn.from_decimal("1"), ): """ :param com_pk: Commitment key :param commitment_A: Commitment of A :param commitment_B: Commitment of B :param A: Matrix formed by rows a_i :param B: Matrix formed by rows b_i :param random_comm_A vector of random values used for commitment_A :param random_comm_B: vector of random values used for commitment_B """ self.order = com_pk.group.order() self.m = len(A) self.n = len(A[0]) self.bilinear_const = bilinear_const # Prepare announcement A.insert(0, [self.order.random() for _ in range(self.n)]) B.append([self.order.random() for _ in range(self.n)]) random_comm_A.insert(0, self.order.random()) random_comm_B.append(self.order.random()) self.announcement_a0, _ = com_pk.commit_reduced( A[0], self.n, random_comm_A[0]) self.announcement_bm, _ = com_pk.commit_reduced( B[-1], self.n, random_comm_B[-1]) diagonals = [] for k in range(2 * self.m + 1): diagonal = 0 for i in range(self.m + 1): j = self.m - k + i if j < 0: continue if j > self.m: break diagonal += (self.bilinear_map(A[i], B[j], self.bilinear_const, self.order)).mod(self.order) diagonals.append(diagonal) commitment_rand_diagonals = [ self.order.random() for _ in range(2 * self.m + 1) ] commitment_rand_diagonals[self.m + 1] = 0 self.announcement_diagonals = [ com_pk.commit_reduced([diagonals[i]], 1, commitment_rand_diagonals[i])[0] for i in range(self.m * 2 + 1) ] # Prepare challenge (for the moment we only put two announcements, as I yet need to determine how to deal with # the matrices. Maybe I form a class, maybe not. Once decided, I'll add them here (same for announcement of # diagonals). self.challenge = compute_challenge( [self.announcement_a0, self.announcement_bm], self.order) # Compute the response A_modified = [[ (A[j][i] * (self.challenge.mod_pow(j, self.order))).mod(self.order) for i in range(self.n) ] for j in range(self.m + 1)] self.response_as = [ modular_sum(x, self.order) for x in zip(*A_modified[:self.m + 1]) ] self.response_randomizer_A = modular_sum( [(self.challenge.mod_pow(i, self.order) * random_comm_A[i]).mod( self.order) for i in range(self.m + 1)], self.order, ) B_modified = [[ B[j][i] * (self.challenge.mod_pow(self.m - j, self.order)) for i in range(self.n) ] for j in range(self.m + 1)] self.response_bs = [ modular_sum(x, self.order) for x in zip(*B_modified[:self.m + 1]) ] self.response_randomizer_B = modular_sum( [(self.challenge.mod_pow(self.m - i, self.order) * random_comm_B[i]).mod(self.order) for i in range(self.m + 1)], self.order, ) self.response_randomizer_diagonals = modular_sum( [(self.challenge.mod_pow(i, self.order) * commitment_rand_diagonals[i]).mod(self.order) for i in range(self.m * 2 + 1)], self.order, )
def __init__( self, com_pk, polynomial_list, commitment_to_eval, commitment_eval, value_to_eval, value_eval, random_to_eval, random_eval, ): """ Generate proof of correct polynomial evaluation. We follow the construction by Bayer and Groth in 'Zero-knowledge Argument for Polynomial Evaluation with Application to Blacklists'. The first step is to transform the polynomial to binary representation. See paper for a further explanation. Note that here we are working with cyclic groups over elliptic curves. This code should be changed if we want to use it over finite fields. :param polynomial_list: Polynomial to evaluate :param commitment_to_eval: Commitment of the value where we evaluate the polynomial :param commitment_eval: Commitment of the result of evaluating polynomial :param value_to_eval: Value where we evaluate the polynomial :param value_eval: Result of evaluating the polynomial :param random_to_eval: Random used for the commitment of value_to_eval :param random_eval: Random used for the commitment of value_eval """ self.group = com_pk.group self.order = com_pk.order self.degree = int( math.pow(2, math.ceil(math.log(len(polynomial_list) - 1, 2)))) self.bit_length = int(math.log(self.degree, 2)) # Pad with zeros the polynomial to size a power of two padded_zeros = [0] * (self.degree - len(polynomial_list)) polynomial_list.extend(padded_zeros) self.polynomial = polynomial_list random_commitments = [ self.order.random() for _ in range(self.bit_length) ] self.commitments = [ com_pk.commit( [value_to_eval.mod_pow(int(math.pow(2, i + 1)), self.order)], random_commitments[i], )[0] for i in range(self.bit_length) ] random_commitments.insert(0, random_to_eval) self.commitments.insert(0, commitment_to_eval) random_commitments_hidden = [ self.order.random() for _ in range(self.bit_length + 1) ] random_hidden = [ self.order.random() for _ in range(self.bit_length + 1) ] self.commitments_hidden = [ com_pk.commit([a], b)[0] for a, b in zip(random_hidden, random_commitments_hidden) ] deltas = self.hidden_polynomial_computation(value_to_eval, random_hidden) random_commitments_deltas = [ self.order.random() for _ in range(self.bit_length + 1) ] self.commitments_deltas = [ com_pk.commit([a], b)[0] for a, b in zip(deltas, random_commitments_deltas) ] random_commitments_exponantiations = [ self.order.random() for _ in range(self.bit_length) ] self.commitments_exponantiations = [ com_pk.commit( [random_hidden[i] * (value_to_eval**(2**i))], random_commitments_exponantiations[i], )[0] for i in range(self.bit_length) ] # Compute challenge self.challenge = compute_challenge( self.commitments + self.commitments_deltas + self.commitments_exponantiations + self.commitments_hidden, self.order, ) # Response self.response_random_hidden = [ self.challenge * value_to_eval**(2**i) + random_hidden[i] for i in range(self.bit_length + 1) ] self.response_random_commitments = [ self.challenge * random_commitments[i] + random_commitments_hidden[i] for i in range(self.bit_length + 1) ] self.response_random_deltas = self.challenge.mod_pow( self.bit_length + 1, self.order) * random_eval + sum([ random_commitments_deltas[i] * self.challenge.mod_pow(i, self.order) for i in range(self.bit_length + 1) ]) self.response_random_exponantiations = [ self.challenge * random_commitments[i + 1] - self.response_random_hidden[i] * random_commitments[i] + random_commitments_exponantiations[i] for i in range(self.bit_length) ]
def __init__(self, com_pk, pk, ciphertexts, shuffled_ciphertexts, permutation, randomizers): self.order = com_pk.group.order() self.m = len(ciphertexts) try: self.n = len(ciphertexts[0]) except TypeError: raise ValueError( "Must reshape ciphertext list to shape m*n. Use functions prepare_ctxts and reshape_m_n." ) if self.n != com_pk.n: raise RuntimeError( "Incorrect length of commitment key length. Input {} expected {}" .format(com_pk.n, self.n)) if (self.m != len(shuffled_ciphertexts) or self.m != len(permutation) or self.m != len(randomizers) or self.n != len(shuffled_ciphertexts[0]) or self.n != len(permutation[0]) or self.n != len(randomizers[0])): raise ValueError( "Shape of ciphertexts, shuffled_ciphertexts, permutation and randomizers must be equal." ) # Prepare announcement randomizers_permutation_comm = [ self.order.random() for _ in range(self.m) ] self.permutation_comm = [ com_pk.commit(permutation[i], randomizers_permutation_comm[i])[0] for i in range(self.m) ] # Compute challenge self.challenge1 = compute_challenge(self.permutation_comm, self.order) # Prepare response randomizers_exp_permutation_comm = [ self.order.random() for _ in range(self.m) ] exp_challenge_pem = [[ self.challenge1.mod_pow(permutation[i][j], self.order) for j in range(self.n) ] for i in range(self.m)] self.exp_permutation_comm = [ com_pk.commit(exp_challenge_pem[i], randomizers_exp_permutation_comm[i])[0] for i in range(self.m) ] # Compute challenges self.challenge2 = compute_challenge( self.permutation_comm + self.exp_permutation_comm, self.order) self.challenge3 = compute_challenge([self.challenge1, self.challenge2], self.order) # Final response commitment_neg_challenge3 = [ com_pk.commit([-self.challenge3] * self.n, 0)[0] for _ in range(self.m) ] commitment_D = [(self.permutation_comm[i]**self.challenge2) * self.exp_permutation_comm[i] for i in range(self.m)] openings_commitment_D = [[(self.challenge2 * permutation[i][j] + exp_challenge_pem[i][j]).mod(self.order) for j in range(self.n)] for i in range(self.m)] randomizers_commitment_D = [ (self.challenge2 * randomizers_permutation_comm[i] + randomizers_exp_permutation_comm[i]).mod(self.order) for i in range(self.m) ] product = (self.challenge2 * 0 + self.challenge1.mod_pow(0, self.order) - self.challenge3) for i in range(1, self.m * self.n): product = (product * self.challenge2 * i + self.challenge1.mod_pow(i, self.order) - self.challenge3).mod(self.order) # Now we start by engaging in the product argument. # We define the matrix A to prove the product argument matrix_A = [[ (openings_commitment_D[i][j] - self.challenge3).mod(self.order) for j in range(self.n) ] for i in range(self.m)] commitment_A = [ commitment_D[i] * commitment_neg_challenge3[i] for i in range(self.m) ] self.product_argument_proof = ProductArgument( com_pk, commitment_A, product, matrix_A, randomizers_commitment_D) # Prepare the statements and witnesses of multiexponantiation argument. reencryption_randomizers = sum([ (-randomizers[i][j] * exp_challenge_pem[i][j]).mod(self.order) for i in range(self.m) for j in range(self.n) ]).mod(self.order) challenge_powers = [ self.challenge1.mod_pow(i, self.order) for i in range(1, self.m * self.n + 1) ] ciphertexts_exponantiated = MultiExponantiation.ctxt_weighted_sum( sum(ciphertexts, []), challenge_powers) self.multi_exponantiation_argument = MultiExponantiation( com_pk, pk, shuffled_ciphertexts, ciphertexts_exponantiated, self.exp_permutation_comm, exp_challenge_pem, randomizers_exp_permutation_comm, reencryption_randomizers, )
def __init__( self, com_pk, pk, ciphertexts, exponantiated_reencrypted_product, exponents_commitment, exponents, commitment_randomizer, reencrypted_randomizer, ): """Shuffle works for both ciphertext of type Ciphertext, or ciphertexts of type BallotBundle""" self.order = com_pk.group.order() self.infinity = pk.group.infinite() self.m = len(ciphertexts) self.n = len(ciphertexts[0]) self.G = pk.generator self.type = type(ciphertexts[0][0]) # If entry is a ballot bundle, then calculate the number of ciphertexts if self.type == BallotBundle: self.nr_candidates = ciphertexts[0][0].vote.length else: self.nr_candidates = None # Prepare announcement announcementA_values = [self.order.random() for _ in range(self.n)] announcementA_randomiser = self.order.random() exponents.insert(0, announcementA_values) commitment_randomizer.insert(0, announcementA_randomiser) announcementB_values = [self.order.random() for _ in range(2 * self.m)] announcementB_randomisers = [ self.order.random() for _ in range(2 * self.m) ] announcement_reencryption_randomisers = [ self.order.random() for _ in range(2 * self.m) ] announcementB_values[self.m] = 0 announcementB_randomisers[self.m] = 0 announcement_reencryption_randomisers[self.m] = reencrypted_randomizer self.announcementA = com_pk.commit(announcementA_values, announcementA_randomiser)[0] self.announcementB = [ com_pk.commit_reduced([announcementB_values[i]], 1, announcementB_randomisers[i])[0] for i in range(2 * self.m) ] diagonals = [] for k in range(2 * self.m): # Initiate diagonal as the zero BallotBundle diagonal = (BallotBundle( elgamal.Ciphertext(self.infinity, self.infinity), elgamal.Ciphertext(self.infinity, self.infinity), elgamal.Ciphertext(self.infinity, self.infinity), VoteVector([ elgamal.Ciphertext(self.infinity, self.infinity) for _ in range(self.nr_candidates) ]), ) if self.type != elgamal.Ciphertext else elgamal.Ciphertext( self.infinity, self.infinity)) for i in range(self.m): j = k - self.m + i + 1 if j < 0: continue if j > self.m: break diagonal *= self.ctxt_weighted_sum(ciphertexts[i], exponents[j]) diagonals.append(diagonal) # We begin with additive notation for the public keys if self.type == elgamal.Ciphertext: self.announcement_reencryption = [ pk.encrypt( announcementB_values[i] * self.G, announcement_reencryption_randomisers[i], ) * diagonals[i] for i in range(2 * self.m) ] elif self.type == BallotBundle: self.announcement_reencryption = [ BallotBundle( pk.encrypt( announcementB_values[i] * self.G, announcement_reencryption_randomisers[i], ), pk.encrypt( announcementB_values[i] * self.G, announcement_reencryption_randomisers[i], ), pk.encrypt( announcementB_values[i] * self.G, announcement_reencryption_randomisers[i], ), VoteVector([ pk.encrypt( announcementB_values[i] * self.G, announcement_reencryption_randomisers[i], ) for _ in range(self.nr_candidates) ]), ) * diagonals[i] for i in range(2 * self.m) ] else: raise ValueError( "Unexpected type of ciphertexts. Expecting Ciphertext or BallotBundle, got {0}", type(self.type), ) # Compute challenge # todo: change challenge self.challenge = compute_challenge( self.announcementB + [self.announcementA], self.order) # Prepare response challenge_powers = [ self.challenge.mod_pow(i, self.order) for i in range(self.m + 1) ] self.responseA = [ sum([ exponents[j][i] * challenge_powers[j] for j in range(self.m + 1) ]) for i in range(self.n) ] self.responseA_randomizers = sum([ commitment_randomizer[i] * challenge_powers[i] for i in range(self.m + 1) ]) self.responseB = sum([ announcementB_values[i] * (self.challenge.mod_pow(i, self.order)) for i in range(self.m * 2) ]) self.responseB_randomizers = sum([ announcementB_randomisers[i] * (self.challenge.mod_pow(i, self.order)) for i in range(self.m * 2) ]) self.response_reencryption_randomisers = sum([ announcement_reencryption_randomisers[i] * (self.challenge.mod_pow(i, self.order)) for i in range(self.m * 2) ])
def verify(self, pk_tally, pk_vote, token, reencrypted_vote, number_dummies): """Verify a proof of reencryption for dummy voter Example: >>> G = EcGroup() >>> kp_tally = elgamal.KeyPair(G) >>> pk_tally = kp_tally.pk >>> kp_vote = elgamal.KeyPair(G) >>> pk_vote = kp_vote.pk >>> number_dummies = G.order().random() >>> added_tokens = pk_tally.encrypt(number_dummies * G.generator()) >>> randomizer = G.order().random() >>> reencrypted_vote = VoteVector([pk_vote.encrypt(0 * G.generator(), randomizer)] * 2) >>> prover = DummyVoterReencryptionProver(kp_tally, pk_vote, added_tokens, reencrypted_vote, randomizer, number_dummies) >>> commitments = prover.commit() >>> proof = prover.create_proof(commitments) >>> proof.verify(pk_tally, pk_vote, added_tokens, reencrypted_vote, number_dummies) True # Cases where should not hold >>> proof.verify(pk_tally, pk_vote, added_tokens, VoteVector([pk_tally.encrypt(7 * G.generator(), randomizer)] * 2), number_dummies) False >>> proof.verify(pk_tally, pk_vote, pk_tally.encrypt(4 * G.generator()), reencrypted_vote, number_dummies) False # Here the token is an encryption of 0 >>> added_tokens = pk_tally.encrypt(7 * G.generator()) >>> prover = DummyVoterReencryptionProver(kp_tally, pk_vote, added_tokens, reencrypted_vote, randomizer, number_dummies) >>> commitments = prover.commit() >>> proof = prover.create_proof(commitments) >>> proof.verify(pk_tally, pk_vote, added_tokens, reencrypted_vote, number_dummies) False # Checking that the simulation works >>> proof = prover.simulate() >>> proof.verify(pk_tally, pk_vote, added_tokens, reencrypted_vote, number_dummies) True # Token is an encryption of 0 >>> added_tokens = pk_tally.encrypt(0 * G.generator()) >>> vote = VoteVector([pk_vote.encrypt(1 * G.generator())] * 2) >>> reencrypted_vote = pk_vote.reencrypt(vote,randomizer) >>> prover = DummyVoterReencryptionProver(kp_tally, pk_vote, added_tokens, reencrypted_vote, randomizer, number_dummies) >>> proof = prover.simulate() >>> proof.verify(pk_tally, pk_vote, added_tokens, reencrypted_vote, number_dummies) True # Here the 'reencryption' is an encryption of a different value >>> added_tokens = pk_tally.encrypt(number_dummies * G.generator()) >>> vote = VoteVector([pk_vote.encrypt(1 * G.generator())] * 2) >>> reencrypted_vote = VoteVector([pk_vote.encrypt(7 * G.generator())] * 2) >>> prover = DummyVoterReencryptionProver(kp_tally, pk_vote, added_tokens, reencrypted_vote, randomizer, number_dummies) >>> proof = prover.simulate() >>> proof.verify(pk_tally, pk_vote, added_tokens, reencrypted_vote, number_dummies) True """ nr_candidates = reencrypted_vote.length if self.challenge is None: self.challenge = compute_challenge( [self.commitment_pk] + [self.commitment_token] + [self.commitment_vote1, self.commitment_vote2] + reencrypted_vote.tolist() + token.tolist(), self.hash_reduction, ) check_pk = (self.response_sk * pk_tally.generator == self.commitment_pk + self.challenge * pk_tally.pk) check_token = (self.response_sk * token.c1 == self.commitment_token + self.challenge * (token.c2 - number_dummies * pk_tally.generator)) check_reencryption1 = ( PointVector([self.response_randomizer * pk_vote.generator] * nr_candidates) == self.commitment_vote1 * reencrypted_vote.c1(pointvector=True)**self.challenge) check_reencryption2 = ( PointVector([self.response_randomizer * pk_vote.pk] * nr_candidates) == self.commitment_vote2 * (reencrypted_vote.c2(pointvector=True) / PointVector( [0 * pk_vote.generator] * nr_candidates))**self.challenge) return check_pk and check_token and check_reencryption1 and check_reencryption2
def __init__(self, kp_tally, pk_vote, tokens, vote, max_tag=None): # Security parameters self.sec_param = 256 self.bn_two = Bn.from_num(2) self.hash_reduction = self.bn_two.pow(self.sec_param) self.pk_tally = kp_tally.pk self.group_tally = self.pk_tally.group self.order_tally = self.group_tally.order() self.pk_vote = pk_vote self.tokens = tokens if max_tag: self.max_tag = max_tag else: self.max_tag = self.tokens[-1] self.dummy = self.max_tag.decrypt( kp_tally.sk) == 1 * kp_tally.group.generator() self.vote = vote self.number_candidates = self.vote.length self.number_dummies = len(tokens) last_token_decryption = self.tokens[-1].decrypt(kp_tally.sk) self.added_token = tokens[0] for token in tokens[1:]: self.added_token = self.added_token * token if type(self.vote) != VoteVector: raise ValueError("Expected vote type of VoteVector") # reencryption of vote randomizer_reencryption = self.order_tally.random() if self.dummy: self.reencrypted_vote = VoteVector([ pk_vote.encrypt(0 * kp_tally.group.generator(), randomizer_reencryption) ] * self.number_candidates) else: self.reencrypted_vote = pk_vote.reencrypt(vote, randomizer_reencryption) # Prover for dummy prover_dummy = DummyVoterReencryptionProver( kp_tally, pk_vote, self.added_token, self.reencrypted_vote, randomizer_reencryption, self.number_dummies, ) # Prover for real prover_real = RealVoterReencryptionProver( kp_tally, pk_vote, self.max_tag, vote, self.reencrypted_vote, randomizer_reencryption, ) # If the voter is real if last_token_decryption == 0 * self.group_tally.generator(): self.proof_dummy = prover_dummy.simulate() commitment_pk, commitment_token, commitment_vote1, commitment_vote2 = list( prover_real.commit()) # Challenge big proof self.main_challenge = compute_challenge( [commitment_pk] + [commitment_token] + commitment_vote1.tolist() + commitment_vote2.tolist() + [self.proof_dummy.commitment_pk] + [self.proof_dummy.commitment_token] + self.proof_dummy.commitment_vote1.tolist() + self.proof_dummy.commitment_vote2.tolist() + self.vote.tolist() + self.added_token.tolist() + [self.pk_vote.pk] + [self.pk_tally.pk], self.hash_reduction, ) self.true_challenge = self.main_challenge - self.proof_dummy.challenge self.proof_real = prover_real.create_proof( [ commitment_pk, commitment_token, commitment_vote1, commitment_vote2 ], self.true_challenge, ) # If the voter is dummy else: self.proof_real = prover_real.simulate() commitment_pk, commitment_token, commitment_vote1, commitment_vote2 = list( prover_dummy.commit()) # Challenge big proof self.main_challenge = compute_challenge( [self.proof_real.commitment_pk] + [self.proof_real.commitment_token] + self.proof_real.commitment_vote1.tolist() + self.proof_real.commitment_vote2.tolist() + [commitment_pk] + [commitment_token] + commitment_vote1.tolist() + commitment_vote2.tolist() + self.vote.tolist() + self.added_token.tolist() + [self.pk_vote.pk] + [self.pk_tally.pk], self.hash_reduction, ) self.true_challenge = self.main_challenge - self.proof_real.challenge self.proof_dummy = prover_dummy.create_proof( [ commitment_pk, commitment_token, commitment_vote1, commitment_vote2 ], self.true_challenge, )