|
- #!/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"
|