def __check_cp_proof_zero_proof(self, pad: int, data: int, zero_pad: int,
                                    zero_data: int, zero_chal: int,
                                    zero_res: int) -> bool:
        """
        check if Chaum-Pedersen proof zero proof(given challenge c0, response v0) is satisfied.

        To proof the zero proof, two equations g ^ v0 = a0 * alpha ^ c0 mod p, K ^ v0 = b0 * beta ^ c0 mod p
        have to be satisfied.
        In the verification process, the challenge c of a selection is allowed to be broken into two components
        in any way as long as c = (c0 + c1) mod p, c0 here is the first component broken from c.

        :param pad: alpha of a selection
        :param data: beta of a selection
        :param zero_pad: zero_pad of a selection
        :param zero_data: zero_data of a selection
        :param zero_chal: zero_challenge of a selection
        :param zero_res: zero_response of a selection
        :return: True if both equations of the zero proof are satisfied, False if either is not satisfied
        """
        equ1_left = pow(self.generator, zero_res, self.large_prime)
        equ1_right = number.mod_p(
            int(zero_pad) * pow(pad, zero_chal, self.large_prime))

        equ2_left = pow(self.public_key, zero_res, self.large_prime)
        equ2_right = number.mod_p(
            int(zero_data) * pow(data, zero_chal, self.large_prime))

        res = number.equals(equ1_left, equ1_right) and number.equals(
            equ2_left, equ2_right)

        if not res:
            print("Chaum-pedersen proof zero proof failure. ")

        return res
예제 #2
0
    def verify_all_params(self) -> bool:
        """
        verify all parameters including p, q, r, g
        :return: True if all parameters are verified to fit in designated equations or have specific values,
                False otherwise
        """
        error = self.initialize_error()

        # check if p and q are the expected values
        if not number.equals(self.large_prime, self.LARGE_PRIME_EXPECTED):
            # if not, use Miller-Rabin algorithm to check the primality of p and q, 5 iterations by default
            if not number.is_prime(self.large_prime):
                error = self.set_error()
                print("Large prime value error. ")

        if not number.equals(self.small_prime, self.SMALL_PRIME_EXPECTED):
            if not number.is_prime(self.small_prime):
                error = self.set_error()
                print("Small prime value error. ")

        # get basic parameters
        cofactor = self.param_g.get_cofactor()

        # check equation p - 1 = qr
        if not number.equals(self.large_prime - 1, self.small_prime * cofactor):
            error = self.set_error()
            print("p - 1 does not equals to r * q.")

        # check q is not a divisor of r
        if number.is_divisor(self.small_prime, cofactor):
            error = self.set_error()
            print("q is a divisor of r.")

        # check 1 < g < p
        if not number.is_within_range(self.generator, 1, self.large_prime):
            error = self.set_error()
            print("g is not in the range of 1 to p. ")

        # check g^q mod p = 1
        if not number.equals(pow(self.generator, self.small_prime, self.large_prime), 1):
            error = self.set_error()
            print("g^q mod p does not equal to 1. ")

        # print out message
        output = "Baseline parameter check"
        if error:
            output += " failure. "
        else:
            output += " success. "
        print(output)

        return not error
예제 #3
0
    def verify_one_guardian(self, index: int) -> bool:
        """
        verify one guardian's key generation
        :param index:  index of this guardian, (0 - number of guardians)
        :return: True if the guardian's key information gets verified, False if not
        """
        coefficients_dic = self.__get_guardian_coeff_by_index(index)

        error = self.initialize_error()

        # loop through every proof
        for i in range(self.quorum):
            error = self.initialize_error()
            # get given values
            coeff_proofs_dic = coefficients_dic.get('coefficient_proofs')[i]
            response = coeff_proofs_dic.get('response')      # u
            commitment = coeff_proofs_dic.get('commitment')  # h
            public_key = coeff_proofs_dic.get('public_key')  # k
            challenge = coeff_proofs_dic.get('challenge')    # c

            # compute challenge
            challenge_computed = self.__compute_guardian_challenge_threshold_separated(public_key, commitment)

            # check if the computed challenge value matches the given
            if not number.equals(challenge, challenge_computed):
                error = self.set_error()
                print("guardian {i}, quorum {j}, challenge number error. ".format(i=index, j=i))
            # check equation
            if not self.__verify_individual_key_computation(response, commitment, public_key, challenge):
                error = self.set_error()
                print("guardian {i}, quorum {j}, equation error. ".format(i=index, j=i))

        return not error
    def __check_cp_proof_beta(self, beta_product: int,
                              votes_allowed: int) -> bool:
        """
        check if equation g ^ (L * c) * K ^ v = b * B ^ C mod p is satisfied
        This function checks the second part of aggregate encryption, B in (A, B), is used together with
         __check_cp_proof_alpha() to form a pair-wise check on a complete encryption value pair (A,B)
        :param beta_product: the accumalative product of pad/beta values of all the selections within a contest
        :param votes_allowed: the maximum votes allowed for this contest
        :return: True if the equation is satisfied, False if not
        """
        left = number.mod_p(
            pow(self.generator,
                number.mod_q(votes_allowed * self.contest_challenge),
                self.large_prime) *
            pow(self.public_key, self.contest_response, self.large_prime))

        right = number.mod_p(
            self.contest_beta *
            pow(beta_product, self.contest_challenge, self.large_prime))

        res = number.equals(left, right)
        if not res:
            print("contest selection limit check equation 2 error. ")

        return res
    def __check_challenge(self, challenge_computed) -> bool:
        """
        check if the given contest response equals to the one computed as c = H(Q-bar, (A,B), (a,b))
        :param challenge_computed: the computed challenge using hash
        :return: True if the given and computed values are the same, False if not
        """
        res = number.equals(challenge_computed, self.contest_challenge)

        if not res:
            print("Contest challenge error. ")

        return res
    def __match_total_across_ballots(self, aggregator: SelectionInfoAggregator,
                                     contest_names: list) -> bool:
        """
        matching the given tallies with accumulative products calculated across all ballots
        :param aggregator: a SelectionInfoAggregator instance for accessing information of a selection
        :param contest_names: a list of unique contest names, listed as "object_id" under contests
        :return: true if all the tallies match, false if not
        """
        error = self.initialize_error()

        dics_by_contest = aggregator.get_dics()
        total_data_dic = aggregator.get_total_data()
        total_pad_dic = aggregator.get_total_pad()

        for contest_name in contest_names:
            # get the corresponding index of pad and data dictionaries given contest name
            pad_dic_idx = aggregator.get_dic_id_by_contest_name(
                contest_name, 'a')
            data_dic_idx = aggregator.get_dic_id_by_contest_name(
                contest_name, 'b')
            pad_dic = dics_by_contest[pad_dic_idx]
            data_dic = dics_by_contest[data_dic_idx]

            selection_names = list(pad_dic.keys())
            for selection_name in selection_names:
                accum_pad = pad_dic.get(selection_name)
                tally_pad = total_pad_dic.get(contest_name,
                                              {}).get(selection_name)
                accum_data = data_dic.get(selection_name)
                tally_data = total_data_dic.get(contest_name,
                                                {}).get(selection_name)
                if not number.equals(accum_pad, tally_pad):
                    error = self.set_error()
                if not number.equals(accum_data, tally_data):
                    error = self.set_error()
        if error:
            print("Tally error.")

        return not error
    def verify_tracking_hash(self) -> bool:
        """
        verify all the middle (index 1 to n) tracking hash
        :return: true if all the tracking hashes are correct, false otherwise
        """
        crypto_hash = self.ballot_dic.get('crypto_hash')
        prev_hash, curr_hash = self.get_tracking_hash()
        timestamp = self.get_timestamp()
        curr_hash_computed = number.hash_elems(prev_hash, timestamp,
                                               crypto_hash)

        res = number.equals(int(curr_hash), int(curr_hash_computed))
        return res
    def __check_cp_proof_one_proof(self, pad: int, data: int, one_pad: int,
                                   one_data: int, one_chal: int,
                                   one_res: int) -> bool:
        """
        check if Chaum-Pedersen proof one proof(given challenge c1, response v1) is satisfied.

        To proof the zero proof, two equations g ^ v1 = a1 * alpha ^ c1 mod p, g ^ c1 * K ^ v1 = b1 * beta ^ c1 mod p
        have to be satisfied.
        In the verification process, the challenge c of a selection is allowed to be broken into two components
        in any way as long as c = (c0 + c1) mod p, c1 here is the second component broken from c.

        :param pad: alpha of a selection
        :param data: beta of a selection
        :param one_pad: one_pad of a selection
        :param one_data: one_data of a selection
        :param one_chal: one_challenge of a selection
        :param one_res: one_response of a selection
        :return: True if both equations of the one proof are satisfied, False if either is not satisfied
        """
        equ1_left = pow(self.generator, one_res, self.large_prime)
        equ1_right = number.mod_p(one_pad *
                                  pow(pad, one_chal, self.large_prime))

        equ2_left = number.mod_p(
            pow(self.generator, one_chal, self.large_prime) *
            pow(self.public_key, one_res, self.large_prime))
        equ2_right = number.mod_p(one_data *
                                  pow(data, one_chal, self.large_prime))

        res = number.equals(equ1_left, equ1_right) and number.equals(
            equ2_left, equ2_right)

        if not res:
            print("Chaum-pedersen proof one proof failure. ")

        return res
    def __match_vote_limit_by_contest(self, contest_name: str,
                                      num_of_placeholders: int) -> bool:
        """
        match the placeholder numbers in each contest with the maximum votes allowed
        :param contest_name: name/id of the contest
        :param num_of_placeholders: number of placeholders appear in this contest
        :return: True if vote limit and the placeholder numbers are equaled, False if not
        """
        vote_limit = int(self.vote_limit_dic.get(contest_name))

        res = number.equals(vote_limit, num_of_placeholders)
        if not res:
            print("contest placeholder number error. ")

        return res
    def __check_hash_comp(chal: int, zero_chal: int, one_chal: int) -> bool:
        """
        check if the hash computation is correct, equation c = c0 + c1 mod q is satisfied.
        :param chal: challenge of a selection
        :param zero_chal: zero_challenge of a selection
        :param one_chal: one_challenge of a selection
        :return: True if the hash computation equation is satisfied, False if not
        """
        # calculated expected challenge value: c0 + c1 mod q
        expected = number.mod_q(int(zero_chal) + int(one_chal))

        res = number.equals(number.mod_q(chal), expected)

        if not res:
            print("challenge value error.")

        return res
예제 #11
0
    def __verify_individual_key_computation(self, response: str, commitment: str, public_key: str, challenge: str) -> bool:
        """
        check the equation generator ^ response mod p = (commitment * public key ^ challenge) mod p
        :param response: response given by a guardian, ui,j
        :param commitment: commitment given by a guardian, hi,j
        :param public_key: public key of a guardian, Ki,j
        :param challenge: challenge of a guardian, ci,j
        :return: True if both sides of the equations are equal, False otherwise
        """
        response = int(response)
        commitment = int(commitment)
        public_key = int(public_key)
        challenge = int(challenge)

        left = pow(self.generator, response, self.large_prime)
        right = number.mod_p(commitment * pow(public_key, challenge, self.large_prime))

        return number.equals(left, right)
    def __check_cp_proof_alpha(self, alpha_product: int) -> bool:
        """
        check if equation g ^ v = a * A ^ c mod p is satisfied,
        This function checks the first part of aggregate encryption, A in (A, B), is used together with
        __check_cp_proof_beta() to form a pair-wise check on a complete encryption value pair (A,B)
        :param alpha_product: the accumulative product of all the alpha/pad values on all selections within a contest
        :return: True if the equation is satisfied, False if not
        """
        left = pow(self.generator, self.contest_response, self.large_prime)
        right = number.mod_p(
            number.mod_p(self.contest_alpha) *
            pow(alpha_product, self.contest_challenge, self.large_prime))

        res = number.equals(left, right)
        if not res:
            print("Contest selection limit check equation 1 error. ")

        return res
    def __check_challenge(self, challenge: int, pad: int, data: int,
                          partial_decrypt: int) -> bool:
        """
        check if the given challenge values Ci satisfies ci = H(Q-bar, (A,B), (ai, bi), Mi)
        :param challenge: given challenge of a share, Ci, for comparison
        :param pad: pad of a share, ai
        :param data: data number of a share, bi
        :param partial_decrypt: partial decryption of a guardian, Mi
        :return: True if the given Ci equals to the ci computed using hash
        """
        challenge_computed = number.hash_elems(self.extended_hash,
                                               self.selection_pad,
                                               self.selection_data, pad, data,
                                               partial_decrypt)

        res = number.equals(challenge, challenge_computed)

        if not res:
            print("challenge value error. ")

        return res
    def verify_tracking_hashes(self, hashes_dic: dict) -> bool:
        """
        verifies the tracking hash chain correctness
        NOTE: didn't check the first and closing hashes
        :param hashes_dic: a dictionary of "previous tracking - current tracking " hash code pairs
        :return: True if all the tracking hashes satisfy Bi, Hi = H(Hi-1, D, T, Bi)
        """
        error = self.initialize_error()
        # get all previous and current hashes
        prev_hashes = set(hashes_dic.values())
        curr_hashes = set(hashes_dic.keys())

        # find the set that only contains first and last hash
        union_set = prev_hashes.union(curr_hashes)
        intersection_set = prev_hashes.intersection(curr_hashes)
        first_last_set = union_set - intersection_set

        first_hash, last_hash = 0, 0

        # find the first and last hash
        for h in first_last_set:
            if h in prev_hashes:
                first_hash = h
            elif h in curr_hashes:
                last_hash = h

        # verify the first hash H0 = H(Q-bar)
        zero_hash = number.hash_elems(self.extended_hash)
        print(hashes_dic)
        print(first_last_set)
        print(self.extended_hash)
        print(zero_hash)
        print(first_hash)
        if not number.equals(int(zero_hash), int(first_hash)):
            error = self.set_error()

        # verify the closing hash, H-bar = H(Hl, 'CLOSE')
        closing_hash_computed = number.hash_elems(last_hash, 'CLOSE')

        return not error
    def __check_equation2(self, response: int, data: int, challenge: int,
                          partial_decrypt: int) -> bool:
        """
        check if equation A ^ vi = bi * (Mi^ ci) mod p is satisfied.
        The equation is checked along with the one specified in check_equation1() to give proof that the guardian has
        knowledge about the secret key that would give the secret key without revealing the actual secret.

        :param response: response of a share, vi
        :param data: data of a share, bi
        :param challenge: challenge of a share, ci
        :param partial_decrypt: partial decryption of a guardian, Mi
        :return True if the equation is satisfied, False if not
        """
        left = pow(self.selection_pad, response, self.large_prime)
        right = number.mod_p(data *
                             pow(partial_decrypt, challenge, self.large_prime))

        res = number.equals(left, right)
        if not res:
            print("equation 2 error. ")

        return res
    def __check_equation1(self, pad: int, response: int, challenge: int,
                          public_key: int) -> bool:
        """
        check if equation g ^ vi = ai * (Ki ^ ci) mod p is satisfied.

        The equation is checked along with the one specified in check_equation2() to give proof that the guardian has
        knowledge about the secret key that would give the secret key without revealing the actual secret.
        :param response: response of a share, vi
        :param pad: pad of a share, ai
        :param public_key: public key of a guardian, Ki
        :param challenge: challenge of a share, ci
        :return True if the equation is satisfied, False if not
        """
        left = pow(self.generator, response, self.large_prime)
        right = number.mod_p(pad *
                             pow(public_key, challenge, self.large_prime))

        res = number.equals(left, right)
        if not res:
            print("equation 1 error. ")

        return res