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 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 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 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 GenCommitmentChain(c_0, u_bold, secparams): """ Algorithm 7.46: Generates a commitment chain c_0 -> c_1 --> ... --> c_N relative to a list of public challenges u_bold and starting with a given commitment c_0. This algorithm is used in Alg. 7.44 Args: c_0 (mpz): Initial commitment c_0 in G_q u_bold (list of mpz): Public challenges u = (u_1, ..., u_N), u_i in Z_q secparams (SecurityParams): Collection of public security parameters Returns: tuple: (c,r) """ r_bold = [] c_bold = [] N = len(u_bold) c_bold.append(c_0) for i in range(N): c_i_minus_1 = c_bold[i] r_i = randomMpz(secparams.q, secparams) c_i = ((gmpy2.powmod(secparams.g, r_i, secparams.p) * gmpy2.powmod(c_i_minus_1, u_bold[i], secparams.p)) % secparams.p) r_bold.append(r_i) c_bold.append(c_i) del c_bold[0] return (c_bold, r_bold)
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 GenPoints(n, k, secparams): """ Algorithm 7.7: Generates a list of n random points picket from t random polynomials A_j(X) of degree k_j - 1 (by picking n_j different random points from each polynomial). Additional, the values y_j = A_j(0) are computed for all random polynomials and returned together with the random points. Args: n (int): Number of candidates n >= 2 k (int): Number of selections 0 < k < n secparams (SecurityParams): Collection of public security parameters Returns: tuple: (p,y'), points p ∈ (Z_p^2)^n, and the y value of x=0 """ AssertInt(n) AssertInt(k) p_bold = [] a_bold = GenPolynomial( k - 1, secparams ) # the number of 1's in the eligibility matrix indicate how many selections the voter can make and therefore decides the degree of the polynomial X = [] for i in range(n): x = mpz(0) # get a unique x from Z_p' while True: x = randomMpz(secparams.p_prime, secparams) if x not in X or secparams.deterministicRandomGen: # if randomMpz is deterministic, we would be stuck in an endless loop X.append(x) break y = GetYValue( x, a_bold, secparams ) # get the corresponding y value of x on the polynomial a_j p_i = Point(x, y) # Point tuple p_bold.append(p_i) # part of the private voter data y_prime = GetYValue(mpz(0), a_bold, secparams) # Point (0,Y(0)) return (p_bold, y_prime)
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 GenReEncryption(e, pk, secparams): """ Algorithm 7.43: Generates a re-encryption e' = (a * pk^r', b*g^r') of the given ElGamal encryption e = (a,b) in G_q^2. The re-encryption e' is returned together with the randomization r' in Z_q Args: e (tuple): ElGamal encryption e = (a,b) pk (mpz): Encryption key pk secparams (SecurityParams): Collection of public security parameters Returns: tuple Re-Encryption (e', r') """ AssertMpz(pk) AssertClass(secparams, SecurityParams) (a, b) = e r_prime = randomMpz(secparams.q, secparams) a_prime = (a * gmpy2.powmod(pk, r_prime, secparams.p)) % secparams.p b_prime = (b * gmpy2.powmod(secparams.g, r_prime, secparams.p)) % secparams.p e_prime = ElGamalEncryption(a_prime, b_prime) return (e_prime, r_prime)
def GenShuffleProof(e_bold, e_prime_bold, r_prime_bold, psi, pk, secparams): """ Algorithm 7.44: Generates a NIZKP of shuffle relative to ElGamal encryptions e and e1, which is equivalent to proving knowledge of a permutation psi and randomizations r_prime such that e_prime = Shuffle_pk(e,r_prime,psi). The algorithm implements Wikström’s proof of a shuffle [19, 18], except for the fact that the offline and online phases are merged. For the proof verification, see Alg. 7.48. For further background information we refer to Section 5.5. Args: e_bold (list): ElGamal Encryptions e_prime_bold (list): Shuffled ElGamal Encryptions e' r_prime_bold (list): Re-encryption randomizations r' psi (list): Permutation pk (mpz): Encryption key pk secparams (SecurityParams): Collection of public security parameters Returns: ShuffleProof """ AssertList(e_bold) AssertList(e_prime_bold) AssertList(r_prime_bold) AssertList(psi) AssertMpz(pk) AssertClass(secparams, SecurityParams) N = len(e_bold) h_bold = GetGenerators(N, secparams) (c_bold, r_bold) = GenPermutationCommitment(psi, h_bold, secparams) u_bold = GetNIZKPChallenges(N, (e_bold, e_prime_bold, c_bold), secparams.tau, secparams) u_prime_bold = [None] * N for i in range(N): u_prime_bold[i] = u_bold[psi[i]] (c_hat_bold, r_hat_bold) = GenCommitmentChain(secparams.h, u_prime_bold, secparams) w_1 = randomMpz(secparams.q, secparams) w_2 = randomMpz(secparams.q, secparams) w_3 = randomMpz(secparams.q, secparams) w_4 = randomMpz(secparams.q, secparams) w_hat_bold = [None] * N w_prime_bold = [None] * N for i in range(N): w_hat_bold[i] = randomMpz(secparams.q, secparams) w_prime_bold[i] = randomMpz(secparams.q, secparams) # Computing the T values: t_1 = gmpy2.powmod(secparams.g, w_1, secparams.p) t_2 = gmpy2.powmod(secparams.g, w_2, secparams.p) h_product = mpz(1) for i in range(N): h_product = (h_product * gmpy2.powmod(h_bold[i],w_prime_bold[i], secparams.p) ) % secparams.p t_3 = (gmpy2.powmod(secparams.g, w_3, secparams.p) * h_product) % secparams.p a_prime_i_product = mpz(1) for i in range(N): a_prime_i_product = (a_prime_i_product * gmpy2.powmod(e_prime_bold[i].a, w_prime_bold[i], secparams.p)) % secparams.p t_4_1 = (gmpy2.powmod(pk, -w_4, secparams.p) * a_prime_i_product) % secparams.p b_prime_i_product = mpz(1) for i in range(N): b_prime_i_product = (b_prime_i_product * gmpy2.powmod(e_prime_bold[i].b, w_prime_bold[i], secparams.p)) % secparams.p t_4_2 = (gmpy2.powmod(secparams.g, -w_4, secparams.p) * b_prime_i_product) % secparams.p c_hat_bold_tmp = [] c_hat_bold_tmp.append(secparams.h) c_hat_bold_tmp.extend(c_hat_bold) t_hat_bold = [None] * N for i in range(N): t_hat_bold[i] = (gmpy2.powmod(secparams.g, w_hat_bold[i], secparams.p) * gmpy2.powmod(c_hat_bold_tmp[i], w_prime_bold[i], secparams.p) ) % secparams.p del c_hat_bold_tmp[0] # remove the element c_0 that we manually inserted ??????????? t = (t_1, t_2, t_3, (t_4_1, t_4_2), t_hat_bold) # Computing the challenge: y = (e_bold, e_prime_bold, c_bold, c_hat_bold, pk) c = GetNIZKPChallenge(y, t, secparams.tau, secparams) # Computing the S values: r_line = mpz(0) for i in range(N): r_line = (r_line + r_bold[i]) % secparams.q s_1 = mpz(0) for i in range(N): s_1 = (w_1 + c * r_line) % secparams.q v_bold = [None] * N v_bold[N-1] = mpz(1) for i in reversed(range(N-1)): v_bold[i] = (u_prime_bold[i+1] * v_bold[i+1]) % secparams.q r_hat = mpz(0) for i in range(N): r_hat = (r_hat + (r_hat_bold[i] * v_bold[i])) % secparams.q s_2 = (w_2 + c * r_hat ) % secparams.q r_tilde = mpz(0) for i in range(N): r_tilde = (r_tilde + (r_bold[i] * u_bold[i])) % secparams.q s_3 = (w_3 + c* r_tilde ) % secparams.q r_prime = mpz(0) for i in range(N): r_prime = (r_prime + (r_prime_bold[i] * u_bold[i])) % secparams.q s_4 = (w_4 + c* r_prime ) % secparams.q s_hat_bold = [None] * N s_prime_bold = [None] * N for i in range(N): s_hat_bold[i] = (w_hat_bold[i] + (c * r_hat_bold[i])) % secparams.q s_prime_bold[i] = (w_prime_bold[i] + (c * u_prime_bold[i])) % secparams.q s = (s_1, s_2, s_3, s_4, s_hat_bold, s_prime_bold) pi = ShuffleProof(t,s,c_bold,c_hat_bold) return pi
def GenResponse(v, a_bold, pk, n_bold, k_bold, E_bold, P_bold, secparams): """ Algorithm 7.25: Generates the response beta for the given OT query a. The messages to transfer are byte array representations of the n points (p_v,1, ... p_v,n). Along with beta, the algorithm also returns the randomizations r used to generate the response. Args: v (int): Voter Index a_bold (list of mpz): Queries pk (mpz): Encryption Key n_bold (list of int): Number of candidates k_bold (list of int): Number of selections E_bold (list of list): Eligibility matrix P_bold (list of list): Points N x n secparams (SecurityParams): Collection of public security parameters Returns: tuple: (beta, r) """ AssertInt(v) AssertList(a_bold) for a in a_bold: assert IsMember(a[0], secparams), "All elements of a_bold must be in G_q" for a in a_bold: assert IsMember(a[1], secparams), "All elements of a_bold must be in G_q" AssertMpz(pk) assert IsMember(pk, secparams), "Public key must be in G_q" assert pk != mpz(1), "Public key cannot be equal to 1" AssertList(n_bold) AssertList(k_bold) AssertList(E_bold) AssertList(P_bold) AssertClass(secparams, SecurityParams) n = sum(n_bold) k = len(a_bold) t = len(n_bold) M = [] z_1 = randomMpz(secparams.q, secparams) z_2 = randomMpz(secparams.q, secparams) beta = [] b_bold = [] for j in range(k): beta.append(randomQuadResMpz(secparams)) b_j = gmpy2.powmod(a_bold[j][0], z_1, secparams.p) b_j *= gmpy2.powmod(a_bold[j][1], z_2, secparams.p) b_j *= beta[j] b_j %= secparams.p b_bold.append(b_j) l_M = ceil(secparams.L_M / secparams.L) p_bold = GetPrimes(n, secparams) n_prime = 0 k_prime = 0 C_bold = [[None for i in range(sum(k_bold))] for i in range(n)] for l in range(len(k_bold)): if E_bold[v][l] != 0: for i in range(n_prime, n_prime + n_bold[l]): p_prime_i = gmpy2.powmod(p_bold[i], z_1, secparams.p) M.append( ToByteArrayN(P_bold[v][i].x, secparams.L_M // 2) + ToByteArrayN(P_bold[v][i].y, secparams.L_M // 2)) for j in range(k_prime, k_prime + E_bold[v][l] * k_bold[l]): k_ij = p_prime_i * beta[j] % secparams.p k_tmp = bytearray() for c in range(l_M): k_tmp += RecHash([k_ij, c], secparams) K_ij = Truncate(k_tmp, secparams.L_M) C_bold[i][j] = XorByteArray([M[i], K_ij]) k_prime = k_prime + E_bold[v][l] * k_bold[l] n_prime = n_prime + n_bold[l] d = (gmpy2.powmod(pk, z_1, secparams.p) * gmpy2.powmod(secparams.g, z_2, secparams.p)) % secparams.p beta = Response(b_bold, C_bold, d) z = (z_1, z_2) return (beta, z)