diff --git a/README.md b/README.md index 4653715..255a30c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ The criteria that are *not* satisfied are, in summary: * twist security; * rigidity. +Tweedledum/Tweedledee is one of the cycles output by ``sage amicable.sage --nearpowerof2 255 32`` +(the first one with constant 5 for both curves and gcd(p-1, 5) = 1, gcd(q-1, 5) = 1). + Prerequisites: * apt-get install sagemath diff --git a/amicable.sage b/amicable.sage new file mode 100644 index 0000000..ef9f0d7 --- /dev/null +++ b/amicable.sage @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +import sys +from multiprocessing import Pool, cpu_count +from traceback import print_exc +from math import ceil +from itertools import combinations + +PROCESSES = None # auto-detect + +# Let Ep/Fp : y^2 = x^3 + bp +# Let Eq/Fq : y^2 = x^3 + bq + +# p and q should each be ~ L bits. + +DEFAULT_TWOADICITY = 21 +DEFAULT_STRETCH = 0 + +# +# It is well known that if g is neither a square nor a cube in Fp, then all +# possible group orders an elliptic curve E : y^2 = x^3 + b can have over Fp +# occur as the order of one of the 6 twists with b \in {1, g, g^2, g^3, g^4, g^5}. + +# : +# If p = 2 (mod 3) then all elements are cubes. +# If p = 1 (mod 3) then a is a cube iff a^((p-1)/3) = 1. + +# section 2: +# [...] the order of a curve satisfying the norm equation 3V^2 = 4p - t^2 has one +# of the six forms {p+1 +/- t, p+1 +/- (t +/- 3V)/2} [IEEE Std 1363-2000, section +# A.14.2.3, item 6]. +# +# We choose 4p = 3V^2 + t^2, where (V-1)/2 and (t-1)/2 are both multiples of 2^twoadicity. +# +# Then 4p = (3(V-1)^2 + 6(V-1) + 3) + ((t-1)^2 + 2(t-1) + 1) +# = 3(V-1)^2 + 6(V-1) + (t-1)^2 + 2(t-1) + 4 +# p = 3((V-1)/2)^2 + 3(V-1)/2 + ((t-1)/2)^2 + (t-1)/2 + 1 +# +# So p-1 will be a multiple of 2^twoadicity, and so will (p+1-t)-1 = (p-1)-(t-1). +# +# We'd also like both p and q to be 1 (mod 3), so that we have efficient endomorphisms +# on both curves. We explicitly check p = 1 (mod 3), and then if t is chosen to be +# 1 (mod 3) then p+1-t will be 1 (mod 3) (but we must still check q since it +# is not necessarily that order). + +def low_hamming_order(L, twoadicity, wid, processes): + Vlen = (L-1)//2 + 1 + Vbase = 1 << Vlen + tlen = (L-1)//4 + tbase = 1 << tlen + trailing_zeros = twoadicity+1 + for w in xrange(wid, tlen-trailing_zeros, processes): + for Vc in combinations(xrange(trailing_zeros, Vlen), w): + V = Vbase + sum([1 << i for i in Vc]) + 1 + assert(((V-1)/2) % (1<> trailing_zeros + for Voffset in symmetric_range(10000, base=wid, step=processes): + V = ((Vbase + Voffset) << trailing_zeros) + 1 + assert(((V-1)/2) % (1 << twoadicity) == 0) + tmp = (1<<(L+2)) - 3*V^2 + if tmp < 0: continue + tbase = isqrt(tmp) >> trailing_zeros + for toffset in symmetric_range(10000): + t = ((tbase + toffset) << trailing_zeros) + 1 + assert(((t-1)/2) % (1< [ [ 1 else DEFAULT_TWOADICITY + stretch = int(args[2]) if len(args) > 2 else DEFAULT_STRETCH + + processes = PROCESSES or cpu_count() + print("Using %d processes." % (processes,)) + pool = Pool(processes=processes) + + try: + for wid in xrange(processes): + pool.apply_async(worker, (strategy, L, twoadicity, stretch, wid, processes)) + + while True: + sleep(1000) + except (KeyboardInterrupt, SystemExit): + pass + finally: + pool.terminate() + +def worker(*args): + try: + real_worker(*args) + except (KeyboardInterrupt, SystemExit): + pass + except: + print_exc() + +def real_worker(*args): + for (p, q, bp, bq, ap, aq) in find_nice_curves(*args): + output = "\n" + output += "p = %s\n" % format_weight(p) + output += "q = %s\n" % format_weight(q) + output += "α_p = %s (mod p)\n" % format_weight(int(ap), detail=False) + output += "α_q = %s (mod q)\n" % format_weight(int(aq), detail=False) + + output += "Ep/Fp : y^2 = x^3 + %d (%ssquare)\n" % (bp, "" if Mod(bp, p).is_square() else "non") + output += "Eq/Fq : y^2 = x^3 + %d (%ssquare)\n" % (bq, "" if Mod(bq, q).is_square() else "non") + + output += "gcd(p-1, %d) = 1\n" % find_lowest_prime(p) + output += "gcd(q-1, %d) = 1\n" % find_lowest_prime(q) + + print(output) # one syscall to minimize tearing + +main()