I worked a while on my code I posted here to make it more flexible and hopefully a little bit more secure. My goal is to implement a secure library for elliptic curve cryptography.
# coding: utf-8
MERSENNE_EXPONENTS = [
2, 3, 5, 7, 13, 17, 19, 31, 61, 89,
107, 127, 521, 607, 1279, 2203, 2281,
3217, 4253, 4423
]
def ext_euclidean(a, b):
t = u = 1
s = v = 0
while b:
a, (q, b) = b, divmod(a, b)
u, s = s, u - q*s
v, t = t, v - q*t
return a, u, v
def legendre(x, p)
return pow(x, p >> 1, p)
def W(n, r, x, m):
if n == 1:
inv = ext_euclidean(x, m)[1]
return (r*r*inv - 2) % m
if n & 1:
w0 = W(n >> 1, r, x, m)
w1 = W((n+1) >> 1, r, x, m)
return (w0*w1 - W(1, r, x, m)) % m
return (W(n >> 1, r, x, m)**2 - 2) % m
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return '(' + str(self.x) + ', ' + str(self.y) + ')'
def __eq__(self, p):
if type(p) != type(self):
return False
return self.x == p.x and self.y == p.y
class EllipticCurve:
"""Provides functions for
calculations on finite elliptic
curves.
"""
def __init__(self, a, b, m, warning=True):
"""Constructs the curve.
a and b are parameters of the
short Weierstraß equation:
y^2 = x^3 + ax + b
m is the order of the finite field,
so the actual equation is
y^2 = x^3 + ax + b (mod m)
"""
self.a = a
self.b = b
self.m = m
if warning:
if m & 3 == 3 and b == 0:
raise Warning
if m % 6 == 5 and a == 0:
raise Warning
def mod_sqrt(self, v):
l = legendre(v, self.m)
if l == (-1) % self.m:
return None
if l == 0:
return 0
if l == 1:
if self.m & 3 == 1:
r = 0
while legendre(r*r - 4*v, self.m) != (-1) % self.m:
r += 1
w1 = W(self.m >> 2, r, v, self.m)
w3 = W((self.m+3) >> 2, r, v, self.m)
inv_r = ext_euclidean(r, self.m)[1]
inv_2 = (self.m+1) >> 1
return (v * (w1+w3) * inv_2 * inv_r) % self.m
if self.m & 3 == 3:
return pow(v, (self.m+1) >> 2, self.m)
raise ValueError
raise ValueError
def generate(self, x):
"""
Generate Point with given x coordinate
"""
x %= self.m
v = (x**3 + self.a*x + self.b) % self.m
y = self.mod_sqrt(v)
if y is None:
return None
return Point(x, y)
def add(self, p, q):
"""
point addition on this curve.
None is the neutral element.
"""
if p is None:
return q
if q is None:
return p
numerator = (q.y - p.y) % self.m
denominator = (q.x - p.x) % self.m
if denominator == 0:
if p == q:
if p.y == 0:
return None
inv = ext_euclidean(2 * p.y, self.m)[1]
slope = inv * (3*p.x**2 + self.a) % self.m
else:
return None # p == -q
else:
inv = ext_euclidean(denominator, self.m)[1]
slope = inv * numerator % self.m
xr = (slope**2 - (p.x + q.x)) % self.m
yr = (slope * (p.x - xr) - p.y) % self.m
return Point(xr, yr)
def mul(self, p, n):
"""binary multiplication.
double and add instead of square and multiply.
"""
if p is None:
return None
if n < 0:
p = Point(p.x, self.m - p.y)
n = -n
r = None
for b in bin(n)[2:]:
r = self.add(r, r)
if b == '1':
r = self.add(r, p)
return r
class MersenneCurve(EllipticCurve):
"""Elliptic Curve where the
curve order is a Mersenne prime.
"""
def __init__(self, a, b, p, warning=True):
if p not in MERSENNE_EXPONENTS:
raise ValueError
if b == 0 and warning:
raise Warning
self.a = a
self.b = b
self.p = p # prime exponent
self.m = ~((~0) << p)
Notes:
- the algorithms for
ext_euclidean(a, b)andW(n, r, x, m)are derived from the descriptions on the german Wikipedia, I didn't find these algorithms I use on the english one, just don't be confused about it. - you should never use
b = 0for aMersenneCurve. In this case, it needs2 * pmultiplications on the curve to break the encryption. I'll post my code for this attack another time. - the function
is_generatorof the first post only works on aMersenneCurvewithb = 0, the same issue that made the function work leads to the attack mentioned above.