| Index: third_party/google-endpoints/jwkest/elliptic.py
|
| diff --git a/third_party/google-endpoints/jwkest/elliptic.py b/third_party/google-endpoints/jwkest/elliptic.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6e762053dd5b6c323b740af4040a335f4abfe286
|
| --- /dev/null
|
| +++ b/third_party/google-endpoints/jwkest/elliptic.py
|
| @@ -0,0 +1,395 @@
|
| +# --- ELLIPTIC CURVE MATH ------------------------------------------------------
|
| +#
|
| +# curve definition: y^2 = x^3 - p*x - q
|
| +# over finite field: Z/nZ* (prime residue classes modulo a prime number n)
|
| +#
|
| +#
|
| +# COPYRIGHT (c) 2010 by Toni Mattis <solaris@live.de>
|
| +# ------------------------------------------------------------------------------
|
| +
|
| +"""
|
| +Module for elliptic curve arithmetic over a prime field GF(n).
|
| +E(GF(n)) takes the form y**2 == x**3 - p*x - q (mod n) for a prime n.
|
| +
|
| +0. Structures used by this module
|
| +
|
| + PARAMETERS and SCALARS are non-negative (long) integers.
|
| +
|
| + A POINT (x, y), usually denoted p1, p2, ...
|
| + is a pair of (long) integers where 0 <= x < n and 0 <= y < n
|
| +
|
| + A POINT in PROJECTIVE COORDINATES, usually denoted jp1, jp2, ...
|
| + takes the form (X, Y, Z, Z**2, Z**3) where x = X / Z**2
|
| + and y = Y / z**3. This form is called Jacobian coordinates.
|
| +
|
| + The NEUTRAL element "0" or "O" is represented by None
|
| + in both coordinate systems.
|
| +
|
| +1. Basic Functions
|
| +
|
| + euclid() Is the Extended Euclidean Algorithm.
|
| + inv() Computes the multiplicative inversion modulo n.
|
| + curve_q() Finds the curve parameter q (mod n)
|
| + when p and a point are given.
|
| + element() Tests whether a point (x, y) is on the curve.
|
| +
|
| +2. Point transformations
|
| +
|
| + to_projective() Converts a point (x, y) to projective coordinates.
|
| + from_projective() Converts a point from projective coordinates
|
| + to (x, y) using the transformation described above.
|
| + neg() Computes the inverse point -P in both coordinate
|
| + systems.
|
| +
|
| +3. Slow point arithmetic
|
| +
|
| + These algorithms make use of basic geometry and modular arithmetic
|
| + thus being suitable for small numbers and academic study.
|
| +
|
| + add() Computes the sum of two (x, y)-points
|
| + mul() Perform scalar multiplication using "double & add"
|
| +
|
| +4. Fast point arithmetic
|
| +
|
| + These algorithms make use of projective coordinates, signed binary
|
| + expansion and a JSP-like approach (joint sparse form).
|
| +
|
| + The following functions consume and return projective coordinates:
|
| +
|
| + addf() Optimized point addition.
|
| + doublef() Optimized point doubling.
|
| + mulf() Highly optimized scalar multiplication.
|
| + muladdf() Highly optimized addition of two products.
|
| +
|
| + The following functions use the optimized ones above but consume
|
| + and output (x, y)-coordinates for a more convenient usage:
|
| +
|
| + mulp() Encapsulates mulf()
|
| + muladdp() Encapsulates muladdf()
|
| +
|
| + For single additions add() is generally faster than an encapsulation of
|
| + addf() which would involve expensive coordinate transformations.
|
| + Hence there is no addp() and doublep().
|
| +"""
|
| +from __future__ import division
|
| +try:
|
| + from builtins import zip
|
| + from builtins import range
|
| +except ImportError:
|
| + pass
|
| +#from past.utils import old_div
|
| +
|
| +# BASIC MATH -------------------------------------------------------------------
|
| +
|
| +
|
| +def euclid(a, b):
|
| + """Solve x*a + y*b = ggt(a, b) and return (x, y, ggt(a, b))"""
|
| + # Non-recursive approach hence suitable for large numbers
|
| + x = yy = 0
|
| + y = xx = 1
|
| + while b:
|
| + q = a // b
|
| + a, b = b, a % b
|
| + x, xx = xx - q * x, x
|
| + y, yy = yy - q * y, y
|
| + return xx, yy, a
|
| +
|
| +
|
| +def inv(a, n):
|
| + """Perform inversion 1/a modulo n. a and n should be COPRIME."""
|
| + # coprimality is not checked here in favour of performance
|
| + i = euclid(a, n)[0]
|
| + while i < 0:
|
| + i += n
|
| + return i
|
| +
|
| +
|
| +def curve_q(x, y, p, n):
|
| + """Find curve parameter q mod n having point (x, y) and parameter p"""
|
| + return ((x * x - p) * x - y * y) % n
|
| +
|
| +
|
| +def element(point, p, q, n):
|
| + """Test, whether the given point is on the curve (p, q, n)"""
|
| + if point:
|
| + x, y = point
|
| + return (x * x * x - p * x - q) % n == (y * y) % n
|
| + else:
|
| + return True
|
| +
|
| +
|
| +def to_projective(p):
|
| + """Transform point p given as (x, y) to projective coordinates"""
|
| + if p:
|
| + return (p[0], p[1], 1, 1, 1)
|
| + else:
|
| + return None # Identity point (0)
|
| +
|
| +
|
| +def from_projective(jp, n):
|
| + """Transform a point from projective coordinates to (x, y) mod n"""
|
| + if jp:
|
| + return (jp[0] * inv(jp[3], n)) % n, (jp[1] * inv(jp[4], n)) % n
|
| + else:
|
| + return None # Identity point (0)
|
| +
|
| +
|
| +def neg(p, n):
|
| + """Compute the inverse point to p in any coordinate system"""
|
| + return (p[0], (n - p[1]) % n) + p[2:] if p else None
|
| +
|
| +
|
| +# POINT ADDITION ---------------------------------------------------------------
|
| +
|
| +# addition of points in y**2 = x**3 - p*x - q over <Z/nZ*; +>
|
| +def add(p, q, n, p1, p2):
|
| + """Add points p1 and p2 over curve (p, q, n)"""
|
| + if p1 and p2:
|
| + x1, y1 = p1
|
| + x2, y2 = p2
|
| + if (x1 - x2) % n:
|
| + s = ((y1 - y2) * inv(x1 - x2, n)) % n # slope
|
| + x = (s * s - x1 - x2) % n # intersection with curve
|
| + return x, n - (y1 + s * (x - x1)) % n
|
| + else:
|
| + if (y1 + y2) % n: # slope s calculated by derivation
|
| + s = ((3 * x1 * x1 - p) * inv(2 * y1, n)) % n
|
| + x = (s * s - 2 * x1) % n # intersection with curve
|
| + return x, n - (y1 + s * (x - x1)) % n
|
| + else:
|
| + return None
|
| + else: # either p1 is not none -> ret. p1, otherwiese p2, which may be
|
| + return p1 if p1 else p2 # none too.
|
| +
|
| +
|
| +# faster addition: redundancy in projective coordinates eliminates
|
| +# expensive inversions mod n.
|
| +def addf(p, q, n, jp1, jp2):
|
| + """Add jp1 and jp2 in projective (jacobian) coordinates."""
|
| + if jp1 and jp2:
|
| +
|
| + x1, y1, z1, z1s, z1c = jp1
|
| + x2, y2, z2, z2s, z2c = jp2
|
| +
|
| + s1 = (y1 * z2c) % n
|
| + s2 = (y2 * z1c) % n
|
| +
|
| + u1 = (x1 * z2s) % n
|
| + u2 = (x2 * z1s) % n
|
| +
|
| + if (u1 - u2) % n:
|
| +
|
| + h = (u2 - u1) % n
|
| + r = (s2 - s1) % n
|
| +
|
| + hs = (h * h) % n
|
| + hc = (hs * h) % n
|
| +
|
| + x3 = (-hc - 2 * u1 * hs + r * r) % n
|
| + y3 = (-s1 * hc + r * (u1 * hs - x3)) % n
|
| + z3 = (z1 * z2 * h) % n
|
| +
|
| + z3s = (z3 * z3) % n
|
| + z3c = (z3s * z3) % n
|
| +
|
| + return x3, y3, z3, z3s, z3c
|
| +
|
| + else:
|
| + if (s1 + s2) % n:
|
| + return doublef(p, q, n, jp1)
|
| + else:
|
| + return None
|
| + else:
|
| + return jp1 if jp1 else jp2
|
| +
|
| +# explicit point doubling using redundant coordinates
|
| +def doublef(p, q, n, jp):
|
| + """Double jp in projective (jacobian) coordinates"""
|
| + if not jp:
|
| + return None
|
| + x1, y1, z1, z1p2, z1p3 = jp
|
| +
|
| + y1p2 = (y1 * y1) % n
|
| + a = (4 * x1 * y1p2) % n
|
| + b = (3 * x1 * x1 - p * z1p3 * z1) % n
|
| + x3 = (b * b - 2 * a) % n
|
| + y3 = (b * (a - x3) - 8 * y1p2 * y1p2) % n
|
| + z3 = (2 * y1 * z1) % n
|
| + z3p2 = (z3 * z3) % n
|
| +
|
| + return x3, y3, z3, z3p2, (z3p2 * z3) % n
|
| +
|
| +
|
| +# SCALAR MULTIPLICATION --------------------------------------------------------
|
| +
|
| +# scalar multiplication p1 * c = p1 + p1 + ... + p1 (c times) in O(log(n))
|
| +def mul(p, q, n, p1, c):
|
| + """multiply point p1 by scalar c over curve (p, q, n)"""
|
| + res = None
|
| + while c > 0:
|
| + if c & 1:
|
| + res = add(p, q, n, res, p1)
|
| + c >>= 1 # c = c / 2
|
| + p1 = add(p, q, n, p1, p1) # p1 = p1 * 2
|
| + return res
|
| +
|
| +
|
| +# this method allows _signed_bin() to choose between 1 and -1. It will select
|
| +# the sign which leaves the higher number of zeroes in the binary
|
| +# representation (the higher GDB).
|
| +def _gbd(n):
|
| + """Compute second greatest base-2 divisor"""
|
| + i = 1
|
| + if n <= 0: return 0
|
| + while not n % i:
|
| + i <<= 1
|
| + return i >> 2
|
| +
|
| +
|
| +# This method transforms n into a binary representation having signed bits.
|
| +# A signed binary expansion contains more zero-bits hence reducing the number
|
| +# of additions required by a multiplication algorithm.
|
| +#
|
| +# Example: 15 ( 0b1111 ) can be written as 16 - 1, resulting in (1,0,0,0,-1)
|
| +# and saving 2 additions. Subtraction can be performed as
|
| +# efficiently as addition.
|
| +def _signed_bin(n):
|
| + """Transform n into an optimized signed binary representation"""
|
| + r = []
|
| + while n > 1:
|
| + if n & 1:
|
| + cp = _gbd(n + 1)
|
| + cn = _gbd(n - 1)
|
| + if cp > cn: # -1 leaves more zeroes -> subtract -1 (= +1)
|
| + r.append(-1)
|
| + n += 1
|
| + else: # +1 leaves more zeroes -> subtract +1 (= -1)
|
| + r.append(+1)
|
| + n -= 1
|
| + else:
|
| + r.append(0) # be glad about one more zero
|
| + n >>= 1
|
| + r.append(n)
|
| + return r[::-1]
|
| +
|
| +
|
| +# This multiplication algorithm combines signed binary expansion and
|
| +# fast addition using projective coordinates resulting in 5 to 10 times
|
| +# faster multiplication.
|
| +def mulf(p, q, n, jp1, c):
|
| + """Multiply point jp1 by c in projective coordinates"""
|
| + sb = _signed_bin(c)
|
| + res = None
|
| + jp0 = neg(jp1, n) # additive inverse of jp1 to be used fot bit -1
|
| + for s in sb:
|
| + res = doublef(p, q, n, res)
|
| + if s:
|
| + res = addf(p, q, n, res, jp1) if s > 0 else \
|
| + addf(p, q, n, res, jp0)
|
| + return res
|
| +
|
| +
|
| +# Encapsulates mulf() in order to enable flat coordinates (x, y)
|
| +def mulp(p, q, n, p1, c):
|
| + """Multiply point p by c using fast multiplication"""
|
| + return from_projective(mulf(p, q, n, to_projective(p1), c), n)
|
| +
|
| +
|
| +# Sum of two products using Shamir's trick and signed binary expansion
|
| +def muladdf(p, q, n, jp1, c1, jp2, c2):
|
| + """Efficiently compute c1 * jp1 + c2 * jp2 in projective coordinates"""
|
| + s1 = _signed_bin(c1)
|
| + s2 = _signed_bin(c2)
|
| + diff = len(s2) - len(s1)
|
| + if diff > 0:
|
| + s1 = [0] * diff + s1
|
| + elif diff < 0:
|
| + s2 = [0] * -diff + s2
|
| +
|
| + jp1p2 = addf(p, q, n, jp1, jp2)
|
| + jp1n2 = addf(p, q, n, jp1, neg(jp2, n))
|
| +
|
| + precomp = ((None, jp2, neg(jp2, n)),
|
| + (jp1, jp1p2, jp1n2),
|
| + (neg(jp1, n), neg(jp1n2, n), neg(jp1p2, n)))
|
| + res = None
|
| +
|
| + for i, j in zip(s1, s2):
|
| + res = doublef(p, q, n, res)
|
| + if i or j:
|
| + res = addf(p, q, n, res, precomp[i][j])
|
| + return res
|
| +
|
| +
|
| +# Encapsulate muladdf()
|
| +def muladdp(p, q, n, p1, c1, p2, c2):
|
| + """Efficiently compute c1 * p1 + c2 * p2 in (x, y)-coordinates"""
|
| + return from_projective(muladdf(p, q, n,
|
| + to_projective(p1), c1,
|
| + to_projective(p2), c2), n)
|
| +
|
| +# POINT COMPRESSION ------------------------------------------------------------
|
| +
|
| +# Compute the square root modulo n
|
| +
|
| +
|
| +# Determine the sign-bit of a point allowing to reconstruct y-coordinates
|
| +# when x and the sign-bit are given:
|
| +def sign_bit(p1):
|
| + """Return the signedness of a point p1"""
|
| + return p1[1] % 2 if p1 else 0
|
| +
|
| +# Reconstruct the y-coordinate when curve parameters, x and the sign-bit of
|
| +# the y coordinate are given:
|
| +def y_from_x(x, p, q, n, sign):
|
| + """Return the y coordinate over curve (p, q, n) for given (x, sign)"""
|
| +
|
| + # optimized form of (x**3 - p*x - q) % n
|
| + a = (((x * x) % n - p) * x - q) % n
|
| +
|
| +
|
| +if __name__ == "__main__":
|
| + import rsa
|
| + import time
|
| +
|
| + t = time.time()
|
| + n = rsa.get_prime(256 / 8, 20)
|
| + tp = time.time() - t
|
| + p = rsa.random.randint(1, n)
|
| + p1 = (rsa.random.randint(1, n), rsa.random.randint(1, n))
|
| + q = curve_q(p1[0], p1[1], p, n)
|
| + r1 = rsa.random.randint(1, n)
|
| + r2 = rsa.random.randint(1, n)
|
| + q1 = mulp(p, q, n, p1, r1)
|
| + q2 = mulp(p, q, n, p1, r2)
|
| + s1 = mulp(p, q, n, q1, r2)
|
| + s2 = mulp(p, q, n, q2, r1)
|
| + s1 == s2
|
| + tt = time.time() - t
|
| +
|
| + def test(tcount, bits=256):
|
| + n = rsa.get_prime(bits / 8, 20)
|
| + p = rsa.random.randint(1, n)
|
| + p1 = (rsa.random.randint(1, n), rsa.random.randint(1, n))
|
| + q = curve_q(p1[0], p1[1], p, n)
|
| + p2 = mulp(p, q, n, p1, rsa.random.randint(1, n))
|
| +
|
| + c1 = [rsa.random.randint(1, n) for i in range(tcount)]
|
| + c2 = [rsa.random.randint(1, n) for i in range(tcount)]
|
| + c = list(zip(c1, c2))
|
| +
|
| + t = time.time()
|
| + for i, j in c:
|
| + from_projective(addf(p, q, n,
|
| + mulf(p, q, n, to_projective(p1), i),
|
| + mulf(p, q, n, to_projective(p2), j)), n)
|
| + t1 = time.time() - t
|
| + t = time.time()
|
| + for i, j in c:
|
| + muladdp(p, q, n, p1, i, p2, j)
|
| + t2 = time.time() - t
|
| +
|
| + return tcount, t1, t2
|
| +
|
| +
|
| +
|
|
|