├── README.md ├── blind_evaluation_of_polynomials.py ├── blind_evaluation_of_polynomials_verifiable.py ├── computations_to_polynomials.py ├── homomorphic_hiding.py └── knowledge_of_coefficient_test_and_assumption.py /README.md: -------------------------------------------------------------------------------- 1 | Simple python for: https://z.cash/technology/zksnarks 2 | 3 | 4 | Accepting PR for cleaner explanations and parts [5 (computation to polynomials)](https://z.cash/blog/snark-explain5), [6 (pinocchio)](https://z.cash/blog/snark-explain6) and [7 (elliptic curve pairings)](https://z.cash/blog/snark-explain7) 5 | 6 | 7 | Parts 5 and 6 rely on QAP (quadratic arithmetic programs). See [Vitalik's compiler](https://github.com/ethereum/research/tree/master/zksnark) 8 | -------------------------------------------------------------------------------- /blind_evaluation_of_polynomials.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | https://z.cash/blog/snark-explain2 4 | Given Alice has a Polynomial of degree d 5 | Given Bob has a point, s, on the polynomial 6 | We want Bob to find out E(P(s)) 7 | Without Alice telling Bob P and without Bob telling Alice about s 8 | 9 | Solution: 10 | Bob sends Alice: E(1), E(s), E(s^2), ... , E(s^d) 11 | Alice computs E(P(s)) from that because: 12 | E(ax + by) = E(x)^a * E(y)^b 13 | So: E(P(x)) = E(x^0)^a_0 + E(x^1)^a_1 + ... + E(x^d)^a_d 14 | """ 15 | 16 | 17 | class Finite_Integer_HH: 18 | def __init__(self, g, p): 19 | self.p = p 20 | self.g = g 21 | 22 | def hh(self, a, b): 23 | return (a * b) % self.p 24 | 25 | def linear_combination(self, hidings, alphas): 26 | """ E(ax + by), given E_x, E_y, a, b """ 27 | result = 1 28 | for hiding, alpha in zip(hidings, alphas): 29 | result = (result * hiding ** alpha) % self.p 30 | return result 31 | 32 | def __call__(self, x): 33 | return (self.g ** x) % self.p 34 | 35 | class Bob: 36 | def __init__(self, E, s, d): 37 | self.E = E 38 | self.s = s 39 | self.d = d 40 | 41 | def create_hidings(self): 42 | return [self.E(self.s**i) for i in range(self.d+1)] 43 | 44 | class Alice: 45 | def __init__(self, E, alphas): 46 | self.E = E 47 | self.alphas = alphas 48 | self.d = len(alphas) - 1 49 | 50 | def compute_E_P_s_zk(self, hidings): 51 | return self.E.linear_combination(hidings, self.alphas) 52 | 53 | def compute_E_P_s(self, s): 54 | P_s = sum(self.alphas[i] * (s ** i) for i in range(self.d + 1)) 55 | return self.E(P_s) 56 | 57 | def test_generate_true_proof(): 58 | E = Finite_Integer_HH(19, 191) # random cyclic group 59 | A = Alice(E, [1, 2]) # 1 + 2 * x 60 | s = 1 61 | B = Bob(E, s, A.d) 62 | hidings = B.create_hidings() 63 | 64 | E_P_s_zk = A.compute_E_P_s_zk(hidings) 65 | E_P_s_full_knowledge = A.compute_E_P_s(s) 66 | print(E_P_s_zk == E_P_s_full_knowledge) 67 | 68 | if __name__ == '__main__': 69 | test_generate_true_proof() 70 | 71 | -------------------------------------------------------------------------------- /blind_evaluation_of_polynomials_verifiable.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | """ 5 | 6 | E(x) = g^x (mod p) 7 | E(x + y) = g^(x+y) = g^x + g^y = E(x) + E(y) 8 | 9 | Alice has a P(x) of order d 10 | Bob chooses alpha in F_p and s on P 11 | computes: 12 | V = = [ E(s**i) for i in range(d+1) ] 13 | V2 = = [E(alpha * s^i)] 14 | Alice computes: 15 | a = g^P(s) <- linear combinations of V 16 | b = g^(alpha * P(s)) <- linear combination of V2 17 | 18 | Bob checks that: 19 | b = a^alpha = g^P(s)^alpha = g^(P(s) * alpha) = b 20 | 21 | """ 22 | import random 23 | def randomly_pick_a_point_in_fp(p): 24 | return random.randint(1, p) 25 | 26 | class Finite_Integer_HH: 27 | def __init__(self, g, p): 28 | self.p = p 29 | self.g = g 30 | 31 | def linear_combination(self, hidings, alphas): 32 | """ E(ax + by), given E_x, E_y, a, b """ 33 | result = 1 34 | for hiding, alpha in zip(hidings, alphas): 35 | result = (result * hiding ** alpha) % self.p 36 | return result 37 | 38 | def __call__(self, x): 39 | return (self.g ** x) % self.p 40 | 41 | class Alice: 42 | def __init__(self, E, alphas): 43 | self.E = E 44 | self.alphas = alphas 45 | self.d = len(alphas) - 1 46 | 47 | def compute_E_P_s_zk(self, hidings): 48 | return self.E.linear_combination(hidings, self.alphas) 49 | 50 | def compute_E_P_s(self, s): 51 | P_s = sum(self.alphas[i] * (s ** i) for i in range(self.d + 1)) 52 | return self.E(P_s) 53 | 54 | class Bob: 55 | def __init__(self, E, s, d): 56 | self.E = E 57 | self.s = s 58 | self.d = d 59 | self.alpha = None 60 | 61 | def check_verifiability(self, a, b): 62 | return b == (a**self.alpha) % self.E.p 63 | 64 | def create_hidings(self): 65 | V, V2 = [], [] 66 | self.alpha = randomly_pick_a_point_in_fp(self.E.p) 67 | for i in range(self.d + 1): 68 | V.append(self.E(self.s**i)) 69 | V2.append(self.E(self.alpha * self.s**i)) 70 | return V, V2 71 | 72 | def test_generate_true_proof(): 73 | E = Finite_Integer_HH(19, 191) # random cyclic group 74 | A = Alice(E, [1, 2, 3, 5]) # 1 + 2 * x + 3 * x ^2 + 5 * x^5 75 | s = 1 76 | B = Bob(E, s, A.d) 77 | hidings_one, hidings_two = B.create_hidings() 78 | 79 | E_P_s_zk_a = A.compute_E_P_s_zk(hidings_one) 80 | E_P_s_zk_b = A.compute_E_P_s_zk(hidings_two) 81 | E_P_s_full_knowledge = A.compute_E_P_s(s) 82 | print(E_P_s_zk_a == E_P_s_full_knowledge) 83 | print(B.check_verifiability(E_P_s_zk_a, E_P_s_zk_b)) 84 | 85 | if __name__ == '__main__': 86 | test_generate_true_proof() -------------------------------------------------------------------------------- /computations_to_polynomials.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | L = [ c[i] * L[i] for i in range(d+1)] 4 | R = [ c[i] * R[i] for i in range(d+1)] 5 | O = [ c[i] * O[i] for i in range(d+1)] 6 | P = L * R - O 7 | 8 | 9 | """ -------------------------------------------------------------------------------- /homomorphic_hiding.py: -------------------------------------------------------------------------------- 1 | # https://z.cash/blog/snark-explain 2 | 3 | """ 4 | Looking for E(x) Such That: 5 | if x != y: 6 | E(x) != E(y) 7 | 8 | if a == E(x) and b == E(y): 9 | there exists f, such that: 10 | f(E(x), E(y)) = E(x + y) 11 | """ 12 | 13 | class Finite_Integer_HH: 14 | def __init__(self, g, p): 15 | self.p = p 16 | self.g = g 17 | 18 | def hh(self, a, b): 19 | return (a * b) % self.p 20 | 21 | def __call__(self, x): 22 | return (self.g ** x) % self.p 23 | 24 | class Simple_Zero_Knowledge_Proof: 25 | """ 26 | proving that you know x,y such that x + y = c 27 | 28 | Not really zero knowledge because an attack can iterate through Z to find E(i) = x 29 | 30 | """ 31 | def __init__(self, E, c): 32 | self.E = E 33 | self.c = c 34 | 35 | def generate_proof(self, x, y): 36 | return self.E(x), self.E(y) 37 | 38 | def validate_proof(self, E_x, E_y): 39 | return self.E.hh(E_x, E_y) == self.E(self.c) 40 | 41 | 42 | def test_generate_true_proof(): 43 | x = 3 44 | y = 5 45 | c = 8 46 | 47 | E = Finite_Integer_HH(19, 191) # random cyclic group 48 | ZK = Simple_Zero_Knowledge_Proof(E, c) 49 | proof = ZK.generate_proof(x, y) 50 | print("true proof is", ZK.validate_proof(*proof)) 51 | 52 | def test_generate_false_proof(): 53 | x = 3 54 | y = 5 55 | c = 9 56 | 57 | E = Finite_Integer_HH(19, 191) # random cyclic group 58 | ZK = Simple_Zero_Knowledge_Proof(E, c) 59 | proof = ZK.generate_proof(x, y) 60 | print("false proof is", ZK.validate_proof(*proof)) 61 | 62 | if __name__ == '__main__': 63 | test_generate_true_proof() 64 | test_generate_false_proof() 65 | 66 | -------------------------------------------------------------------------------- /knowledge_of_coefficient_test_and_assumption.py: -------------------------------------------------------------------------------- 1 | # https://z.cash/blog/snark-explain3/ 2 | 3 | """ 4 | 5 | We need a way to enforce that Alice does in fact send E(P(s)) and not a random value 6 | 7 | """ 8 | import random 9 | from functools import reduce 10 | 11 | def get_factors(n): 12 | """ https://stackoverflow.com/questions/6800193/what-is-the-most-efficient-way-of-finding-all-the-factors-of-a-number-in-python#6800214 """ 13 | return set(reduce(list.__add__, 14 | ([i, n//i] for i in range(1, int(n**0.5) + 1) if n % i == 0))) 15 | 16 | def randomly_pick_a_point_in_fp(p): 17 | return random.randint(1, p) 18 | 19 | class Integer_Finite_Field: 20 | def __init__(self, p): 21 | self.p = p 22 | self.generators = self.get_generators() 23 | 24 | def get_generators(self): 25 | def test_for_gen(gen): 26 | n = (self.p - 1) // 2 27 | n = int(n) 28 | return gen**2 != 1 and gen**n != 1 29 | possible_generators = get_factors(self.p - 1) 30 | return list(filter(test_for_gen, possible_generators)) 31 | 32 | def pick_random_gen(self): 33 | return random.choice(self.generators) 34 | 35 | def __eq__(self, other): 36 | return self.p == other.p 37 | 38 | class Integer_Finite_Field_Element: 39 | def __init__(self, value, field): 40 | self.value = value 41 | self.field = field 42 | 43 | def __mul__(self, other): 44 | if not isinstance(other, int): 45 | other = other.value 46 | return Integer_Finite_Field_Element((other ** self.value) % self.field.p, self.field) 47 | 48 | def __repr__(self): 49 | return "Integer_Finite_Field_Element(" + str(self.value) + ")" 50 | 51 | def __eq__(self, other): 52 | return self.value == other.value and self.field == other.field 53 | 54 | class Bob: 55 | def __init__(self, field): 56 | self.field = field 57 | self.alpha = self.a = None 58 | 59 | def randomly_pick_a_point_in_fp(self): 60 | alpha = randomly_pick_a_point_in_fp(self.field.p) 61 | self.alpha = Integer_Finite_Field_Element(alpha, self.field) 62 | return self.alpha 63 | 64 | def randomly_pick_a_generator(self): 65 | self.a = Integer_Finite_Field_Element(self.field.pick_random_gen(), self.field) 66 | return self.a 67 | 68 | def get_b(self): 69 | return self.alpha * self.a 70 | 71 | class Alice: 72 | def __init__(self, field): 73 | self.field = field 74 | 75 | def get_a_b_prime(self, a, b): 76 | """ b_prime = gamma * b = gamma * alpha * a = alpha * (gamma * a) = alpha * a_prime """ 77 | gamma = randomly_pick_a_point_in_fp(self.field.p) 78 | gamma = Integer_Finite_Field_Element(gamma, self.field) 79 | return gamma * a, gamma * b 80 | 81 | 82 | def KC_test(Bob, Alice): 83 | alpha = Bob.randomly_pick_a_point_in_fp() 84 | a = Bob.randomly_pick_a_generator() 85 | b = Bob.get_b() # alpha * a 86 | a_prime, b_prime = Alice.get_a_b_prime(a, b) 87 | return b_prime == alpha * a_prime 88 | 89 | 90 | def test_generate_true_proof(): 91 | p = 191 92 | field = Integer_Finite_Field(p) 93 | B = Bob(field) 94 | A = Alice(field) 95 | print(KC_test(B, A)) 96 | 97 | if __name__ == '__main__': 98 | test_generate_true_proof() 99 | 100 | --------------------------------------------------------------------------------