second-round version of SQIsign
Co-authored-by: Marius A. Aardal <marius.andre.aardal@gmail.com> Co-authored-by: Gora Adj <gora.adj@tii.ae> Co-authored-by: Diego F. Aranha <dfaranha@cs.au.dk> Co-authored-by: Andrea Basso <sqisign@andreabasso.com> Co-authored-by: Isaac Andrés Canales Martínez <icanalesm0500@gmail.com> Co-authored-by: Jorge Chávez-Saab <jorgechavezsaab@gmail.com> Co-authored-by: Maria Corte-Real Santos <mariascrsantos98@gmail.com> Co-authored-by: Luca De Feo <github@defeo.lu> Co-authored-by: Max Duparc <max.duparc@epfl.ch> Co-authored-by: Jonathan Komada Eriksen <jonathan.eriksen97@gmail.com> Co-authored-by: Décio Luiz Gazzoni Filho <decio@decpp.net> Co-authored-by: Basil Hess <bhe@zurich.ibm.com> Co-authored-by: Antonin Leroux <antonin.leroux@polytechnique.org> Co-authored-by: Patrick Longa <plonga@microsoft.com> Co-authored-by: Luciano Maino <mainoluciano.96@gmail.com> Co-authored-by: Michael Meyer <michael@random-oracles.org> Co-authored-by: Hiroshi Onuki <onuki@mist.i.u-tokyo.ac.jp> Co-authored-by: Lorenz Panny <lorenz@yx7.cc> Co-authored-by: Giacomo Pope <giacomopope@gmail.com> Co-authored-by: Krijn Reijnders <reijnderskrijn@gmail.com> Co-authored-by: Damien Robert <damien.robert@inria.fr> Co-authored-by: Francisco Rodríguez-Henriquez <francisco.rodriguez@tii.ae> Co-authored-by: Sina Schaeffler <sschaeffle@student.ethz.ch> Co-authored-by: Benjamin Wesolowski <benjamin.wesolowski@ens-lyon.fr>
This commit is contained in:
committed by
Lorenz Panny
parent
ff34a8cd18
commit
91e9e464fe
148
scripts/Namespace.scala
Normal file
148
scripts/Namespace.scala
Normal file
@@ -0,0 +1,148 @@
|
||||
import io.StdIn.readLine
|
||||
|
||||
// 1. #define DISABLE_NAMESPACING in sqisign_namespace.h
|
||||
// 2. build (cmake and make)
|
||||
// 3. find . -name '*.a' -exec nm {} \; | grep '.c.o:\|T ' | scala ../scripts/Namespace.scala > sqisign_namespace.h
|
||||
// 4. cp sqisign_namespace.h $SQISIGN_DIR/include
|
||||
|
||||
object Namespace extends App {
|
||||
|
||||
val PREAMBLE = """
|
||||
#ifndef SQISIGN_NAMESPACE_H
|
||||
#define SQISIGN_NAMESPACE_H
|
||||
|
||||
//#define DISABLE_NAMESPACING
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define SQISIGN_API __declspec(dllexport)
|
||||
#else
|
||||
#define SQISIGN_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#define PARAM_JOIN3_(a, b, c) sqisign_##a##_##b##_##c
|
||||
#define PARAM_JOIN3(a, b, c) PARAM_JOIN3_(a, b, c)
|
||||
#define PARAM_NAME3(end, s) PARAM_JOIN3(SQISIGN_VARIANT, end, s)
|
||||
|
||||
#define PARAM_JOIN2_(a, b) sqisign_##a##_##b
|
||||
#define PARAM_JOIN2(a, b) PARAM_JOIN2_(a, b)
|
||||
#define PARAM_NAME2(end, s) PARAM_JOIN2(end, s)
|
||||
|
||||
#ifndef DISABLE_NAMESPACING
|
||||
#define SQISIGN_NAMESPACE_GENERIC(s) PARAM_NAME2(gen, s)
|
||||
#else
|
||||
#define SQISIGN_NAMESPACE_GENERIC(s) s
|
||||
#endif
|
||||
|
||||
#if defined(SQISIGN_VARIANT) && !defined(DISABLE_NAMESPACING)
|
||||
#if defined(SQISIGN_BUILD_TYPE_REF)
|
||||
#define SQISIGN_NAMESPACE(s) PARAM_NAME3(ref, s)
|
||||
#elif defined(SQISIGN_BUILD_TYPE_OPT)
|
||||
#define SQISIGN_NAMESPACE(s) PARAM_NAME3(opt, s)
|
||||
#elif defined(SQISIGN_BUILD_TYPE_BROADWELL)
|
||||
#define SQISIGN_NAMESPACE(s) PARAM_NAME3(broadwell, s)
|
||||
#elif defined(SQISIGN_BUILD_TYPE_ARM64CRYPTO)
|
||||
#define SQISIGN_NAMESPACE(s) PARAM_NAME3(arm64crypto, s)
|
||||
#else
|
||||
#error "Build type not known"
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define SQISIGN_NAMESPACE(s) s
|
||||
#endif
|
||||
"""
|
||||
|
||||
val EPILOGUE = """
|
||||
#endif
|
||||
"""
|
||||
|
||||
val x = Iterator
|
||||
.continually(readLine)
|
||||
.takeWhile(_ != null).toList
|
||||
|
||||
var scfile = ""
|
||||
val allFuns: List[(String, String)] = x.flatMap {
|
||||
case i if i.contains(".c.o:") =>
|
||||
scfile = i
|
||||
None
|
||||
case i =>
|
||||
i.split(" ").last match {
|
||||
case j if j.startsWith("_") =>
|
||||
Some((j.substring(1), scfile))
|
||||
case j =>
|
||||
Some((j, scfile))
|
||||
}
|
||||
|
||||
}. // removing duplicates..
|
||||
groupBy(_._1).mapValues(k => k.distinct.toList.sortBy(_._2).reduceLeft((i,j) => ((i._1, s"${i._2}, ${j._2}")))).values.toList
|
||||
|
||||
val maxFunLen = allFuns.map(i => i._1.length).max
|
||||
|
||||
val filterFiles = List(
|
||||
"fips202.c",
|
||||
"tools.c",
|
||||
"randombytes_system.c",
|
||||
"randombytes_ctrdrbg.c",
|
||||
"randombytes_ctrdrbg_aesni.c",
|
||||
"foo.c",
|
||||
"aes_c.c",
|
||||
"aes_ni.c",
|
||||
"ctr_drbg.c"
|
||||
)
|
||||
|
||||
val genericFiles = List(
|
||||
// quaternion module
|
||||
"intbig.c",
|
||||
"algebra.c",
|
||||
"ideal.c",
|
||||
"dim4.c",
|
||||
"dim2.c",
|
||||
"integers.c",
|
||||
"lattice.c",
|
||||
"lat_ball.c",
|
||||
"finit.c",
|
||||
"printer.c",
|
||||
"rationals.c",
|
||||
"l2.c",
|
||||
"lll_verification.c",
|
||||
"lll_applications.c",
|
||||
"rationals.c",
|
||||
"normeq.c",
|
||||
"ibz_division.c",
|
||||
"hnf_internal.c",
|
||||
"hnf.c",
|
||||
"random_input_generation.c",
|
||||
"mem.c",
|
||||
// mp module
|
||||
"mp.c"
|
||||
).map(i => s"$i.o:")
|
||||
|
||||
val groupedByFile =
|
||||
allFuns.
|
||||
groupBy(_._2).
|
||||
map(i => (i._1, i._2.distinct.sorted)).
|
||||
filter(i => filterFiles.forall(j => !i._1.contains(j))).toList.sortBy(_._1)
|
||||
|
||||
println(PREAMBLE)
|
||||
|
||||
|
||||
|
||||
groupedByFile.foreach(i => {
|
||||
println(s"// Namespacing symbols exported from ${i._1.replaceAll("\\.o:", "")}:")
|
||||
i._2.foreach(j =>
|
||||
println(s"#undef ${j._1}")
|
||||
)
|
||||
println
|
||||
i._2.foreach(j => {
|
||||
val padded = j._1.padTo(maxFunLen, " ").mkString
|
||||
if (genericFiles.contains(j._2)) {
|
||||
println(s"#define $padded SQISIGN_NAMESPACE_GENERIC(${j._1})")
|
||||
} else {
|
||||
println(s"#define $padded SQISIGN_NAMESPACE(${j._1})")
|
||||
}
|
||||
}
|
||||
)
|
||||
println
|
||||
})
|
||||
|
||||
println(EPILOGUE)
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/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)
|
||||
|
||||
36
scripts/check_namespace.sh
Executable file
36
scripts/check_namespace.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f "include/sqisign_namespace.h" ]; then
|
||||
echo "Please run script from the sqisign root directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' 's|//#define DISABLE_NAMESPACING|#define DISABLE_NAMESPACING|' ./include/sqisign_namespace.h
|
||||
else
|
||||
sed -i 's|//#define DISABLE_NAMESPACING|#define DISABLE_NAMESPACING|' ./include/sqisign_namespace.h
|
||||
fi
|
||||
|
||||
mkdir -p build_broadwell && cd build_broadwell && cmake -DSQISIGN_BUILD_TYPE=broadwell .. && make -j8 && cd ..
|
||||
mkdir -p build && cd build && cmake .. && make -j8
|
||||
find . ../build_broadwell -name '*.a' -exec nm {} \; | grep '.c.o:\|T ' | scala -nc ../scripts/Namespace.scala > sqisign_namespace.h
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' 's|#define DISABLE_NAMESPACING|//#define DISABLE_NAMESPACING|' ../include/sqisign_namespace.h
|
||||
else
|
||||
sed -i 's|#define DISABLE_NAMESPACING|//#define DISABLE_NAMESPACING|' ../include/sqisign_namespace.h
|
||||
fi
|
||||
|
||||
diff sqisign_namespace.h ../include/sqisign_namespace.h
|
||||
|
||||
|
||||
# Check the exit code of diff
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "No change in namespace."
|
||||
exit 0
|
||||
else
|
||||
echo "Namespace changed, please update."
|
||||
exit 1
|
||||
fi
|
||||
16
scripts/gen_kat_files.sh
Executable file
16
scripts/gen_kat_files.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo 'Running Script for Level 1...'
|
||||
./build/apps/PQCgenKAT_sign_lvl1
|
||||
mv PQCsignKAT_353_SQIsign_lvl1.req ./KAT/PQCsignKAT_353_SQIsign_lvl1.req
|
||||
mv PQCsignKAT_353_SQIsign_lvl1.rsp ./KAT/PQCsignKAT_353_SQIsign_lvl1.rsp
|
||||
|
||||
echo 'Running Script for Level 3...'
|
||||
./build/apps/PQCgenKAT_sign_lvl3
|
||||
mv PQCsignKAT_529_SQIsign_lvl3.req ./KAT/PQCsignKAT_529_SQIsign_lvl3.req
|
||||
mv PQCsignKAT_529_SQIsign_lvl3.rsp ./KAT/PQCsignKAT_529_SQIsign_lvl3.rsp
|
||||
|
||||
echo 'Running Script for Level 5...'
|
||||
./build/apps/PQCgenKAT_sign_lvl5
|
||||
mv PQCsignKAT_701_SQIsign_lvl5.req ./KAT/PQCsignKAT_701_SQIsign_lvl5.req
|
||||
mv PQCsignKAT_701_SQIsign_lvl5.rsp ./KAT/PQCsignKAT_701_SQIsign_lvl5.rsp
|
||||
60
scripts/gen_pqm4_sources.sh
Executable file
60
scripts/gen_pqm4_sources.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script should be run in the root folder of the repository, and creates pqm4 files in "src/pqm4/sqisign{1,3,5}"
|
||||
|
||||
if [ -d "src/pqm4" ]; then
|
||||
echo Destination folder src/pqm4 already exists. Delete it before running this script. Aborting.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for LEVEL in 1 3 5
|
||||
do
|
||||
LVL=lvl${LEVEL}
|
||||
DST_PATH=src/pqm4/sqisign_${LVL}/ref
|
||||
|
||||
PQCGENKAT_SIGN_PQM4_BINARY=build/apps/PQCgenKAT_sign_pqm4_${LVL}
|
||||
|
||||
if [ ! -f ${PQCGENKAT_SIGN_PQM4_BINARY} ]; then
|
||||
echo ${PQCGENKAT_SIGN_PQM4_BINARY} not found. Build it before running this script, or change the build folder in the script. Aborting.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p ${DST_PATH}
|
||||
|
||||
# Run API generation script
|
||||
${PQCGENKAT_SIGN_PQM4_BINARY}
|
||||
|
||||
CPPFLAGS="-DRADIX_32 -DSQISIGN_BUILD_TYPE_REF -DSQISIGN_GF_IMPL_REF -DSQISIGN_VARIANT=${LVL} -DTARGET_ARM -DTARGET_OS_OTHER -DNDEBUG -DDISABLE_NAMESPACING -DBIG_PUBLIC_KEY_TESTS"
|
||||
PQM4_NAME="crypto_sign_sqisign_${LVL}_ref"
|
||||
|
||||
echo "elf/${PQM4_NAME}_%.elf: CPPFLAGS+=${CPPFLAGS}" > ${DST_PATH}/config.mk
|
||||
echo "obj/lib${PQM4_NAME}.a: CPPFLAGS+=${CPPFLAGS}" >> ${DST_PATH}/config.mk
|
||||
|
||||
cp include/{sig,sqisign_namespace}.h ${DST_PATH}/
|
||||
|
||||
cp src/sqisign.c ${DST_PATH}/
|
||||
|
||||
cp src/common/generic/include/{tools,tutil}.h ${DST_PATH}/
|
||||
|
||||
cp src/ec/ref/lvlx/{basis,ec_jac,ec,isog_chains,xeval,xisog}.c ${DST_PATH}/
|
||||
cp src/ec/ref/include/{ec,isog}.h ${DST_PATH}/
|
||||
|
||||
cp src/gf/ref/lvlx/{fp,fp2}.c ${DST_PATH}/
|
||||
cp src/gf/ref/include/{fp,fp2}.h ${DST_PATH}/
|
||||
|
||||
cp src/hd/ref/lvlx/{hd.c,theta_isogenies.c,theta_isogenies.h,theta_structure.c,theta_structure.h} ${DST_PATH}/
|
||||
cp src/hd/ref/include/hd.h ${DST_PATH}/
|
||||
|
||||
cp src/mp/ref/generic/mp.c ${DST_PATH}/
|
||||
cp src/mp/ref/generic/include/mp.h ${DST_PATH}/
|
||||
|
||||
cp src/precomp/ref/${LVL}/include/{e0_basis,ec_params,encoded_sizes,fp_constants,hd_splitting_transforms}.h ${DST_PATH}/
|
||||
cp src/precomp/ref/${LVL}/{e0_basis,ec_params,hd_splitting_transforms}.c ${DST_PATH}/
|
||||
|
||||
cp src/verification/ref/lvlx/{common,encode_verification,verify}.c ${DST_PATH}/
|
||||
cp src/verification/ref/include/verification.h ${DST_PATH}/
|
||||
done
|
||||
|
||||
cp src/gf/ref/lvl1/fp_p5248_32.c src/pqm4/sqisign_lvl1/ref/
|
||||
cp src/gf/ref/lvl3/fp_p65376_32.c src/pqm4/sqisign_lvl3/ref/
|
||||
cp src/gf/ref/lvl5/fp_p27500_32.c src/pqm4/sqisign_lvl5/ref/
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/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']
|
||||
|
||||
128
scripts/precomp/cformat.py
Normal file
128
scripts/precomp/cformat.py
Normal file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, itertools
|
||||
from math import ceil, 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 FpEl:
|
||||
ref_p5248_radix_map = { 16: 13, 32: 29, 64: 51 }
|
||||
ref_p65376_radix_map = { 16: 13, 32: 28, 64: 55 }
|
||||
ref_p27500_radix_map = { 16: 13, 32: 29, 64: 57 }
|
||||
def __init__(self, n, p, montgomery=True):
|
||||
self.n = n
|
||||
self.p = p
|
||||
self.montgomery = montgomery
|
||||
def __get_radix(self, word_size, arith=None):
|
||||
if arith == "ref" or arith is None:
|
||||
# lvl1
|
||||
if self.p == 0x4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:
|
||||
return self.ref_p5248_radix_map[word_size]
|
||||
# lvl3
|
||||
elif self.p == 0x40ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:
|
||||
return self.ref_p65376_radix_map[word_size]
|
||||
# lvl5
|
||||
elif self.p == 0x1afffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:
|
||||
return self.ref_p27500_radix_map[word_size]
|
||||
raise ValueError(f'Invalid prime \"{self.p}\"')
|
||||
elif arith == "broadwell":
|
||||
return word_size
|
||||
raise ValueError(f'Invalid arithmetic implementation type \"{arith}\"')
|
||||
def _literal(self, sz, arith=None):
|
||||
radix = self.__get_radix(sz, arith=arith)
|
||||
l = 1 + floor(log(self.p, 2**radix))
|
||||
# If we're using Montgomery representation, we need to multiply
|
||||
# by the Montgomery factor R = 2^nw (n = limb number, w = radix)
|
||||
if self.montgomery:
|
||||
R = 2**(radix * ceil(log(self.p, 2**radix)))
|
||||
else:
|
||||
R = 1
|
||||
el = (self.n * R) % self.p
|
||||
vs = [(int(el) >> radix*i) % 2**radix for i in range(l)]
|
||||
return '{' + ', '.join(map(hex, vs)) + '}'
|
||||
|
||||
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):
|
||||
def rec(obj):
|
||||
if isinstance(obj, int):
|
||||
if obj < 256: return str(obj)
|
||||
else: return hex(obj)
|
||||
if isinstance(obj, sage.all.Integer):
|
||||
if obj < 256: return str(obj)
|
||||
else: return hex(obj)
|
||||
if isinstance(obj, Ibz):
|
||||
literal = "\n#if 0"
|
||||
for sz in (16, 32, 64):
|
||||
literal += f"\n#elif GMP_LIMB_BITS == {sz}"
|
||||
literal += f"\n{obj._literal(sz)}"
|
||||
return literal + "\n#endif\n"
|
||||
if isinstance(obj, FpEl):
|
||||
literal = "\n#if 0"
|
||||
for sz in (16, 32, 64):
|
||||
literal += f"\n#elif RADIX == {sz}"
|
||||
if sz == 64:
|
||||
literal += "\n#if defined(SQISIGN_GF_IMPL_BROADWELL)"
|
||||
literal += f"\n{obj._literal(sz, 'broadwell')}"
|
||||
literal += "\n#else"
|
||||
literal += f"\n{obj._literal(sz, 'ref')}"
|
||||
literal += "\n#endif"
|
||||
else:
|
||||
literal += f"\n{obj._literal(sz, 'ref')}"
|
||||
return literal + "\n#endif\n"
|
||||
if isinstance(obj, list) or isinstance(obj, tuple):
|
||||
return '{' + ', '.join(map(rec, obj)) + '}'
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
raise NotImplementedError(f'unknown type {type(obj)} in Formatter')
|
||||
return rec(self.obj)
|
||||
|
||||
def _definition(self):
|
||||
return f'const {self.ty[0]} {self.name}{self.ty[1]} = ' + self._literal() + ';'
|
||||
|
||||
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):
|
||||
for obj in self.objs:
|
||||
assert isinstance(obj, Object)
|
||||
print(obj._definition(), file=file)
|
||||
41
scripts/precomp/ec_params.sage
Executable file
41
scripts/precomp/ec_params.sage
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from sage.all import *
|
||||
from parameters import p, f
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
cof = (p+1)//(2**f)
|
||||
|
||||
from cformat import Object, ObjectFormatter
|
||||
|
||||
obj_cof = ObjectFormatter(
|
||||
[
|
||||
Object('digit_t[]', 'p_cofactor_for_2f', [cof]),
|
||||
]
|
||||
)
|
||||
|
||||
with open("include/ec_params.h", "w") as hfile:
|
||||
with open("ec_params.c", "w") as cfile:
|
||||
|
||||
hfile.write('#ifndef EC_PARAMS_H\n')
|
||||
hfile.write('#define EC_PARAMS_H\n')
|
||||
hfile.write('\n')
|
||||
|
||||
hfile.write('#include <fp.h>\n')
|
||||
cfile.write('#include <ec_params.h>\n')
|
||||
hfile.write('\n')
|
||||
|
||||
hfile.write(f'#define TORSION_EVEN_POWER {f}\n')
|
||||
hfile.write('\n')
|
||||
|
||||
hfile.write('// p+1 divided by the power of 2\n')
|
||||
cfile.write('// p+1 divided by the power of 2\n')
|
||||
obj_cof.header(file=hfile)
|
||||
obj_cof.implementation(file=cfile)
|
||||
hfile.write(f'#define P_COFACTOR_FOR_2F_BITLENGTH {((p+1)//(2**f)).bit_length()}\n')
|
||||
hfile.write('\n')
|
||||
cfile.write('\n')
|
||||
|
||||
|
||||
hfile.write('#endif\n')
|
||||
88
scripts/precomp/maxorders.py
Normal file
88
scripts/precomp/maxorders.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from sage.all import *
|
||||
|
||||
from sage.misc.banner import require_version
|
||||
if not require_version(10, 5, print_message=True):
|
||||
exit('')
|
||||
|
||||
from parameters import p, num_orders as num
|
||||
|
||||
################################################################
|
||||
|
||||
# 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)
|
||||
|
||||
Quat1, (i,j,k) = QuaternionAlgebra(-1, -p).objgens()
|
||||
assert Quat1.discriminant() == p # ramifies correctly
|
||||
|
||||
O0mat = matrix([list(g) for g in [Quat1(1), i, (i+j)/2, (1+k)/2]])
|
||||
O0 = Quat1.quaternion_order(list(O0mat))
|
||||
|
||||
orders = [ (1, identity_matrix(QQ,4), O0mat, i, O0mat, vector((1,0,0,0))) ]
|
||||
|
||||
q = ZZ(1)
|
||||
while len(orders) < num:
|
||||
q = next_prime(q)
|
||||
if q % 4 != 1: # restricting to q ≡ 1 (mod 4)
|
||||
continue
|
||||
|
||||
Quatq, (ii,jj,kk) = QuaternionAlgebra(-q, -p).objgens()
|
||||
if Quatq.discriminant() != p: # ramifies incorrectly
|
||||
continue
|
||||
|
||||
x, y = QuadraticForm(QQ, 2, [1,0,p]).solve(q)
|
||||
gamma = x + j*y
|
||||
assert gamma.reduced_norm() == q
|
||||
ims1 = [Quat1(1), i*gamma, j, k*gamma]
|
||||
assert ims1[1]**2 == -q
|
||||
assert ims1[2]**2 == -p
|
||||
assert ims1[1]*ims1[2] == ims1[3]
|
||||
assert ims1[2]*ims1[1] == -ims1[3]
|
||||
# (1,ii,jj,kk)->ims1 is an isomorphism Quatq->Quat1
|
||||
iso1q = ~matrix(map(list, ims1))
|
||||
|
||||
r = min(map(ZZ, Mod(-p, 4*q).sqrt(all=True)))
|
||||
|
||||
basq = [
|
||||
Quatq(1),
|
||||
ii,
|
||||
(1 + jj) / 2,
|
||||
(r + jj) * ii / 2 / q,
|
||||
]
|
||||
|
||||
Oq = Quatq.quaternion_order(basq)
|
||||
assert Oq.discriminant() == p # is maximal
|
||||
|
||||
mat1 = matrix(map(list, basq)) * ~iso1q
|
||||
O1 = Quat1.quaternion_order(list(mat1))
|
||||
assert O1.discriminant() == p # is maximal
|
||||
assert j in O1 # p-extremal
|
||||
|
||||
# look for an odd connecting ideal
|
||||
I = O0 * O1
|
||||
I *= I.norm().denominator()
|
||||
assert I.is_integral()
|
||||
for v in IntegralLattice(I.gram_matrix()).enumerate_short_vectors():
|
||||
elt = sum(c*g for c,g in zip(v,I.basis()))
|
||||
if ZZ(elt.reduced_norm() / I.norm()) % 2:
|
||||
break
|
||||
I = I * (elt.conjugate() / I.norm())
|
||||
assert I.is_integral()
|
||||
assert I.norm() % 2
|
||||
assert I.left_order() == O0
|
||||
|
||||
O1_ = I.right_order()
|
||||
assert O1_.unit_ideal() == elt * O1 * ~elt
|
||||
idl1 = matrix(map(list, I.basis()))
|
||||
|
||||
# q
|
||||
# isomorphism from (-1,-p) algebra to (-q,-p) algebra
|
||||
# basis of maximal order O₁ in (-1,-p) algebra
|
||||
# element sqrt(-q) in O₁ in (-1,-p) algebra
|
||||
# basis of connecting ideal I from O₀ in (-1,-p) algebra
|
||||
# element γ such that I has right order γ O₁ γ^-1
|
||||
orders.append((q, iso1q, mat1, ims1[1], idl1, vector(elt)))
|
||||
|
||||
16
scripts/precomp/parameters.py
Normal file
16
scripts/precomp/parameters.py
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/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', 'num_orders'):
|
||||
m = re.search(rf'^\s*{k}\s*=\s*([x0-9a-f]+)', l)
|
||||
if m:
|
||||
v = ZZ(m.groups()[0], 0)
|
||||
globals()[k] = v
|
||||
|
||||
f = (p+1).valuation(2)
|
||||
|
||||
__all__ = ['lvl', 'p', 'f', 'num_orders']
|
||||
|
||||
38
scripts/precomp/precompute_E0_basis.sage
Executable file
38
scripts/precomp/precompute_E0_basis.sage
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
################################################################
|
||||
|
||||
from parameters import p, f
|
||||
|
||||
if p % 4 != 3:
|
||||
raise NotImplementedError('requires p ≡ 3 (mod 4)')
|
||||
|
||||
assert (1 << f).divides(p + 1)
|
||||
Fp2.<i> = GF((p,2), modulus=[1,0,1])
|
||||
E0 = EllipticCurve(Fp2, [1, 0])
|
||||
|
||||
from torsion_basis import even_torsion_basis_E0
|
||||
P, Q = even_torsion_basis_E0(E0, f)
|
||||
|
||||
################################################################
|
||||
|
||||
from cformat import FpEl, Object, ObjectFormatter
|
||||
|
||||
def Fp2_to_list(el):
|
||||
return [FpEl(int(c), p, True) for c in Fp2(el)]
|
||||
|
||||
objs = ObjectFormatter([
|
||||
Object('fp2_t', 'BASIS_E0_PX', Fp2_to_list(P.x())),
|
||||
Object('fp2_t', 'BASIS_E0_QX', Fp2_to_list(Q.x())),
|
||||
])
|
||||
|
||||
################################################################
|
||||
|
||||
with open('include/e0_basis.h','w') as hfile:
|
||||
with open('e0_basis.c','w') as cfile:
|
||||
print(f'#include <fp2.h>', file=hfile)
|
||||
print(f'#include <e0_basis.h>', file=cfile)
|
||||
|
||||
objs.header(file=hfile)
|
||||
objs.implementation(file=cfile)
|
||||
303
scripts/precomp/precompute_endomorphism_action.sage
Executable file
303
scripts/precomp/precompute_endomorphism_action.sage
Executable file
@@ -0,0 +1,303 @@
|
||||
#!/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, f
|
||||
from torsion_basis import even_torsion_basis_E0
|
||||
|
||||
################################################################
|
||||
|
||||
from sage.groups.generic import order_from_multiple
|
||||
pari.allocatemem(1 << 34) # 16G
|
||||
|
||||
if p % 4 != 3:
|
||||
raise NotImplementedError('requires p ≡ 3 (mod 4)')
|
||||
|
||||
assert (1 << f).divides(p + 1)
|
||||
Fp2.<i> = GF((p,2), modulus=[1,0,1])
|
||||
|
||||
sqrtm1 = min(Fp2(-1).sqrt(all=True))
|
||||
|
||||
def compute(q, mat, idl, iso1q):
|
||||
print(f'\x1b[33m{q = }\x1b[0m')
|
||||
E0 = EllipticCurve(Fp2, [1,0])
|
||||
E0.set_order((p+1)^2)
|
||||
|
||||
if q == 1:
|
||||
E1 = E0
|
||||
P1, Q1 = even_torsion_basis_E0(E1, f)
|
||||
print(f'E0 = {E1}')
|
||||
print(f'P0 = {P1}')
|
||||
print(f'Q0 = {Q1}')
|
||||
|
||||
else:
|
||||
Quat.<i,j,k> = QuaternionAlgebra(-1, -p)
|
||||
I = Quat.ideal(map(Quat, idl))
|
||||
# print(f'{I = }')
|
||||
O0 = Quat.quaternion_order(list(map(Quat, orders[0][2])))
|
||||
# print(f'{O0 = }')
|
||||
O1 = I.right_order()
|
||||
# print(f'{O1 = }')
|
||||
assert I.left_order() == O0
|
||||
assert O0.is_maximal() and O1.is_maximal()
|
||||
assert I.norm() % 2
|
||||
|
||||
from deuring2d import Deuring2D
|
||||
ctx = Deuring2D(p)
|
||||
assert ctx.O0.order == O0
|
||||
assert ctx.E0 == E0
|
||||
ctx.sqrtm1 = sqrtm1
|
||||
|
||||
P0, Q0 = data[0][1]
|
||||
|
||||
for deg in range(1,10):
|
||||
|
||||
print(f'trying {deg = }...')
|
||||
ctx.e = E0.cardinality(extension_degree=2^deg).sqrt().valuation(2) - 1
|
||||
|
||||
first = True
|
||||
for suitable in ctx.SuitableIdeals(I, attempts=10**6, bound=10**3):
|
||||
|
||||
if first:
|
||||
Fbig.<U> = Fp2.extension(2^deg)
|
||||
ctx.E0 = E0.change_ring(Fbig)
|
||||
ctx.P = P0.change_ring(Fbig)
|
||||
ctx.Q = Q0.change_ring(Fbig)
|
||||
assert ctx.e == ctx.E0.order().sqrt().valuation(2) - 1
|
||||
for _ in range(ctx.e - f):
|
||||
ctx.P = ctx.P.division_points(2)[0]
|
||||
ctx.Q = ctx.Q.division_points(2)[0]
|
||||
ctx.P.set_order(multiple=2^ctx.e)
|
||||
ctx.Q.set_order(multiple=2^ctx.e)
|
||||
first = False
|
||||
|
||||
try:
|
||||
E1, P1, Q1 = ctx.IdealToIsogeny(I, suitable=suitable)
|
||||
break
|
||||
except Deuring2D.Failure:
|
||||
continue
|
||||
|
||||
else:
|
||||
continue
|
||||
break
|
||||
|
||||
else:
|
||||
raise NotImplementedError('Deuring2D failed')
|
||||
|
||||
E1 = E1.change_ring(Fp2)
|
||||
|
||||
j = GF(p)(E1.j_invariant())
|
||||
X = polygen(GF(p))
|
||||
for A,_ in sorted((256*(X^2-3)^3 - (X^2-4)*j).roots()):
|
||||
E1_ = EllipticCurve(Fp2, [0,A,0,1,0])
|
||||
try:
|
||||
iso = min(E1.isomorphisms(E1_))
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
E1 = iso.codomain()
|
||||
P1 = iso._eval(P1)
|
||||
Q1 = iso._eval(Q1)
|
||||
print(f'{E1 = }')
|
||||
|
||||
P1 *= ctx.P.order() // P0.order()
|
||||
Q1 *= ctx.Q.order() // Q0.order()
|
||||
P1 = P1.change_ring(Fp2)
|
||||
Q1 = Q1.change_ring(Fp2)
|
||||
print(f'{P1 = }')
|
||||
print(f'{Q1 = }')
|
||||
P1.set_order(P0.order())
|
||||
Q1.set_order(Q0.order())
|
||||
assert P0.order() == Q0.order() == P1.order() == Q1.order() == 2^f
|
||||
|
||||
assert P1.weil_pairing(Q1,2^f) == P0.weil_pairing(Q0,2^f)^I.norm()
|
||||
|
||||
if q == 1:
|
||||
endo_i, = (a for a in E1.automorphisms() if a.scaling_factor() == sqrtm1)
|
||||
else:
|
||||
iso = E1.isomorphism(min(Fp2(-q).sqrt(all=True)), is_codomain=True)
|
||||
try:
|
||||
endo_i = iso * E1.isogeny(None, codomain=iso.domain(), degree=q)
|
||||
except ValueError:
|
||||
assert False
|
||||
# assert endo_i^2 == -q
|
||||
|
||||
endo_1 = E1.scalar_multiplication(1)
|
||||
endo_j = E1.frobenius_isogeny()
|
||||
endo_k = endo_i * endo_j
|
||||
|
||||
if __debug__:
|
||||
R = E1.random_point()
|
||||
assert (endo_i^2)(R) == -q*R
|
||||
assert (endo_j^2)(R) == -p*R
|
||||
assert (endo_j*endo_i)(R) == -(endo_i*endo_j)(R)
|
||||
|
||||
denom = mat.denominator()
|
||||
coprime = denom.prime_to_m_part(lcm(P1.order(), Q1.order()))
|
||||
P1d, Q1d = (inverse_mod(coprime, T.order()) * T for T in (P1, Q1))
|
||||
|
||||
denom //= coprime
|
||||
|
||||
extdeg = next(d for d in range(1,denom+1) if ((denom<<f)^2).divides(E1.order(extension_degree=d)))
|
||||
if extdeg == 1:
|
||||
Fbig = Fp2
|
||||
else:
|
||||
Fbig.<U> = Fp2.extension(extdeg)
|
||||
|
||||
P1d, Q1d = (T.change_ring(Fbig) for T in (P1d, Q1d))
|
||||
P1d.set_order(multiple=denom<<f)
|
||||
for l,m in denom.factor():
|
||||
for i in range(m):
|
||||
assert l.divides(P1d.order())
|
||||
P1d = P1d.division_points(l)[0]
|
||||
P1d.set_order(multiple=denom<<f)
|
||||
for Q1d_ in Q1d.division_points(l):
|
||||
o = order_from_multiple(P1d.weil_pairing(Q1d_, P1d.order()), denom<<f, operation='*')
|
||||
if o == P1d.order():
|
||||
Q1d = Q1d_
|
||||
break
|
||||
else:
|
||||
assert False
|
||||
assert hasattr(P1d, '_order')
|
||||
Q1d.set_order(multiple=denom<<f)
|
||||
|
||||
denom *= coprime
|
||||
|
||||
PQ1d = P1d, Q1d
|
||||
# mat1 = matrix(Zmod(1<<f), [endo_1._eval(T).log(PQ1d) for T in PQ1d])
|
||||
# assert mat1 == 1 # identity; omit
|
||||
mati = matrix(Zmod(1<<f), [endo_i._eval(T).log(PQ1d) for T in PQ1d])
|
||||
matj = matrix(Zmod(1<<f), [endo_j._eval(T).log(PQ1d) for T in PQ1d])
|
||||
# matk = matrix(Zmod(1<<f), [endo_k._eval(T).log(PQ1d) for T in PQ1d])
|
||||
# assert matk == matj * mati # redundant; omit
|
||||
matk = matj * mati
|
||||
|
||||
gens = []
|
||||
for row in denom * mat:
|
||||
endo = sum(ZZ(c)*e for c,e in zip(row, (endo_1,endo_i,endo_j,endo_k)))
|
||||
gens.append(endo)
|
||||
gen1, gen2, gen3, gen4 = gens
|
||||
|
||||
assert mat[0] == vector((1,0,0,0))
|
||||
# mat1 = matrix(ZZ, [gen1._eval(T).log(PQ1d) for T in PQ1d]) / denom
|
||||
# assert mat1 == 1 # identity; omit
|
||||
mat2 = matrix(ZZ, [gen2._eval(T).log(PQ1d) for T in PQ1d]) / denom
|
||||
mat3 = matrix(ZZ, [gen3._eval(T).log(PQ1d) for T in PQ1d]) / denom
|
||||
mat4 = matrix(ZZ, [gen4._eval(T).log(PQ1d) for T in PQ1d]) / denom
|
||||
mat2, mat3, mat4 = (M.change_ring(Zmod(1<<f)) for M in (mat2,mat3,mat4))
|
||||
|
||||
A = E1.a2()
|
||||
assert E1.a_invariants() == (0,A,0,1,0)
|
||||
|
||||
return (A, (A+2)/4), (P1, Q1), (mati,matj,matk), (mat2,mat3,mat4)
|
||||
|
||||
################################################################
|
||||
|
||||
from maxorders import orders
|
||||
|
||||
print('qs:', [q for q,_,_,_,_,_ in orders])
|
||||
|
||||
todo = [(q, mat*iso1q, idl, iso1q) for q,iso1q,mat,_,idl,_ in orders]
|
||||
data = [None] * len(todo)
|
||||
|
||||
assert todo[0][0] == 1
|
||||
data[0] = compute(*todo[0]) # compute this first; we need it for the others
|
||||
print(f'[\x1b[32m+\x1b[0m] finished precomputation for \x1b[36mq = {todo[0][0]}\x1b[0m.')
|
||||
|
||||
####XXX
|
||||
##for idx,inp in enumerate(todo[1:],1):
|
||||
## data[idx] = compute(*inp)
|
||||
## print(f'[\x1b[32m+\x1b[0m] finished precomputation for \x1b[36mq = {inp[0]}\x1b[0m.')
|
||||
##todo = []
|
||||
####XXX
|
||||
|
||||
for (inp,_),res in parallel(8)(compute)(todo[1:]):
|
||||
q,_,_,_ = inp
|
||||
idx, = (i for i,(qq,_,_,_) in enumerate(todo) if qq == q)
|
||||
assert data[idx] is None
|
||||
data[idx] = res
|
||||
print(f'[\x1b[32m+\x1b[0m] finished precomputation for \x1b[36m{q = }\x1b[0m.')
|
||||
|
||||
################################################################
|
||||
|
||||
from cformat import FpEl, Ibz, Object, ObjectFormatter
|
||||
|
||||
def Fp2_to_list(el):
|
||||
return [FpEl(int(c), p, True) for c in Fp2(el)]
|
||||
|
||||
def basis2field(P, Q):
|
||||
vs = [
|
||||
[Fp2_to_list(T[0]), Fp2_to_list(T[2])]
|
||||
for T in (P,Q,P-Q)
|
||||
]
|
||||
return vs
|
||||
|
||||
################################################################
|
||||
|
||||
objs = ObjectFormatter([
|
||||
Object('curve_with_endomorphism_ring_t[]', 'CURVES_WITH_ENDOMORPHISMS',
|
||||
[
|
||||
[
|
||||
[Fp2_to_list(A), Fp2_to_list(1), # ec_curve_t A, C
|
||||
[Fp2_to_list(A24), Fp2_to_list(1)], "true"], # ec_curve_t A24, is_A24_computed_and_normalized
|
||||
basis2field(*basis), # ec_basis_t
|
||||
[[Ibz(v) for v in vs] for vs in mati.transpose()], # ibz_mat_2x2_t
|
||||
[[Ibz(v) for v in vs] for vs in matj.transpose()], # ibz_mat_2x2_t
|
||||
[[Ibz(v) for v in vs] for vs in matk.transpose()], # ibz_mat_2x2_t
|
||||
[[Ibz(v) for v in vs] for vs in mat2.transpose()], # ibz_mat_2x2_t
|
||||
[[Ibz(v) for v in vs] for vs in mat3.transpose()], # ibz_mat_2x2_t
|
||||
[[Ibz(v) for v in vs] for vs in mat4.transpose()], # ibz_mat_2x2_t
|
||||
]
|
||||
for (A,A24),basis,(mati,matj,matk),(mat2,mat3,mat4)
|
||||
in data
|
||||
])
|
||||
])
|
||||
|
||||
with open('include/endomorphism_action.h','w') as hfile:
|
||||
with open('endomorphism_action.c','w') as cfile:
|
||||
print(f'#ifndef ENDOMORPHISM_ACTION_H', file=hfile)
|
||||
print(f'#define ENDOMORPHISM_ACTION_H', file=hfile)
|
||||
print(f'#include <sqisign_namespace.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)
|
||||
|
||||
print('''
|
||||
/** Type for precomputed endomorphism rings applied to precomputed torsion bases.
|
||||
*
|
||||
* Precomputed by the precompute scripts.
|
||||
*
|
||||
* @typedef curve_with_endomorphism_ring_t
|
||||
*
|
||||
* @struct curve_with_endomorphism_ring
|
||||
**/
|
||||
typedef struct curve_with_endomorphism_ring {
|
||||
ec_curve_t curve;
|
||||
ec_basis_t basis_even;
|
||||
ibz_mat_2x2_t action_i, action_j, action_k;
|
||||
ibz_mat_2x2_t action_gen2, action_gen3, action_gen4;
|
||||
} curve_with_endomorphism_ring_t;
|
||||
'''.strip(), file=hfile)
|
||||
|
||||
print(f'#define CURVE_E0 (CURVES_WITH_ENDOMORPHISMS->curve)', file=hfile)
|
||||
print(f'#define BASIS_EVEN (CURVES_WITH_ENDOMORPHISMS->basis_even)', file=hfile)
|
||||
print(f'#define ACTION_I (CURVES_WITH_ENDOMORPHISMS->action_i)', file=hfile)
|
||||
print(f'#define ACTION_J (CURVES_WITH_ENDOMORPHISMS->action_j)', file=hfile)
|
||||
print(f'#define ACTION_K (CURVES_WITH_ENDOMORPHISMS->action_k)', file=hfile)
|
||||
print(f'#define ACTION_GEN2 (CURVES_WITH_ENDOMORPHISMS->action_gen2)', file=hfile)
|
||||
print(f'#define ACTION_GEN3 (CURVES_WITH_ENDOMORPHISMS->action_gen3)', file=hfile)
|
||||
print(f'#define ACTION_GEN4 (CURVES_WITH_ENDOMORPHISMS->action_gen4)', file=hfile)
|
||||
print(f'#define NUM_ALTERNATE_STARTING_CURVES {len(data)-1}', file=hfile)
|
||||
print(f'#define ALTERNATE_STARTING_CURVES (CURVES_WITH_ENDOMORPHISMS+1)', file=hfile)
|
||||
|
||||
objs.header(file=hfile)
|
||||
objs.implementation(file=cfile)
|
||||
|
||||
print(f'#endif', file=hfile)
|
||||
158
scripts/precomp/precompute_hd_splitting.sage
Executable file
158
scripts/precomp/precompute_hd_splitting.sage
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
################################################################
|
||||
|
||||
from parameters import p
|
||||
|
||||
# Field
|
||||
Fp2.<i> = GF((p,2), modulus=[1,0,1])
|
||||
|
||||
Fp2_constants = [
|
||||
[Fp2(0), Fp2(1), Fp2(i), Fp2(-1), Fp2(-i)],
|
||||
["FP2_ZERO", "FP2_ONE", "FP2_I", "FP2_MINUS_ONE", "FP2_MINUS_I"]
|
||||
]
|
||||
|
||||
################################################################
|
||||
|
||||
from cformat import FpEl
|
||||
|
||||
def Fp2_to_list(el):
|
||||
return [FpEl(int(c), p, True) for c in Fp2(el)]
|
||||
|
||||
def Fp2_to_name(el):
|
||||
return Fp2_constants[1][Fp2_constants[0].index(el)]
|
||||
|
||||
################################################################
|
||||
|
||||
# Splitting Data
|
||||
|
||||
chi_eval = [
|
||||
[1,1,1,1],
|
||||
[1,-1,1,-1],
|
||||
[1,1,-1,-1],
|
||||
[1,-1,-1,1]
|
||||
]
|
||||
|
||||
even_indices = [
|
||||
[0, 0],
|
||||
[0, 1],
|
||||
[0, 2],
|
||||
[0, 3],
|
||||
[1, 0],
|
||||
[1, 2],
|
||||
[2, 0],
|
||||
[2, 1],
|
||||
[3, 0],
|
||||
[3, 3],
|
||||
]
|
||||
|
||||
splitting_map = {
|
||||
(0, 2): [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, -1, 0]],
|
||||
(3, 3): [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
|
||||
(0, 3): [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]],
|
||||
(2, 1): [[1, 1, 1, 1], [1, -1, 1, -1], [1, -1, -1, 1], [1, 1, -1, -1]],
|
||||
(0, 1): [[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, -1, 0, 0]],
|
||||
(1, 2): [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]],
|
||||
(2, 0): [[1, 1, 1, 1], [1, -1, 1, -1], [1, -1, -1, 1], [-1, -1, 1, 1]],
|
||||
(3, 0): [[1, 1, 1, 1], [1, -1, 1, -1], [1, 1, -1, -1], [-1, 1, 1, -1]],
|
||||
(1, 0): [[1, 1, 1, 1], [1, -1, -1, 1], [1, 1, -1, -1], [-1, 1, -1, 1]],
|
||||
(0, 0): [[1, i, 1, i], [1, -i, -1, i], [1, i, -1, -i], [-1, i, -1, i]],
|
||||
}
|
||||
|
||||
# 24 2x2 maps used for normalization. Applying a uniform matrix to a level 2
|
||||
# theta null point ensure that we get a random theta null point in the
|
||||
# equivalence class of Γ/Γ(2,4)
|
||||
full_normalization_maps_2d = [
|
||||
matrix(2, 2, [1, 0, 0, 1]),
|
||||
matrix(2, 2, [0, 1, 1, 0]),
|
||||
matrix(2, 2, [1, 0, 0, -1]),
|
||||
matrix(2, 2, [0, 1, -1, 0]),
|
||||
|
||||
matrix(2, 2, [1, 1, 1, -1]),
|
||||
matrix(2, 2, [1, -1, 1, 1]),
|
||||
matrix(2, 2, [1, 1, -1, 1]),
|
||||
matrix(2, 2, [-1, 1, 1, 1]),
|
||||
|
||||
matrix(2, 2, [1, 0, 0, i]),
|
||||
matrix(2, 2, [0, i, 1, 0]),
|
||||
matrix(2, 2, [1, 0, 0, -i]),
|
||||
matrix(2, 2, [0, i, -1, 0]),
|
||||
|
||||
matrix(2, 2, [i, 1, 1, i]),
|
||||
matrix(2, 2, [1, i, i, 1]),
|
||||
matrix(2, 2, [i, 1, -1, -i]),
|
||||
matrix(2, 2, [1, i, -i, -1]),
|
||||
|
||||
matrix(2, 2, [1, i, 1, -i]),
|
||||
matrix(2, 2, [1, -i, 1, i]),
|
||||
matrix(2, 2, [1, i, -1, i]),
|
||||
matrix(2, 2, [1, -i, -1, -i]),
|
||||
|
||||
matrix(2, 2, [1, 1, i, -i]),
|
||||
matrix(2, 2, [i, -i, 1, 1]),
|
||||
matrix(2, 2, [1, 1, -i, i]),
|
||||
matrix(2, 2, [i, -i, -1, -1]),
|
||||
]
|
||||
|
||||
# 6 2x2 maps used for normalisation. A subset of the preceding 24 matrices,
|
||||
# that are sufficient to ensure uniform normalisation (under Γ/Γ^0(4))
|
||||
# when using the Montgomery model
|
||||
normalization_maps_2d = [
|
||||
matrix(2, 2, [1, 0, 0, 1]),
|
||||
matrix(2, 2, [0, 1, 1, 0]),
|
||||
matrix(2, 2, [1, 1, 1, -1]),
|
||||
matrix(2, 2, [-1, 1, 1, 1]),
|
||||
matrix(2, 2, [i, 1, 1, i]),
|
||||
matrix(2, 2, [1, i, i, 1]),
|
||||
]
|
||||
|
||||
|
||||
# Format from dictionary to list of lists
|
||||
splitting_matrices = []
|
||||
for ind in even_indices:
|
||||
# Create a list of all ten matrices (represented as 4x4 matrices)
|
||||
splitting_matrices.append([[Fp2_to_name(Fp2(x)) for x in row] for row in splitting_map[tuple(ind)]])
|
||||
|
||||
# 6 4x4 maps constructed from the above
|
||||
normalization_maps_4d = []
|
||||
for m in normalization_maps_2d:
|
||||
M = m.tensor_product(m, subdivide=False).list()
|
||||
matrix_elements_list = list(map(Fp2, M))
|
||||
# Reshape into matrix
|
||||
matrix_elements = [ matrix_elements_list[i:i+4] for i in range(0, len(matrix_elements_list), 4) ]
|
||||
normalization_maps_4d.append([[Fp2_to_name(x) for x in row] for row in matrix_elements])
|
||||
|
||||
################################################################
|
||||
|
||||
from cformat import Object, ObjectFormatter
|
||||
|
||||
objs = ObjectFormatter(
|
||||
[
|
||||
Object('int[][]', 'EVEN_INDEX', even_indices),
|
||||
Object('int[][]', 'CHI_EVAL', chi_eval),
|
||||
Object('fp2_t[]', 'FP2_CONSTANTS', list(map(Fp2_to_list, Fp2_constants[0]))),
|
||||
Object('precomp_basis_change_matrix_t[]', 'SPLITTING_TRANSFORMS', [[x] for x in splitting_matrices]),
|
||||
Object('precomp_basis_change_matrix_t[]', 'NORMALIZATION_TRANSFORMS', [[x] for x in normalization_maps_4d]),
|
||||
]
|
||||
)
|
||||
|
||||
with open("include/hd_splitting_transforms.h", "w") as hfile:
|
||||
with open("hd_splitting_transforms.c", "w") as cfile:
|
||||
print("#ifndef HD_SPLITTING_H", file=hfile)
|
||||
print("#define HD_SPLITTING_H", file=hfile)
|
||||
print(f"\n#include <hd.h>", file=hfile)
|
||||
print(f"#include <stdint.h>\n", file=hfile)
|
||||
print("typedef struct precomp_basis_change_matrix {", file=hfile)
|
||||
print(" uint8_t m[4][4];", file=hfile)
|
||||
print("} precomp_basis_change_matrix_t;\n", file=hfile)
|
||||
|
||||
print(f"#include <hd_splitting_transforms.h>\n", file=cfile)
|
||||
for i in range(len(Fp2_constants[1])):
|
||||
print(f"#define {Fp2_constants[1][i]} {i}", file=cfile)
|
||||
print("", file=cfile)
|
||||
|
||||
objs.header(file=hfile)
|
||||
objs.implementation(file=cfile)
|
||||
|
||||
print("\n#endif\n", file=hfile)
|
||||
44
scripts/precomp/precompute_quaternion_constants.sage
Executable file
44
scripts/precomp/precompute_quaternion_constants.sage
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
################################################################
|
||||
|
||||
from parameters import p
|
||||
negl = 2**-64
|
||||
|
||||
################################################################
|
||||
|
||||
logp = ceil(log(p, 2))
|
||||
loglogp = ceil(log(logp,2))
|
||||
tors2val = (p+1).valuation(2)
|
||||
|
||||
defs = dict()
|
||||
|
||||
# RepresentInteger data
|
||||
small = ceil(log(negl, 2) / -1)
|
||||
assert 2**-small <= negl
|
||||
|
||||
add_shift = ceil(log(log(negl, 1-1/(64*logp)), 2))
|
||||
assert (1 - 1/(64*logp)) ** (2**(add_shift)) <= negl
|
||||
|
||||
defs['QUAT_primality_num_iter'] = ceil(-log(negl, 4))
|
||||
defs['QUAT_repres_bound_input'] = add_shift
|
||||
|
||||
# Equivalent ideal data
|
||||
defs['QUAT_equiv_bound_coeff'] = 2**(1 + add_shift//4)
|
||||
|
||||
# Find_uv constants
|
||||
m = 2 + floor((logp - tors2val) / 4)
|
||||
defs['FINDUV_box_size'] = m
|
||||
defs['FINDUV_cube_size'] = (2 * m + 1)**4 - 1
|
||||
|
||||
################################################################
|
||||
|
||||
with open('include/quaternion_constants.h','w') as hfile:
|
||||
print(f'#include <quaternion.h>', file=hfile)
|
||||
|
||||
for k,v in defs.items():
|
||||
v = ZZ(v)
|
||||
print(f'#define {k} {v}', file=hfile)
|
||||
|
||||
|
||||
91
scripts/precomp/precompute_quaternion_data.sage
Executable file
91
scripts/precomp/precompute_quaternion_data.sage
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
|
||||
|
||||
from maxorders import p, orders
|
||||
|
||||
from cformat import Ibz, Object, ObjectFormatter
|
||||
|
||||
# Prime of same size than p for random ideal of fixed norm
|
||||
bitlength_p = int(p).bit_length()
|
||||
prime_cofactor = next_prime((2^(bitlength_p)))
|
||||
|
||||
algobj = [Ibz(p)]
|
||||
|
||||
objs = \
|
||||
[
|
||||
[
|
||||
# basis (columns)
|
||||
[
|
||||
Ibz(mat.denominator()),
|
||||
[[Ibz(v) for v in vs]
|
||||
for vs in mat.transpose()*mat.denominator()],
|
||||
],
|
||||
# sqrt(-q)
|
||||
[
|
||||
Ibz(mat.denominator()),
|
||||
[Ibz(c) for c in ii*mat.denominator()],
|
||||
],
|
||||
# sqrt(-p)
|
||||
[
|
||||
Ibz(1),
|
||||
[Ibz(c) for c in (0,0,1,0)]
|
||||
],
|
||||
q
|
||||
]
|
||||
for q,_,mat,ii,_,_ in orders
|
||||
]
|
||||
|
||||
idlobjs = \
|
||||
[
|
||||
[
|
||||
# basis (columns)
|
||||
[
|
||||
Ibz(idl.denominator()),
|
||||
[[Ibz(v) for v in vs]
|
||||
for vs in idl.transpose()*idl.denominator()],
|
||||
],
|
||||
# norm
|
||||
Ibz(abs(idl.row_space(ZZ).intersection((ZZ^4).submodule([[1,0,0,0]])).basis()[0][0])),
|
||||
# left order
|
||||
'&MAXORD_O0',
|
||||
]
|
||||
for _,_,mat,_,idl,_ in orders
|
||||
]
|
||||
|
||||
gammaobjs = \
|
||||
[
|
||||
[
|
||||
Ibz(gamma.denominator()),
|
||||
list(map(Ibz, gamma * gamma.denominator())),
|
||||
]
|
||||
for _,_,_,_,_,gamma in orders
|
||||
]
|
||||
|
||||
objs = ObjectFormatter([
|
||||
Object('ibz_t', 'QUAT_prime_cofactor', Ibz(prime_cofactor)),
|
||||
Object('quat_alg_t', 'QUATALG_PINFTY', algobj),
|
||||
Object('quat_p_extremal_maximal_order_t[]', 'EXTREMAL_ORDERS', objs),
|
||||
Object('quat_left_ideal_t[]', 'CONNECTING_IDEALS', idlobjs), # ideal corresponding to an isogeny from E0 which acts as identity w.r.t. the basis_even
|
||||
Object('quat_alg_elem_t[]', 'CONJUGATING_ELEMENTS', gammaobjs), # elements γ such that each I has right order γ O₁ γ^-1
|
||||
])
|
||||
|
||||
with open('include/quaternion_data.h','w') as hfile:
|
||||
with open('quaternion_data.c','w') as cfile:
|
||||
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)
|
||||
|
||||
#FIXME this should eventually go away?
|
||||
print(f'#define MAXORD_O0 (EXTREMAL_ORDERS->order)', file=hfile)
|
||||
print(f'#define STANDARD_EXTREMAL_ORDER (EXTREMAL_ORDERS[0])', file=hfile)
|
||||
print(f'#define NUM_ALTERNATE_EXTREMAL_ORDERS {len(orders)-1}', file=hfile)
|
||||
print(f'#define ALTERNATE_EXTREMAL_ORDERS (EXTREMAL_ORDERS+1)', file=hfile)
|
||||
print(f'#define ALTERNATE_CONNECTING_IDEALS (CONNECTING_IDEALS+1)', file=hfile)
|
||||
print(f'#define ALTERNATE_CONJUGATING_ELEMENTS (CONJUGATING_ELEMENTS+1)', file=hfile)
|
||||
|
||||
objs.header(file=hfile)
|
||||
objs.implementation(file=cfile)
|
||||
|
||||
93
scripts/precomp/precompute_sizes.sage
Executable file
93
scripts/precomp/precompute_sizes.sage
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/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))
|
||||
tors2val = (p+1).valuation(2)
|
||||
tors2part = (p+1).p_primary_part(2)
|
||||
tors3part = (p+1).p_primary_part(3)
|
||||
|
||||
defs = dict()
|
||||
|
||||
TORSION_2POWER_BYTES = (tors2part.bit_length() + 7) // 8
|
||||
SECURITY_BITS = round(p.bit_length() / 128) * 64
|
||||
RESPONSE_LENGTH = ceil(p.bit_length()/2)
|
||||
RESPONSE_BYTES = (RESPONSE_LENGTH + 9) // 8
|
||||
|
||||
fpsz = (logp + 63)//64*8
|
||||
fp2sz = 2 * fpsz
|
||||
defs['SECURITY_BITS'] = SECURITY_BITS
|
||||
defs['SQIsign_response_length'] = ceil(logp/2)
|
||||
defs['HASH_ITERATIONS'] = 2**(32 * ceil( logp/64 ) - (tors2val - ceil(logp/2)))
|
||||
defs['FP_ENCODED_BYTES'] = fpsz
|
||||
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['PUBLICKEY_BYTES'] = defs['EC_CURVE_ENCODED_BYTES'] + 1 # extra byte for hint
|
||||
defs['SECRETKEY_BYTES'] = defs['PUBLICKEY_BYTES'] + 5*defs['FP_ENCODED_BYTES'] + 4*TORSION_2POWER_BYTES
|
||||
defs['SIGNATURE_BYTES'] = defs['EC_CURVE_ENCODED_BYTES'] + 2 + 4*RESPONSE_BYTES + (SECURITY_BITS//8) + 1 + 1
|
||||
|
||||
size_privkey = defs['SECRETKEY_BYTES']
|
||||
size_pubkey = defs['PUBLICKEY_BYTES']
|
||||
size_signature = defs['SIGNATURE_BYTES']
|
||||
|
||||
algname = f'SQIsign_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
|
||||
|
||||
#include <sqisign_namespace.h>
|
||||
|
||||
#define CRYPTO_SECRETKEYBYTES {size_privkey}
|
||||
#define CRYPTO_PUBLICKEYBYTES {size_pubkey}
|
||||
#define CRYPTO_BYTES {size_signature}
|
||||
|
||||
#define CRYPTO_ALGNAME "{algname}"
|
||||
|
||||
#if defined(ENABLE_SIGN)
|
||||
SQISIGN_API
|
||||
int
|
||||
crypto_sign_keypair(unsigned char *pk, unsigned char *sk);
|
||||
|
||||
SQISIGN_API
|
||||
int
|
||||
crypto_sign(unsigned char *sm, unsigned long long *smlen,
|
||||
const unsigned char *m, unsigned long long mlen,
|
||||
const unsigned char *sk);
|
||||
#endif
|
||||
|
||||
SQISIGN_API
|
||||
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)
|
||||
|
||||
40
scripts/precomp/precompute_torsion_constants.sage
Executable file
40
scripts/precomp/precompute_torsion_constants.sage
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env sage
|
||||
proof.all(False) # faster
|
||||
|
||||
################################################################
|
||||
|
||||
from parameters import p
|
||||
|
||||
################################################################
|
||||
|
||||
tors2part = (p+1).p_primary_part(2)
|
||||
lambda_security = round(p.bit_length() / 128) * 64
|
||||
N_sec = next_prime(1 << 4*lambda_security)
|
||||
N_com = N_sec
|
||||
|
||||
defs = {
|
||||
'TORSION_2POWER_BYTES': (tors2part.bit_length() + 7) // 8,
|
||||
}
|
||||
|
||||
from cformat import Ibz, Object, ObjectFormatter
|
||||
|
||||
objs = ObjectFormatter([
|
||||
Object('ibz_t', 'TWO_TO_SECURITY_BITS', Ibz(1 << lambda_security)), # lambda_security = SECURITY_BITS (128, 192, 256)
|
||||
Object('ibz_t', 'TORSION_PLUS_2POWER', Ibz(tors2part)),
|
||||
Object('ibz_t', 'SEC_DEGREE', Ibz(N_sec)),
|
||||
Object('ibz_t', 'COM_DEGREE', Ibz(N_com)),
|
||||
])
|
||||
|
||||
with open('include/torsion_constants.h','w') as hfile:
|
||||
with open('torsion_constants.c','w') as cfile:
|
||||
print(f'#include <quaternion.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)
|
||||
|
||||
75
scripts/precomp/torsion_basis.py
Normal file
75
scripts/precomp/torsion_basis.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from sage.all import ZZ, GF, EllipticCurve, parallel
|
||||
|
||||
def even_torsion_basis_E0(E0, f):
|
||||
"""
|
||||
For the case when A = 0 we can't use the entangled basis algorithm
|
||||
so we do something "stupid" to simply get something canonical
|
||||
"""
|
||||
assert E0.a_invariants() == (0, 0, 0, 1, 0)
|
||||
|
||||
Fp2 = E0.base_ring()
|
||||
p = Fp2.characteristic()
|
||||
|
||||
def points_order_two_f():
|
||||
"""
|
||||
Compute a point P of order 2^f with x(P) = 1 + i*x_im
|
||||
"""
|
||||
x_im = 0
|
||||
while True:
|
||||
x_im += 1
|
||||
x = Fp2([1, x_im])
|
||||
if not E0.is_x_coord(x):
|
||||
continue
|
||||
# compares a+bi <= c+di iff (a,b) <= (c,d) as tuples, where integers
|
||||
# modulo p are compared via their minimal non-negative representatives
|
||||
P = min(E0.lift_x(x, all=True), key = lambda pt: list(pt.y()))
|
||||
P.set_order(multiple=p+1)
|
||||
if P.order() % (1 << f) == 0:
|
||||
P *= P.order() // (1 << f)
|
||||
P.set_order(1 << f)
|
||||
yield P
|
||||
|
||||
pts = points_order_two_f()
|
||||
P = next(pts)
|
||||
for Q in pts:
|
||||
# Q is picked to be in E[2^f] AND we must ensure that
|
||||
# <P, Q> form a basis, which is the same as e(P, Q) having
|
||||
# full order 1 << f.
|
||||
e = P.weil_pairing(Q, 1 << f)
|
||||
if e ** (1 << f - 1) == -1:
|
||||
break
|
||||
|
||||
# Finally we want to make sure Q is above (0, 0)
|
||||
P2 = (1 << f - 1) * P
|
||||
Q2 = (1 << f - 1) * Q
|
||||
if Q2 == E0(0, 0):
|
||||
pass
|
||||
elif P2 == E0(0, 0):
|
||||
P, Q = Q, P
|
||||
else:
|
||||
Q += P
|
||||
|
||||
assert P.weil_pairing(Q, 1 << f) ** (1 << f - 1) == -1
|
||||
assert (1 << f - 1) * Q == E0(0, 0)
|
||||
|
||||
return P, Q
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# p, f = 5 * 2**248 - 1, 248
|
||||
# p, f = 65 * 2**376 - 1, 376
|
||||
p, f = 27 * 2**500 - 1, 500
|
||||
print(f"p = {ZZ(p+1).factor()} - 1")
|
||||
Fp2 = GF(p**2, modulus=[1, 0, 1], names="i")
|
||||
E = EllipticCurve(Fp2, [1, 0])
|
||||
E.set_order((p + 1) ** 2)
|
||||
|
||||
P, Q = even_torsion_basis_E0(E, f)
|
||||
print(f"{P = }")
|
||||
print(f"{Q = }")
|
||||
|
||||
assert P.order() == 1 << f
|
||||
assert Q.order() == 1 << f
|
||||
e = P.weil_pairing(Q, 1 << f)
|
||||
assert e ** (1 << f - 1) == -1
|
||||
print("all good")
|
||||
@@ -1,203 +0,0 @@
|
||||
#!/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)
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
#!/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)
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
#!/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)
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/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)
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/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