Exemplo n.º 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,
            )
Exemplo n.º 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
Exemplo n.º 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
Exemplo 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
Exemplo n.º 5
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)

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

        else:
            raise ValueError(
                "Unexpected type of ciphertexts. Expecting Ciphertext, got {0}"
                .format(type(ctxts[0])))

        ctxts.extend(zeros)
        return ctxts, n
Exemplo n.º 6
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
        )
Exemplo n.º 7
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),
            )
Exemplo n.º 8
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)
        ])