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:
2
scripts/.gitignore
vendored
Normal file
2
scripts/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
__pycache__
|
||||
*.sage.py
|
||||
107
scripts/IntBigTest.scala
Normal file
107
scripts/IntBigTest.scala
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Code to analyze instrumented code from the SQIsign IntBig module.
|
||||
*
|
||||
* Features:
|
||||
* - verifies arithmetic
|
||||
* - aggregate number of errors / ok per function
|
||||
* - aggregate minimum / maximum values per function
|
||||
*
|
||||
* Prerequisite: enable debug output in intbig.x: #define DEBUG_VERBOSE
|
||||
* Usage: ./<test app> | scala IntBigTest.scala
|
||||
* Usage with unit test: sqisign_test_intbig <reps> <bits> | scala IntBigTest.scala
|
||||
*
|
||||
* Run option -v: verbose full output
|
||||
*/
|
||||
|
||||
object IntBigTest {
|
||||
|
||||
// Test functions
|
||||
object IntBigTestFuns {
|
||||
def ibz_add(a: Array[BigInt]) = IntBigRes(a(0) == a(1) + a(2), a(1) + a(2), a)
|
||||
def ibz_sub(a: Array[BigInt]) = IntBigRes(a(0) == a(1) - a(2), a(1) - a(2), a)
|
||||
def ibz_mul(a: Array[BigInt]) = IntBigRes(a(0) == a(1) * a(2), a(1) * a(2), a)
|
||||
def ibz_div(a: Array[BigInt]) = IntBigRes(a(0) == a(2) / a(3) && a(1) == a(2) % a(3), a(2) / a(3), a)
|
||||
def ibz_pow_mod(a: Array[BigInt]) = IntBigRes(a(0) == a(1).modPow(a(2), a(3)), a(1).modPow(a(2), a(3)), a)
|
||||
def ibz_cmp(a: Array[BigInt]) = IntBigRes(
|
||||
if (a(1) == a(2)) a(0) == 0 else if (a(1) < a(2)) a(0) < 0 else a(0) > 0,
|
||||
if (a(1) == a(2)) -1 else if (a(1) < a(2)) 1 else 0,
|
||||
a)
|
||||
def ibz_is_zero(a: Array[BigInt]) = IntBigRes(if (a(1) == 0) a(0) == 1 else a(0) == 0, if (a(1) == 0) 1 else 0, a)
|
||||
def ibz_is_one(a: Array[BigInt]) = IntBigRes(if (a(1) == 1) a(0) == 1 else a(0) == 0, if (a(1) == 1) 1 else 0, a)
|
||||
def ibz_probab_prime(a: Array[BigInt]) = IntBigRes(if (a(1).isProbablePrime(a(2).toInt)) a(0) > 0 else a(0) == 0, if (a(1).isProbablePrime(a(2).toInt)) 1 else 0, a)
|
||||
def ibz_gcd(a: Array[BigInt]) = IntBigRes(a(1).gcd(a(2)) == a(0), a(1).gcd(a(2)), a)
|
||||
def ibz_sqrt_mod_p(in: Array[BigInt]): IntBigRes = {
|
||||
val sqrt = in(0)
|
||||
val p = in(2)
|
||||
val a = if (in(1).mod(p) < 0) p + in(1).mod(p) else in(1).mod(p)
|
||||
val exp0 = sqrt.modPow(2, p)
|
||||
IntBigRes(exp0 == a || (p - exp0) == a, sqrt.modPow(2, p), in)
|
||||
}
|
||||
def ibz_sqrt_mod_2p(a: Array[BigInt]): IntBigRes = IntBigRes(a(0).modPow(2, 2 * a(2)) == a(1), a(0), a)
|
||||
}
|
||||
|
||||
val funList = Map(
|
||||
//"ibz_add" -> ibz_add _,
|
||||
"ibz_sqrt_mod_p" -> IntBigTestFuns.ibz_sqrt_mod_p _,
|
||||
"ibz_sqrt_mod_2p" -> IntBigTestFuns.ibz_sqrt_mod_2p _,
|
||||
"ibz_add" -> IntBigTestFuns.ibz_add _,
|
||||
"ibz_sub" -> IntBigTestFuns.ibz_sub _,
|
||||
"ibz_mul" -> IntBigTestFuns.ibz_mul _,
|
||||
"ibz_div" -> IntBigTestFuns.ibz_div _,
|
||||
"ibz_pow_mod" -> IntBigTestFuns.ibz_pow_mod _,
|
||||
"ibz_cmp" -> IntBigTestFuns.ibz_cmp _,
|
||||
"ibz_is_zero" -> IntBigTestFuns.ibz_is_zero _,
|
||||
"ibz_is_one" -> IntBigTestFuns.ibz_is_one _,
|
||||
"ibz_probab_prime" -> IntBigTestFuns.ibz_probab_prime _,
|
||||
"ibz_gcd" -> IntBigTestFuns.ibz_gcd _
|
||||
)
|
||||
|
||||
case class AggregateResults(funName: String, errors: Int, ok: Int, max: Option[Int], min: Option[Int]) {
|
||||
def errInc = AggregateResults(funName, errors + 1, ok, max, min)
|
||||
def okInc(operands: List[BigInt]) = {
|
||||
val operandsBitLen = operands.map(_.bitLength)
|
||||
val newMax = Some((max.getOrElse(0) :: operandsBitLen).max)
|
||||
val newMin = Some((min.getOrElse(Int.MaxValue) :: operandsBitLen).min)
|
||||
AggregateResults(funName, errors, ok + 1, newMax, newMin)
|
||||
}
|
||||
|
||||
override def toString: String = s"$funName: $errors errors, $ok ok, max value: ${max.getOrElse(BigInt(0))} bits, min value: ${min.getOrElse(BigInt(0))} bits)"
|
||||
}
|
||||
|
||||
def main(args: Array[String]) = {
|
||||
val v = args.length >= 1 && args(0) == "-v"
|
||||
var err = Map() ++ funList.map(i => (i._1 -> AggregateResults(i._1, 0, 0, None, None)))
|
||||
var cont = true
|
||||
while (cont) {
|
||||
val l = scala.io.StdIn.readLine()
|
||||
if (l == null) {
|
||||
val numerr =
|
||||
err.foreach(i => println(i._2))
|
||||
println(s"==========\n${err.values.map(_.errors).sum} errors found\n${err.values.map(_.ok).sum} checks ok")
|
||||
cont = false
|
||||
} else {
|
||||
err = check(l, v, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class IntBigRes(verif: Boolean, expected: BigInt, got: Array[BigInt])
|
||||
|
||||
def check(line: String, v: Boolean, agg: Map[String, AggregateResults]): Map[String, AggregateResults] = {
|
||||
line.split(",").toList match {
|
||||
case x :: xs if funList.contains(x) =>
|
||||
val funA = xs.map(i => BigInt(i, 16)).toArray
|
||||
funList(x)(funA) match {
|
||||
case IntBigRes(false, exp, got) =>
|
||||
println(s"function: $x\ngot:\n${got.map(_.toString(16)).mkString(",")}\nexpected:\n${exp.toString(16)}")
|
||||
agg.map(i => if (i._1 == x) i._1 -> i._2.errInc else i)
|
||||
case _ =>
|
||||
agg.map(i => if (i._1 == x) i._1 -> i._2.okInc(funA.toList) else i)
|
||||
}
|
||||
case _ =>
|
||||
if (v) println(line)
|
||||
agg
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
92
scripts/cformat.py
Normal file
92
scripts/cformat.py
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, itertools
|
||||
from math import floor, log
|
||||
import sage.all
|
||||
|
||||
class Ibz:
|
||||
def __init__(self, v):
|
||||
self.v = int(v)
|
||||
def _literal(self, sz):
|
||||
val = int(self.v)
|
||||
sgn = val < 0
|
||||
num_limbs = (abs(val).bit_length() + sz-1) // sz if val else 0
|
||||
limbs = [(abs(val) >> sz*i) & (2**sz-1) for i in range(num_limbs or 1)]
|
||||
data = {
|
||||
'._mp_alloc': 0,
|
||||
'._mp_size': (-1)**sgn * num_limbs,
|
||||
'._mp_d': '(mp_limb_t[]) {' + ','.join(map(hex,limbs)) + '}',
|
||||
}
|
||||
return '{{' + ', '.join(f'{k} = {v}' for k,v in data.items()) + '}}'
|
||||
|
||||
class Object:
|
||||
def __init__(self, ty, name, obj):
|
||||
if '[' in ty:
|
||||
idx = ty.index('[')
|
||||
depth = ty.count('[]')
|
||||
def rec(os, d):
|
||||
assert d >= 0
|
||||
if not d:
|
||||
return ()
|
||||
assert isinstance(os,list) or isinstance(os,tuple)
|
||||
r, = {rec(o, d-1) for o in os}
|
||||
return (len(os),) + r
|
||||
dims = rec(obj, depth)
|
||||
self.ty = ty[:idx], ''.join(f'[{d}]' for d in dims)
|
||||
else:
|
||||
self.ty = ty, ''
|
||||
self.name = name
|
||||
self.obj = obj
|
||||
|
||||
def _declaration(self):
|
||||
return f'extern const {self.ty[0]} {self.name}{self.ty[1]};'
|
||||
|
||||
def _literal(self, mp_limb_t_bits):
|
||||
def rec(obj):
|
||||
if isinstance(obj, int):
|
||||
return hex(obj)
|
||||
if isinstance(obj, sage.all.Integer):
|
||||
return hex(obj)
|
||||
if isinstance(obj, Ibz):
|
||||
return obj._literal(mp_limb_t_bits)
|
||||
if isinstance(obj, list) or isinstance(obj, tuple):
|
||||
return '{' + ', '.join(map(rec, obj)) + '}'
|
||||
raise NotImplementedError(f'unknown type {type(obj)} in Formatter')
|
||||
return rec(self.obj)
|
||||
|
||||
def _definition(self, mp_limb_t_bits):
|
||||
return f'const {self.ty[0]} {self.name}{self.ty[1]} = ' + self._literal(mp_limb_t_bits) + ';'
|
||||
|
||||
|
||||
class ObjectFormatter:
|
||||
def __init__(self, objs):
|
||||
self.objs = objs
|
||||
|
||||
def header(self, file=None):
|
||||
for obj in self.objs:
|
||||
assert isinstance(obj, Object)
|
||||
print(obj._declaration(), file=file)
|
||||
|
||||
def implementation(self, file=None):
|
||||
print('#if 0', file=file)
|
||||
for sz in (16, 32, 64):
|
||||
print(f'#elif 8*DIGIT_LEN == {sz}', file=file)
|
||||
for obj in self.objs:
|
||||
assert isinstance(obj, Object)
|
||||
print(obj._definition(sz), file=file)
|
||||
print('#endif', file=file)
|
||||
|
||||
|
||||
|
||||
def field(v, F=None):
|
||||
if F:
|
||||
v = F(v)
|
||||
p = F.characteristic()
|
||||
l = 1 + floor(log(p,2**64))
|
||||
vs = [[(c >> 64*i) & (2**64-1) for i in range(l)] for c in v]
|
||||
return vs
|
||||
|
||||
def xonly(T, *args):
|
||||
if not T: raise NotImplementedError('is point at infinity')
|
||||
x, _ = T.xy()
|
||||
return field(x, *args)
|
||||
|
||||
31
scripts/parameters.py
Normal file
31
scripts/parameters.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
from sage.all import *
|
||||
proof.all(False) # faster
|
||||
|
||||
import re
|
||||
for l in open('sqisign_parameters.txt'):
|
||||
for k in ('lvl', 'p', 'B'):
|
||||
m = re.search(rf'^\s*{k}\s*=\s*([x0-9a-f]+)', l)
|
||||
if m:
|
||||
v = ZZ(m.groups()[0], 0)
|
||||
globals()[k] = v
|
||||
|
||||
L = {l for l,_ in (p**2 - 1).factor(limit=B+5) if l <= B}
|
||||
assert 2 in L
|
||||
L.remove(2)
|
||||
f = (p+1).valuation(2)
|
||||
if (p-1).valuation(2) > f:
|
||||
raise NotImplementedError('2-power torsion is on twist')
|
||||
Lpls = {l for l in L if (p+1).valuation(l) >= (p-1).valuation(l)}
|
||||
Lmin = L - Lpls
|
||||
Lpls, Lmin = map(sorted, (Lpls, Lmin))
|
||||
Epls = [(p+1).valuation(l) for l in Lpls]
|
||||
Emin = [(p-1).valuation(l) for l in Lmin]
|
||||
Tpls = prod(l**e for l,e in zip(Lpls,Epls))
|
||||
Tmin = prod(l**e for l,e in zip(Lmin,Emin))
|
||||
|
||||
Dcom = (Tpls*Tmin).prime_to_m_part(2*3)
|
||||
Dchall = prod(l**(p+1).valuation(l) for l in (2,3))
|
||||
|
||||
__all__ = ['lvl', 'p', 'B', 'f', 'Tpls', 'Tmin', 'Dcom', 'Dchall']
|
||||
|
||||
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)
|
||||
|
||||
114
scripts/precompute_klpt_constants.sage
Executable file
114
scripts/precompute_klpt_constants.sage
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
from sage.misc.banner import require_version
|
||||
if not require_version(9, 8, print_message=True):
|
||||
exit('')
|
||||
|
||||
################################################################
|
||||
|
||||
from parameters import f, p, Tpls, Tmin
|
||||
negl = 2**-64 #TODO optimize
|
||||
|
||||
################################################################
|
||||
|
||||
logp = ceil(log(p, 2))
|
||||
logT = ceil(log(Tpls*Tmin, 2))
|
||||
tors2val = (p+1).valuation(2)
|
||||
|
||||
defs = dict()
|
||||
|
||||
# lideal_equiv
|
||||
|
||||
defs['KLPT_equiv_bound_coeff'] = ceil((log(negl, 1-2/logp) ** (1/4) - 1) / 2) + 2
|
||||
assert (1 - 2/logp) ** ((2 * defs['KLPT_equiv_bound_coeff'] + 1) ** 4) <= negl
|
||||
|
||||
defs['KLPT_equiv_num_iter'] = (2 * defs['KLPT_equiv_bound_coeff'] + 1) ** 4
|
||||
|
||||
defs['KLPT_primality_num_iter'] = ceil(-log(negl, 4))
|
||||
|
||||
# signing KLPT
|
||||
|
||||
defs['KLPT_signing_klpt_length'] = f * ceil (ceil((log(negl, 2) / -2) + 15/4*logp + 25)/f)
|
||||
assert 2**(-2 * (defs['KLPT_signing_klpt_length'] - 15/4*logp - 25)) <= negl
|
||||
|
||||
defs['KLPT_signing_num_gamma_trial'] = ceil(log(negl, 2) / -1)
|
||||
assert 2 ** ( - defs['KLPT_signing_num_gamma_trial']) <= negl
|
||||
|
||||
defs['KLPT_gamma_exponent_interval_size'] = 0
|
||||
|
||||
defs['KLPT_gamma_exponent_center_shift'] = ceil(log(log(negl, 1-1/logp) + defs['KLPT_signing_num_gamma_trial'], 2) + defs['KLPT_gamma_exponent_interval_size'])
|
||||
assert (1 - 1/logp) ** (2**(defs['KLPT_gamma_exponent_center_shift'] - defs['KLPT_gamma_exponent_interval_size']) - defs['KLPT_signing_num_gamma_trial']) <= negl
|
||||
|
||||
defs['KLPT_repres_num_gamma_trial'] = 2**(defs['KLPT_gamma_exponent_center_shift'] + defs['KLPT_gamma_exponent_interval_size'])
|
||||
|
||||
defs['KLPT_signing_number_strong_approx'] = ceil(log(1/64, 1-4/13/logp))
|
||||
assert (1 - 4/13/logp) ** defs['KLPT_signing_number_strong_approx'] <= 1/64
|
||||
|
||||
# keygen KLPT
|
||||
|
||||
defs['KLPT_random_prime_attempts'] = 64
|
||||
|
||||
defs['KLPT_secret_key_prime_size'] = ceil(logp / 4)
|
||||
|
||||
defs['KLPT_keygen_length'] = f* ceil ( ceil(log(negl, 2) / -2 + 5/2*logp -25 ) / f)
|
||||
assert 2 ** (-2 * (defs['KLPT_keygen_length'] - 5/2*logp +25)) <= negl
|
||||
|
||||
defs['KLPT_keygen_num_gamma_trial'] = ceil(log(negl, 2) / -1)
|
||||
|
||||
defs['KLPT_eichler_smallnorm_bitsize'] = ceil(1/2*logp - 4/3*( logT - 5/4*logp))
|
||||
|
||||
defs['KLPT_keygen_number_strong_approx'] = ceil(log(1/64, 1-2/5/logp))
|
||||
assert (1 - 2/5/logp) ** defs['KLPT_keygen_number_strong_approx'] <= 1/64
|
||||
|
||||
# Eichler
|
||||
|
||||
defs['KLPT_eichler_number_mu_norm'] = ceil((logT - 5/4*logp) / log(3,2))
|
||||
|
||||
defs['KLPT_eichler_strong_approx_log_margin'] = 2
|
||||
|
||||
defs['KLPT_eichler_num_equiv_ideal'] = ceil(logp / 10)
|
||||
|
||||
defs['KLPT_eichler_number_strong_approx'] = ceil(10 * logp)
|
||||
|
||||
# signature response
|
||||
|
||||
defs['SQISIGN_response_attempts'] = 64
|
||||
|
||||
# signature isogeny degrees
|
||||
|
||||
defs['SQISIGN_random_length'] = 0
|
||||
defs['SQISIGN_signing_total_length'] = defs['KLPT_signing_klpt_length']
|
||||
defs['SQISIGN_signing_length'] = ZZ(defs['SQISIGN_signing_total_length'] / tors2val)
|
||||
defs['SQISIGN_keygen_length'] = ZZ(defs['KLPT_keygen_length'] / tors2val)
|
||||
|
||||
# prime data for Cornacchia
|
||||
|
||||
primes_1mod4 = [p for p in primes(100) if p%4==1]
|
||||
prod_primes_3mod4 = prod(p for p in primes(100) if p%4==3)
|
||||
|
||||
################################################################
|
||||
|
||||
from cformat import Ibz, Object, ObjectFormatter
|
||||
|
||||
objs = ObjectFormatter([
|
||||
Object('short[]', 'SMALL_PRIMES_1MOD4', [int(v) for v in primes_1mod4]),
|
||||
Object('ibz_t', 'PROD_SMALL_PRIMES_3MOD4', Ibz(prod_primes_3mod4)),
|
||||
])
|
||||
|
||||
################################################################
|
||||
|
||||
with open('include/klpt_constants.h','w') as hfile:
|
||||
with open('klpt_constants.c','w') as cfile:
|
||||
print(f'#include <intbig.h>', file=hfile)
|
||||
print(f'#include <stddef.h>', file=cfile)
|
||||
print(f'#include <stdint.h>', file=cfile)
|
||||
print(f'#include <klpt_constants.h>', file=cfile)
|
||||
|
||||
for k,v in defs.items():
|
||||
v = ZZ(v)
|
||||
print(f'#define {k} {v}', file=hfile)
|
||||
|
||||
objs.header(file=hfile)
|
||||
objs.implementation(file=cfile)
|
||||
|
||||
115
scripts/precompute_quaternion_data.sage
Executable file
115
scripts/precompute_quaternion_data.sage
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
from sage.misc.banner import require_version
|
||||
if not require_version(9, 8, print_message=True):
|
||||
exit('')
|
||||
|
||||
################################################################
|
||||
|
||||
from parameters import p
|
||||
num = 7 #TODO how many extra maximal orders to precompute?
|
||||
|
||||
################################################################
|
||||
|
||||
# Underlying theory:
|
||||
# - Ibukiyama, On maximal orders of division quaternion algebras with certain optimal embeddings
|
||||
# - https://ia.cr/2023/106 Lemma 10
|
||||
|
||||
from sage.algebras.quatalg.quaternion_algebra import basis_for_quaternion_lattice
|
||||
bfql = lambda els: basis_for_quaternion_lattice(els, reverse=True)
|
||||
|
||||
Quat.<i,j,k> = QuaternionAlgebra(-1, -p)
|
||||
assert Quat.discriminant() == p # ramifies correctly
|
||||
|
||||
orders = []
|
||||
|
||||
q = 1
|
||||
while len(orders) < num:
|
||||
q = next_prime(q)
|
||||
|
||||
if q == 2:
|
||||
continue
|
||||
|
||||
Quat2.<ii,jj,kk> = QuaternionAlgebra(-q, -p)
|
||||
if Quat2.discriminant() != p: # ramifies incorrectly
|
||||
continue
|
||||
|
||||
x, y = QuadraticForm(QQ, 2, [1,0,p]).solve(q)
|
||||
gamma = x + j*y
|
||||
assert gamma.reduced_norm() == q
|
||||
ims = [Quat(1), i*gamma, j, k*gamma]
|
||||
assert ims[1]^2 == -q
|
||||
assert ims[2]^2 == -p
|
||||
assert ims[1]*ims[2] == ims[3]
|
||||
assert ims[2]*ims[1] == -ims[3]
|
||||
# (1,ii,jj,kk)->ims is an isomorphism Quat2->Quat
|
||||
|
||||
r = min(map(ZZ, Mod(-p, 4*q).sqrt(all=True)))
|
||||
|
||||
if q % 4 == 3:
|
||||
bas2 = [
|
||||
Quat2(1),
|
||||
(1 + ii) / 2,
|
||||
jj * (1 + ii) / 2,
|
||||
(r + jj) * ii / q,
|
||||
]
|
||||
else:
|
||||
bas2 = [
|
||||
Quat2(1),
|
||||
ii,
|
||||
(1 + jj) / 2,
|
||||
(r + jj) * ii / 2 / q,
|
||||
]
|
||||
O2 = Quat2.quaternion_order(bas2)
|
||||
assert O2.discriminant() == p # is maximal
|
||||
|
||||
bas = [sum(c*im for c,im in zip(el,ims)) for el in bas2]
|
||||
bas = bfql(bas)
|
||||
O = Quat.quaternion_order(bas)
|
||||
assert O.discriminant() == p # is maximal
|
||||
assert j in O # p-extremal
|
||||
|
||||
mat = matrix(map(list, bas))
|
||||
# print(f'{q = }\nsqrt(-q) = {ims[1]}\n {(chr(10)+" ").join(map(str,bas))}', file=sys.stderr)
|
||||
assert mat[0] == vector((1,0,0,0))
|
||||
orders.append((q, ims[1], mat))
|
||||
|
||||
################################################################
|
||||
|
||||
gram = matrix(ZZ, [
|
||||
[((gi+gj).reduced_norm() - gi.reduced_norm() - gj.reduced_norm()) / 2
|
||||
for gi in Quat.basis()] for gj in Quat.basis()])
|
||||
|
||||
O0mat = matrix([list(g) for g in [Quat(1), i, (i+j)/2, (1+k)/2]])
|
||||
|
||||
################################################################
|
||||
|
||||
from cformat import Ibz, Object, ObjectFormatter
|
||||
|
||||
algobj = [Ibz(p), [[Ibz(v) for v in vs] for vs in gram]]
|
||||
O0ord = [Ibz(O0mat.denominator()), [[Ibz(v*O0mat.denominator()) for v in vs] for vs in O0mat.transpose()]]
|
||||
O0obj = [O0ord, [Ibz(1), [Ibz(c) for c in (0,1,0,0)]], [Ibz(1), [Ibz(c) for c in (0,0,1,0)]], 1]
|
||||
|
||||
objs = [[[Ibz(mat.denominator()), [[Ibz(v*mat.denominator()) for v in vs] for vs in mat.transpose()]], [Ibz(mat.denominator()), [Ibz(c*mat.denominator()) for c in ii]], [Ibz(1), [Ibz(c) for c in (0,0,1,0)]], q] for q,ii,mat in orders]
|
||||
|
||||
objs = ObjectFormatter([
|
||||
Object('quat_alg_t', 'QUATALG_PINFTY', algobj),
|
||||
Object('quat_order_t', 'MAXORD_O0', O0ord),
|
||||
Object('quat_p_extremal_maximal_order_t', 'STANDARD_EXTREMAL_ORDER', O0obj),
|
||||
Object('quat_p_extremal_maximal_order_t[]', 'ALTERNATE_EXTREMAL_ORDERS', objs),
|
||||
])
|
||||
|
||||
with open('include/quaternion_data.h','w') as hfile:
|
||||
with open('quaternion_data.c','w') as cfile:
|
||||
print(f'#include <intbig.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 <quaternion_data.h>', file=cfile)
|
||||
|
||||
print(f'#define NUM_ALTERNATE_EXTREMAL_ORDERS {len(orders)}', file=hfile)
|
||||
|
||||
objs.header(file=hfile)
|
||||
objs.implementation(file=cfile)
|
||||
|
||||
92
scripts/precompute_sizes.sage
Executable file
92
scripts/precompute_sizes.sage
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
from sage.misc.banner import require_version
|
||||
if not require_version(9, 8, print_message=True):
|
||||
exit('')
|
||||
|
||||
################################################################
|
||||
|
||||
from parameters import lvl, f, p
|
||||
|
||||
################################################################
|
||||
|
||||
logp = ceil(log(p, 2))
|
||||
tors2part = (p+1).p_primary_part(2)
|
||||
tors3part = (p+1).p_primary_part(3)
|
||||
|
||||
#XXX first load the constants from klpt_constants.h
|
||||
import re
|
||||
klpt_consts = dict()
|
||||
for l in open('include/klpt_constants.h'):
|
||||
m = re.search(r'#define *([^ ]+) *([x0-9]+)$', l)
|
||||
if m:
|
||||
k,v = m.groups()
|
||||
klpt_consts[k] = int(v, 0)
|
||||
|
||||
defs = dict()
|
||||
|
||||
fp2sz = (logp + 63)//64*8 * 2
|
||||
defs['FP2_ENCODED_BYTES'] = fp2sz
|
||||
defs['EC_CURVE_ENCODED_BYTES'] = fp2sz # just the A
|
||||
defs['EC_POINT_ENCODED_BYTES'] = fp2sz # just the x
|
||||
defs['EC_BASIS_ENCODED_BYTES'] = 3 * defs['EC_POINT_ENCODED_BYTES']
|
||||
|
||||
defs['CHAIN_LENGTH'] = klpt_consts['SQISIGN_keygen_length']
|
||||
|
||||
defs['QUAT_ALG_ELEM_ENCODED_BITS'] = ceil(((logp/4) + klpt_consts['KLPT_keygen_length'])/2 +55) #TODO FIXME figure this out XXX XXX
|
||||
defs['QUAT_ALG_ELEM_ENCODED_BYTES'] = (defs['QUAT_ALG_ELEM_ENCODED_BITS'] + 7)//8
|
||||
defs['ID2ISO_LONG_TWO_ISOG_ENCODED_BYTES'] = defs['CHAIN_LENGTH'] * (defs['EC_CURVE_ENCODED_BYTES'] + defs['EC_POINT_ENCODED_BYTES'] + 2)
|
||||
|
||||
defs['ZIP_CHAIN_LEN'] = klpt_consts['SQISIGN_signing_length']
|
||||
defs['ID2ISO_COMPRESSED_LONG_TWO_ISOG_ZIP_CHAIN_BYTES'] = (f + 7) // 8
|
||||
defs['ID2ISO_COMPRESSED_LONG_TWO_ISOG_BYTES'] = defs['ZIP_CHAIN_LEN'] * defs['ID2ISO_COMPRESSED_LONG_TWO_ISOG_ZIP_CHAIN_BYTES'] + 1
|
||||
|
||||
defs['SIGNATURE_LEN'] = defs['ID2ISO_COMPRESSED_LONG_TWO_ISOG_BYTES'] + ((tors2part*tors3part).bit_length()+7)//8 + 1 + (tors2part.bit_length()+7)//8 + (tors3part.bit_length()+7)//8
|
||||
defs['PUBLICKEY_BYTES'] = defs['EC_CURVE_ENCODED_BYTES']
|
||||
defs['SECRETKEY_BYTES'] = defs['EC_CURVE_ENCODED_BYTES'] + 5*defs['QUAT_ALG_ELEM_ENCODED_BYTES'] + defs['EC_POINT_ENCODED_BYTES'] + defs['EC_BASIS_ENCODED_BYTES'] + defs['EC_BASIS_ENCODED_BYTES']
|
||||
|
||||
size_privkey = defs['SECRETKEY_BYTES']
|
||||
size_pubkey = defs['PUBLICKEY_BYTES']
|
||||
size_signature = defs['SIGNATURE_LEN']
|
||||
|
||||
algname = f'lvl{lvl}'
|
||||
|
||||
################################################################
|
||||
|
||||
with open('include/encoded_sizes.h','w') as hfile:
|
||||
for k,v in defs.items():
|
||||
v = ZZ(v)
|
||||
print(f'#define {k} {v}', file=hfile)
|
||||
|
||||
api = f'''
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef api_h
|
||||
#define api_h
|
||||
|
||||
#define CRYPTO_SECRETKEYBYTES {size_privkey:4}
|
||||
#define CRYPTO_PUBLICKEYBYTES {size_pubkey:4}
|
||||
#define CRYPTO_BYTES {size_signature:4}
|
||||
|
||||
#define CRYPTO_ALGNAME "{algname}"
|
||||
|
||||
int
|
||||
crypto_sign_keypair(unsigned char *pk, unsigned char *sk);
|
||||
|
||||
int
|
||||
crypto_sign(unsigned char *sm, unsigned long long *smlen,
|
||||
const unsigned char *m, unsigned long long mlen,
|
||||
const unsigned char *sk);
|
||||
|
||||
int
|
||||
crypto_sign_open(unsigned char *m, unsigned long long *mlen,
|
||||
const unsigned char *sm, unsigned long long smlen,
|
||||
const unsigned char *pk);
|
||||
|
||||
#endif /* api_h */
|
||||
'''.strip()
|
||||
|
||||
with open(f'../../../nistapi/lvl{lvl}/api.h', 'w') as f:
|
||||
print(api, file=f)
|
||||
|
||||
67
scripts/precompute_torsion_constants.sage
Executable file
67
scripts/precompute_torsion_constants.sage
Executable file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
from sage.misc.banner import require_version
|
||||
if not require_version(9, 8, print_message=True):
|
||||
exit('')
|
||||
|
||||
################################################################
|
||||
|
||||
from parameters import p, B, f, Tpls, Tmin, Dcom, Dchall
|
||||
|
||||
################################################################
|
||||
|
||||
Lpls = sorted(set(Tpls.prime_factors()) - {2})
|
||||
Epls = [Tpls.valuation(l) for l in Lpls]
|
||||
|
||||
Lmin = sorted(set(Tmin.prime_factors()) - {2})
|
||||
Emin = [Tmin.valuation(l) for l in Lmin]
|
||||
|
||||
tors2part = (p+1).p_primary_part(2)
|
||||
tors3part = (p+1).p_primary_part(3)
|
||||
tors23part = tors2part * tors3part
|
||||
|
||||
defs = {
|
||||
'TORSION_2POWER_BYTES': (int(tors2part).bit_length() + 7) // 8,
|
||||
'TORSION_3POWER_BYTES': (int(tors3part).bit_length() + 7) // 8,
|
||||
'TORSION_23POWER_BYTES': (int(tors23part).bit_length() + 7) // 8,
|
||||
}
|
||||
|
||||
from cformat import Ibz, Object, ObjectFormatter
|
||||
|
||||
objs = ObjectFormatter([
|
||||
Object('uint64_t', 'TORSION_PLUS_EVEN_POWER', int(f)),
|
||||
Object('uint64_t[]', 'TORSION_ODD_PRIMES', Lpls + Lmin),
|
||||
Object('uint64_t[]', 'TORSION_ODD_POWERS', Epls + Emin),
|
||||
Object('uint64_t[]', 'TORSION_PLUS_ODD_PRIMES', Lpls), # TODO deduplicate?
|
||||
Object('size_t[]', 'TORSION_PLUS_ODD_POWERS', Epls), # TODO deduplicate?
|
||||
Object('uint64_t[]', 'TORSION_MINUS_ODD_PRIMES', Lmin), # TODO deduplicate?
|
||||
Object('size_t[]', 'TORSION_MINUS_ODD_POWERS', Emin), # TODO deduplicate?
|
||||
Object('size_t[]', 'DEGREE_COMMITMENT_POWERS', [Dcom.valuation(l) for l in Lpls+Lmin]), #FIXME should be ec_degree_odd_t
|
||||
Object('ibz_t', 'CHARACTERISTIC', Ibz(p)),
|
||||
Object('ibz_t', 'TORSION_ODD', Ibz(Tpls * Tmin)),
|
||||
Object('ibz_t[]', 'TORSION_ODD_PRIMEPOWERS', [Ibz(l^e) for Tpm in (Tpls,Tmin) for l,e in Tpm.factor()]),
|
||||
Object('ibz_t', 'TORSION_ODD_PLUS', Ibz(Tpls)),
|
||||
Object('ibz_t', 'TORSION_ODD_MINUS', Ibz(Tmin)),
|
||||
Object('ibz_t', 'TORSION_PLUS_2POWER', Ibz(tors2part)),
|
||||
Object('ibz_t', 'TORSION_PLUS_3POWER', Ibz(tors3part)),
|
||||
Object('ibz_t', 'TORSION_PLUS_23POWER', Ibz(tors23part)),
|
||||
Object('ibz_t', 'DEGREE_COMMITMENT', Ibz(Dcom)),
|
||||
Object('ibz_t', 'DEGREE_COMMITMENT_PLUS', Ibz(gcd(Dcom, Tpls))),
|
||||
Object('ibz_t', 'DEGREE_COMMITMENT_MINUS', Ibz(gcd(Dcom, Tmin))),
|
||||
Object('ibz_t', 'DEGREE_CHALLENGE', Ibz(Dchall)),
|
||||
])
|
||||
|
||||
with open('include/torsion_constants.h','w') as hfile:
|
||||
with open('torsion_constants.c','w') as cfile:
|
||||
print(f'#include <intbig.h>', file=hfile)
|
||||
print(f'#include <stddef.h>', file=cfile)
|
||||
print(f'#include <stdint.h>', file=cfile)
|
||||
print(f'#include <torsion_constants.h>', file=cfile)
|
||||
|
||||
for k,v in defs.items():
|
||||
print(f'#define {k} {v}', file=hfile)
|
||||
|
||||
objs.header(file=hfile)
|
||||
objs.implementation(file=cfile)
|
||||
|
||||
Reference in New Issue
Block a user