129 lines
3.2 KiB
Python
129 lines
3.2 KiB
Python
|
#!/usr/bin/env sage
|
||
|
#coding: utf8
|
||
|
|
||
|
proof.all(False)
|
||
|
|
||
|
|
||
|
# parameters.
|
||
|
|
||
|
ls = list(primes(3, 374)) + [587] # Elkies primes
|
||
|
#ls = list(primes(3, 47)) + [97] # (a smaller example)
|
||
|
p = 4 * prod(ls) - 1
|
||
|
assert is_prime(p)
|
||
|
print "\nElkies primes:", " ".join(map(str, ls))
|
||
|
|
||
|
max_exp = ceil((sqrt(p) ** (1/len(ls)) - 1) / 2)
|
||
|
assert (2 * max_exp + 1) ** len(ls) >= sqrt(p)
|
||
|
print "exponents are chosen in the range {}..{}.".format(-max_exp, max_exp)
|
||
|
|
||
|
base = GF(p)(0) # Montgomery coefficient of starting curve
|
||
|
|
||
|
|
||
|
# helper functions.
|
||
|
|
||
|
# NB: all the operations can be computed entirely over the prime field,
|
||
|
# but for simplicity of this implementation we will make use of curves
|
||
|
# defined over GF(p^2). note this slows everything down quite a bit.
|
||
|
|
||
|
Fp2.<i> = GF(p**2, modulus = x**2 + 1)
|
||
|
|
||
|
def montgomery_curve(A):
|
||
|
return EllipticCurve(Fp2, [0, A, 0, 1, 0])
|
||
|
|
||
|
# sage's isogeny formulas return Weierstraß curves, hence we need this...
|
||
|
def montgomery_coefficient(E):
|
||
|
Ew = E.change_ring(GF(p)).short_weierstrass_model()
|
||
|
_, _, _, a, b = Ew.a_invariants()
|
||
|
R.<z> = GF(p)[]
|
||
|
r = (z**3 + a*z + b).roots(multiplicities=False)[0]
|
||
|
s = sqrt(3 * r**2 + a)
|
||
|
if not is_square(s): s = -s
|
||
|
A = 3 * r / s
|
||
|
assert montgomery_curve(A).change_ring(GF(p)).is_isomorphic(Ew)
|
||
|
return GF(p)(A)
|
||
|
|
||
|
|
||
|
# actual implementation.
|
||
|
|
||
|
def private():
|
||
|
return [randrange(-max_exp, max_exp + 1) for _ in range(len(ls))]
|
||
|
|
||
|
def validate(A):
|
||
|
while True:
|
||
|
k = 1
|
||
|
P = montgomery_curve(A).lift_x(GF(p).random_element())
|
||
|
for l in ls:
|
||
|
Q = (p + 1) // l * P
|
||
|
if not Q: continue
|
||
|
if l * Q: return False
|
||
|
k *= l
|
||
|
if k > 4 * sqrt(p): return True
|
||
|
|
||
|
def action(pub, priv):
|
||
|
|
||
|
E = montgomery_curve(pub)
|
||
|
es = priv[:]
|
||
|
|
||
|
while any(es):
|
||
|
|
||
|
E._order = (p + 1)**2 # else sage computes this
|
||
|
|
||
|
P = E.lift_x(GF(p).random_element())
|
||
|
s = +1 if P.xy()[1] in GF(p) else -1
|
||
|
k = prod(l for l, e in zip(ls, es) if sign(e) == s)
|
||
|
P *= (p + 1) // k
|
||
|
|
||
|
for i, (l, e) in enumerate(zip(ls, es)):
|
||
|
|
||
|
if sign(e) != s: continue
|
||
|
|
||
|
Q = k // l * P
|
||
|
if not Q: continue
|
||
|
Q._order = l # else sage computes this
|
||
|
phi = E.isogeny(Q)
|
||
|
|
||
|
E, P = phi.codomain(), phi(P)
|
||
|
es[i] -= s
|
||
|
k //= l
|
||
|
|
||
|
return montgomery_coefficient(E)
|
||
|
|
||
|
|
||
|
# example.
|
||
|
|
||
|
print
|
||
|
|
||
|
print "testing public-key validation on random ordinary curves (should be all 0s):\n ",
|
||
|
for _ in range(16):
|
||
|
while True:
|
||
|
A = GF(p).random_element()
|
||
|
if montgomery_curve(A).is_ordinary(): break
|
||
|
print int(validate(A)),
|
||
|
print
|
||
|
|
||
|
privA = private()
|
||
|
print "\nAlice's private key:\n ", " ".join(map('{:2d}'.format, privA))
|
||
|
|
||
|
pubA = action(base, privA)
|
||
|
print "\nAlice's public key:\n ", pubA,
|
||
|
print " (valid: {})".format(int(validate(pubA)))
|
||
|
|
||
|
privB = private()
|
||
|
print "\nBob's private key:\n ", " ".join(map('{:2d}'.format, privB))
|
||
|
|
||
|
pubB = action(base, privB)
|
||
|
print "\nBob's public key:\n ", pubB,
|
||
|
print " (valid: {})".format(int(validate(pubB)))
|
||
|
|
||
|
sharedA = action(pubB, privA)
|
||
|
print "\nAlice's shared secret:\n ", sharedA
|
||
|
|
||
|
sharedB = action(pubA, privB)
|
||
|
print "\nBob's shared secret:\n ", sharedB
|
||
|
|
||
|
if sharedA == sharedB:
|
||
|
print "\n--> equal!\n"
|
||
|
else:
|
||
|
print "\n--> NOT EQUAL?!\n"
|
||
|
|