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
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
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
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