Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: third_party/google-endpoints/jwkest/elliptic.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # --- ELLIPTIC CURVE MATH ------------------------------------------------------
2 #
3 # curve definition: y^2 = x^3 - p*x - q
4 # over finite field: Z/nZ* (prime residue classes modulo a prime number n)
5 #
6 #
7 # COPYRIGHT (c) 2010 by Toni Mattis <solaris@live.de>
8 # ------------------------------------------------------------------------------
9
10 """
11 Module for elliptic curve arithmetic over a prime field GF(n).
12 E(GF(n)) takes the form y**2 == x**3 - p*x - q (mod n) for a prime n.
13
14 0. Structures used by this module
15
16 PARAMETERS and SCALARS are non-negative (long) integers.
17
18 A POINT (x, y), usually denoted p1, p2, ...
19 is a pair of (long) integers where 0 <= x < n and 0 <= y < n
20
21 A POINT in PROJECTIVE COORDINATES, usually denoted jp1, jp2, ...
22 takes the form (X, Y, Z, Z**2, Z**3) where x = X / Z**2
23 and y = Y / z**3. This form is called Jacobian coordinates.
24
25 The NEUTRAL element "0" or "O" is represented by None
26 in both coordinate systems.
27
28 1. Basic Functions
29
30 euclid() Is the Extended Euclidean Algorithm.
31 inv() Computes the multiplicative inversion modulo n.
32 curve_q() Finds the curve parameter q (mod n)
33 when p and a point are given.
34 element() Tests whether a point (x, y) is on the curve.
35
36 2. Point transformations
37
38 to_projective() Converts a point (x, y) to projective coordinates.
39 from_projective() Converts a point from projective coordinates
40 to (x, y) using the transformation described above.
41 neg() Computes the inverse point -P in both coordinate
42 systems.
43
44 3. Slow point arithmetic
45
46 These algorithms make use of basic geometry and modular arithmetic
47 thus being suitable for small numbers and academic study.
48
49 add() Computes the sum of two (x, y)-points
50 mul() Perform scalar multiplication using "double & add"
51
52 4. Fast point arithmetic
53
54 These algorithms make use of projective coordinates, signed binary
55 expansion and a JSP-like approach (joint sparse form).
56
57 The following functions consume and return projective coordinates:
58
59 addf() Optimized point addition.
60 doublef() Optimized point doubling.
61 mulf() Highly optimized scalar multiplication.
62 muladdf() Highly optimized addition of two products.
63
64 The following functions use the optimized ones above but consume
65 and output (x, y)-coordinates for a more convenient usage:
66
67 mulp() Encapsulates mulf()
68 muladdp() Encapsulates muladdf()
69
70 For single additions add() is generally faster than an encapsulation of
71 addf() which would involve expensive coordinate transformations.
72 Hence there is no addp() and doublep().
73 """
74 from __future__ import division
75 try:
76 from builtins import zip
77 from builtins import range
78 except ImportError:
79 pass
80 #from past.utils import old_div
81
82 # BASIC MATH -------------------------------------------------------------------
83
84
85 def euclid(a, b):
86 """Solve x*a + y*b = ggt(a, b) and return (x, y, ggt(a, b))"""
87 # Non-recursive approach hence suitable for large numbers
88 x = yy = 0
89 y = xx = 1
90 while b:
91 q = a // b
92 a, b = b, a % b
93 x, xx = xx - q * x, x
94 y, yy = yy - q * y, y
95 return xx, yy, a
96
97
98 def inv(a, n):
99 """Perform inversion 1/a modulo n. a and n should be COPRIME."""
100 # coprimality is not checked here in favour of performance
101 i = euclid(a, n)[0]
102 while i < 0:
103 i += n
104 return i
105
106
107 def curve_q(x, y, p, n):
108 """Find curve parameter q mod n having point (x, y) and parameter p"""
109 return ((x * x - p) * x - y * y) % n
110
111
112 def element(point, p, q, n):
113 """Test, whether the given point is on the curve (p, q, n)"""
114 if point:
115 x, y = point
116 return (x * x * x - p * x - q) % n == (y * y) % n
117 else:
118 return True
119
120
121 def to_projective(p):
122 """Transform point p given as (x, y) to projective coordinates"""
123 if p:
124 return (p[0], p[1], 1, 1, 1)
125 else:
126 return None # Identity point (0)
127
128
129 def from_projective(jp, n):
130 """Transform a point from projective coordinates to (x, y) mod n"""
131 if jp:
132 return (jp[0] * inv(jp[3], n)) % n, (jp[1] * inv(jp[4], n)) % n
133 else:
134 return None # Identity point (0)
135
136
137 def neg(p, n):
138 """Compute the inverse point to p in any coordinate system"""
139 return (p[0], (n - p[1]) % n) + p[2:] if p else None
140
141
142 # POINT ADDITION ---------------------------------------------------------------
143
144 # addition of points in y**2 = x**3 - p*x - q over <Z/nZ*; +>
145 def add(p, q, n, p1, p2):
146 """Add points p1 and p2 over curve (p, q, n)"""
147 if p1 and p2:
148 x1, y1 = p1
149 x2, y2 = p2
150 if (x1 - x2) % n:
151 s = ((y1 - y2) * inv(x1 - x2, n)) % n # slope
152 x = (s * s - x1 - x2) % n # intersection with curve
153 return x, n - (y1 + s * (x - x1)) % n
154 else:
155 if (y1 + y2) % n: # slope s calculated by derivation
156 s = ((3 * x1 * x1 - p) * inv(2 * y1, n)) % n
157 x = (s * s - 2 * x1) % n # intersection with curve
158 return x, n - (y1 + s * (x - x1)) % n
159 else:
160 return None
161 else: # either p1 is not none -> ret. p1, otherwiese p2, which may be
162 return p1 if p1 else p2 # none too.
163
164
165 # faster addition: redundancy in projective coordinates eliminates
166 # expensive inversions mod n.
167 def addf(p, q, n, jp1, jp2):
168 """Add jp1 and jp2 in projective (jacobian) coordinates."""
169 if jp1 and jp2:
170
171 x1, y1, z1, z1s, z1c = jp1
172 x2, y2, z2, z2s, z2c = jp2
173
174 s1 = (y1 * z2c) % n
175 s2 = (y2 * z1c) % n
176
177 u1 = (x1 * z2s) % n
178 u2 = (x2 * z1s) % n
179
180 if (u1 - u2) % n:
181
182 h = (u2 - u1) % n
183 r = (s2 - s1) % n
184
185 hs = (h * h) % n
186 hc = (hs * h) % n
187
188 x3 = (-hc - 2 * u1 * hs + r * r) % n
189 y3 = (-s1 * hc + r * (u1 * hs - x3)) % n
190 z3 = (z1 * z2 * h) % n
191
192 z3s = (z3 * z3) % n
193 z3c = (z3s * z3) % n
194
195 return x3, y3, z3, z3s, z3c
196
197 else:
198 if (s1 + s2) % n:
199 return doublef(p, q, n, jp1)
200 else:
201 return None
202 else:
203 return jp1 if jp1 else jp2
204
205 # explicit point doubling using redundant coordinates
206 def doublef(p, q, n, jp):
207 """Double jp in projective (jacobian) coordinates"""
208 if not jp:
209 return None
210 x1, y1, z1, z1p2, z1p3 = jp
211
212 y1p2 = (y1 * y1) % n
213 a = (4 * x1 * y1p2) % n
214 b = (3 * x1 * x1 - p * z1p3 * z1) % n
215 x3 = (b * b - 2 * a) % n
216 y3 = (b * (a - x3) - 8 * y1p2 * y1p2) % n
217 z3 = (2 * y1 * z1) % n
218 z3p2 = (z3 * z3) % n
219
220 return x3, y3, z3, z3p2, (z3p2 * z3) % n
221
222
223 # SCALAR MULTIPLICATION --------------------------------------------------------
224
225 # scalar multiplication p1 * c = p1 + p1 + ... + p1 (c times) in O(log(n))
226 def mul(p, q, n, p1, c):
227 """multiply point p1 by scalar c over curve (p, q, n)"""
228 res = None
229 while c > 0:
230 if c & 1:
231 res = add(p, q, n, res, p1)
232 c >>= 1 # c = c / 2
233 p1 = add(p, q, n, p1, p1) # p1 = p1 * 2
234 return res
235
236
237 # this method allows _signed_bin() to choose between 1 and -1. It will select
238 # the sign which leaves the higher number of zeroes in the binary
239 # representation (the higher GDB).
240 def _gbd(n):
241 """Compute second greatest base-2 divisor"""
242 i = 1
243 if n <= 0: return 0
244 while not n % i:
245 i <<= 1
246 return i >> 2
247
248
249 # This method transforms n into a binary representation having signed bits.
250 # A signed binary expansion contains more zero-bits hence reducing the number
251 # of additions required by a multiplication algorithm.
252 #
253 # Example: 15 ( 0b1111 ) can be written as 16 - 1, resulting in (1,0,0,0,-1)
254 # and saving 2 additions. Subtraction can be performed as
255 # efficiently as addition.
256 def _signed_bin(n):
257 """Transform n into an optimized signed binary representation"""
258 r = []
259 while n > 1:
260 if n & 1:
261 cp = _gbd(n + 1)
262 cn = _gbd(n - 1)
263 if cp > cn: # -1 leaves more zeroes -> subtract -1 (= +1)
264 r.append(-1)
265 n += 1
266 else: # +1 leaves more zeroes -> subtract +1 (= -1)
267 r.append(+1)
268 n -= 1
269 else:
270 r.append(0) # be glad about one more zero
271 n >>= 1
272 r.append(n)
273 return r[::-1]
274
275
276 # This multiplication algorithm combines signed binary expansion and
277 # fast addition using projective coordinates resulting in 5 to 10 times
278 # faster multiplication.
279 def mulf(p, q, n, jp1, c):
280 """Multiply point jp1 by c in projective coordinates"""
281 sb = _signed_bin(c)
282 res = None
283 jp0 = neg(jp1, n) # additive inverse of jp1 to be used fot bit -1
284 for s in sb:
285 res = doublef(p, q, n, res)
286 if s:
287 res = addf(p, q, n, res, jp1) if s > 0 else \
288 addf(p, q, n, res, jp0)
289 return res
290
291
292 # Encapsulates mulf() in order to enable flat coordinates (x, y)
293 def mulp(p, q, n, p1, c):
294 """Multiply point p by c using fast multiplication"""
295 return from_projective(mulf(p, q, n, to_projective(p1), c), n)
296
297
298 # Sum of two products using Shamir's trick and signed binary expansion
299 def muladdf(p, q, n, jp1, c1, jp2, c2):
300 """Efficiently compute c1 * jp1 + c2 * jp2 in projective coordinates"""
301 s1 = _signed_bin(c1)
302 s2 = _signed_bin(c2)
303 diff = len(s2) - len(s1)
304 if diff > 0:
305 s1 = [0] * diff + s1
306 elif diff < 0:
307 s2 = [0] * -diff + s2
308
309 jp1p2 = addf(p, q, n, jp1, jp2)
310 jp1n2 = addf(p, q, n, jp1, neg(jp2, n))
311
312 precomp = ((None, jp2, neg(jp2, n)),
313 (jp1, jp1p2, jp1n2),
314 (neg(jp1, n), neg(jp1n2, n), neg(jp1p2, n)))
315 res = None
316
317 for i, j in zip(s1, s2):
318 res = doublef(p, q, n, res)
319 if i or j:
320 res = addf(p, q, n, res, precomp[i][j])
321 return res
322
323
324 # Encapsulate muladdf()
325 def muladdp(p, q, n, p1, c1, p2, c2):
326 """Efficiently compute c1 * p1 + c2 * p2 in (x, y)-coordinates"""
327 return from_projective(muladdf(p, q, n,
328 to_projective(p1), c1,
329 to_projective(p2), c2), n)
330
331 # POINT COMPRESSION ------------------------------------------------------------
332
333 # Compute the square root modulo n
334
335
336 # Determine the sign-bit of a point allowing to reconstruct y-coordinates
337 # when x and the sign-bit are given:
338 def sign_bit(p1):
339 """Return the signedness of a point p1"""
340 return p1[1] % 2 if p1 else 0
341
342 # Reconstruct the y-coordinate when curve parameters, x and the sign-bit of
343 # the y coordinate are given:
344 def y_from_x(x, p, q, n, sign):
345 """Return the y coordinate over curve (p, q, n) for given (x, sign)"""
346
347 # optimized form of (x**3 - p*x - q) % n
348 a = (((x * x) % n - p) * x - q) % n
349
350
351 if __name__ == "__main__":
352 import rsa
353 import time
354
355 t = time.time()
356 n = rsa.get_prime(256 / 8, 20)
357 tp = time.time() - t
358 p = rsa.random.randint(1, n)
359 p1 = (rsa.random.randint(1, n), rsa.random.randint(1, n))
360 q = curve_q(p1[0], p1[1], p, n)
361 r1 = rsa.random.randint(1, n)
362 r2 = rsa.random.randint(1, n)
363 q1 = mulp(p, q, n, p1, r1)
364 q2 = mulp(p, q, n, p1, r2)
365 s1 = mulp(p, q, n, q1, r2)
366 s2 = mulp(p, q, n, q2, r1)
367 s1 == s2
368 tt = time.time() - t
369
370 def test(tcount, bits=256):
371 n = rsa.get_prime(bits / 8, 20)
372 p = rsa.random.randint(1, n)
373 p1 = (rsa.random.randint(1, n), rsa.random.randint(1, n))
374 q = curve_q(p1[0], p1[1], p, n)
375 p2 = mulp(p, q, n, p1, rsa.random.randint(1, n))
376
377 c1 = [rsa.random.randint(1, n) for i in range(tcount)]
378 c2 = [rsa.random.randint(1, n) for i in range(tcount)]
379 c = list(zip(c1, c2))
380
381 t = time.time()
382 for i, j in c:
383 from_projective(addf(p, q, n,
384 mulf(p, q, n, to_projective(p1), i),
385 mulf(p, q, n, to_projective(p2), j)), n)
386 t1 = time.time() - t
387 t = time.time()
388 for i, j in c:
389 muladdp(p, q, n, p1, i, p2, j)
390 t2 = time.time() - t
391
392 return tcount, t1, t2
393
394
395
OLDNEW
« no previous file with comments | « third_party/google-endpoints/jwkest/ecc.py ('k') | third_party/google-endpoints/jwkest/extra.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698