#!/usr/bin/env sage proof.all(False) # faster from sage.misc.banner import require_version if not require_version(10, 0, print_message=True): exit('') ################################################################ from parameters import p, B, f, Tpls, Tmin, Dcom, Dchall T = Tpls * Tmin ################################################################ if p % 4 != 3: raise NotImplementedError('requires p ≡ 3 (mod 4)') Fp2. = GF((p,2), modulus=[1,0,1]) Fp4 = Fp2.extension(2,'u') E = EllipticCurve(Fp4, [1,0]) assert E.j_invariant() == 1728 assert E.is_supersingular() assert E.change_ring(Fp2).frobenius() == -p assert E.order() == (p^2-1)^2 endo_1 = E.scalar_multiplication(1) endo_i = E.automorphisms()[-1] endo_j = E.frobenius_isogeny() endo_k = endo_i * endo_j if 0: # skipped for speed, for now assert endo_i^2 == E.scalar_multiplication(-1) assert endo_j^2 == E.scalar_multiplication(-p) assert endo_j * endo_i == - endo_i * endo_j else: R = E.random_point() assert (endo_i^2)(R) == -1*R assert (endo_j^2)(R) == -p*R assert (endo_j*endo_i)(R) == -(endo_i*endo_j)(R) def half_endo(summands): def _eval(P): E = P.curve() assert P in E F = E.base_field() if (halves := P.division_points(2)): Q = halves[0] else: Q = E.change_ring(F.extension(2,'v'))(P) R = sum(endo._eval(Q) for endo in summands) return E(R) return _eval gen1 = endo_1._eval gen2 = endo_i._eval gen3 = half_endo([endo_i, endo_j]) gen4 = half_endo([endo_1, endo_k]) ################################################################ from sage.groups.generic import order_from_multiple x = Fp4.gen() while True: x += 1 try: P = E.lift_x(x) except ValueError: continue o = order_from_multiple(P, p^2-1) if (T< = QuaternionAlgebra(-1, -p) O0 = Quat.quaternion_order([1, i, (i+j)/2, (1+k)/2]) assert Dcom % 2 == 1 # odd mat = block_matrix(Zmod(Dcom), [[identity_matrix(2), mati, matj, matk]])[:,::2] ker = list(map(Quat, mat.right_kernel_matrix())) idealP = sum((O0*g for g in ker), O0*Dcom) assert idealP.norm() == Dcom for b in idealP.basis(): assert sum(Mod(c,Dcom)*g for c,g in zip(b,(1,mati,matj,matk)))[:,0] == 0 # kills P for v in (ZZ^4): idealPgen = sum(c*g for c,g in zip(v, idealP.basis())) if vector(list(idealPgen)).denominator() == 2: idealPgen *= 2 if gcd(idealPgen.reduced_norm(), Dcom^2) == Dcom: break assert idealP == O0*Dcom + O0*idealPgen mat = mat # still rhs = vector(Zmod(Dcom), [0,1]) cs = mat.solve_right(rhs) distorter = Quat(cs) assert sum(Mod(c,Dcom)*g for c,g in zip(distorter,(1,mati,matj,matk))).columns()[0] == vector((0,1)) # maps P->Q ################################################################ from cformat import Ibz, Object, ObjectFormatter def field2limbs(el): l = 1 + floor(log(p, 2**64)) el = Fp2(el) vs = [[(int(c) >> 64*i) % 2**64 for i in range(l)] for c in el] return vs def fmt_basis(name, P, Q): vs = [ [field2limbs(T[0]), field2limbs(T[2])] for T in (P,Q,P-Q) ] return Object('ec_basis_t', name, vs) bases = { 'EVEN': 1<', file=hfile) print(f'#include ', file=hfile) print(f'#include ', file=hfile) print(f'#include ', file=cfile) print(f'#include ', file=cfile) print(f'#include ', file=cfile) objs.header(file=hfile) objs.implementation(file=cfile)