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:
SQIsign team
2025-02-06 00:00:00 +00:00
committed by Lorenz Panny
parent ff34a8cd18
commit 91e9e464fe
481 changed files with 80785 additions and 55963 deletions

View File

@@ -0,0 +1,770 @@
#include <biextension.h>
#include <assert.h>
#include <inttypes.h>
#include <mp.h>
/*
* We implement the biextension arithmetic by using the cubical torsor
* representation. For now only implement the 2^e-ladder.
*
* Warning: cubicalADD is off by a factor x4 with respect to the correct
* cubical arithmetic. This does not affect the Weil pairing or the Tate
* pairing over F_{p^2} (due to the final exponentiation), but would give
* the wrong result if we compute the Tate pairing over F_p.
*/
// this would be exactly like xADD if PQ was 'antinormalised' as (1,z)
// Cost: 3M + 2S + 3a + 3s
// Note: if needed, cubicalDBL is simply xDBL_A24 normalized and
// costs 3M + 2S + 2a + 2s
static void
cubicalADD(ec_point_t *R, const ec_point_t *P, const ec_point_t *Q, const fp2_t *ixPQ)
{
fp2_t t0, t1, t2, t3;
fp2_add(&t0, &P->x, &P->z);
fp2_sub(&t1, &P->x, &P->z);
fp2_add(&t2, &Q->x, &Q->z);
fp2_sub(&t3, &Q->x, &Q->z);
fp2_mul(&t0, &t0, &t3);
fp2_mul(&t1, &t1, &t2);
fp2_add(&t2, &t0, &t1);
fp2_sub(&t3, &t0, &t1);
fp2_sqr(&R->z, &t3);
fp2_sqr(&t2, &t2);
fp2_mul(&R->x, ixPQ, &t2);
}
// Given cubical reps of P, Q and x(P - Q) = (1 : ixPQ)
// compute P + Q, [2]Q
// Cost: 6M + 4S + 4a + 4s
static void
cubicalDBLADD(ec_point_t *PpQ,
ec_point_t *QQ,
const ec_point_t *P,
const ec_point_t *Q,
const fp2_t *ixPQ,
const ec_point_t *A24)
{
// A24 = (A+2C/4C: 1)
assert(fp2_is_one(&A24->z));
fp2_t t0, t1, t2, t3;
fp2_add(&t0, &P->x, &P->z);
fp2_sub(&t1, &P->x, &P->z);
fp2_add(&PpQ->x, &Q->x, &Q->z);
fp2_sub(&t3, &Q->x, &Q->z);
fp2_sqr(&t2, &PpQ->x);
fp2_sqr(&QQ->z, &t3);
fp2_mul(&t0, &t0, &t3);
fp2_mul(&t1, &t1, &PpQ->x);
fp2_add(&PpQ->x, &t0, &t1);
fp2_sub(&t3, &t0, &t1);
fp2_sqr(&PpQ->z, &t3);
fp2_sqr(&PpQ->x, &PpQ->x);
fp2_mul(&PpQ->x, ixPQ, &PpQ->x);
fp2_sub(&t3, &t2, &QQ->z);
fp2_mul(&QQ->x, &t2, &QQ->z);
fp2_mul(&t0, &t3, &A24->x);
fp2_add(&t0, &t0, &QQ->z);
fp2_mul(&QQ->z, &t0, &t3);
}
// iterative biextension doubling
static void
biext_ladder_2e(uint32_t e,
ec_point_t *PnQ,
ec_point_t *nQ,
const ec_point_t *PQ,
const ec_point_t *Q,
const fp2_t *ixP,
const ec_point_t *A24)
{
copy_point(PnQ, PQ);
copy_point(nQ, Q);
for (uint32_t i = 0; i < e; i++) {
cubicalDBLADD(PnQ, nQ, PnQ, nQ, ixP, A24);
}
}
// Compute the monodromy ratio X/Z above as a (X:Z) point to avoid a division
// We implicitly use (1,0) as a cubical point above 0_E
static void
point_ratio(ec_point_t *R, const ec_point_t *PnQ, const ec_point_t *nQ, const ec_point_t *P)
{
// Sanity tests
assert(ec_is_zero(nQ));
assert(ec_is_equal(PnQ, P));
fp2_mul(&R->x, &nQ->x, &P->x);
fp2_copy(&R->z, &PnQ->x);
}
// Compute the cubical translation of P by a point of 2-torsion T
static void
translate(ec_point_t *P, const ec_point_t *T)
{
// When we translate, the following three things can happen:
// T = (A : 0) then the translation of P should be P
// T = (0 : B) then the translation of P = (X : Z) should be (Z : X)
// Otherwise T = (A : B) and P translates to (AX - BZ : BX - AZ)
// We compute this in constant time by computing the generic case
// and then using constant time swaps.
fp2_t PX_new, PZ_new;
{
fp2_t t0, t1;
// PX_new = AX - BZ
fp2_mul(&t0, &T->x, &P->x);
fp2_mul(&t1, &T->z, &P->z);
fp2_sub(&PX_new, &t0, &t1);
// PZ_new = BX - AZ
fp2_mul(&t0, &T->z, &P->x);
fp2_mul(&t1, &T->x, &P->z);
fp2_sub(&PZ_new, &t0, &t1);
}
// When we have A zero we should return (Z : X)
uint32_t TA_is_zero = fp2_is_zero(&T->x);
fp2_select(&PX_new, &PX_new, &P->z, TA_is_zero);
fp2_select(&PZ_new, &PZ_new, &P->x, TA_is_zero);
// When we have B zero we should return (X : Z)
uint32_t TB_is_zero = fp2_is_zero(&T->z);
fp2_select(&PX_new, &PX_new, &P->x, TB_is_zero);
fp2_select(&PZ_new, &PZ_new, &P->z, TB_is_zero);
// Set the point to the desired result
fp2_copy(&P->x, &PX_new);
fp2_copy(&P->z, &PZ_new);
}
// Compute the biextension monodromy g_P,Q^{2^g} (in level 1) via the
// cubical arithmetic of P+2^e Q.
// The suffix _i means that we are given 1/x(P) as parameter. Warning: to
// get meaningful result when using the monodromy to compute pairings, we
// need P, Q, PQ, A24 to be normalised (this is not strictly necessary, but
// care need to be taken when they are not normalised. Only handle the
// normalised case for now)
static void
monodromy_i(ec_point_t *R, const pairing_params_t *pairing_data, bool swap_PQ)
{
fp2_t ixP;
ec_point_t P, Q, PnQ, nQ;
// When we compute the Weil pairing we need both P + [2^e]Q and
// Q + [2^e]P which we can do easily with biext_ladder_2e() below
// we use a bool to decide wether to use Q, ixP or P, ixQ in the
// ladder and P or Q in translation.
if (!swap_PQ) {
copy_point(&P, &pairing_data->P);
copy_point(&Q, &pairing_data->Q);
fp2_copy(&ixP, &pairing_data->ixP);
} else {
copy_point(&P, &pairing_data->Q);
copy_point(&Q, &pairing_data->P);
fp2_copy(&ixP, &pairing_data->ixQ);
}
// Compute the biextension ladder P + [2^e]Q
biext_ladder_2e(pairing_data->e - 1, &PnQ, &nQ, &pairing_data->PQ, &Q, &ixP, &pairing_data->A24);
translate(&PnQ, &nQ);
translate(&nQ, &nQ);
point_ratio(R, &PnQ, &nQ, &P);
}
// Normalize the points and also store 1/x(P), 1/x(Q)
static void
cubical_normalization(pairing_params_t *pairing_data, const ec_point_t *P, const ec_point_t *Q)
{
fp2_t t[4];
fp2_copy(&t[0], &P->x);
fp2_copy(&t[1], &P->z);
fp2_copy(&t[2], &Q->x);
fp2_copy(&t[3], &Q->z);
fp2_batched_inv(t, 4);
// Store PZ / PX and QZ / QX
fp2_mul(&pairing_data->ixP, &P->z, &t[0]);
fp2_mul(&pairing_data->ixQ, &Q->z, &t[2]);
// Store x(P), x(Q) normalised to (X/Z : 1)
fp2_mul(&pairing_data->P.x, &P->x, &t[1]);
fp2_mul(&pairing_data->Q.x, &Q->x, &t[3]);
fp2_set_one(&pairing_data->P.z);
fp2_set_one(&pairing_data->Q.z);
}
// Weil pairing, PQ should be P+Q in (X:Z) coordinates
// We assume the points are normalised correctly
static void
weil_n(fp2_t *r, const pairing_params_t *pairing_data)
{
ec_point_t R0, R1;
monodromy_i(&R0, pairing_data, true);
monodromy_i(&R1, pairing_data, false);
fp2_mul(r, &R0.x, &R1.z);
fp2_inv(r);
fp2_mul(r, r, &R0.z);
fp2_mul(r, r, &R1.x);
}
// Weil pairing, PQ should be P+Q in (X:Z) coordinates
// Normalise the points and call the code above
// The code will crash (division by 0) if either P or Q is (0:1)
void
weil(fp2_t *r, uint32_t e, const ec_point_t *P, const ec_point_t *Q, const ec_point_t *PQ, ec_curve_t *E)
{
pairing_params_t pairing_data;
// Construct the structure for the Weil pairing
// Set (PX/PZ : 1), (QX : QZ : 1), PZ/PX and QZ/QX
pairing_data.e = e;
cubical_normalization(&pairing_data, P, Q);
copy_point(&pairing_data.PQ, PQ);
// Ensure the input curve has A24 normalised and store
// in a struct
ec_curve_normalize_A24(E);
copy_point(&pairing_data.A24, &E->A24);
// Compute the Weil pairing e_(2^n)(P, Q)
weil_n(r, &pairing_data);
}
// two helper functions for reducing the tate pairing
// clear_cofac clears (p + 1) // 2^f for an Fp2 value
void
clear_cofac(fp2_t *r, const fp2_t *a)
{
digit_t exp = *p_cofactor_for_2f;
exp >>= 1;
fp2_t x;
fp2_copy(&x, a);
fp2_copy(r, a);
// removes cofac
while (exp > 0) {
fp2_sqr(r, r);
if (exp & 1) {
fp2_mul(r, r, &x);
}
exp >>= 1;
}
}
// applies frobenius a + ib --> a - ib to an fp2 element
void
fp2_frob(fp2_t *out, const fp2_t *in)
{
fp_copy(&(out->re), &(in->re));
fp_neg(&(out->im), &(in->im));
}
// reduced Tate pairing, normalizes the points, assumes PQ is P+Q in (X:Z)
// coordinates. Computes 1/x(P) and 1/x(Q) for efficient cubical ladder
void
reduced_tate(fp2_t *r, uint32_t e, const ec_point_t *P, const ec_point_t *Q, const ec_point_t *PQ, ec_curve_t *E)
{
uint32_t e_full = TORSION_EVEN_POWER;
uint32_t e_diff = e_full - e;
ec_point_t R;
pairing_params_t pairing_data;
// Construct the structure for the Weil pairing
// Set (PX/PZ : 1), (QX : QZ : 1), PZ/PX and QZ/QX
pairing_data.e = e;
cubical_normalization(&pairing_data, P, Q);
copy_point(&pairing_data.PQ, PQ);
// Ensure the input curve has A24 normalised and store
// in a struct
ec_curve_normalize_A24(E);
copy_point(&pairing_data.A24, &E->A24);
monodromy_i(&R, &pairing_data, true);
// we get unreduced tate as R.X, R.Z
// reduced tate is -(R.Z/R.X)^((p^2 - 1) div 2^f)
// we reuse R.X and R.Z to split reduction step ^(p-1) into frobenius and ^-1
fp2_t frob, tmp;
fp2_copy(&tmp, &R.x);
fp2_frob(&frob, &R.x);
fp2_mul(&R.x, &R.z, &frob);
fp2_frob(&frob, &R.z);
fp2_mul(&R.z, &tmp, &frob);
fp2_inv(&R.x);
fp2_mul(r, &R.x, &R.z);
clear_cofac(r, r);
// clear remaining 2^e_diff
for (uint32_t j = 0; j < e_diff; j++) {
fp2_sqr(r, r);
}
}
// Functions to compute discrete logs by computing the Weil pairing of points
// followed by computing the dlog in Fp^2
// (If we work with full order points, it would be faster to use the Tate
// pairings rather than the Weil pairings; this is not implemented yet)
// recursive dlog function
static bool
fp2_dlog_2e_rec(digit_t *a, long len, fp2_t *pows_f, fp2_t *pows_g, long stacklen)
{
if (len == 0) {
// *a = 0;
for (int i = 0; i < NWORDS_ORDER; i++) {
a[i] = 0;
}
return true;
} else if (len == 1) {
if (fp2_is_one(&pows_f[stacklen - 1])) {
// a = 0;
for (int i = 0; i < NWORDS_ORDER; i++) {
a[i] = 0;
}
for (int i = 0; i < stacklen - 1; ++i) {
fp2_sqr(&pows_g[i], &pows_g[i]); // new_g = g^2
}
return true;
} else if (fp2_is_equal(&pows_f[stacklen - 1], &pows_g[stacklen - 1])) {
// a = 1;
a[0] = 1;
for (int i = 1; i < NWORDS_ORDER; i++) {
a[i] = 0;
}
for (int i = 0; i < stacklen - 1; ++i) {
fp2_mul(&pows_f[i], &pows_f[i], &pows_g[i]); // new_f = f*g
fp2_sqr(&pows_g[i], &pows_g[i]); // new_g = g^2
}
return true;
} else {
return false;
}
} else {
long right = (double)len * 0.5;
long left = len - right;
pows_f[stacklen] = pows_f[stacklen - 1];
pows_g[stacklen] = pows_g[stacklen - 1];
for (int i = 0; i < left; i++) {
fp2_sqr(&pows_f[stacklen], &pows_f[stacklen]);
fp2_sqr(&pows_g[stacklen], &pows_g[stacklen]);
}
// uint32_t dlp1 = 0, dlp2 = 0;
digit_t dlp1[NWORDS_ORDER], dlp2[NWORDS_ORDER];
bool ok;
ok = fp2_dlog_2e_rec(dlp1, right, pows_f, pows_g, stacklen + 1);
if (!ok)
return false;
ok = fp2_dlog_2e_rec(dlp2, left, pows_f, pows_g, stacklen);
if (!ok)
return false;
// a = dlp1 + 2^right * dlp2
multiple_mp_shiftl(dlp2, right, NWORDS_ORDER);
mp_add(a, dlp2, dlp1, NWORDS_ORDER);
return true;
}
}
// compute DLP: compute scal such that f = g^scal with f, 1/g as input
static bool
fp2_dlog_2e(digit_t *scal, const fp2_t *f, const fp2_t *g_inverse, int e)
{
long log, len = e;
for (log = 0; len > 1; len >>= 1)
log++;
log += 1;
fp2_t pows_f[log], pows_g[log];
pows_f[0] = *f;
pows_g[0] = *g_inverse;
for (int i = 0; i < NWORDS_ORDER; i++) {
scal[i] = 0;
}
bool ok = fp2_dlog_2e_rec(scal, e, pows_f, pows_g, 1);
assert(ok);
return ok;
}
// Normalize the bases (P, Q), (R, S) and store their inverse
// and additionally normalise the curve to (A/C : 1)
static void
cubical_normalization_dlog(pairing_dlog_params_t *pairing_dlog_data, ec_curve_t *curve)
{
fp2_t t[11];
ec_basis_t *PQ = &pairing_dlog_data->PQ;
ec_basis_t *RS = &pairing_dlog_data->RS;
fp2_copy(&t[0], &PQ->P.x);
fp2_copy(&t[1], &PQ->P.z);
fp2_copy(&t[2], &PQ->Q.x);
fp2_copy(&t[3], &PQ->Q.z);
fp2_copy(&t[4], &PQ->PmQ.x);
fp2_copy(&t[5], &PQ->PmQ.z);
fp2_copy(&t[6], &RS->P.x);
fp2_copy(&t[7], &RS->P.z);
fp2_copy(&t[8], &RS->Q.x);
fp2_copy(&t[9], &RS->Q.z);
fp2_copy(&t[10], &curve->C);
fp2_batched_inv(t, 11);
fp2_mul(&pairing_dlog_data->ixP, &PQ->P.z, &t[0]);
fp2_mul(&PQ->P.x, &PQ->P.x, &t[1]);
fp2_set_one(&PQ->P.z);
fp2_mul(&pairing_dlog_data->ixQ, &PQ->Q.z, &t[2]);
fp2_mul(&PQ->Q.x, &PQ->Q.x, &t[3]);
fp2_set_one(&PQ->Q.z);
fp2_mul(&PQ->PmQ.x, &PQ->PmQ.x, &t[5]);
fp2_set_one(&PQ->PmQ.z);
fp2_mul(&pairing_dlog_data->ixR, &RS->P.z, &t[6]);
fp2_mul(&RS->P.x, &RS->P.x, &t[7]);
fp2_set_one(&RS->P.z);
fp2_mul(&pairing_dlog_data->ixS, &RS->Q.z, &t[8]);
fp2_mul(&RS->Q.x, &RS->Q.x, &t[9]);
fp2_set_one(&RS->Q.z);
fp2_mul(&curve->A, &curve->A, &t[10]);
fp2_set_one(&curve->C);
}
// Given two bases <P, Q> and basis = <R, S> compute
// x(P - R), x(P - S), x(R - Q), x(S - Q)
static void
compute_difference_points(pairing_dlog_params_t *pairing_dlog_data, ec_curve_t *curve)
{
jac_point_t xyP, xyQ, xyR, xyS, temp;
// lifting the two basis points, assumes that x(P) and x(R)
// and the curve itself are normalised to (X : 1)
lift_basis_normalized(&xyP, &xyQ, &pairing_dlog_data->PQ, curve);
lift_basis_normalized(&xyR, &xyS, &pairing_dlog_data->RS, curve);
// computation of the differences
// x(P - R)
jac_neg(&temp, &xyR);
ADD(&temp, &temp, &xyP, curve);
jac_to_xz(&pairing_dlog_data->diff.PmR, &temp);
// x(P - S)
jac_neg(&temp, &xyS);
ADD(&temp, &temp, &xyP, curve);
jac_to_xz(&pairing_dlog_data->diff.PmS, &temp);
// x(R - Q)
jac_neg(&temp, &xyQ);
ADD(&temp, &temp, &xyR, curve);
jac_to_xz(&pairing_dlog_data->diff.RmQ, &temp);
// x(S - Q)
jac_neg(&temp, &xyQ);
ADD(&temp, &temp, &xyS, curve);
jac_to_xz(&pairing_dlog_data->diff.SmQ, &temp);
}
// Inline all the Weil pairing computations needed for ec_dlog_2_weil
static void
weil_dlog(digit_t *r1, digit_t *r2, digit_t *s1, digit_t *s2, pairing_dlog_params_t *pairing_dlog_data)
{
ec_point_t nP, nQ, nR, nS, nPQ, PnQ, nPR, PnR, nPS, PnS, nRQ, RnQ, nSQ, SnQ;
copy_point(&nP, &pairing_dlog_data->PQ.P);
copy_point(&nQ, &pairing_dlog_data->PQ.Q);
copy_point(&nR, &pairing_dlog_data->RS.P);
copy_point(&nS, &pairing_dlog_data->RS.Q);
copy_point(&nPQ, &pairing_dlog_data->PQ.PmQ);
copy_point(&PnQ, &pairing_dlog_data->PQ.PmQ);
copy_point(&nPR, &pairing_dlog_data->diff.PmR);
copy_point(&nPS, &pairing_dlog_data->diff.PmS);
copy_point(&PnR, &pairing_dlog_data->diff.PmR);
copy_point(&PnS, &pairing_dlog_data->diff.PmS);
copy_point(&nRQ, &pairing_dlog_data->diff.RmQ);
copy_point(&nSQ, &pairing_dlog_data->diff.SmQ);
copy_point(&RnQ, &pairing_dlog_data->diff.RmQ);
copy_point(&SnQ, &pairing_dlog_data->diff.SmQ);
for (uint32_t i = 0; i < pairing_dlog_data->e - 1; i++) {
cubicalADD(&nPQ, &nPQ, &nP, &pairing_dlog_data->ixQ);
cubicalADD(&nPR, &nPR, &nP, &pairing_dlog_data->ixR);
cubicalDBLADD(&nPS, &nP, &nPS, &nP, &pairing_dlog_data->ixS, &pairing_dlog_data->A24);
cubicalADD(&PnQ, &PnQ, &nQ, &pairing_dlog_data->ixP);
cubicalADD(&RnQ, &RnQ, &nQ, &pairing_dlog_data->ixR);
cubicalDBLADD(&SnQ, &nQ, &SnQ, &nQ, &pairing_dlog_data->ixS, &pairing_dlog_data->A24);
cubicalADD(&PnR, &PnR, &nR, &pairing_dlog_data->ixP);
cubicalDBLADD(&nRQ, &nR, &nRQ, &nR, &pairing_dlog_data->ixQ, &pairing_dlog_data->A24);
cubicalADD(&PnS, &PnS, &nS, &pairing_dlog_data->ixP);
cubicalDBLADD(&nSQ, &nS, &nSQ, &nS, &pairing_dlog_data->ixQ, &pairing_dlog_data->A24);
}
// weil(&w0,e,&PQ->P,&PQ->Q,&PQ->PmQ,&A24);
translate(&nPQ, &nP);
translate(&nPR, &nP);
translate(&nPS, &nP);
translate(&PnQ, &nQ);
translate(&RnQ, &nQ);
translate(&SnQ, &nQ);
translate(&PnR, &nR);
translate(&nRQ, &nR);
translate(&PnS, &nS);
translate(&nSQ, &nS);
translate(&nP, &nP);
translate(&nQ, &nQ);
translate(&nR, &nR);
translate(&nS, &nS);
// computation of the reference weil pairing
ec_point_t T0, T1;
fp2_t w1[5], w2[5];
// e(P, Q) = w0
point_ratio(&T0, &nPQ, &nP, &pairing_dlog_data->PQ.Q);
point_ratio(&T1, &PnQ, &nQ, &pairing_dlog_data->PQ.P);
// For the first element we need it's inverse for
// fp2_dlog_2e so we swap w1 and w2 here to save inversions
fp2_mul(&w2[0], &T0.x, &T1.z);
fp2_mul(&w1[0], &T1.x, &T0.z);
// e(P,R) = w0^r2
point_ratio(&T0, &nPR, &nP, &pairing_dlog_data->RS.P);
point_ratio(&T1, &PnR, &nR, &pairing_dlog_data->PQ.P);
fp2_mul(&w1[1], &T0.x, &T1.z);
fp2_mul(&w2[1], &T1.x, &T0.z);
// e(R,Q) = w0^r1
point_ratio(&T0, &nRQ, &nR, &pairing_dlog_data->PQ.Q);
point_ratio(&T1, &RnQ, &nQ, &pairing_dlog_data->RS.P);
fp2_mul(&w1[2], &T0.x, &T1.z);
fp2_mul(&w2[2], &T1.x, &T0.z);
// e(P,S) = w0^s2
point_ratio(&T0, &nPS, &nP, &pairing_dlog_data->RS.Q);
point_ratio(&T1, &PnS, &nS, &pairing_dlog_data->PQ.P);
fp2_mul(&w1[3], &T0.x, &T1.z);
fp2_mul(&w2[3], &T1.x, &T0.z);
// e(S,Q) = w0^s1
point_ratio(&T0, &nSQ, &nS, &pairing_dlog_data->PQ.Q);
point_ratio(&T1, &SnQ, &nQ, &pairing_dlog_data->RS.Q);
fp2_mul(&w1[4], &T0.x, &T1.z);
fp2_mul(&w2[4], &T1.x, &T0.z);
fp2_batched_inv(w1, 5);
for (int i = 0; i < 5; i++) {
fp2_mul(&w1[i], &w1[i], &w2[i]);
}
fp2_dlog_2e(r2, &w1[1], &w1[0], pairing_dlog_data->e);
fp2_dlog_2e(r1, &w1[2], &w1[0], pairing_dlog_data->e);
fp2_dlog_2e(s2, &w1[3], &w1[0], pairing_dlog_data->e);
fp2_dlog_2e(s1, &w1[4], &w1[0], pairing_dlog_data->e);
}
void
ec_dlog_2_weil(digit_t *r1,
digit_t *r2,
digit_t *s1,
digit_t *s2,
ec_basis_t *PQ,
const ec_basis_t *RS,
ec_curve_t *curve,
int e)
{
assert(test_point_order_twof(&PQ->Q, curve, e));
// precomputing the correct curve data
ec_curve_normalize_A24(curve);
pairing_dlog_params_t pairing_dlog_data;
pairing_dlog_data.e = e;
pairing_dlog_data.PQ = *PQ;
pairing_dlog_data.RS = *RS;
pairing_dlog_data.A24 = curve->A24;
cubical_normalization_dlog(&pairing_dlog_data, curve);
compute_difference_points(&pairing_dlog_data, curve);
weil_dlog(r1, r2, s1, s2, &pairing_dlog_data);
#ifndef NDEBUG
ec_point_t test;
ec_biscalar_mul(&test, r1, r2, e, PQ, curve);
// R = [r1]P + [r2]Q
assert(ec_is_equal(&test, &RS->P));
ec_biscalar_mul(&test, s1, s2, e, PQ, curve);
// S = [s1]P + [s2]Q
assert(ec_is_equal(&test, &RS->Q));
#endif
}
// Inline all the Tate pairing computations needed for ec_dlog_2_weil
// including reduction, assumes a bases PQ of full E[2^e_full] torsion
// and a bases RS of smaller E[2^e] torsion
static void
tate_dlog_partial(digit_t *r1, digit_t *r2, digit_t *s1, digit_t *s2, pairing_dlog_params_t *pairing_dlog_data)
{
uint32_t e_full = TORSION_EVEN_POWER;
uint32_t e_diff = e_full - pairing_dlog_data->e;
ec_point_t nP, nQ, nR, nS, nPQ, PnR, PnS, nRQ, nSQ;
copy_point(&nP, &pairing_dlog_data->PQ.P);
copy_point(&nQ, &pairing_dlog_data->PQ.Q);
copy_point(&nR, &pairing_dlog_data->RS.P);
copy_point(&nS, &pairing_dlog_data->RS.Q);
copy_point(&nPQ, &pairing_dlog_data->PQ.PmQ);
copy_point(&PnR, &pairing_dlog_data->diff.PmR);
copy_point(&PnS, &pairing_dlog_data->diff.PmS);
copy_point(&nRQ, &pairing_dlog_data->diff.RmQ);
copy_point(&nSQ, &pairing_dlog_data->diff.SmQ);
for (uint32_t i = 0; i < e_full - 1; i++) {
cubicalDBLADD(&nPQ, &nP, &nPQ, &nP, &pairing_dlog_data->ixQ, &pairing_dlog_data->A24);
}
for (uint32_t i = 0; i < pairing_dlog_data->e - 1; i++) {
cubicalADD(&PnR, &PnR, &nR, &pairing_dlog_data->ixP);
cubicalDBLADD(&nRQ, &nR, &nRQ, &nR, &pairing_dlog_data->ixQ, &pairing_dlog_data->A24);
cubicalADD(&PnS, &PnS, &nS, &pairing_dlog_data->ixP);
cubicalDBLADD(&nSQ, &nS, &nSQ, &nS, &pairing_dlog_data->ixQ, &pairing_dlog_data->A24);
}
translate(&nPQ, &nP);
translate(&PnR, &nR);
translate(&nRQ, &nR);
translate(&PnS, &nS);
translate(&nSQ, &nS);
translate(&nP, &nP);
translate(&nQ, &nQ);
translate(&nR, &nR);
translate(&nS, &nS);
// computation of the reference Tate pairing
ec_point_t T0;
fp2_t w1[5], w2[5];
// t(P, Q)^(2^e_diff) = w0
point_ratio(&T0, &nPQ, &nP, &pairing_dlog_data->PQ.Q);
fp2_copy(&w1[0], &T0.x);
fp2_copy(&w2[0], &T0.z);
// t(R,P) = w0^r2
point_ratio(&T0, &PnR, &nR, &pairing_dlog_data->PQ.P);
fp2_copy(&w1[1], &T0.x);
fp2_copy(&w2[1], &T0.z);
// t(R,Q) = w0^r1
point_ratio(&T0, &nRQ, &nR, &pairing_dlog_data->PQ.Q);
fp2_copy(&w2[2], &T0.x);
fp2_copy(&w1[2], &T0.z);
// t(S,P) = w0^s2
point_ratio(&T0, &PnS, &nS, &pairing_dlog_data->PQ.P);
fp2_copy(&w1[3], &T0.x);
fp2_copy(&w2[3], &T0.z);
// t(S,Q) = w0^s1
point_ratio(&T0, &nSQ, &nS, &pairing_dlog_data->PQ.Q);
fp2_copy(&w2[4], &T0.x);
fp2_copy(&w1[4], &T0.z);
// batched reduction using projective representation
for (int i = 0; i < 5; i++) {
fp2_t frob, tmp;
fp2_copy(&tmp, &w1[i]);
// inline frobenius for ^p
// multiply by inverse to get ^(p-1)
fp2_frob(&frob, &w1[i]);
fp2_mul(&w1[i], &w2[i], &frob);
// repeat for denom
fp2_frob(&frob, &w2[i]);
fp2_mul(&w2[i], &tmp, &frob);
}
// batched normalization
fp2_batched_inv(w2, 5);
for (int i = 0; i < 5; i++) {
fp2_mul(&w1[i], &w1[i], &w2[i]);
}
for (int i = 0; i < 5; i++) {
clear_cofac(&w1[i], &w1[i]);
// removes 2^e_diff
for (uint32_t j = 0; j < e_diff; j++) {
fp2_sqr(&w1[i], &w1[i]);
}
}
fp2_dlog_2e(r2, &w1[1], &w1[0], pairing_dlog_data->e);
fp2_dlog_2e(r1, &w1[2], &w1[0], pairing_dlog_data->e);
fp2_dlog_2e(s2, &w1[3], &w1[0], pairing_dlog_data->e);
fp2_dlog_2e(s1, &w1[4], &w1[0], pairing_dlog_data->e);
}
void
ec_dlog_2_tate(digit_t *r1,
digit_t *r2,
digit_t *s1,
digit_t *s2,
const ec_basis_t *PQ,
const ec_basis_t *RS,
ec_curve_t *curve,
int e)
{
// assume PQ is a full torsion basis
// returns a, b, c, d such that R = [a]P + [b]Q, S = [c]P + [d]Q
#ifndef NDEBUG
int e_full = TORSION_EVEN_POWER;
int e_diff = e_full - e;
#endif
assert(test_basis_order_twof(PQ, curve, e_full));
// precomputing the correct curve data
ec_curve_normalize_A24(curve);
pairing_dlog_params_t pairing_dlog_data;
pairing_dlog_data.e = e;
pairing_dlog_data.PQ = *PQ;
pairing_dlog_data.RS = *RS;
pairing_dlog_data.A24 = curve->A24;
cubical_normalization_dlog(&pairing_dlog_data, curve);
compute_difference_points(&pairing_dlog_data, curve);
tate_dlog_partial(r1, r2, s1, s2, &pairing_dlog_data);
#ifndef NDEBUG
ec_point_t test;
ec_biscalar_mul(&test, r1, r2, e, PQ, curve);
ec_dbl_iter(&test, e_diff, &test, curve);
// R = [r1]P + [r2]Q
assert(ec_is_equal(&test, &RS->P));
ec_biscalar_mul(&test, s1, s2, e, PQ, curve);
ec_dbl_iter(&test, e_diff, &test, curve);
// S = [s1]P + [s2]Q
assert(ec_is_equal(&test, &RS->Q));
#endif
}