commit 8ae65c6f2b39b0220bb534f86b45353614b3d2f7 Author: laniakea Date: Fri Apr 10 00:23:03 2026 +0200 push diff --git a/file.sage b/file.sage new file mode 100644 index 0000000..cbbb892 --- /dev/null +++ b/file.sage @@ -0,0 +1,168 @@ +def torsion_basis(E, l, e, p): + n = E.order() + cofactor = n // l^e + while True: + P = cofactor * E.random_point() + if P * l^(e-1) != E(0): + break + while True: + Q = cofactor * E.random_point() + if Q * l^(e-1) != E(0): + eP = P.weil_pairing(Q, l^e) + if eP^(l^(e-1)) != eP.parent()(1): + break + return P, Q +def velu_isogeny(E, K): + phi = E.isogeny(K) + return phi, phi.codomain() + +def find_curve_with_l_torsion(p, l): + Fp = GF(p) + for _ in range(2000): + a, b = Fp.random_element(), Fp.random_element() + if 4*a^3 + 27*b^2 == 0: + continue + E = EllipticCurve(Fp, [a, b]) + if E.order() % l == 0: + return E + raise ValueError(f"I cant find a curve over GF({p}) with {l}-torsion") + +def random_l_isogeny(E, l): + n = E.order() + if n % l != 0: + raise ValueError(f"l={l} does not divide #E = {n}") + cofactor = n // l + for _ in range(500): + P = E.random_point() + K = cofactor * P + if K != E(0) and K.order() == l: + return velu_isogeny(E, K) + raise ValueError("no kernel point found after too many tries") + + +def csidh_params(): + small_primes = [3, 5, 7, 11, 13, 17, 19] + product = 4 * prod(small_primes) + for k in range(1, 10000): + p = product * k - 1 + if p > 0 and is_prime(p) and p % 4 == 3: + break + else: + raise ValueError("Could not find CSIDH prime") + print(f" [csidh_params] p = {p} (p mod 4 = {p % 4})") + Fp = GF(p) + E0 = EllipticCurve(Fp, [1, 0]) + assert E0.is_supersingular(), "E0 must be supersingular" + return p, small_primes, E0 + +def _csidh_step(E_cur, l, sign, p): + Fp = GF(p) + n = E_cur.order() + if n % l != 0: + E_try = E_cur.quadratic_twist() + if E_try.order() % l == 0: + E_cur = E_try + n = E_cur.order() + else: + raise ValueError(f"neither E nor its twist has {l}-torsion (n={n}, are you fr now?)") + cofactor = n // l + for _ in range(1000): + R = E_cur.random_point() + K = cofactor * R + if K != E_cur(0) and K.order() == l: + phi = E_cur.isogeny(K) + return phi.codomain() + raise ValueError(f"no {l}-torsion kernel point found") + +def csidh_action(E, exp_vec, primes, p): + E_cur = E + for l, e in zip(primes, exp_vec): + if e == 0: + continue + sign = 1 if e > 0 else -1 + for _ in range(abs(e)): + E_cur = _csidh_step(E_cur, l, sign, p) + return E_cur + +def make_sidh_prime(eA, eB): + for f in range(1, 100000): + p = 2^eA * 3^eB * f - 1 + if p > 0 and is_prime(p): + return p, f + raise ValueError("I cant find a SIDH prime!!!!!!!!!!") + +def sidh_setup(p, eA, eB): + R. = GF(p)[] + Fp2. = GF(p^2, modulus=x^2+1) + E0 = EllipticCurve(Fp2, [1, 0]) + PA, QA = torsion_basis(E0, 2, eA, p) + PB, QB = torsion_basis(E0, 3, eB, p) + return E0, PA, QA, PB, QB + +def sidh_keygen_A(E0, PA, QA, PB, QB, eA): + while True: + mA, nA = randint(0, 2^eA-1), randint(0, 2^eA-1) + if (mA, nA) != (0, 0): + break + KA = mA*PA + nA*QA + phi_A = E0.isogeny(KA, algorithm='factored') + EA = phi_A.codomain() + return (mA, nA), EA, phi_A(PB), phi_A(QB) + +def sidh_keygen_B(E0, PA, QA, PB, QB, eB): + while True: + mB, nB = randint(0, 3^eB-1), randint(0, 3^eB-1) + if (mB, nB) != (0, 0): + break + KB = mB*PB + nB*QB + phi_B = E0.isogeny(KB, algorithm='factored') + EB = phi_B.codomain() + return (mB, nB), EB, phi_B(PA), phi_B(QA) + +def sidh_shared_A(mA, nA, EB, phi_PA, phi_QA): + KA = mA*phi_PA + nA*phi_QA + return EB.isogeny(KA, algorithm='factored').codomain().j_invariant() + +def sidh_shared_B(mB, nB, EA, phi_PB, phi_QB): + KB = mB*phi_PB + nB*phi_QB + return EA.isogeny(KB, algorithm='factored').codomain().j_invariant() +def isogeny_graph_neighbors(E, l): + poly = E.division_polynomial(l) + neighbors = [] + seen_j = set() + for fac, _ in poly.factor(): + if fac.degree() != 1: + continue + x0 = -fac[0] / fac[1] + try: + pts = E.lift_x(x0, all=True) + for P in pts: + if P != E(0) and (l * P) == E(0): + phi = E.isogeny(P) + F = phi.codomain() + j = F.j_invariant() + if j not in seen_j: + seen_j.add(j) + neighbors.append(F) + break + except ValueError: + pass + return neighbors + +def bfs_isogeny_path(E_start, j_end, l, max_depth=8): + from collections import deque + queue = deque([(E_start, [E_start.j_invariant()])]) + visited = {E_start.j_invariant()} + while queue: + E, path = queue.popleft() + if E.j_invariant() == j_end: + return path + if len(path) > max_depth: + continue + for F in isogeny_graph_neighbors(E, l): + j = F.j_invariant() + if j not in visited: + visited.add(j) + queue.append((F, path + [j])) + return None +