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
Example #3
0
    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
Example #4
0
    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,
        )
Example #8
0
    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)
        ]
Example #9
0
    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,
        )
Example #10
0
    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,
            )