initial version of SQIsign
Co-authored-by: Jorge Chavez-Saab <jorgechavezsaab@gmail.com> Co-authored-by: Maria Corte-Real Santos <36373796+mariascrs@users.noreply.github.com> Co-authored-by: Luca De Feo <github@defeo.lu> Co-authored-by: Jonathan Komada Eriksen <jonathan.eriksen97@gmail.com> Co-authored-by: Basil Hess <bhe@zurich.ibm.com> Co-authored-by: Antonin Leroux <18654258+tonioecto@users.noreply.github.com> Co-authored-by: Patrick Longa <plonga@microsoft.com> Co-authored-by: Lorenz Panny <lorenz@yx7.cc> Co-authored-by: Francisco Rodríguez-Henríquez <francisco.rodriguez@tii.ae> Co-authored-by: Sina Schaeffler <108983332+syndrakon@users.noreply.github.com> Co-authored-by: Benjamin Wesolowski <19474926+Calodeon@users.noreply.github.com>
This commit is contained in:
203
scripts/precompute_endomorphism_action.sage
Executable file
203
scripts/precompute_endomorphism_action.sage
Executable file
@@ -0,0 +1,203 @@
|
||||
#!/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.<i> = 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<<f).divides(o):
|
||||
P *= o // (T<<f)
|
||||
P.set_order(T<<f)
|
||||
break
|
||||
|
||||
x = Fp4.gen()
|
||||
while True:
|
||||
x += 1
|
||||
try:
|
||||
Q = E.lift_x(x)
|
||||
except ValueError:
|
||||
continue
|
||||
o = order_from_multiple(Q, p^2-1)
|
||||
if not (T<<f).divides(o):
|
||||
continue
|
||||
Q *= o // (T<<f)
|
||||
Q.set_order(T<<f)
|
||||
if order_from_multiple(P.weil_pairing(Q, T<<f), T<<f, operation='*') == T<<f:
|
||||
break
|
||||
|
||||
def dlp(P, Q, R):
|
||||
n = P.order()
|
||||
assert P.order() == Q.order()
|
||||
assert R.order().divides(P.order())
|
||||
e = Fp2(P.weil_pairing(Q, n))
|
||||
a = Fp2(R.weil_pairing(Q, n)).log(e)
|
||||
b = Fp2(P.weil_pairing(R, n)).log(e)
|
||||
assert a*P + b*Q == R
|
||||
return a, b
|
||||
|
||||
def matrix_of_isogeny(phi):
|
||||
imP, imQ = map(phi, (P,Q))
|
||||
vecP = dlp(P, Q, imP)
|
||||
vecQ = dlp(P, Q, imQ)
|
||||
mat = matrix(Zmod(T<<f), [vecP, vecQ]).transpose()
|
||||
assert imP == ZZ(mat[0][0])*P + ZZ(mat[1][0])*Q
|
||||
assert imQ == ZZ(mat[0][1])*P + ZZ(mat[1][1])*Q
|
||||
return mat
|
||||
|
||||
#mat1 = matrix_of_isogeny(endo_1)
|
||||
mati = matrix_of_isogeny(endo_i)
|
||||
matj = matrix_of_isogeny(endo_j)
|
||||
matk = matrix_of_isogeny(endo_k)
|
||||
#assert mat1 == 1 # identity; omit
|
||||
|
||||
#mat1 = matrix_of_isogeny(gen1)
|
||||
mat2 = matrix_of_isogeny(gen2)
|
||||
mat3 = matrix_of_isogeny(gen3)
|
||||
mat4 = matrix_of_isogeny(gen4)
|
||||
#assert mat1 == 1 # identity; omit
|
||||
|
||||
################################################################
|
||||
|
||||
Quat.<i,j,k> = 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<<f,
|
||||
'ODD_PLUS': Tpls,
|
||||
'ODD_MINUS': Tmin,
|
||||
'COMMITMENT_PLUS': gcd(Tpls, Dcom),
|
||||
'COMMITMENT_MINUS': gcd(Tmin, Dcom),
|
||||
'CHALLENGE': Dchall,
|
||||
}
|
||||
|
||||
assert P.order() == Q.order()
|
||||
|
||||
objs = ObjectFormatter([
|
||||
fmt_basis(f'BASIS_{k}', ZZ(P.order()/v)*P, ZZ(Q.order()/v)*Q)
|
||||
for k,v in bases.items()
|
||||
] + [
|
||||
Object('ec_curve_t', 'CURVE_E0', [[[int(0)]], [[int(1)]]]),
|
||||
Object('ec_point_t', 'CURVE_E0_A24', [[[int(0)]], [[int(1)]]]),
|
||||
Object('ibz_mat_2x2_t', 'ACTION_I', [[Ibz(v) for v in vs] for vs in mati]),
|
||||
Object('ibz_mat_2x2_t', 'ACTION_J', [[Ibz(v) for v in vs] for vs in matj]),
|
||||
Object('ibz_mat_2x2_t', 'ACTION_K', [[Ibz(v) for v in vs] for vs in matk]),
|
||||
Object('ibz_mat_2x2_t', 'ACTION_GEN2', [[Ibz(v) for v in vs] for vs in mat2]),
|
||||
Object('ibz_mat_2x2_t', 'ACTION_GEN3', [[Ibz(v) for v in vs] for vs in mat3]),
|
||||
Object('ibz_mat_2x2_t', 'ACTION_GEN4', [[Ibz(v) for v in vs] for vs in mat4]),
|
||||
Object('quat_alg_elem_t', 'COMMITMENT_IDEAL_UNDISTORTED_GEN', [Ibz(1), [Ibz(ZZ(v)) for v in idealPgen]]),
|
||||
Object('quat_alg_elem_t', 'COMMITMENT_IDEAL_DISTORTION_ENDO', [Ibz(1), [Ibz(ZZ(v)) for v in distorter]]),
|
||||
])
|
||||
|
||||
with open('include/endomorphism_action.h','w') as hfile:
|
||||
with open('endomorphism_action.c','w') as cfile:
|
||||
print(f'#include <intbig.h>', file=hfile)
|
||||
print(f'#include <ec.h>', file=hfile)
|
||||
print(f'#include <quaternion.h>', file=hfile)
|
||||
print(f'#include <stddef.h>', file=cfile)
|
||||
print(f'#include <stdint.h>', file=cfile)
|
||||
print(f'#include <endomorphism_action.h>', file=cfile)
|
||||
|
||||
objs.header(file=hfile)
|
||||
objs.implementation(file=cfile)
|
||||
|
||||
Reference in New Issue
Block a user