예제 #1
0
    def create_single_dummy(self, pk, vid=None, counter=None, dummy=True):
        voting_key = pk
        infinity = voting_key.group.infinite()
        ts_key = self.pk

        dummy_vote = VoteVector(
            [elgamal.Ciphertext(infinity, infinity) for _ in range(self.nr_candidates)]
        )
        dummy_tag = elgamal.Ciphertext(
            ts_key.group.infinite(), ts_key.group.generator()
        )
        real_tag = elgamal.Ciphertext(ts_key.group.infinite(), ts_key.group.infinite())

        if vid and counter:
            encrypted_vid = ts_key.encrypt(vid)
            counter_elem = (counter - 1) * self.generator

            self.decrypted_vid.append(vid)
            self.decrypted_counter.append(counter_elem)

            encrypted_counter = ts_key.encrypt(counter_elem)
            self.counter_lookup_table[counter_elem] = counter - 1

            # Add deterministic tag
            encrypted_tag = dummy_tag if dummy else real_tag

            return (
                BallotBundle(
                    encrypted_vid, encrypted_counter, encrypted_tag, dummy_vote
                ),
                None,
                None,
            )
        else:
            vid = self.order.random() * self.generator
            counter = self.order.random()
            counter_elem = counter * self.generator
            self.counter_lookup_table[counter_elem] = counter

            self.decrypted_vid.append(vid)
            self.decrypted_counter.append(counter_elem)

            encrypted_vid = ts_key.encrypt(vid)
            encrypted_counter = ts_key.encrypt(counter_elem)
            encrypted_tag = dummy_tag

            return (
                BallotBundle(
                    encrypted_vid, encrypted_counter, encrypted_tag, dummy_vote
                ),
                vid,
                counter,
            )
예제 #2
0
def generate_ballots(pk,
                     vids,
                     counters,
                     single_vote=True,
                     revotes_fraction=0.0):
    G = pk.group
    infinity = pk.group.infinite()

    lookup_table = {}
    lookup_table[infinity] = 0

    vote = VoteVector([pk.encrypt(1 * G.generator())])

    ctxts = []
    nr_revotes = 0

    if single_vote:
        for i, vid in enumerate(vids):
            repr = counters[i] * pk.generator
            lookup_table[repr] = counters[i]
            ctxts.append(
                BallotBundle(
                    pk.encrypt(vid * G.generator()),
                    pk.encrypt(counters[i] * G.generator()),
                    elgamal.Ciphertext(infinity, infinity),
                    vote,
                ))

    else:
        # Since it doesn't matter, dump all revotes on the first voter
        # voter_revoted = np.random.randint(max_votes_added, size=len(vids))
        voter_revoted = np.zeros(len(vids), dtype=int)
        voter_revoted[0] = round(revotes_fraction * len(vids))
        nr_revotes = sum(voter_revoted)

        for i, vid in enumerate(vids):
            while voter_revoted[i] >= 0:
                repr = counters[i] * pk.generator
                lookup_table[repr] = counters[i]
                ctxts.append(
                    BallotBundle(
                        pk.encrypt(vid * G.generator()),
                        pk.encrypt(counters[i] * G.generator()),
                        elgamal.Ciphertext(infinity, infinity),
                        vote,
                    ))
                counters[i] += 1
                voter_revoted[i] -= 1
    return ctxts, lookup_table, nr_revotes
예제 #3
0
    def prepare_ctxts(ctxts, m, election_key):
        """
        Prepares the ctxts list to a compatible ctxts list for the format m * n for the given m, i.e. we append encrypted
        zeros (with randomization 0) till we reach a length of m * (ceil(len(ctxts) / m)
        """
        import math

        if len(ctxts) < m:
            raise ValueError(
                "Lengths of ciphertexts expected greater than value m.")
        n = math.ceil(len(ctxts) / m)

        if type(ctxts[0]) == elgamal.Ciphertext:
            group = ctxts[0].group
            zeros = [elgamal.Ciphertext(group.infinite(), group.infinite())
                     ] * (m * n - len(ctxts))

        elif type(ctxts[0]) == BallotBundle:
            # todo: attention, we are assuming all values in the BallotBundle come from the same group.
            nr_candidates = ctxts[0].vote.length
            group = ctxts[0].vid.group
            vid = group.order().random()
            counter = group.order().random()
            encrypted_vid = election_key.encrypt(vid * group.generator())
            encrypted_counter = election_key.encrypt(counter *
                                                     group.generator())
            encrypted_tag = election_key.encrypt(1 * group.generator())
            zeros = [
                BallotBundle(
                    encrypted_vid,
                    encrypted_counter,
                    encrypted_tag,
                    VoteVector([
                        elgamal.Ciphertext(group.infinite(), group.infinite())
                        for _ in range(nr_candidates)
                    ]),
                )
            ] * (m * n - len(ctxts))
        else:
            raise ValueError(
                "Unexpected type of ciphertexts. Expecting Ciphertext or BallotBundle, got {0}",
                type(ctxts[0]),
            )

        ctxts.extend(zeros)
        return ctxts, n
예제 #4
0
    def prepare_ctxts(self, ctxts, m, election_key):
        """
        Prepares the ctxts list to a compatible ctxts list for the format m * n for the given m, i.e. we append encrypted
        zeros (with randomization 0) till we reach a length of m * (ceil(len(ctxts) / m)
        """
        import math

        if len(ctxts) < m:
            raise ValueError("Lengths of ciphertexts expected greater than value m.")
        n = math.ceil(len(ctxts) / m)
        infinity = election_key.pk.group.infinite()

        dummies = []
        if type(ctxts[0]) == BallotBundle:
            # TODO: attention, we are assuming all values in the BallotBundle come from the same group.
            for _ in range(m * n - len(ctxts)):
                dummy, vid, counter = self.create_single_dummy(election_key, dummy=True)
                dummies.append(dummy)
        elif type(ctxts[0]) == VoteVector:
            # TODO: this should not be necessary anymore
            for _ in range(m * n - len(ctxts)):
                dummy_vote = VoteVector(
                    [
                        elgamal.Ciphertext(infinity, infinity)
                        for _ in range(self.nr_candidates)
                    ]
                )
                dummies.append(dummy_vote)
        elif type(ctxts[0]) == elgamal.Ciphertext:
            for _ in range(m * n - len(ctxts)):
                dummy_vote = elgamal.Ciphertext(infinity, infinity)
                dummies.append(dummy_vote)
        else:
            raise ValueError(
                "Unexpected type of ciphertexts. Expecting BallotBundle or VoteVector, got {0}",
                type(ctxts[0]),
            )

        ctxts.extend(dummies)
        return ctxts, n
예제 #5
0
    def verify(self):
        """Verify that the full tally proceedure has been correctly performed. Again, to avoid passing variables, we define
        everything that should be posted in the BB as a class variable. This should not be done in a proper implementation.

        todo: verify function should get the result of the filtering as input

        # Example:
        # >>> G = EcGroup()
        #     >>> key_pair = elgamal.KeyPair(G)
        #     >>> pk = key_pair.pk
        #     >>> m = 2
        #     >>> nr_candidates = 1
        #     >>> number_ballots = 10
        #     >>> security_param = 128
        #     >>> vids, counters = election_setup(G, number_ballots, security_param)
        #     >>> ctxts, counter_lookup_table, _ = generate_ballots(pk, vids, counters, nr_candidates, single_vote=False, revotes_fraction=0.4)
        #     >>>
        #     >>> tally_proof = Filter(key_pair, key_pair.pk, ctxts, m, counter_lookup_table)
        #     >>> tally_proof.verify()
        #     True

        """

        verification_start = time.process_time()

        # Step 2a + 2b: verify tags of dummy ballots
        # verify all dummies encrypt zero
        dummies_verif = []
        zero_vote = VoteVector(
            [self.election_key.encrypt(self.group.infinite(), 0)] * self.nr_candidates
        )
        dummy_tag = elgamal.Ciphertext(self.group.infinite(), self.group.generator())
        for dummies in self.dummies:
            dummies_verif.append(dummies.vote == zero_vote)
            # TODO: added this check, should be here, make sure this doesn't break things
            dummies_verif.append(dummies.tag == dummy_tag)
        dummies_time_verif = time.process_time()

        # Step 2c: Verify the shuffle proof
        ver_shuffle_proof = self.proof_of_shuffle.verify(
            self.com_pk, self.pk, self.ctxts, self.shuffled_ctxts
        )
        shuffle_time_ver_end = time.process_time()

        # Step 2d: Verify correctness of decryptions of vid and indexes
        proof_decryptions = []
        for index, entry in enumerate(self.decrypted_vid_index):
            proof_decryptions.append(
                entry[1].verify(self.reshaped_shuffled_ctxts[index].vid, entry[0])
            )
            proof_decryptions.append(
                entry[3].verify(self.reshaped_shuffled_ctxts[index].index, entry[2])
            )
        dec_time_ver = time.process_time()

        # Step 2e: Verify reencryption and grouping
        # MISISNG: verify should comput its own grouping, but ok
        # Verify correctness of reencryptions. Here we are verifying the reencryptions of each ciphertext corresponding
        # to a particular candidate.
        # TODO: are we sure this "corresponding to a particular candidate" is still correct?
        proof_reencryptions = []
        for index, proof in enumerate(self.reencryption_proofs):
            proof_reencryptions.append(
                proof[1].verify(
                    self.pk,
                    self.pk,
                    getattr(self.tags, str(self.decrypted_vid_index[proof[0]][0])),
                    self.reshaped_shuffled_ctxts[proof[0]].vote,
                )
            )
        reenc_time_ver = time.process_time()

        # Step 2f: Verify the final shuffle proof
        ver_final_shuffle_proof = self.final_proof_of_shuffle.verify(
            self.final_com_pk,
            self.pk,
            self.selected_votes_padded,
            self.selected_votes_shuffled,
        )
        final_shuffle_time = time.process_time()

        # Step 2g: Verify opening of dummy ballots before tallying
        # TODO: for now just recomputing ciphertexts
        for ind, rand in zip(
            self.revealed_dummy_indices, self.revealed_dummy_randomizers
        ):
            zero_vote = VoteVector(
                [self.election_key.encrypt(self.group.infinite(), rand)]
                * self.nr_candidates
            )
            # TODO: actually compare against something
        final_open_time = time.process_time()

        self.dummies_time_ver = dummies_time_verif - verification_start
        self.shufle_time_ver = shuffle_time_ver_end - dummies_time_verif
        self.dec_time_ver = dec_time_ver - shuffle_time_ver_end
        self.reenc_time_ver = reenc_time_ver - dec_time_ver
        self.final_shuffle_time_ver = final_shuffle_time - reenc_time_ver
        self.final_open_time_ver = final_open_time - final_shuffle_time

        return (
            ver_shuffle_proof
            and all(proof_decryptions)
            and all(proof_reencryptions)
            and all(dummies_verif)
            and ver_final_shuffle_proof
        )
예제 #6
0
    def ctxt_weighted_sum(list_ctxts, weights):
        """
        Function wsum applied to our object of ciphertexts
        Example:
            >>> G = EcGroup()
            >>> key_pair = elgamal.KeyPair(G)
            >>> pk = key_pair.pk
            >>> ctxts = [pk.encrypt((i) * G.generator()) for i in range(9)]
            >>> weights = [Bn.from_num(i) for i in range(9)]
            >>> function_sum = MultiExponantiation.ctxt_weighted_sum(ctxts, weights)
            >>> weighted_sum = prod([ctxts[i] ** weights[i] for i in range(9)])
            >>> function_sum == weighted_sum
            True

        """
        ctxt_type = type(list_ctxts[0])
        if ctxt_type == elgamal.Ciphertext:
            group = list_ctxts[0].group
            c1s = [ctxts.c1 for ctxts in list_ctxts]
            c2s = [ctxts.c2 for ctxts in list_ctxts]

            return elgamal.Ciphertext(group.wsum(weights, c1s),
                                      group.wsum(weights, c2s))

        elif ctxt_type == BallotBundle:
            # todo: again, we are assuming that all elements of BallotBundle come from the same group.
            group = list_ctxts[0].vid.group
            nr_candidates = list_ctxts[0].vote.length
            c1s_vid = []
            c2s_vid = []
            c1s_index = []
            c2s_index = []
            c1s_tag = []
            c2s_tag = []
            c1s_vote = [[] for _ in range(nr_candidates)]
            c2s_vote = [[] for _ in range(nr_candidates)]
            for ctxts in list_ctxts:
                c1s_vid.append(ctxts.vid.c1)
                c2s_vid.append(ctxts.vid.c2)

                c1s_index.append(ctxts.index.c1)
                c2s_index.append(ctxts.index.c2)

                c1s_tag.append(ctxts.tag.c1)
                c2s_tag.append(ctxts.tag.c2)

                candidates_c1 = ctxts.vote.c1()
                for a, b in zip(c1s_vote, candidates_c1):
                    a.extend(b)
                candidates_c2 = ctxts.vote.c2()
                for a, b in zip(c2s_vote, candidates_c2):
                    a.extend(b)

            return BallotBundle(
                elgamal.Ciphertext(group.wsum(weights, c1s_vid),
                                   group.wsum(weights, c2s_vid)),
                elgamal.Ciphertext(group.wsum(weights, c1s_index),
                                   group.wsum(weights, c2s_index)),
                elgamal.Ciphertext(group.wsum(weights, c1s_tag),
                                   group.wsum(weights, c2s_tag)),
                VoteVector([
                    elgamal.Ciphertext(
                        group.wsum(weights, c1s_votes),
                        group.wsum(weights, c2s_votes),
                    ) for c1s_votes, c2s_votes in zip(c1s_vote, c2s_vote)
                ]),
            )
        else:
            raise ValueError(
                "Unexpected type of ciphertexts. Expecting Ciphertext or BallotBundle, got {0}",
                type(ctxt_type),
            )
예제 #7
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)
        ])
예제 #8
0
    def verify(
        self,
        com_pk,
        pk,
        ciphertexts,
        exponantiated_reencrypted_product,
        exponents_commitment,
    ):
        """
        Verify multi-exponantiation argument.
        Example:

            >>> G = EcGroup()
            >>> com_pk = com.PublicKey(G, 3)
            >>> key_pair = elgamal.KeyPair(G)
            >>> pk = key_pair.pk
            >>> ctxts = [pk.encrypt((i) * G.generator()) for i in range(9)]
            >>> ctxts = [ctxts[i*3:(i+1)*3] for i in range(3)]
            >>> exponents = [2, 0, 1, 3, 5, 8, 6, 7, 4]
            >>> exponents_Bn = [Bn.from_num(i) for i in exponents]
            >>> exponents = [exponents_Bn[i * 3:(i + 1) * 3] for i in range(3)]
            >>> randomizers = [G.order().random() for _ in range(3)]
            >>>
            >>> reencryption_randomization = G.order().random()
            >>> product_ctxts = prod([MultiExponantiation.ctxt_weighted_sum(ctxts[i], exponents[i]) for i in range(3)])
            >>>
            >>> exponantiated_reencrypted_product = pk.encrypt(G.infinite(), reencryption_randomization) * product_ctxts
            >>>
            >>> commitment_permutation = [com_pk.commit(exponents[i], randomizers[i])[0] for i in range(3)]
            >>> proof = MultiExponantiation(com_pk, pk, ctxts, exponantiated_reencrypted_product, commitment_permutation, exponents, randomizers, reencryption_randomization)
            >>> proof.verify(com_pk, pk, ctxts, exponantiated_reencrypted_product, commitment_permutation)
            True

            >>> ctxts_fake = [pk.encrypt((i + 1) * G.generator()) for i in range(9)]
            >>> ctxts_fake = [ctxts_fake[i*3:(i+1)*3] for i in range(3)]
            >>> exponents = [2, 0, 1, 3, 5, 8, 6, 7, 4]
            >>> exponents_Bn = [Bn.from_num(i) for i in exponents]
            >>> exponents = [exponents_Bn[i * 3:(i + 1) * 3] for i in range(3)]
            >>> randomizers = [G.order().random() for _ in range(3)]
            >>>
            >>> reencryption_randomization = G.order().random()
            >>> product_ctxts = prod([MultiExponantiation.ctxt_weighted_sum(ctxts[i], exponents[i]) for i in range(3)])
            >>>
            >>> exponantiated_reencrypted_product = pk.encrypt(G.infinite(), reencryption_randomization) * product_ctxts
            >>>
            >>> commitment_permutation = [com_pk.commit(exponents[i], randomizers[i])[0] for i in range(3)]
            >>> proof = MultiExponantiation(com_pk, pk, ctxts_fake, exponantiated_reencrypted_product, commitment_permutation, exponents, randomizers, reencryption_randomization)
            >>> proof.verify(com_pk, pk, ctxts_fake, exponantiated_reencrypted_product, commitment_permutation)
            False


        """

        check1 = com_pk.group.check_point(self.announcementA.commitment)
        check2 = all([
            com_pk.group.check_point(self.announcementB[i].commitment)
            for i in range(self.m)
        ])
        if self.type == elgamal.Ciphertext:
            check3 = all([
                pk.group.check_point(self.announcement_reencryption[i].c1)
                and pk.group.check_point(self.announcement_reencryption[i].c2)
                for i in range(self.m * 2)
            ])
        elif self.type == BallotBundle:
            check3 = all([
                pk.group.check_point(self.announcement_reencryption[i].vid.c1)
                and pk.group.check_point(
                    self.announcement_reencryption[i].vid.c2)
                and pk.group.check_point(
                    self.announcement_reencryption[i].index.c1)
                and pk.group.check_point(
                    self.announcement_reencryption[i].index.c2) and
                pk.group.check_point(self.announcement_reencryption[i].tag.c1)
                and pk.group.check_point(
                    self.announcement_reencryption[i].tag.c2) and all([
                        pk.group.check_point(c1s[0])
                        for c1s in self.announcement_reencryption[i].vote.c1()
                    ]) and all([
                        pk.group.check_point(c2s[0])
                        for c2s in self.announcement_reencryption[i].vote.c2()
                    ]) for i in range(self.m * 2)
            ])
        else:
            raise ValueError(
                "Unexpected ciphertext type. Expected either 'Ciphertext' or 'BallotBundle'. Got {0})",
                self.type,
            )

        check4 = self.announcementB[self.m] == com_pk.commit_reduced([0], 1,
                                                                     0)[0]
        check5 = (self.announcement_reencryption[self.m] ==
                  exponantiated_reencrypted_product)

        exponents_product_A = [
            self.challenge.mod_pow(i, self.order)
            for i in range(1, self.m + 1)
        ]
        product_A = self.announcementA * self.comm_weighted_sum(
            exponents_commitment, exponents_product_A)
        check6 = (product_A == com_pk.commit(self.responseA,
                                             self.responseA_randomizers)[0])

        exponents_product_B = [
            self.challenge.mod_pow(i, self.order) for i in range(self.m * 2)
        ]
        product_B = self.comm_weighted_sum(self.announcementB,
                                           exponents_product_B)
        check7 = (product_B == com_pk.commit_reduced(
            [self.responseB], 1, self.responseB_randomizers)[0])

        exponents_product_E = [
            self.challenge.mod_pow(i, self.order) for i in range(self.m * 2)
        ]
        product_E = self.ctxt_weighted_sum(self.announcement_reencryption,
                                           exponents_product_E)

        encryption_responseB = pk.encrypt(
            self.responseB * self.G, self.response_reencryption_randomisers)
        reencryption_value = (BallotBundle(
            encryption_responseB,
            encryption_responseB,
            encryption_responseB,
            VoteVector(
                [encryption_responseB for _ in range(self.nr_candidates)]),
        ) if self.type != elgamal.Ciphertext else encryption_responseB)

        verification_product_E = reencryption_value * prod([
            self.ctxt_weighted_sum(
                ciphertexts[i],
                [(self.challenge.mod_pow(self.m - (i + 1), self.order)) *
                 self.responseA[j] for j in range(self.n)],
            ) for i in range(self.m)
        ])

        check8 = product_E == verification_product_E

        return all(
            [check1, check2, check3, check4, check5, check6, check7, check8])
예제 #9
0
    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,
            )