def GetPoints(beta, s_bold, r_bold, secparams): """ Algorithm 7.26: Computes the k-by-s matrix P_s = (P_ij)k x s of the points obtained from the s authorities for the selection s. The points are derived from the messages included in the OT responses beta = (beta_1, ..., beta_s) Args: beta (Response): Oblivious Transfer Response s_bold (list of int): Selections r_bold (list of mpz): Randomizations secparams (SecurityParams): Collection of public security parameters Returns: list Points """ AssertClass(beta, Response) AssertList(s_bold) AssertList(r_bold) AssertClass(secparams, SecurityParams) (b_bold, C_bold, d) = beta k = len(s_bold) l_M = ceil(secparams.L_M // secparams.L) p_bold = [] for j in range(k): k_j = (b_bold[j] * gmpy2.powmod(d, -r_bold[j], secparams.p)) % secparams.p K_j = bytearray() for c in range(l_M): K_j += RecHash([k_j, c], secparams) K_j = Truncate(K_j, secparams.L_M) M_j = XorByteArray([C_bold[s_bold[j]][j], K_j]) x_j = ToInteger(Truncate(M_j, secparams.L_M // 2)) y_j = ToInteger(Skip(M_j, secparams.L_M // 2)) if x_j >= secparams.p_prime or y_j >= secparams.p_prime: return None p_bold.append(Point(x_j, y_j)) return p_bold
def GenQuery(q_bold, pk, secparams): """ Algorithm 7.20: Generates an OT query a from the prime numbers representing the voter's selection and a for a given public encryption key (which serves as a generator). Args: q_bold (list): Selected primes pk (mpz): Encryption key secparams (SecurityParams): Collection of public security parameters Returns: (a, r): The OT query """ AssertList(q_bold) AssertMpz(pk) AssertClass(secparams, SecurityParams) k = len(q_bold) a_bold = [None] * k r_bold = [None] * k for j in range(k): r_bold[j] = randomMpz(secparams.q, secparams) a_j_1 = (q_bold[j] * gmpy2.powmod(pk, r_bold[j], secparams.p)) % secparams.p a_j_2 = gmpy2.powmod(secparams.g, r_bold[j], secparams.p) a_bold[j] = (a_j_1, a_j_2) return (a_bold, r_bold)
def GenSecretVoterData(p_bold, secparams): """ Algorithm 7.10: Generates the secret data for a single voter, which is sent to the voter prior to an election event via the printing authority. Args: p_bold (list): A list of n points = (p_1, ... , p_n) in Z_p' secparams (SecurityParams): Collection of public security parameters Returns: tuple: Secret voter data (x,y,F,r) """ AssertList(p_bold) AssertClass(secparams, SecurityParams) q_hat_apos_x = floor(secparams.q_hat_X // secparams.s) q_hat_apos_y = floor(secparams.q_hat_Y // secparams.s) x = randomMpz(q_hat_apos_x, secparams) y = randomMpz(q_hat_apos_y, secparams) F = Truncate(RecHash(p_bold, secparams), secparams.L_F) # Finalization code r_bold = [] # Return codes n = len(p_bold) for i in range(n): r_bold.append(Truncate(RecHash(p_bold[i], secparams), secparams.L_R)) return SecretVoterData(x, y, F, r_bold)
def GetYValue(x, a_bold, secparams): """ Algorithm 7.9: Computes the value y = A(x) ∈ Z_p' obtained from evaluating the polynomial A(X) = Sigma(i=0...d) a_i X^i mod p' at position x. The algorithm is an implementation of Horners method. Args: x (mpz): value x \in Z_p', normally mpz is used except for x = 0 a (list of mpz): list of coefficients secparams (SecurityParams): Collection of public security parameters Returns: mpz: the y value for x on the polynomial """ AssertMpz(x) AssertList(a_bold) AssertClass(secparams, SecurityParams) if x == mpz(0): y = mpz(a_bold[0]) else: y = mpz(0) for i in reversed(range(len(a_bold))): y = (a_bold[i] + x * y) % secparams.p_prime AssertMpz(y) return y
def GenPolynomial(d, secparams): """ Algorithm 7.8: Generates the coefficients a_0,...,a_d of a random polynomial A(X) = Sigma(i=0...d) a_i X^i mod p' of degree d >= 0. The algorithm also accepts d = -1 as input, which we interpret as the polynomial A(X) = 0. In this case, the algorithm returns the coefficient list a = (0). Args: d (int): Degree d >= -1 secparams (SecurityParams): Collection of public security parameters Returns: list of mpz: A list of coefficients a_0 ... a_d of polynomial A(X) """ AssertInt(d) AssertClass(secparams, SecurityParams) assert (d >= -1) a_bold = [] a_d = 0 if (d == -1): return [0] else: for i in range(d): a_bold.append(randomMpz(secparams.p_prime, secparams)) # generate the last coefficient != 0 while a_d == 0: a_d = randomMpz(secparams.p_prime, secparams) a_bold.append(a_d) return a_bold
def CheckDecryptionProofs(pi_prime_bold, pk_bold, e_bold, B_prime_bold, secparams): """ Algorithm 7.51: Checks if the decryption proofs generated by s different authorities are correct. Args: pi_prime_bold (list of DecryptionProof): Decryption proofs pk_bold (list of mpz): Encryption key shares e_bold (list of ElGamalEncryption): ElGamal encryptions B_prime_bold (list): Partial decryptions secparams (SecurityParams): Collection of public security parameters Returns: bool """ AssertList(pi_prime_bold) AssertList(pk_bold) AssertList(e_bold) AssertList(B_prime_bold) AssertClass(secparams, SecurityParams) s = len(pk_bold) for j in range(s): if not CheckDecryptionProof(pi_prime_bold[j], pk_bold[j], e_bold, B_prime_bold[j], secparams): return False return True
def GetPrimes(n, secparams): """ Algorithm 7.1: Computes the first n prime numbers from G_q. The computation possibly fails if n is large and p is small, but this case is very unlikely in practice. In a more efficient implementation of this algorithm, the resulting list of primes is precomputed for the largest expected value n. Args: n (int): Number of primes to be calculated secparams (SecurityParams): Collection of public security parameters Returns: list of mpz : A list with length n containing the first n prime numbers in G_p """ AssertInt(n) AssertClass(secparams, SecurityParams) x = mpz(1) primes = [] for i in range(n): # i = 0, ... , n-1 while True: x += 1 if x <= 2 else mpz(2) if x >= secparams.p: return [] # n is incompatible with p if gmpy2.is_prime(x) and IsMember(x, secparams): # see Alg. 7.2 break primes.append(x) return primes # p \elementof G_p \cap P)^n
def GenConfirmationProof(y, y_prime, y_hat, secparams): """ Algorithm 7.32: Generates a NIZKP of knowledge of the secret confirmation credential y that matches with a given public confirmation credential y_hat. Note that this proof is equivalent to a Schnorr identification proof. For the verification of pi, see Alg. 7.36. Args: y: Secret confirmation credential y_prime: Secret validity credential y_hat: Public confirmation credential secparams (SecurityParams): Collection of public security parameters Returns: π: The NIZKP challenge """ AssertMpz(y) AssertClass(secparams, SecurityParams) w = randomMpz(secparams.q_hat, secparams) t = gmpy2.powmod(secparams.g_hat, w, secparams.p_hat) c = GetNIZKPChallenge(y_hat, t, secparams.tau, secparams) s = w + c * (y + y_prime) % secparams.q_hat pi = (t, s) return pi
def GenPermutationCommitment(psi, h_bold, secparams): """ Algorithm 7.45: Generates a commitment c = com(psi, r_bold) to a permutation psi by committing to the columns of the corresponding permutation matrix. This algorithm is used in Alg. 7.44. Args: psi (list of int): Permutation h_bold (list of mpz): Independent generators h = (h_1, ..., h_N), h_i in G_q\{1} secparams (SecurityParams): Collection of public security parameters Returns: tuple: (c,r) """ AssertList(psi) AssertList(h_bold) AssertClass(secparams, SecurityParams) r_bold = [None] * len(psi) c_bold = [None] * len(psi) N = len(psi) for i in range(N): r_bold[psi[i]] = randomMpz(secparams.q, secparams) c_bold[psi[i]] = (gmpy2.powmod(secparams.g, r_bold[psi[i]], secparams.p) * h_bold[i]) % secparams.p return (c_bold, r_bold)
def CheckShuffleProofs(pi_bold, e_0_bold, E_bold, pk, i, secparams): """ Algorithm 7.47: Checks if a chain of shuffle proofs generated by s different authorities is correct. Args: pi_bold (list of ShuffleProof): Shuffle proof e_0_bold (list): ElGamal Encryptions E_bold (list): Shuffled ElGamal Encryptions pk (mpz): Encryption key pk i (int): Authority index secparams (SecurityParams): Collection of public security parameters Returns: bool """ AssertList(pi_bold) AssertList(e_0_bold) AssertList(E_bold) AssertMpz(pk) AssertInt(i) AssertClass(secparams, SecurityParams) N = len(e_0_bold) e_bold_tmp = [] e_bold_tmp.append(e_0_bold) e_bold_tmp.extend(E_bold) for j in range(secparams.s): if i != j: if not CheckShuffleProof(pi_bold[j], e_bold_tmp[j], e_bold_tmp[j+1],pk, secparams): return False return True
def GenConfirmation(Y, P_bold, secparams): """ Algorithm 7.30: Generates the confirmation gamma, which consists of the public confirmation credential y_hat and a NIZKP of knowledge pi of the secret confirmation and validity credentials y and y_prime. Args: Y: Confirmation code P_bold (list of points): Points secparams (SecurityParams): Collection of public security parameters Returns: gamma: Confirmation """ AssertList(P_bold) AssertClass(secparams, SecurityParams) y_prime = 0 for i in range(len(P_bold)): p_bold_i = P_bold[i] y_prime_i = GetValue(p_bold_i, secparams) y_prime += y_prime_i y_prime %= secparams.q_hat y = StringToInteger(Y, secparams.A_Y) % secparams.q_hat y_hat = gmpy2.powmod(secparams.g_hat, (y + y_prime) % secparams.q_hat, secparams.p_hat) pi = GenConfirmationProof(y, y_prime, y_hat, secparams) gamma = Confirmation(y_hat, pi) return gamma
def GetValue(p_bold, secparams): """ Algorithm 7.32: Computes a polynomial A(X) of degree k - 1 from given points p = (p_1, ..., p_k) using Lagrange's interpolation method and returns the value y = A(0). Args: p_bold (list of points): Given points secparams (SecurityParams): Collection of public security parameters Returns: y: y = A(0) of polynomial A(X) """ AssertList(p_bold) AssertClass(secparams, SecurityParams) y = 0 k = len(p_bold) for i in range(k): n = mpz(1) d = mpz(1) for j in range(k): if i != j: n = (n * p_bold[j].x) % secparams.p_prime d = (d * (p_bold[j].x - p_bold[i].x)) % secparams.p_prime y = (y + p_bold[i].y * n * gmpy2.invert(d, secparams.p_prime)) % secparams.p_prime return y
def GenPermutation(N, secparams): """ Algorithm 7.42: Generates a random permutation ψ ∈ Ψ following Knuth's shuffle algorithm. Args: N (int): Permutation size secparams (SecurityParams): Collection of public security parameters Returns: list of int: Permutation """ AssertInt(N) AssertClass(secparams, SecurityParams) I = list(range(N)) res = [] for i in range(N): k = randomBoundedInt(i, N - 1, secparams) j_i = I[k] I[k] = I[i] res.append(j_i) return res
def GetReturnCodes(s_bold, P_s_bold, secparams): """ Algorithm 7.28: Computes the k return codes rcs = (RC_s_1, ... , RC_s_k) for the selected candidates by combining the hash values of the transferred points p_ij in P_s from different authorities. Args: s_bold (list of int): Selections P_s_bold (list of Point): Points secparams (SecurityParams): Collection of public security parameters Returns: list Points """ AssertList(s_bold) AssertList(P_s_bold) AssertClass(secparams, SecurityParams) rc_s_bold = [] k = len(s_bold) s = len(P_s_bold) for i in range(k): R_j = [] for j in range(s): R_j.append(Truncate(RecHash(P_s_bold[j][i], secparams), secparams.L_R)) R = MarkByteArray(XorByteArray(R_j), s_bold[i], secparams.n_max) rc_s_bold.append(ByteArrayToString(R, secparams.A_R)) return rc_s_bold
def GetDecryptions(e_bold, B_prime_bold, secparams): """ Algorithm 7.53: Computes the list of decryptions m = (m_1, ..., m_N) by assembling the partial decryptions b_prime_ij obtained from s different authorities. Args: e_bold (list of ElGamalEncryption): ElGamal encryptions B_prime_bold (list of list): Partial decryptions secparams (SecurityParams): Collection of public security parameters Returns: list of mpz Partial decryptions """ AssertList(e_bold) AssertList(B_prime_bold) AssertClass(secparams, SecurityParams) m_bold = [] N = len(e_bold) for i in range(N): b_prime_i = mpz(1) for j in range(secparams.s): b_prime_i = (b_prime_i * B_prime_bold[j][i]) % secparams.p m_bold.append((e_bold[i][0] * gmpy2.invert(b_prime_i, secparams.p)) % secparams.p) return m_bold
def GetFinalization(v, P_bold, B, secparams): """ Algorithm 7.36: Computes the finalization code F_v for voter v from the given points p_i and returns F_v together with the randomizations r_v used in the OT response. Args: v (int): Voter index p_bold (list of points): Points B (list): Ballot list secparams (SecurityParams): Collection of public security parameters Returns: tuple: delta (F_v, r_bold_v) """ AssertInt(v) AssertList(P_bold) AssertList(B) AssertClass(secparams, SecurityParams) p_bold_v = P_bold[v] F = Truncate(RecHash(p_bold_v, secparams), secparams.L_F) for i in range(len(B)): if (B[i].voterId == v): z_i = B[i].randomizations delta = (F, z_i) return delta
def GetPointMatrix(beta_bold, s_bold, r_bold, secparams): """ Algorithm 7.26: Computes the k-by-s matrix P_s = (P_ij)k x s of the points obtained from the s authorities for the selection s. The points are derived from the messages included in the OT responses beta = (beta_1, ..., beta_s) Args: beta_bold (list of Response): Oblivious Transfer Responses k_bold (list of int): Number of selections s_bold (list of int): Selections r_bold (list of mpz): Randomizations secparams (SecurityParams): Collection of public security parameters Returns: list Points """ AssertList(beta_bold) AssertList(s_bold) AssertList(r_bold) AssertClass(secparams, SecurityParams) s = len(beta_bold) P_s_bold = [None] * s for i in range(s): P_s_bold[i] = GetPoints(beta_bold[i], s_bold, r_bold, secparams) # Check that the length of P_s is s x k for elem in P_s_bold: assert len(elem) == len(s_bold) return P_s_bold
def GetNIZKPChallenges(n, y, kappa, secparams): """ Algorithm 7.5: Computes n challenges c ∈ Z_q for a given of public value y. The domain Y of the input value is unspecified. The results in c = (c_1, ..., c_n) are identical to c_i = RecHash(y, i), but precomputing H makes the algorithm more efficient. Args: y (unspecified): Public value t (unspecified): Commitment kappa (int): Soundness strength 1 <= kappa <= 8L secparams (SecurityParams): Collection of public security parameters Retuns: list of mpz: List containing n computed challenges (mpz) """ AssertInt(n) AssertInt(kappa) assert kappa >= 1 and kappa <= 8 * secparams.L, "Constraint for kappa: 1 <= kappa <= 8L" AssertClass(secparams, SecurityParams) H = RecHash(y, secparams) c = [] for i in range(1, n + 1): I = RecHash(i, secparams) c.append(mpz(ToInteger(secparams.hash(H + I)) % 2 ^ kappa)) return c
def CheckBallotProof(pi, x_hat, a_bold, pk, secparams): """ Algorithm 7.24: Checks the correctness of a ballot proof Pi generated by Alg. 7.21. The public values of this proof are the public voting credential x_hat and the ElGamal encryption (a,b) Args: pi (BallotProof): Ballot proof pi = (t,s) x_hat (mpz): Voting credential x_hat a_bold (list): OT query a_bold pk (mpz): ElGamal key pk secparams (SecurityParams): Collection of public security parameters Returns: bool: True if (t_1 == t'_1 and t_2 == t'_2 and t_3 == t'_3) holds """ AssertClass(pi, BallotProof) AssertMpz(x_hat) AssertList(a_bold) AssertMpz(pk) AssertClass(secparams, SecurityParams) y = (x_hat, a_bold) t = pi[0] (t_1, t_2, t_3) = t s = pi[1] (s_1, s_2, s_3) = s c = GetNIZKPChallenge(y, t, secparams.tau, secparams) a_1 = mpz(1) a_2 = mpz(1) for j in range(len(a_bold)): a_1 = (a_1 * a_bold[j][0]) % secparams.p a_2 = (a_2 * a_bold[j][1]) % secparams.p # e = (a, b) t_prime_1 = (gmpy2.powmod(x_hat, -c, secparams.p_hat) * gmpy2.powmod( secparams.g_hat, s_1, secparams.p_hat)) % secparams.p_hat t_prime_2 = (gmpy2.powmod(a_1, -c, secparams.p) * s_2 * gmpy2.powmod(pk, s_3, secparams.p)) % secparams.p t_prime_3 = (gmpy2.powmod(a_2, -c, secparams.p) * gmpy2.powmod(secparams.g, s_3, secparams.p)) % secparams.p return t_1 == t_prime_1 and t_2 == t_prime_2 and t_3 == t_prime_3
def GenBallot(X, s, pk, secparams, manipulatedPublicCredential=None, manipulatedPublicKey=None): """ Algorithm 7.18: Generates a ballot based on the selection s and the voting code X. The ballot includes an OT query a and a proof pi. The algorithm also returns the random values used to generate the OT query. These random values are required in Alg. 7.27 to derive the transferred messages from the OT response, which itself is generated by Alg. 7.25. Args: X (str): Voting Code X ∈ A_X^l_X s (list of int): Selection s = (s_1, ... , s_k), 1 <= s_1 < ... < s_k pk (mpz): ElGamal key pk ∈ G_p \ {1} secparams (SecurityParams): Collection of public security parameters Returns: tuple: alpha = (r, Ballot) = (r, (x_hat, a, b, pi)) """ AssertMpz(pk) AssertList(s) AssertClass(secparams, SecurityParams) x = mpz(StringToInteger(X, secparams.A_X)) x_hat = gmpy2.powmod(secparams.g_hat, x, secparams.p_hat) if manipulatedPublicCredential != None: x_hat = mpz(manipulatedPublicCredential) if manipulatedPublicKey != None: pk = mpz(manipulatedPublicKey) q_bold = GetSelectedPrimes(s, secparams) # q = (q_1, ... , q_k) (a_bold, r_bold) = GenQuery(q_bold, pk, secparams) m = mpz(1) for j in range(len(q_bold)): m = m * q_bold[j] if m >= secparams.p: return None #a = mpz(1) #b = mpz(1) r = mpz(0) for j in range(len(q_bold)): #a = (a * a_bold[j][0]) % secparams.p #b = (b * a_bold[j][1]) % secparams.p r = (r + r_bold[j]) % secparams.q #e = (a, b) pi = GenBallotProof(x, m, r, x_hat, a_bold, pk, secparams) alpha = Ballot(x_hat, a_bold, pi) return (alpha, r_bold)
def CheckBallot(v, alpha, pk, k_bold, E_bold, x_hat_bold, B, secparams): """ Algorithm 7.22: Checks if a ballot alpha obtained from voter v is valid. For this, voter i must not have submitted a valid ballot before, pi must be valid, and x_hat must be the public voting credential of voter v. Note that parameter checking |a| ki for ki °tj1 kij is an important initial step of this algorithm. Args: v (int): Voter index alpha (Ballot): Ballot pk (mpz): Public Key k_bold (list): Number of selections E_bold (list): Eligibility matrix E x_hat_bold (list): Public voting credentials B (list): Ballot List secparams (SecurityParams): Collection of public security parameters Returns: bool: True if the ballot is valid, False if not """ AssertInt(v) AssertClass(alpha, Ballot) AssertMpz(pk) assert IsMember(pk, secparams), "pk must be in G_q" AssertList(k_bold) AssertList(x_hat_bold) AssertList(B) AssertClass(secparams, SecurityParams) k_prime = 0 for j in range(len(k_bold)): k_prime = k_prime + E_bold[v][j] * k_bold[ j] # if voter i is eligible to cast a vote in election j, multiply 1 * the number of selections in j hasBallot = HasBallot(v, B, secparams) credentialCheck = x_hat_bold[v] == alpha.x_hat queryLength = len(alpha.a_bold) == k_prime if not hasBallot and credentialCheck and queryLength: ballotProofCheck = CheckBallotProof(alpha.pi, alpha.x_hat, alpha.a_bold, pk, secparams) if ballotProofCheck: return True, [] return (False, [False, hasBallot, credentialCheck, queryLength])
def GetEncryptions(B, C, n_bold, w_bold, secparams): """ Algorithm 7.40: Computes a sorted list of ElGamal encryptions from the list of submitted ballots, for which a valid confirmation is available. Sorting this list is necessary to guarantee a unique order. For this, we define a total order over G_q^2 by e_i <= e_j <-> (a_i < a_j) or (a_i = a_a and b_i <= b_j), for e_i = (a_i, b_i) and e_j = (a_j, b_j) Args: B (list of Ballot): Ballot List C (list of tuple): Confirmation list C n_bold (list): Number of candidates n w_bold (list): Counting circles w secparams (SecurityParams): Collection of public security parameters Returns: list of ElGamalEncryption: ElGamal Encryptions """ AssertList(B) AssertList(C) AssertList(n_bold) AssertList(w_bold) AssertClass(secparams, SecurityParams) n = sum(n_bold) w = max(w_bold) p_bold = GetPrimes(n + w, secparams) i = 0 e_bold = [] for j in range(len(B)): #(v, alpha, z) = B[j] v = B[j].voterId alpha = B[j].ballot z = B[j].randomizations a_1 = mpz(1) a_2 = mpz(1) if HasConfirmation(v, C, secparams): a_j_1_product = mpz(1) for j in range(len(alpha.a_bold)): a_j_1_product = (a_j_1_product * alpha.a_bold[j][0]) % secparams.p a_2 = (a_2 * alpha.a_bold[j][1]) % secparams.p a_1 = (p_bold[(n - 1) + w_bold[v]] * a_j_1_product) % secparams.p e_bold.append(ElGamalEncryption(a_1, a_2)) i += 1 e_bold.sort(key=lambda enc: (enc.a, enc.b), reverse=False) return e_bold
def GetVotes(m_bold, n_bold, w_bold, secparams): """ Algorithm 7.54: Computes the election result matrix V = (v_ij) N x n from the products of encoded selections m = (m_1, ..., m_N) by retrieving the prime factors of each mi. Each resulting vector vi represents somebody’s vote, and each value vij 1 represents somebody’s vote for a specific candidate j in {1, ..., n} Args: m_bold (List): Products of encoded selections m = (m_1, ..., m_N), m_i in G_q n_bold (List): Number of candidates n >= 2 w_bold (List): Counting circles w = (w_1, ..., w_N_E), w_i \in N secparams (SecurityParams): Collection of public security parameters Returns: list of int: Election result matrix V = (v_ij) N x n """ AssertList(m_bold) for m in m_bold: assert IsMember(m, secparams), "m_bold elements must be in G_q" AssertList(n_bold) for n in n_bold: assert n >= 2, "n must be greater than or equal 2" AssertList(w_bold) AssertClass(secparams, SecurityParams) n = sum(n_bold) w = max(w_bold) N = len(m_bold) V_bold = [] W_bold = [] p_bold = GetPrimes(n + w, secparams) for i in range(N): v_bold_i = [None] * n for j in range(n): if m_bold[i] % p_bold[j] == 0: v_bold_i[j] = 1 else: v_bold_i[j] = 0 V_bold.append(v_bold_i) w_bold_i = [None] * w for j in range(w): if m_bold[i] % p_bold[n + j] == 0: w_bold_i[j] = 1 else: w_bold_i[j] = 0 W_bold.append(w_bold_i) return (V_bold, W_bold)
def GenElectorateData(n_bold, k_bold, E_bold, secparams): """ Algorithm 7.6: Generates the data for the whole electorate Args: n_bold (list of int): A list containing the number of candidates: (n_1, ... , n_t) k_bold (list of int): A list containing the number of possible selections per election: (k_1, ... , k_t) E_bold (list of list): Eligibility matrix [N][t], 1 means eligible secparams (SecurityParams): Collection of public security parameters Returns: tuple: (d, d^, P) """ AssertList(n_bold) AssertList(k_bold) AssertList(E_bold) AssertClass(secparams, SecurityParams) n = sum(n_bold) N_E = len(E_bold) t = len(k_bold) d_bold = [] d_hat_bold = [] P_bold = [] for i in range(N_E): # loop over N (all voters) k_prime_i = 0 for j in range(t): k_prime_i = k_prime_i + ( E_bold[i][j] * k_bold[j] ) # if voter i is eligible to cast a vote in election j, multiply 1 * the number of selections in j # generate n random points p_bold_i, y_prime_i = GenPoints(n, k_prime_i, secparams) P_bold.append(p_bold_i) # points on the polynomials # generate x, y values, finalization code and return codes d_i = GenSecretVoterData(p_bold_i, secparams) d_bold.append(d_i) # private voter data d_hat_i = GetPublicVoterData(d_i.x_i, d_i.y_i, y_prime_i, secparams) d_hat_bold.append(d_hat_i) # public voter data return (d_bold, d_hat_bold, P_bold)
def CheckFinalizationCode(FC, FC_prime, secparams): """ Algorithm 7.39: Checks if the displayed finalization code FC1 matches with the finalization code FC from the voting card. Note that this algorithm is executed by humans. Args: FC (string): Printed finalization code FC_prime (string): Displayed finalization code secparams (SecurityParams): Collection of public security parameters Returns: bool: FC == FC_prime """ AssertClass(secparams, SecurityParams) return FC == FC_prime
def GenBallotProof(x, m, r, x_hat, a_bold, pk, secparams): """ Algorithm 7.21: Generates a NIZKP, which proves that the ballot has been formed properly. This proof includes a proof of knowledge of the secret voting credential x that matches with the public voting credential x_hat. Note that this is equivalent to a Schnorr Identification proof. Args: x (mpz): Voting credential ∈ Z_q_hat m (mpz): Product of selected primes m ∈ G_q r (mpz): Randomization r ∈ Z_q a_bold (list): OT queries pk (mpz): Encryption key pk ∈ G_q secparams (SecurityParams): Collection of public security parameters Returns: tuple: ((t_1, t_2, t_3), (s_1, s_2, s_3)) """ AssertMpz(x) AssertMpz(m) AssertMpz(r) AssertMpz(x_hat) AssertList(a_bold) AssertMpz(pk) AssertClass(secparams, SecurityParams) w_1 = randomMpz(secparams.q_hat, secparams) w_2 = randomQuadResMpz(secparams) w_3 = randomMpz(secparams.q, secparams) t_1 = gmpy2.powmod(secparams.g_hat, w_1, secparams.p_hat) t_2 = (w_2 * gmpy2.powmod(pk, w_3, secparams.p)) % secparams.p t_3 = gmpy2.powmod(secparams.g, w_3, secparams.p) y = (x_hat, a_bold) t = (t_1, t_2, t_3) c = GetNIZKPChallenge(y, t, secparams.tau, secparams) s_1 = (w_1 + c * x) % secparams.q_hat s_2 = (w_2 * gmpy2.powmod(m, c, secparams.p)) % secparams.p s_3 = (w_3 + c * r) % secparams.q s = (s_1, s_2, s_3) return BallotProof(t,s)
def GenKeyPair(secparams): """ Algorithm 7.15: Generates a random ElGamal encryption key pair (sk, pk) ∈ Z_q x G_q. This algorithm is used in Prot. 6.3 by the authorities to generate private shares of a common public encryption key. Args: secparams (SecurityParams): Collection of public security parameters Returns: tuple: Key Pair (sk, pk) in Z_q x G_q """ AssertClass(secparams, SecurityParams) sk = randomMpz(secparams.q, secparams) pk = gmpy2.powmod(secparams.g, sk, secparams.p) assert IsMember(pk, secparams) return (sk, pk)
def GenDecryptionProof(sk_j, pk_j, e_bold, b_prime_bold, secparams): """ Algorithm 7.50: Generates a decryption proof relative to ElGamal encryptions e and partial decryptions b'. This is essentially a NIZKP of knowledge of the private key share sk_j satisfying b'_i = b_i^sk_j for all input encryptions e_i = (a_i, b_i) and pk_j = g^sk_j. Proof verification, see Alg. 7.52. Args: sj_j (mpz): Decryption key share pk_j (mpz): Encryption key share e_bold (list of ElGamalEncryption): ElGamal encryptions e_bold b_prime_bold (list): Partial decryptions secparams (SecurityParams): Collection of public security parameters Returns: DecryptionProof """ AssertMpz(sk_j) AssertMpz(pk_j) AssertList(e_bold) AssertList(b_prime_bold) AssertClass(secparams, SecurityParams) w = randomMpz(secparams.q, secparams) t_0 = gmpy2.powmod(secparams.g, w, secparams.p) t_1_to_N = [] N = len(e_bold) for i in range(N): t_1_to_N.append(gmpy2.powmod(e_bold[i].b, w, secparams.p)) t = (t_0, t_1_to_N) b_bold = [e.b for e in e_bold] y = (pk_j, b_bold, b_prime_bold) c = GetNIZKPChallenge(y, t, secparams.tau, secparams) s = (w + c * sk_j) % secparams.q pi = DecryptionProof(t, s) return pi
def IsMember(x, secparams): """ Algorithm 7.2: Checks if x is an element of G_q. The core of the algorithm is the computation of the Jacobi symbol for which we refer to existing algorithms Args: x (mpz): The number to test x \in N secparams (SecurityParams): Collection of public security parameters Returns: bool: True if x is a member of G_q, False if not """ AssertNumeric(x) AssertClass(secparams, SecurityParams) if 1 <= x and x < secparams.p: return jacobi(x, secparams.p) == 1 return False
def GetFinalizationCode(delta_bold, secparams): """ Algorithm 7.38: Computes a finalization code FC by combining the values Fj received from the authorities. Args: delta_bold (list): Finalizations secparams (SecurityParams): Collection of public security parameters Returns: string: Finalization code """ AssertList(delta_bold) AssertClass(secparams, SecurityParams) F_j = [delta_bold[j][0] for j in range(len(delta_bold))] FC = ByteArrayToString(XorByteArray(F_j), secparams.A_F) return FC