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 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 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 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 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 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 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 GetVotingPage(v, c_bold, n_bold, k_bold): """ Algorithm 7.17: Computes a string P in A_UCS^*, which represents the voting page displayed to voter. Specifying the details of presenting the information on the voting page is beyond the scope of this document. Args: v (int): Voter index c_bold (list): Candidate descriptions c = (C_1, ..., C_n), c_v ∈ A_UCS^* n_bold (list): Number of candidates n =(n_1, ..., n_t), n_j >= 2 k_bold (list): Number of selections k = (k_1, ..., k_t), 0 <= k_j < n_j Returns: string: String P ∈ A_UCS^* displayed to the voter """ AssertInt(v) AssertList(c_bold) AssertList(n_bold) AssertList(k_bold) electionString = "" candidateIndex = 0 for j in range(len(k_bold)): electionString += "# Election {}\n".format(j) for l in range(n_bold[j]): electionString += "Candidate {}: {}\n".format( candidateIndex, c_bold[candidateIndex]) candidateIndex += 1 electionString += "You can make {} selection(s) for this particular election\n\n".format( k_bold[j]) P = \ """ Voting page for voter {v}: Simultaneous elections: {elections} """.format( v = v , elections = electionString ) return P
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 GenShuffle(e_bold, pk, secparams): """ Algorithm 7.41: Generates a random permutation psi and uses it to shuffle a given list e = (e_1, ..., e_n) of ElGamal encryptions e_i = (a_i, b_i) in G_q^2. With psi_N = {(j_1, ..., j_N) : j_i in {1,...,N}, j_i_1 != j_i_2, forAll i_1 != i2} we denote the set of all N! possible permutations of the values {1, ..., N} Args: e_bold (list): ElGamal Encryptions pk (mpz): Encryption key pk secparams (SecurityParams): Collection of public security parameters Returns: tuple (e_prime, r_prime, psi) """ AssertList(e_bold) AssertMpz(pk) psi_bold = GenPermutation(len(e_bold), secparams) e_prime_bold = [] r_prime_bold = [] N = len(e_bold) for i in range(N): (e_prime_i, r_prime_i) = GenReEncryption(e_bold[i], pk, secparams) e_prime_bold.append(e_prime_i) r_prime_bold.append(r_prime_i) e_prime_shuffled = [None] * len(e_prime_bold) for i in range(N): e_prime_shuffled[i] = e_prime_bold[psi_bold[i]] return (e_prime_shuffled, r_prime_bold, psi_bold)
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 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 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 ToString(x, k, A): """ Algorithm 4.6: Computes a string representation of length k in big-endian order of a given non negative integer x in N Args: x (mpz): Integer x ∈ N k (int) String length k >= log_N (x) A (list) Alphabet A = {c_1, ..., c_N} Returns: string: The string representation of x """ AssertMpz(x) AssertInt(k) AssertList(A) S = "" N = len(A) for i in reversed(range(k)): s_k = A[x % N] x = x // N S = s_k + S return S
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 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 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 CheckDecryptionProof(pi_prime, pk_j, e_bold, b_prime_bold, secparams): """ Algorithm 7.52: Checks the correctness of a decryption proof generated by Alg. 7.50. The public values are the ElGamal encryptions e, the partial decryptions b1, and the share pkj of the public encryption key. Args: pi_prime (DecryptionProof): Decryption pk_j (mpz): Encryption key share e_bold (list of ElGamalEncryption): ElGamal encryptions b_prime_bold (list): Partial decryptions secparams (SecurityParams): Collection of public security parameters Returns: bool: (t_0 = t_prime_0) and for i = 1..N: t_i = t_prime_i """ #AssertMpz(pi_prime) AssertMpz(pk_j) AssertList(e_bold) AssertList(b_prime_bold) AssertClass(secparams, SecurityParams) t_0 = pi_prime.t[0] t_1_to_N = pi_prime.t[1] b_bold = [e.b for e in e_bold] y = (pk_j, b_bold, b_prime_bold) c = GetNIZKPChallenge(y, pi_prime.t, secparams.tau, secparams) t_prime_0 = (gmpy2.powmod(pk_j, -c, secparams.p) * gmpy2.powmod( secparams.g, pi_prime.s, secparams.p)) % secparams.p t_prime_bold = [] N = len(e_bold) for i in range(N): t_prime_bold.append( (gmpy2.powmod(b_prime_bold[i], -c, secparams.p) * gmpy2.powmod(b_bold[i], pi_prime.s, secparams.p)) % secparams.p) return (t_0 == t_prime_0 and t_prime_bold == t_1_to_N)
def CheckConfirmation(v, gamma, y_hat_bold, B, C, secparams): """ Algorithm 7.33: Checks if a confirmation γ obtained from voter v is valid. For this, voter v must have submitted a valid ballot before, but not a valid confirmation. The check then succeeds if π is valid and if y_hat is the public confirmation credential of voter v. Args: v (int): Voter index gamma: Confirmation y_hat_bold (list): Public confirmation credentials B (list): Ballot list C (list): Confirmation list secparams (SecurityParams): Collection of public security parameters Returns: bool True if the given confirmation is valid, False otherwise. """ AssertNumeric(v) AssertList(y_hat_bold) AssertList(B) AssertList(C) AssertClass(secparams, SecurityParams) #(y_hat_, pi) = gamma hasBallot = HasBallot(v, B, secparams) hasConfirmation = HasConfirmation(v, C, secparams) credentialCheck = gamma.y_hat == y_hat_bold[v] if hasBallot and not hasConfirmation and credentialCheck: confirmationProofCheck = CheckConfirmationProof(gamma.pi, gamma.y_hat, secparams) if confirmationProofCheck: return (True, []) return (False, [False, hasBallot, hasConfirmation, credentialCheck])
def CheckVerificationCodes(rc_bold, rc_prime_bold, s_bold): """ Algorithm 7.29: Checks if every displayed verification code RC'_i i matches with the return code RC_s_i of the selected candidate s_i as printed on the code sheet. Note that this algorithm is executed by humans. Args: rc_bold (list): Printed return codes rc_prime_bold (list): Displayed return codes rc' s_bold (list): Selections Returns: bool """ AssertList(rc_bold) AssertList(rc_prime_bold) AssertList(s_bold) for i in range(len(s_bold)): if rc_bold[s_bold[i]] != rc_prime_bold[i]: return False return True
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 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 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
def GetSelectedPrimes(s_bold, secparams): """ Algorithm 7.19: Selects k prime numbers from Gq corresponding to the given indices s = (s_1, ..., s_k). For example, s = (1, 3, 7) means selecting the first, the third, and the seventh prime from G_q. Args: s_bold (list of int): Selections secparams (SecurityParams): Collection of public security parameters Returns: list of mpz: List of the selected prime numbers """ AssertList(s_bold) AssertClass(secparams, SecurityParams) s_k = max(s_bold) + 1 p_bold = GetPrimes(s_k, secparams) q_bold = [p_bold[s_i] for s_i in s_bold] return q_bold
def HasConfirmation(v, C, secparams): """ Algorithm 7.34: Checks if the confirmation list C contains an entry for v. Args: v (int): Voter Index v in N C (list of Confirmation): Confirmation list C secparams (SecurityParams): Collection of public security parameters Returns: bool: True if the list C contains a Confirmation of voter i """ AssertNumeric(v) AssertList(C) AssertClass(secparams, SecurityParams) for voterConfirmation in C: if v == voterConfirmation.voterId: return True return False