You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

344 lines
9.2 KiB

  1. from hashlib import sha256
  2. # Implementation of Sigma protocol & OR proofs
  3. def hash_two_points(a, b):
  4. h = sha256((str(a)+str(b)).encode('utf-8'))
  5. return int(h.hexdigest(), 16)
  6. def generic_verify(g, X, A, c, z):
  7. return g * int(z) == X * int(c) + A
  8. ###
  9. # Sigma protocol interactive
  10. ###
  11. class Prover_interactive:
  12. def __init__(self, F, g):
  13. self.F = F # Z_q
  14. self.g = g # elliptic curve generator
  15. def new_key(self):
  16. self.w = self.F.random_element()
  17. X = self.g * int(self.w)
  18. return X
  19. def new_commitment(self):
  20. self.a = self.F.random_element()
  21. A = self.g * int(self.a)
  22. return A
  23. def gen_proof(self, c):
  24. return int(self.a) + int(c) * int(self.w)
  25. class Verifier_interactive:
  26. def __init__(self, F, g):
  27. self.F = F
  28. self.g = g
  29. def new_challenge(self, A):
  30. self.A = A
  31. self.c = self.F.random_element()
  32. return self.c
  33. def verify(self, X, z):
  34. return self.g * int(z) == X * int(self.c) + self.A
  35. ###
  36. # Sigma protocol non-interactive
  37. ###
  38. class Prover:
  39. def __init__(self, F, g):
  40. self.F = F # Z_p
  41. self.g = g # elliptic curve generator
  42. def new_key(self):
  43. self.w = self.F.random_element()
  44. X = self.g * int(self.w)
  45. return X
  46. def gen_proof(self, X):
  47. a = self.F.random_element()
  48. A = self.g * int(a)
  49. c = hash_two_points(A, X)
  50. z = int(a) + c * int(self.w)
  51. return A, z
  52. class Verifier:
  53. def __init__(self, F, g):
  54. self.F = F
  55. self.g = g
  56. def verify(self, X, A, z):
  57. c = hash_two_points(A, X)
  58. return self.g * int(z) == X * c + A
  59. class Simulator:
  60. def __init__(self, F, g):
  61. self.F = F
  62. self.g = g
  63. def simulate(self, X):
  64. c = self.F.random_element()
  65. z = self.F.random_element()
  66. # A = g * int(z) + X*(-int(c))
  67. A = g * int(z) - X * int(c)
  68. return A, c, z
  69. ###
  70. # OR proof (with 2 parties)
  71. ###
  72. class ORProver_2parties:
  73. def __init__(self, F, g):
  74. self.F = F # Z_p
  75. self.g = g # elliptic curve generator
  76. def new_key(self):
  77. self.w = self.F.random_element()
  78. X = self.g * int(self.w)
  79. return X
  80. def gen_commitments(self, xs):
  81. # gen commitment A
  82. self.a = self.F.random_element()
  83. A = self.g * int(self.a)
  84. # run the simulator for 1-b
  85. sim = Simulator(self.F, self.g)
  86. A_1, c_1, z_1 = sim.simulate(xs[1])
  87. self.A_1 = A_1
  88. self.c_1 = c_1
  89. self.z_1 = z_1
  90. return [A, A_1]
  91. def gen_proof(self, s):
  92. # split the challenge s = c xor c_1
  93. c = int(s) ^^ int(self.c_1)
  94. # compute z
  95. z = int(self.a) + int(c) * int(self.w)
  96. # note, here the order of the returned elements is always the same, in
  97. # a real-world implementation would be shuffled
  98. return [c, self.c_1], [z, self.z_1]
  99. class ORVerifier_2parties:
  100. def __init__(self, F, g):
  101. self.F = F
  102. self.g = g
  103. def new_challenge(self, As):
  104. self.As = As
  105. self.s = self.F.random_element()
  106. return self.s
  107. def verify(self, Xs, cs, zs):
  108. assert self.s == int(cs[0]) ^^ int(cs[1])
  109. assert self.g * int(zs[0]) == Xs[0] * int(cs[0]) + self.As[0]
  110. assert self.g * int(zs[1]) == Xs[1] * int(cs[1]) + self.As[1]
  111. ###
  112. # OR proof (with n parties)
  113. ###
  114. class ORProver:
  115. def __init__(self, F, g):
  116. self.F = F # Z_p
  117. self.g = g # elliptic curve generator
  118. def new_key(self):
  119. self.w = self.F.random_element()
  120. X = self.g * int(self.w)
  121. return X
  122. def gen_commitments(self, xs):
  123. # gen commitment A
  124. self.a = self.F.random_element()
  125. A = self.g * int(self.a)
  126. self.As = [A]
  127. # run the simulator for the rest of Xs
  128. sim = Simulator(self.F, self.g)
  129. self.cs = []
  130. self.zs = []
  131. for i in range(1, len(xs)):
  132. A_1, c_1, z_1 = sim.simulate(xs[i])
  133. self.As.append(A_1)
  134. self.cs.append(c_1)
  135. self.zs.append(z_1)
  136. return self.As
  137. def gen_proof(self, s):
  138. # split the challenge s = c xor c_1 xor c_2 xor ... xor c_n
  139. c = int(s)
  140. for i in range(len(self.cs)):
  141. c = c ^^ int(self.cs[i])
  142. self.cs.insert(0, c) # add c at the beginning of cs array
  143. # compute z
  144. z = int(self.a) + int(c) * int(self.w)
  145. self.zs.insert(0, z) # add z at the beginning of zs array
  146. # note, here the order of the returned elements is always the same, in
  147. # a real-world implementation would be shuffled
  148. return self.cs, self.zs
  149. class ORVerifier:
  150. def __init__(self, F, g):
  151. self.F = F
  152. self.g = g
  153. def new_challenge(self, As):
  154. self.As = As
  155. self.s = self.F.random_element()
  156. return self.s
  157. def verify(self, Xs, cs, zs):
  158. # check s == c_0 xor c_1 xor c_2 xor ... xor c_n
  159. computed_s = int(cs[0])
  160. for i in range(1, len(cs)):
  161. computed_s = computed_s ^^ int(cs[i])
  162. assert self.s == computed_s
  163. # check g*z == X*c + A (in multiplicative notation would g^z ==X^c * A)
  164. for i in range(len(Xs)):
  165. assert self.g * int(zs[i]) == Xs[i] * int(cs[i]) + self.As[i]
  166. # Tests
  167. import unittest, operator
  168. # ethereum elliptic curve
  169. p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
  170. a = 0
  171. b = 7
  172. F = GF(p)
  173. E = EllipticCurve(F, [a,b])
  174. GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
  175. GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
  176. g = E(GX,GY)
  177. n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
  178. h = 1
  179. q = g.order()
  180. assert is_prime(p)
  181. assert is_prime(q)
  182. class TestSigmaProtocol(unittest.TestCase):
  183. def test_interactive(self):
  184. alice = Prover_interactive(F, g)
  185. # Alice generates witness w & statement X
  186. X = alice.new_key()
  187. assert X == alice.g * int(alice.w)
  188. # Alice generates the commitment A
  189. A = alice.new_commitment()
  190. assert A == alice.g * int(alice.a)
  191. # Bob generates the challenge (and stores A)
  192. bob = Verifier_interactive(F, g)
  193. c = bob.new_challenge(A)
  194. # Alice generates the proof
  195. z = alice.gen_proof(c)
  196. # Bob verifies the proof
  197. assert bob.verify(X, z)
  198. # check with the generic_verify function
  199. assert generic_verify(g, X, A, c, z)
  200. def test_non_interactive(self):
  201. alice = Prover(F, g)
  202. # Alice generates witness w & statement X
  203. X = alice.new_key()
  204. assert X == alice.g * int(alice.w)
  205. # Alice generates the proof
  206. A, z = alice.gen_proof(X)
  207. # Bob generates the challenge
  208. bob = Verifier(F, g)
  209. # Bob verifies the proof
  210. assert bob.verify(X, A, z)
  211. # check with the generic_verify function
  212. c = hash_two_points(A, X)
  213. assert generic_verify(g, X, A, c, z)
  214. def test_simulator(self):
  215. sim = Simulator(F, g)
  216. # set a public key X, for which we don't know w
  217. unknown_w = 3
  218. X = g * unknown_w
  219. # simulate for X
  220. A, c, z = sim.simulate(X)
  221. # verify the simulated proof
  222. assert generic_verify(g, X, A, c, z)
  223. class TestORProof(unittest.TestCase):
  224. def test_2_parties(self):
  225. # set a public key X, for which we don't know w
  226. unknown_w = 3
  227. X_1 = g * unknown_w
  228. alice = ORProver_2parties(F, g)
  229. # Alice generates key pair
  230. X = alice.new_key()
  231. Xs = [X, X_1]
  232. # Alice generates commitments (internally running the simulator)
  233. As = alice.gen_commitments(Xs)
  234. # Bob generates the challenge (and stores As)
  235. bob = ORVerifier_2parties(F, g)
  236. s = bob.new_challenge(As)
  237. # Alice generates the ORproof
  238. cs, zs = alice.gen_proof(s)
  239. # Bob verifies the proofs
  240. bob.verify(Xs, cs, zs)
  241. def test_n_parties(self):
  242. # set n public keys X, for which we don't know w
  243. Xs = []
  244. for i in range(10):
  245. X_i = g * i
  246. Xs.append(X_i)
  247. alice = ORProver(F, g)
  248. # Alice generates key pair
  249. X = alice.new_key()
  250. Xs.insert(0, X) # add X at the beginning of Xs array
  251. # Alice generates commitments (internally running the simulator)
  252. As = alice.gen_commitments(Xs)
  253. # Bob generates the challenge (and stores As)
  254. bob = ORVerifier(F, g)
  255. s = bob.new_challenge(As)
  256. # Alice generates the ORproof
  257. cs, zs = alice.gen_proof(s)
  258. # Bob verifies the proofs
  259. bob.verify(Xs, cs, zs)
  260. if __name__ == '__main__':
  261. unittest.main()