org.bouncycastle.pqc.crypto.sike.SIDH_Compressed Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-jdk15to18 Show documentation
Show all versions of bcprov-jdk15to18 Show documentation
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for JDK 1.5 to JDK 1.8.
package org.bouncycastle.pqc.crypto.sike;
import java.security.SecureRandom;
import org.bouncycastle.util.Arrays;
class SIDH_Compressed
{
private SIKEEngine engine;
public SIDH_Compressed(SIKEEngine engine)
{
this.engine = engine;
}
protected void init_basis(long[] gen, long[][] XP, long[][] XQ, long[][] XR)
{ // Initialization of basis points
engine.fpx.fpcopy(gen, 0, XP[0]);
engine.fpx.fpcopy(gen, engine.params.NWORDS_FIELD, XP[1]);
engine.fpx.fpcopy(gen, 2*engine.params.NWORDS_FIELD, XQ[0]);
engine.fpx.fpcopy(gen, 3*engine.params.NWORDS_FIELD, XQ[1]);
engine.fpx.fpcopy(gen, 4*engine.params.NWORDS_FIELD, XR[0]);
engine.fpx.fpcopy(gen, 5*engine.params.NWORDS_FIELD, XR[1]);
}
protected void FormatPrivKey_B(byte[] skB)
{
skB[engine.params.SECRETKEY_B_BYTES-2] &= engine.params.MASK3_BOB;
skB[engine.params.SECRETKEY_B_BYTES-1] &= engine.params.MASK2_BOB; // Clear necessary bits so that 3*ephemeralsk is still less than Bob_order
engine.fpx.mul3(skB); // Multiply ephemeralsk by 3
}
// Generation of Alice's secret key
// Outputs random value in [0, 2^eA - 1]
protected void random_mod_order_A(byte[] random_digits, SecureRandom random)
{
byte[] temp = new byte[engine.params.SECRETKEY_A_BYTES];
random.nextBytes(temp);
System.arraycopy(temp, 0, random_digits, 0, engine.params.SECRETKEY_A_BYTES);
random_digits[0] &= 0xFE; // Make private scalar even
random_digits[engine.params.SECRETKEY_A_BYTES-1] &= engine.params.MASK_ALICE; // Masking last byte
}
// Generation of Bob's secret key
// Outputs random value in [0, 2^Floor(Log(2, oB)) - 1]
protected void random_mod_order_B(byte[] random_digits, SecureRandom random)
{
byte[] temp = new byte[engine.params.SECRETKEY_B_BYTES];
random.nextBytes(temp);
System.arraycopy(temp, 0, random_digits, 0, engine.params.SECRETKEY_A_BYTES);
FormatPrivKey_B(random_digits);
}
// Project 3-point ladder
protected void Ladder3pt_dual(PointProj[] Rs, long[] m, int AliceOrBob, PointProj R, long[][] A24)
{
PointProj R0 = new PointProj(engine.params.NWORDS_FIELD),
R2 = new PointProj(engine.params.NWORDS_FIELD);
long mask;
int i, nbits, bit, swap, prevbit = 0;
if (AliceOrBob == engine.params.ALICE)
{
nbits = engine.params.OALICE_BITS;
} else
{
nbits = engine.params.OBOB_BITS;
}
engine.fpx.fp2copy(Rs[1].X, R0.X);
engine.fpx.fp2copy(Rs[1].Z, R0.Z);
engine.fpx.fp2copy(Rs[2].X, R2.X);
engine.fpx.fp2copy(Rs[2].Z, R2.Z);
engine.fpx.fp2copy(Rs[0].X, R.X);
engine.fpx.fp2copy(Rs[0].Z, R.Z);
// Main loop
for (i = 0; i < nbits; i++)
{
bit = (int) ((m[i >>> Internal.LOG2RADIX] >>> (i & (Internal.RADIX-1))) & 1);
swap = bit ^ prevbit;
prevbit = bit;
mask = 0 - (long)swap;
engine.isogeny.swap_points(R, R2, mask);
engine.isogeny.xDBLADD(R0, R2, R.X, A24);
engine.fpx.fp2mul_mont(R2.X, R.Z, R2.X);
}
swap = 0 ^ prevbit;
mask = 0 - (long)swap;
engine.isogeny.swap_points(R, R2, mask);
}
protected void Elligator2(long[][] a24, int[] r, int rIndex, long[][] x, byte[] bit, int bitOffset, int COMPorDEC)
{ // Generate an x-coordinate of a point on curve with (affine) coefficient a24
// Use the counter r
int i;
long[] one_fp = new long[engine.params.NWORDS_FIELD],
a2 = new long[engine.params.NWORDS_FIELD],
b2 = new long[engine.params.NWORDS_FIELD],
N = new long[engine.params.NWORDS_FIELD],
temp0 = new long[engine.params.NWORDS_FIELD],
temp1 = new long[engine.params.NWORDS_FIELD];
long[][] A = new long[2][engine.params.NWORDS_FIELD],
y2 = new long[2][engine.params.NWORDS_FIELD];
int t_ptr = 0;
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, one_fp);
engine.fpx.fp2add(a24, a24, A);
engine.fpx.fpsubPRIME(A[0], one_fp, A[0]);
engine.fpx.fp2add(A, A, A); // A = 4*a24-2
// Elligator computation
t_ptr = r[rIndex]; //(long[][] *)&v_3_torsion[r];
engine.fpx.fp2mul_mont(A, engine.params.v_3_torsion[t_ptr], x); // x = A*v; v := 1/(1 + U*r^2) table lookup
engine.fpx.fp2neg(x); // x = -A*v;
if (COMPorDEC == 0) //COMPRESSION
{
engine.fpx.fp2add(A, x, y2); // y2 = x + A
engine.fpx.fp2mul_mont(y2, x, y2); // y2 = x*(x + A)
engine.fpx.fpaddPRIME(y2[0], one_fp, y2[0]); // y2 = x(x + A) + 1
engine.fpx.fp2mul_mont(x, y2, y2); // y2 = x*(x^2 + Ax + 1);
engine.fpx.fpsqr_mont(y2[0], a2);
engine.fpx.fpsqr_mont(y2[1], b2);
engine.fpx.fpaddPRIME(a2, b2, N); // N := norm(y2);
engine.fpx.fpcopy(N,0, temp0);
for (i = 0; i < engine.params.OALICE_BITS - 2; i++)
{
engine.fpx.fpsqr_mont(temp0, temp0);
}
for (i = 0; i < engine.params.OBOB_EXPON; i++)
{
engine.fpx.fpsqr_mont(temp0, temp1);
engine.fpx.fpmul_mont(temp0, temp1, temp0);
}
engine.fpx.fpsqr_mont(temp0, temp1); // z = N^((p + 1) div 4);
engine.fpx.fpcorrectionPRIME(temp1);
engine.fpx.fpcorrectionPRIME(N);
if(!Fpx.subarrayEquals(temp1, N, engine.params.NWORDS_FIELD))
{
engine.fpx.fp2neg(x);
engine.fpx.fp2sub(x, A, x); // x = -x - A;
if (COMPorDEC == 0)
{
bit[bitOffset] = 1;
}
}
}
else
{
if (bit[bitOffset] == 1)
{
engine.fpx.fp2neg(x);
engine.fpx.fp2sub(x,A,x); // x = -x - A;
}
}
}
protected void make_positive(long[][] x)
{
int nbytes = engine.params.NWORDS_FIELD;
long[] zero = new long[engine.params.NWORDS_FIELD];
engine.fpx.from_fp2mont(x, x);
if (!Fpx.subarrayEquals(x[0], zero, nbytes))
{
if ((x[0][0] & 1) == 1)
{
engine.fpx.fp2neg(x);
}
}
else
{
if ((x[1][0] & 1) == 1)
{
engine.fpx.fp2neg(x);
}
}
engine.fpx.to_fp2mont(x, x);
}
protected void BiQuad_affine(long[][] a24, long[][] x0, long[][] x1, PointProj R)
{
long[][] Ap2 = new long[2][engine.params.NWORDS_FIELD],
aa = new long[2][engine.params.NWORDS_FIELD],
bb = new long[2][engine.params.NWORDS_FIELD],
cc = new long[2][engine.params.NWORDS_FIELD],
t0 = new long[2][engine.params.NWORDS_FIELD],
t1 = new long[2][engine.params.NWORDS_FIELD];
engine.fpx.fp2add(a24, a24, Ap2);
engine.fpx.fp2add(Ap2, Ap2, Ap2); // Ap2 = a+2 = 4*a24
engine.fpx.fp2sub(x0, x1, aa);
engine.fpx.fp2sqr_mont(aa, aa);
engine.fpx.fp2mul_mont(x0, x1, cc);
engine.fpx.fpsubPRIME(cc[0], engine.params.Montgomery_one, cc[0]);
engine.fpx.fp2sqr_mont(cc, cc);
engine.fpx.fpsubPRIME(x0[0], engine.params.Montgomery_one, bb[0]);
engine.fpx.fpcopy(x0[1], 0, bb[1]);
engine.fpx.fp2sqr_mont(bb, bb);
engine.fpx.fp2mul_mont(Ap2, x0, t0);
engine.fpx.fp2add(bb, t0, bb);
engine.fpx.fp2mul_mont(x1, bb, bb);
engine.fpx.fpsubPRIME(x1[0], engine.params.Montgomery_one, t0[0]);
engine.fpx.fpcopy(x1[1], 0, t0[1]);
engine.fpx.fp2sqr_mont(t0, t0);
engine.fpx.fp2mul_mont(Ap2, x1, t1);
engine.fpx.fp2add(t0, t1, t0);
engine.fpx.fp2mul_mont(x0, t0, t0);
engine.fpx.fp2add(bb, t0, bb);
engine.fpx.fp2add(bb, bb, bb);
engine.fpx.fp2sqr_mont(bb, t0);
engine.fpx.fp2mul_mont(aa, cc, t1);
engine.fpx.fp2add(t1, t1, t1);
engine.fpx.fp2add(t1, t1, t1);
engine.fpx.fp2sub(t0, t1, t0);
engine.fpx.sqrt_Fp2(t0, t0);
make_positive(t0); // Make the sqrt "positive"
engine.fpx.fp2add(bb, t0, R.X);
engine.fpx.fp2add(aa, aa, R.Z);
}
protected void get_4_isog_dual(PointProj P, long[][] A24, long[][] C24, long[][][] coeff)
{
engine.fpx.fp2sub(P.X, P.Z, coeff[1]);
engine.fpx.fp2add(P.X, P.Z, coeff[2]);
engine.fpx.fp2sqr_mont(P.Z, coeff[4]);
engine.fpx.fp2add(coeff[4], coeff[4], coeff[0]);
engine.fpx.fp2sqr_mont(coeff[0], C24);
engine.fpx.fp2add(coeff[0], coeff[0], coeff[0]);
engine.fpx.fp2sqr_mont(P.X, coeff[3]);
engine.fpx.fp2add(coeff[3], coeff[3], A24);
engine.fpx.fp2sqr_mont(A24, A24);
}
protected void eval_dual_2_isog(long[][] X2, long[][] Z2, PointProj P)
{
long[][] t0 = new long[2][engine.params.NWORDS_FIELD];
engine.fpx.fp2add(P.X, P.Z, t0);
engine.fpx.fp2sub(P.X, P.Z, P.Z);
engine.fpx.fp2sqr_mont(t0, t0);
engine.fpx.fp2sqr_mont(P.Z, P.Z);
engine.fpx.fp2sub(t0, P.Z, P.Z);
engine.fpx.fp2mul_mont(X2, P.Z, P.Z);
engine.fpx.fp2mul_mont(Z2, t0, P.X);
}
protected void eval_final_dual_2_isog(PointProj P)
{
long[][] t0 = new long[2][engine.params.NWORDS_FIELD],
t1 = new long[2][engine.params.NWORDS_FIELD];
long[] t2 = new long[engine.params.NWORDS_FIELD];
engine.fpx.fp2add(P.X, P.Z, t0);
engine.fpx.fp2mul_mont(P.X, P.Z, t1);
engine.fpx.fp2sqr_mont(t0, P.X);
engine.fpx.fpcopy(P.X[0], 0, t2);
engine.fpx.fpcopy(P.X[1], 0, (P.X)[0]);
engine.fpx.fpcopy(t2, 0, P.X[1]);
engine.fpx.fpnegPRIME((P.X)[1]);
engine.fpx.fp2add(t1, t1, P.Z);
engine.fpx.fp2add(P.Z, P.Z, P.Z);
}
protected void eval_dual_4_isog_shared(long[][] X4pZ4, long[][] X42, long[][] Z42, long[][][] coeff, int coeffOffset)
{
engine.fpx.fp2sub(X42, Z42, coeff[0 + coeffOffset]);
engine.fpx.fp2add(X42, Z42, coeff[1 + coeffOffset]);
engine.fpx.fp2sqr_mont(X4pZ4, coeff[2 + coeffOffset]);
engine.fpx.fp2sub(coeff[2 + coeffOffset], coeff[1 + coeffOffset], coeff[2 + coeffOffset]);
}
protected void eval_dual_4_isog(long[][] A24, long[][] C24, long[][][] coeff, int coeffOffset, PointProj P)
{
long[][] t0 = new long[2][engine.params.NWORDS_FIELD],
t1 = new long[2][engine.params.NWORDS_FIELD],
t2 = new long[2][engine.params.NWORDS_FIELD],
t3 = new long[2][engine.params.NWORDS_FIELD];
engine.fpx.fp2add(P.X, P.Z, t0);
engine.fpx.fp2sub(P.X, P.Z, t1);
engine.fpx.fp2sqr_mont(t0, t0);
engine.fpx.fp2sqr_mont(t1, t1);
engine.fpx.fp2sub(t0, t1, t2);
engine.fpx.fp2sub(C24, A24, t3);
engine.fpx.fp2mul_mont(t2, t3, t3);
engine.fpx.fp2mul_mont(C24, t0, t2);
engine.fpx.fp2sub(t2, t3, t2);
engine.fpx.fp2mul_mont(t2, t0, P.X);
engine.fpx.fp2mul_mont(t3, t1, P.Z);
engine.fpx.fp2mul_mont(coeff[0 + coeffOffset], P.X, P.X);
engine.fpx.fp2mul_mont(coeff[1 + coeffOffset], P.Z, t0);
engine.fpx.fp2add(P.X, t0, P.X);
engine.fpx.fp2mul_mont(coeff[2 + coeffOffset], P.Z, P.Z);
}
protected void eval_full_dual_4_isog(long[][][][] As, PointProj P)
{
for(int i = 0; i < engine.params.MAX_Alice; i++)
{
eval_dual_4_isog(As[engine.params.MAX_Alice-i][0],
As[engine.params.MAX_Alice-i][1],
As[engine.params.MAX_Alice-i-1], 2,
P);
}
if (engine.params.OALICE_BITS % 2 == 1)
{
eval_dual_2_isog(As[engine.params.MAX_Alice][2], As[engine.params.MAX_Alice][3], P);
}
eval_final_dual_2_isog(P); // to A = 0
}
protected void TripleAndParabola_proj(PointProjFull R, long[][] l1x, long[][] l1z)
{
engine.fpx.fp2sqr_mont(R.X, l1z);
engine.fpx.fp2add(l1z, l1z, l1x);
engine.fpx.fp2add(l1x, l1z, l1x);
engine.fpx.fpaddPRIME(l1x[0], engine.params.Montgomery_one, l1x[0]);
engine.fpx.fp2add(R.Y, R.Y, l1z);
}
protected void Tate3_proj(PointProjFull P, PointProjFull Q, long[][] gX, long[][] gZ)
{
long[][] t0 = new long[2][engine.params.NWORDS_FIELD],
l1x = new long[2][engine.params.NWORDS_FIELD];
TripleAndParabola_proj(P, l1x, gZ);
engine.fpx.fp2sub(Q.X, P.X, gX);
engine.fpx.fp2mul_mont(l1x, gX, gX);
engine.fpx.fp2sub(P.Y, Q.Y, t0);
engine.fpx.fp2mul_mont(gZ, t0, t0);
engine.fpx.fp2add(gX, t0, gX);
}
protected void FinalExpo3(long[][] gX, long[][] gZ)
{
int i;
long[][] f_ = new long[2][engine.params.NWORDS_FIELD];
engine.fpx.fp2copy(gZ, f_);
engine.fpx.fpnegPRIME(f_[1]);
engine.fpx.fp2mul_mont(gX, f_, f_);
engine.fpx.fp2inv_mont_bingcd(f_);
engine.fpx.fpnegPRIME(gX[1]);
engine.fpx.fp2mul_mont(gX,gZ, gX);
engine.fpx.fp2mul_mont(gX,f_, gX);
for(i = 0; i < engine.params.OALICE_BITS; i++)
{
engine.fpx.fp2sqr_mont(gX, gX);
}
for(i = 0; i < engine.params.OBOB_EXPON-1; i++)
{
engine.fpx.cube_Fp2_cycl(gX, engine.params.Montgomery_one);
}
}
protected void FinalExpo3_2way(long[][][] gX, long[][][] gZ)
{
int i, j;
long[][][] f_ = new long[2][2][engine.params.NWORDS_FIELD],
finv = new long[2][2][engine.params.NWORDS_FIELD];
for(i = 0; i < 2; i++)
{
engine.fpx.fp2copy(gZ[i], f_[i]);
engine.fpx.fpnegPRIME(f_[i][1]); // Conjugate
engine.fpx.fp2mul_mont(gX[i], f_[i], f_[i]);
}
engine.fpx.mont_n_way_inv(f_, 2, finv);
for(i = 0; i < 2; i++)
{
engine.fpx.fpnegPRIME(gX[i][1]);
engine.fpx.fp2mul_mont(gX[i], gZ[i], gX[i]);
engine.fpx.fp2mul_mont(gX[i], finv[i], gX[i]);
for(j = 0; j < engine.params.OALICE_BITS; j++)
{
engine.fpx.fp2sqr_mont(gX[i], gX[i]);
}
for(j = 0; j < engine.params.OBOB_EXPON-1; j++)
{
engine.fpx.cube_Fp2_cycl(gX[i], engine.params.Montgomery_one);
}
}
}
private boolean FirstPoint_dual(PointProj P, PointProjFull R, byte[] ind)
{
PointProjFull R3 = new PointProjFull(engine.params.NWORDS_FIELD),
S3 = new PointProjFull(engine.params.NWORDS_FIELD);
long[][][] gX = new long[2][2][engine.params.NWORDS_FIELD],
gZ = new long[2][2][engine.params.NWORDS_FIELD];
long[] zero = new long[engine.params.NWORDS_FIELD];
int nbytes = engine.params.NWORDS_FIELD;
int alpha,beta;
engine.fpx.fpcopy(engine.params.B_gen_3_tors, 0*engine.params.NWORDS_FIELD, (R3.X)[0]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, 1*engine.params.NWORDS_FIELD, (R3.X)[1]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, 2*engine.params.NWORDS_FIELD, (R3.Y)[0]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, 3*engine.params.NWORDS_FIELD, (R3.Y)[1]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, 4*engine.params.NWORDS_FIELD, (S3.X)[0]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, 5*engine.params.NWORDS_FIELD, (S3.X)[1]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, 6*engine.params.NWORDS_FIELD, (S3.Y)[0]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, 7*engine.params.NWORDS_FIELD, (S3.Y)[1]);
engine.isogeny.CompletePoint(P,R);
Tate3_proj(R3,R,gX[0],gZ[0]);
Tate3_proj(S3,R,gX[1],gZ[1]);
FinalExpo3_2way(gX,gZ);
// Do small DLog with respect to g_R3_S3
engine.fpx.fp2correction(gX[0]);
engine.fpx.fp2correction(gX[1]);
if (Fpx.subarrayEquals(gX[0][1], zero, nbytes)) // = 1
{
alpha = 0;
}
else if (Fpx.subarrayEquals(gX[0][1], engine.params.g_R_S_im, nbytes)) // = g_R3_S3
{
alpha = 1;
}
else // = g_R3_S3^2
{
alpha = 2;
}
if (Fpx.subarrayEquals(gX[1][1], zero, nbytes)) // = 1
{
beta = 0;
}
else if (Fpx.subarrayEquals(gX[1][1], engine.params.g_R_S_im, nbytes))// = g_R3_S3
{
beta = 1;
}
else // = g_R3_S3^2
{
beta = 2;
}
if (alpha == 0 && beta == 0) // Not full order
{
return false;
}
// Return the 3-torsion point that R lies above
if (alpha == 0) // Lies above R3
{
ind[0] = 0;
}
else if (beta == 0) // Lies above S3
{
ind[0] = 1;
}
else if (alpha + beta == 3) // Lies above R3+S3
{
ind[0] = 3;
}
else // Lies above R3-S3
{
ind[0] = 2;
}
return true;
}
private boolean SecondPoint_dual(PointProj P, PointProjFull R, byte[] ind)
{
PointProjFull RS3 = new PointProjFull(engine.params.NWORDS_FIELD);
long[][] gX = new long[2][engine.params.NWORDS_FIELD],
gZ = new long[2][engine.params.NWORDS_FIELD];
long[] zero = new long[engine.params.NWORDS_FIELD];
int nbytes = engine.params.NWORDS_FIELD;
// Pair with 3-torsion point determined by first point
engine.fpx.fpcopy(engine.params.B_gen_3_tors, (4*ind[0] + 0)*engine.params.NWORDS_FIELD, (RS3.X)[0]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, (4*ind[0] + 1)*engine.params.NWORDS_FIELD, (RS3.X)[1]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, (4*ind[0] + 2)*engine.params.NWORDS_FIELD, (RS3.Y)[0]);
engine.fpx.fpcopy(engine.params.B_gen_3_tors, (4*ind[0] + 3)*engine.params.NWORDS_FIELD, (RS3.Y)[1]);
engine.isogeny.CompletePoint(P, R);
Tate3_proj(RS3, R, gX, gZ);
FinalExpo3(gX, gZ);
engine.fpx.fp2correction(gX);
if (!Fpx.subarrayEquals(gX[1], zero, nbytes)) // Not equal to 1
{
return true;
}
else
{
return false;
}
}
protected void FirstPoint3n(long[][] a24, long[][][][] As, long[][] x, PointProjFull R, int[] r, byte[] ind, byte[] bitEll)
{
boolean b = false;
PointProj P = new PointProj(engine.params.NWORDS_FIELD);
long[] zero = new long[engine.params.NWORDS_FIELD];
r[0] = 0;
while (!b)
{
bitEll[0] = 0;
Elligator2(a24, r, 0, x, bitEll,0, 0); // Get x-coordinate on curve a24
engine.fpx.fp2copy(x, P.X);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, P.Z[0]);
engine.fpx.fpcopy(zero, 0, P.Z[1]);
eval_full_dual_4_isog(As, P); // Move x over to A = 0
b = FirstPoint_dual(P, R, ind); // Compute DLog with 3-torsion points
r[0] = r[0] + 1;
}
}
protected void SecondPoint3n(long[][] a24, long[][][][] As, long[][] x, PointProjFull R, int[] r, byte[] ind, byte[] bitEll)
{
boolean b = false;
PointProj P = new PointProj(engine.params.NWORDS_FIELD);
long[] zero = new long[engine.params.NWORDS_FIELD];
while (!b)
{
bitEll[0] = 0;
Elligator2(a24, r, 1, x, bitEll, 0, 0);
engine.fpx.fp2copy(x, P.X);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, P.Z[0]);
engine.fpx.fpcopy(zero, 0, P.Z[1]);
eval_full_dual_4_isog(As, P); // Move x over to A = 0
b = SecondPoint_dual(P, R, ind);
r[1] = r[1] + 1;
}
}
protected void makeDiff(PointProjFull R, PointProjFull S, PointProj D)
{
long[][] t0 = new long[2][engine.params.NWORDS_FIELD],
t1 = new long[2][engine.params.NWORDS_FIELD],
t2 = new long[2][engine.params.NWORDS_FIELD];
int nbytes = engine.params.NWORDS_FIELD;
engine.fpx.fp2sub(R.X, S.X, t0);
engine.fpx.fp2sub(R.Y, S.Y, t1);
engine.fpx.fp2sqr_mont(t0, t0);
engine.fpx.fp2sqr_mont(t1, t1);
engine.fpx.fp2add(R.X, S.X, t2);
engine.fpx.fp2mul_mont(t0, t2, t2);
engine.fpx.fp2sub(t1, t2, t1);
engine.fpx.fp2mul_mont(D.Z, t1, t1);
engine.fpx.fp2mul_mont(D.X, t0, t0);
engine.fpx.fp2correction(t0);
engine.fpx.fp2correction(t1);
if (Fpx.subarrayEquals(t0[0], t1[0], nbytes) & Fpx.subarrayEquals(t0[1], t1[1], nbytes))
{
engine.fpx.fp2neg(S.Y);
}
}
protected void BuildOrdinary3nBasis_dual(long[][] a24, long[][][][] As, PointProjFull[] R, int[] r, int[] bitsEll, int bitsEllOffset)
{
PointProj D = new PointProj(engine.params.NWORDS_FIELD);
long[][][] xs = new long[2][2][engine.params.NWORDS_FIELD];
byte[] ind = new byte[1],
bit = new byte[1];
FirstPoint3n(a24, As, xs[0], R[0], r, ind, bit);
bitsEll[bitsEllOffset] = bit[0];
r[1] = r[0];
SecondPoint3n(a24, As, xs[1], R[1], r, ind, bit);
bitsEll[bitsEllOffset] |= ((int)bit[0] << 1);
// Get x-coordinate of difference
BiQuad_affine(a24, xs[0], xs[1], D);
eval_full_dual_4_isog(As, D); // Move x over to A = 0
makeDiff(R[0], R[1], D);
}
protected void FullIsogeny_A_dual(byte[] PrivateKeyA, long[][][][] As, long[][] a24, int sike)
{
// Input: a private key PrivateKeyA in the range [0, 2^eA - 1].
// Output: the public key PublicKeyA consisting of 3 elements in GF(p^2) which are encoded by removing leading 0 bytes.
PointProj R = new PointProj(engine.params.NWORDS_FIELD);
PointProj[] pts = new PointProj[engine.params.MAX_INT_POINTS_ALICE];
long[][] XPA = new long[2][engine.params.NWORDS_FIELD],
XQA = new long[2][engine.params.NWORDS_FIELD],
XRA = new long[2][engine.params.NWORDS_FIELD],
A24 = new long[2][engine.params.NWORDS_FIELD],
C24 = new long[2][engine.params.NWORDS_FIELD],
A = new long[2][engine.params.NWORDS_FIELD];
long[][][] coeff = new long[5][2][engine.params.NWORDS_FIELD];
int i, row, m, index = 0, npts = 0, ii = 0;
int[] pts_index = new int[engine.params.MAX_INT_POINTS_ALICE];
long[] SecretKeyA = new long[engine.params.NWORDS_ORDER];
// Initialize basis points
init_basis(engine.params.A_gen, XPA, XQA, XRA);
// Initialize constants: A24 = A+2C, C24 = 4C, where A=6, C=1
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, A24[0]);
engine.fpx.fp2add(A24, A24, A24);
engine.fpx.fp2add(A24, A24, C24);
engine.fpx.fp2add(A24, C24, A);
engine.fpx.fp2add(C24, C24, A24);
// Retrieve kernel point
engine.fpx.decode_to_digits(PrivateKeyA, engine.params.MSG_BYTES, SecretKeyA, engine.params.SECRETKEY_A_BYTES, engine.params.NWORDS_ORDER);
engine.isogeny.LADDER3PT(XPA, XQA, XRA, SecretKeyA, engine.params.ALICE, R, A);
engine.fpx.fp2inv_mont(R.Z);
engine.fpx.fp2mul_mont(R.X,R.Z,R.X);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, R.Z[0]);
engine.fpx.fpzero(R.Z[1]);
if (sike == 1)
{
engine.fpx.fp2_encode(R.X, PrivateKeyA, engine.params.MSG_BYTES + engine.params.SECRETKEY_A_BYTES + engine.params.CRYPTO_PUBLICKEYBYTES); // privA ||= x(KA) = x(PA + sk_A*QA)
}
if (engine.params.OALICE_BITS % 2 == 1)
{
PointProj S = new PointProj(engine.params.NWORDS_FIELD);
engine.isogeny.xDBLe(R, S, A24, C24, (int) (engine.params.OALICE_BITS - 1));
engine.isogeny.get_2_isog(S, A24, C24);
engine.isogeny.eval_2_isog(R, S);
engine.fpx.fp2copy(S.X, As[engine.params.MAX_Alice][2]);
engine.fpx.fp2copy(S.Z, As[engine.params.MAX_Alice][3]);
}
// Traverse tree
index = 0;
for (row = 1; row < engine.params.MAX_Alice; row++)
{
while (index < engine.params.MAX_Alice-row)
{
pts[npts] = new PointProj(engine.params.NWORDS_FIELD);
engine.fpx.fp2copy(R.X, pts[npts].X);
engine.fpx.fp2copy(R.Z, pts[npts].Z);
pts_index[npts++] = index;
m = engine.params.strat_Alice[ii++];
engine.isogeny.xDBLe(R, R, A24, C24, (int)(2*m));
index += m;
}
engine.fpx.fp2copy(A24, As[row-1][0]);
engine.fpx.fp2copy(C24, As[row-1][1]);
get_4_isog_dual(R, A24, C24, coeff);
for (i = 0; i < npts; i++)
{
engine.isogeny.eval_4_isog(pts[i], coeff);
}
eval_dual_4_isog_shared(coeff[2], coeff[3], coeff[4], As[row-1], 2);
engine.fpx.fp2copy(pts[npts-1].X, R.X);
engine.fpx.fp2copy(pts[npts-1].Z, R.Z);
index = pts_index[npts-1];
npts -= 1;
}
engine.fpx.fp2copy(A24, As[engine.params.MAX_Alice-1][0]);
engine.fpx.fp2copy(C24, As[engine.params.MAX_Alice-1][1]);
get_4_isog_dual(R, A24, C24, coeff);
eval_dual_4_isog_shared(coeff[2], coeff[3], coeff[4], As[engine.params.MAX_Alice-1], 2);
engine.fpx.fp2copy(A24, As[engine.params.MAX_Alice][0]);
engine.fpx.fp2copy(C24, As[engine.params.MAX_Alice][1]);
engine.fpx.fp2inv_mont_bingcd(C24);
engine.fpx.fp2mul_mont(A24, C24, a24);
}
protected void Dlogs3_dual(long[][][] f, int[] D, long[] d0, long[] c0, long[] d1, long[] c1)
{
solve_dlog(f[0], D, d0, 3);
solve_dlog(f[2], D, c0, 3);
solve_dlog(f[1], D, d1, 3);
solve_dlog(f[3], D, c1, 3);
engine.fpx.mp_sub(engine.params.Bob_order, c0, c0, engine.params.NWORDS_ORDER);
engine.fpx.mp_sub(engine.params.Bob_order, c1, c1, engine.params.NWORDS_ORDER);
}
protected void BuildOrdinary3nBasis_Decomp_dual(long[][] A24, PointProj[] Rs, int[] r, int[] bitsEll, int bitsEllIndex)
{
byte[] bitEll = new byte[2];
bitEll[0] = (byte) (bitsEll[bitsEllIndex] & 0x1);
bitEll[1] = (byte) ((bitsEll[bitsEllIndex] >>> 1) & 0x1);
// Elligator2 both x-coordinates
r[0] -= 1;
Elligator2(A24, r, 0, Rs[0].X, bitEll, 0, 1);
r[1] -= 1;
Elligator2(A24, r, 1, Rs[1].X, bitEll, 1, 1);
// Get x-coordinate of difference
BiQuad_affine(A24, Rs[0].X, Rs[1].X, Rs[2]);
}
protected void PKADecompression_dual(byte[] SecretKeyB, byte[] CompressedPKA, PointProj R, long[][] A)
{
byte bit;
int[] rs = new int[3];
long[][] A24 = new long[2][engine.params.NWORDS_FIELD];
PointProj[] Rs = new PointProj[3];
Rs[0] = new PointProj(engine.params.NWORDS_FIELD);
Rs[1] = new PointProj(engine.params.NWORDS_FIELD);
Rs[2] = new PointProj(engine.params.NWORDS_FIELD);
long[] t1 = new long[engine.params.NWORDS_ORDER],
t2 = new long[engine.params.NWORDS_ORDER],
t3 = new long[engine.params.NWORDS_ORDER],
t4 = new long[engine.params.NWORDS_ORDER],
vone = new long[engine.params.NWORDS_ORDER],
temp = new long[engine.params.NWORDS_ORDER],
SKin = new long[engine.params.NWORDS_ORDER];
engine.fpx.fp2_decode(CompressedPKA, A, 3*engine.params.ORDER_B_ENCODED_BYTES);
vone[0] = 1;
engine.fpx.to_Montgomery_mod_order(vone, vone, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1); // Converting to Montgomery representation
bit = (byte) ((CompressedPKA[3*engine.params.ORDER_B_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] & 0xff) >> 7);
byte[] rs_temp = new byte[3];
System.arraycopy(CompressedPKA, 3*engine.params.ORDER_B_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES, rs_temp, 0, 3);
rs[0] = rs_temp[0] & 0xffff;
rs[1] = rs_temp[1] & 0xffff;
rs[2] = rs_temp[2] & 0xffff;
rs[0] &= 0x7F;
engine.fpx.fpaddPRIME(A[0], engine.params.Montgomery_one, A24[0]);
engine.fpx.fpcopy(A[1], 0, A24[1]);
engine.fpx.fpaddPRIME(A24[0], engine.params.Montgomery_one, A24[0]);
engine.fpx.fp2div2(A24, A24);
engine.fpx.fp2div2(A24, A24);
BuildOrdinary3nBasis_Decomp_dual(A24, Rs, rs, rs, 2);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, Rs[0].Z[0]);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, Rs[1].Z[0]);
engine.isogeny.swap_points(Rs[0], Rs[1], -(long) bit);
engine.fpx.decode_to_digits(SecretKeyB, 0, SKin, engine.params.SECRETKEY_B_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.to_Montgomery_mod_order(SKin, t1, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1); // Converting to Montgomery representation
engine.fpx.decode_to_digits(CompressedPKA, 0, temp, engine.params.ORDER_B_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.to_Montgomery_mod_order(temp, t2, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
engine.fpx.decode_to_digits(CompressedPKA, engine.params.ORDER_B_ENCODED_BYTES, temp, engine.params.ORDER_B_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.to_Montgomery_mod_order(temp, t3, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
engine.fpx.decode_to_digits(CompressedPKA, 2*engine.params.ORDER_B_ENCODED_BYTES, temp, engine.params.ORDER_B_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.to_Montgomery_mod_order(temp, t4, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
if (bit == 0)
{
engine.fpx.Montgomery_multiply_mod_order(t1, t3, t3, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.mp_add(t3, vone, t3, engine.params.NWORDS_ORDER);
engine.fpx.Montgomery_inversion_mod_order_bingcd(t3, t3, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
engine.fpx.Montgomery_multiply_mod_order(t1, t4, t4, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.mp_add(t2, t4, t4, engine.params.NWORDS_ORDER);
engine.fpx.Montgomery_multiply_mod_order(t3, t4, t3, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.from_Montgomery_mod_order(t3, t3, engine.params.Bob_order, engine.params.Montgomery_RB2); // Converting back from Montgomery representation
Ladder3pt_dual(Rs, t3, engine.params.BOB, R, A24);
}
else
{
engine.fpx.Montgomery_multiply_mod_order(t1, t4, t4, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.mp_add(t4, vone, t4, engine.params.NWORDS_ORDER);
engine.fpx.Montgomery_inversion_mod_order_bingcd(t4, t4, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
engine.fpx.Montgomery_multiply_mod_order(t1, t3, t3, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.mp_add(t2, t3, t3, engine.params.NWORDS_ORDER);
engine.fpx.Montgomery_multiply_mod_order(t3, t4, t3, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.from_Montgomery_mod_order(t3, t3, engine.params.Bob_order, engine.params.Montgomery_RB2); // Converting back from Montgomery representation
Ladder3pt_dual(Rs,t3,engine.params.BOB, R, A24);
}
engine.isogeny.Double(R, R, A24, engine.params.OALICE_BITS); // x, z := Double(A24, x, 1, eA);
}
protected void Compress_PKA_dual(long[] d0, long[] c0, long[] d1, long[] c1, long[][] a24, int[] rs, byte[] CompressedPKA)
{
int bit;
long[] temp = new long[engine.params.NWORDS_ORDER],
inv = new long[engine.params.NWORDS_ORDER];
long[][] A = new long[2][engine.params.NWORDS_FIELD];
engine.fpx.fp2add(a24,a24,A);
engine.fpx.fp2add(A,A,A);
engine.fpx.fpsubPRIME(A[0],engine.params.Montgomery_one,A[0]);
engine.fpx.fpsubPRIME(A[0],engine.params.Montgomery_one,A[0]); // 4*a24-2
bit = engine.fpx.mod3(d1);
engine.fpx.to_Montgomery_mod_order(c0, c0, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1); // Converting to Montgomery representation
engine.fpx.to_Montgomery_mod_order(c1, c1, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
engine.fpx.to_Montgomery_mod_order(d0, d0, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
engine.fpx.to_Montgomery_mod_order(d1, d1, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
if (bit != 0)
{ // Storing [d1*c0inv, c1*c0inv, d0*c0inv] and setting bit "NBITS_ORDER" to 0
engine.fpx.Montgomery_inversion_mod_order_bingcd(d1, inv, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
engine.fpx.Montgomery_neg(d0, engine.params.Bob_order);
engine.fpx.Montgomery_multiply_mod_order(d0, inv, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.from_Montgomery_mod_order(temp, temp, engine.params.Bob_order, engine.params.Montgomery_RB2); // Converting back from Montgomery representation
engine.fpx.encode_to_bytes(temp, CompressedPKA, 0, engine.params.ORDER_B_ENCODED_BYTES);
engine.fpx.Montgomery_neg(c1, engine.params.Bob_order);
engine.fpx.Montgomery_multiply_mod_order(c1, inv, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.from_Montgomery_mod_order(temp, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.encode_to_bytes(temp, CompressedPKA, engine.params.ORDER_B_ENCODED_BYTES, engine.params.ORDER_B_ENCODED_BYTES);
engine.fpx.Montgomery_multiply_mod_order(c0, inv, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.from_Montgomery_mod_order(temp, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.encode_to_bytes(temp, CompressedPKA, 2*engine.params.ORDER_B_ENCODED_BYTES, engine.params.ORDER_B_ENCODED_BYTES);
CompressedPKA[3*engine.params.ORDER_B_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] = 0x00;
}
else
{ // Storing [d1*d0inv, c1*d0inv, c0*d0inv] and setting bit "NBITS_ORDER" to 1
engine.fpx.Montgomery_inversion_mod_order_bingcd(d0, inv, engine.params.Bob_order, engine.params.Montgomery_RB2, engine.params.Montgomery_RB1);
engine.fpx.Montgomery_neg(d1, engine.params.Bob_order);
engine.fpx.Montgomery_multiply_mod_order(d1, inv, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.from_Montgomery_mod_order(temp, temp, engine.params.Bob_order, engine.params.Montgomery_RB2); // Converting back from Montgomery representation
engine.fpx.encode_to_bytes(temp, CompressedPKA, 0, engine.params.ORDER_B_ENCODED_BYTES);
engine.fpx.Montgomery_multiply_mod_order(c1, inv, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.from_Montgomery_mod_order(temp, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.encode_to_bytes(temp, CompressedPKA, engine.params.ORDER_B_ENCODED_BYTES, engine.params.ORDER_B_ENCODED_BYTES);
engine.fpx.Montgomery_neg(c0, engine.params.Bob_order);
engine.fpx.Montgomery_multiply_mod_order(c0, inv, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.from_Montgomery_mod_order(temp, temp, engine.params.Bob_order, engine.params.Montgomery_RB2);
engine.fpx.encode_to_bytes(temp, CompressedPKA, 2*engine.params.ORDER_B_ENCODED_BYTES, engine.params.ORDER_B_ENCODED_BYTES);
CompressedPKA[3*engine.params.ORDER_B_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] = (byte) 0x80;
}
engine.fpx.fp2_encode(A, CompressedPKA, 3*engine.params.ORDER_B_ENCODED_BYTES);
CompressedPKA[3*engine.params.ORDER_B_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] |= (byte)rs[0];
CompressedPKA[3*engine.params.ORDER_B_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES + 1] = (byte) rs[1];
CompressedPKA[3*engine.params.ORDER_B_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES + 2] = (byte) rs[2];
}
// Alice's ephemeral public key generation using compression -- SIKE protocol
// Output: PrivateKeyA[MSG_BYTES + engine.params.SECRETKEY_A_BYTES] <- x(K_A) where K_A = PA + sk_A*Q_A
protected int EphemeralKeyGeneration_A_extended(byte[] PrivateKeyA, byte[] CompressedPKA)
{
int[] rs = new int[3],
D = new int[engine.params.DLEN_3];
long[][] a24 = new long[2][engine.params.NWORDS_FIELD];
long[][][][] As = new long[engine.params.MAX_Alice+1][5][2][engine.params.NWORDS_FIELD];
long[][][] f = new long[4][2][engine.params.NWORDS_FIELD];
long[] c0 = new long[engine.params.NWORDS_ORDER],
d0 = new long[engine.params.NWORDS_ORDER],
c1 = new long[engine.params.NWORDS_ORDER],
d1 = new long[engine.params.NWORDS_ORDER];
PointProjFull[] Rs = new PointProjFull[2];
Rs[0] = new PointProjFull(engine.params.NWORDS_FIELD);
Rs[1] = new PointProjFull(engine.params.NWORDS_FIELD);
FullIsogeny_A_dual(PrivateKeyA, As, a24, 1);
BuildOrdinary3nBasis_dual(a24, As, Rs, rs, rs, 2);
Tate3_pairings(Rs, f);
Dlogs3_dual(f, D, d0, c0, d1, c1);
Compress_PKA_dual(d0, c0, d1, c1, a24, rs, CompressedPKA);
return 0;
}
// Alice's ephemeral public key generation using compression -- SIDH protocol
// Output: PrivateKeyA[MSG_BYTES + engine.params.SECRETKEY_A_BYTES] <- x(K_A) where K_A = PA + sk_A*Q_A
private int EphemeralKeyGeneration_A(byte[] PrivateKeyA, byte[] CompressedPKA)
{
int[] rs = new int[3],
D = new int[engine.params.DLEN_3];
long[] c0 = new long[engine.params.NWORDS_ORDER],
d0 = new long[engine.params.NWORDS_ORDER],
c1 = new long[engine.params.NWORDS_ORDER],
d1 = new long[engine.params.NWORDS_ORDER];
long[][] a24 = new long[2][engine.params.NWORDS_FIELD];
long[][][] f = new long[4][2][engine.params.NWORDS_FIELD];
long[][][][] As = new long[engine.params.MAX_Alice+1][5][2][engine.params.NWORDS_FIELD];
PointProjFull[] Rs = new PointProjFull[2];
FullIsogeny_A_dual(PrivateKeyA, As, a24, 0);
BuildOrdinary3nBasis_dual(a24, As, Rs, rs, rs, 2);
Tate3_pairings(Rs, f);
Dlogs3_dual(f, D, d0, c0, d1, c1);
Compress_PKA_dual(d0, c0, d1, c1, a24, rs, CompressedPKA);
return 0;
}
// Bob's ephemeral shared secret computation using compression
// It produces a shared secret key SharedSecretB using his secret key PrivateKeyB and Alice's decompressed data point_R and param_A
// Inputs: Bob's PrivateKeyB is an integer in the range [1, oB-1], where oB = 3^OBOB_EXP.
// Alice's decompressed data consists of point_R in (X:Z) coordinates and the curve parameter param_A in GF(p^2).
// Output: a shared secret SharedSecretB that consists of one element in GF(p^2).
int EphemeralSecretAgreement_B(byte[] PrivateKeyB, byte[] PKA, byte[] SharedSecretB)
{
int i, ii = 0, row, m, index = 0, npts = 0;
int[] pts_index = new int[engine.params.MAX_INT_POINTS_BOB];
long[][] A24plus = new long[2][engine.params.NWORDS_FIELD],
A24minus = new long[2][engine.params.NWORDS_FIELD];
PointProj R = new PointProj(engine.params.NWORDS_FIELD);
PointProj[] pts = new PointProj[engine.params.MAX_INT_POINTS_BOB];
long[][] jinv = new long[2][engine.params.NWORDS_FIELD], A = new long[2][engine.params.NWORDS_FIELD];
long[][][] coeff = new long[3][2][engine.params.NWORDS_FIELD];
long[][] param_A = new long[2][engine.params.NWORDS_FIELD];
PKADecompression_dual(PrivateKeyB, PKA, R, param_A);
engine.fpx.fp2copy(param_A, A);
engine.fpx.fpaddPRIME(engine.params.Montgomery_one, engine.params.Montgomery_one, A24minus[0]);
engine.fpx.fp2add(A, A24minus, A24plus);
engine.fpx.fp2sub(A, A24minus, A24minus);
// Traverse tree
index = 0;
for (row = 1; row < engine.params.MAX_Bob; row++)
{
while (index < engine.params.MAX_Bob-row)
{
pts[npts] = new PointProj(engine.params.NWORDS_FIELD);
engine.fpx.fp2copy(R.X, pts[npts].X);
engine.fpx.fp2copy(R.Z, pts[npts].Z);
pts_index[npts++] = index;
m = engine.params.strat_Bob[ii++];
engine.isogeny.xTPLe(R, R, A24minus, A24plus, (int)m);
index += m;
}
engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
for (i = 0; i < npts; i++)
{
engine.isogeny.eval_3_isog(pts[i], coeff);
}
engine.fpx.fp2copy(pts[npts-1].X, R.X);
engine.fpx.fp2copy(pts[npts-1].Z, R.Z);
index = pts_index[npts-1];
npts -= 1;
}
engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
engine.fpx.fp2add(A24plus, A24minus, A);
engine.fpx.fp2add(A, A, A);
engine.fpx.fp2sub(A24plus, A24minus, A24plus);
engine.isogeny.j_inv(A, A24plus, jinv);
engine.fpx.fp2_encode(jinv, SharedSecretB, 0); // Format shared secret
return 0;
}
protected void BuildEntangledXonly(long[][] A, PointProj[] R, byte[] qnr, byte[] ind)
{
long[] s = new long[engine.params.NWORDS_FIELD];
long[][] t_ptr, r = new long[2][engine.params.NWORDS_FIELD],
t = new long[2][engine.params.NWORDS_FIELD];
int t_ptrOffset = 0;
// Select the correct table
if (engine.fpx.is_sqr_fp2(A, s))
{
t_ptr = engine.params.table_v_qnr;
qnr[0] = 1;
}
else
{
t_ptr = engine.params.table_v_qr;
qnr[0] = 0;
}
// Get x0
ind[0] = 0;
do
{
engine.fpx.fp2mul_mont(A, t_ptr, t_ptrOffset, R[0].X); // R[0].X = A*v
t_ptrOffset += 2;
engine.fpx.fp2neg(R[0].X); // R[0].X = -A*v
engine.fpx.fp2add(R[0].X, A, t);
engine.fpx.fp2mul_mont(R[0].X, t, t);
engine.fpx.fpaddPRIME(t[0], engine.params.Montgomery_one, t[0]);
engine.fpx.fp2mul_mont(R[0].X, t, t); // t = R[0].X^3 + A*R[0].X^2 + R[0].X
ind[0] += 1;
} while (!engine.fpx.is_sqr_fp2(t, s));
ind[0] -= 1;
if (qnr[0] == 1)
{
engine.fpx.fpcopy(engine.params.table_r_qnr[ind[0]], 0, r[0]);
}
else
{
engine.fpx.fpcopy(engine.params.table_r_qr[ind[0]], 0, r[0]);
}
// Get x1
engine.fpx.fp2add(R[0].X, A, R[1].X);
engine.fpx.fp2neg(R[1].X); // R[1].X = -R[0].X-A
// Get difference x2, z2
engine.fpx.fp2sub(R[0].X, R[1].X, R[2].Z);
engine.fpx.fp2sqr_mont(R[2].Z, R[2].Z);
engine.fpx.fpcopy(r[0], 0, r[1]); // (1+i)*ind
engine.fpx.fpaddPRIME(engine.params.Montgomery_one, r[0], r[0]);
engine.fpx.fp2sqr_mont(r, r);
engine.fpx.fp2mul_mont(t, r, R[2].X);
}
protected void RecoverY(long[][] A, PointProj[] xs, PointProjFull[] Rs)
{
long[][] t0 = new long[2][engine.params.NWORDS_FIELD],
t1 = new long[2][engine.params.NWORDS_FIELD],
t2 = new long[2][engine.params.NWORDS_FIELD],
t3 = new long[2][engine.params.NWORDS_FIELD],
t4 = new long[2][engine.params.NWORDS_FIELD];
engine.fpx.fp2mul_mont(xs[2].X, xs[1].Z, t0);
engine.fpx.fp2mul_mont(xs[1].X, xs[2].Z, t1);
engine.fpx.fp2mul_mont(xs[1].X, xs[2].X, t2);
engine.fpx.fp2mul_mont(xs[1].Z, xs[2].Z, t3);
engine.fpx.fp2sqr_mont(xs[1].X, t4);
engine.fpx.fp2sqr_mont(xs[1].Z, Rs[1].X);
engine.fpx.fp2sub(t2, t3, Rs[1].Y);
engine.fpx.fp2mul_mont(xs[1].X, Rs[1].Y, Rs[1].Y);
engine.fpx.fp2add(t4, Rs[1].X, t4);
engine.fpx.fp2mul_mont(xs[2].Z, t4, t4);
engine.fpx.fp2mul_mont(A, t1, Rs[1].X);
engine.fpx.fp2sub(t0, t1, Rs[1].Z);
engine.fpx.fp2mul_mont(Rs[0].X, Rs[1].Z, t0);
engine.fpx.fp2add(t2, Rs[1].X, t1);
engine.fpx.fp2add(t1, t1, t1);
engine.fpx.fp2sub(t0, t1, t0);
engine.fpx.fp2mul_mont(xs[1].Z, t0, t0);
engine.fpx.fp2sub(t0, t4, t0);
engine.fpx.fp2mul_mont(Rs[0].X, t0, t0);
engine.fpx.fp2add(t0, Rs[1].Y, Rs[1].Y);
engine.fpx.fp2mul_mont(Rs[0].Y, t3, t0);
engine.fpx.fp2mul_mont(xs[1].X, t0, Rs[1].X);
engine.fpx.fp2add(Rs[1].X, Rs[1].X, Rs[1].X);
engine.fpx.fp2mul_mont(xs[1].Z, t0, Rs[1].Z);
engine.fpx.fp2add(Rs[1].Z, Rs[1].Z, Rs[1].Z);
engine.fpx.fp2inv_mont_bingcd(Rs[1].Z);
engine.fpx.fp2mul_mont(Rs[1].X, Rs[1].Z, Rs[1].X);
engine.fpx.fp2mul_mont(Rs[1].Y, Rs[1].Z, Rs[1].Y);
}
protected void BuildOrdinary2nBasis_dual(long[][] A, long[][][][] Ds, PointProjFull[] Rs, byte[] qnr, byte[] ind)
{
int i;
long[] t0 = new long[engine.params.NWORDS_FIELD];
long[][] A6 = new long[2][engine.params.NWORDS_FIELD];
PointProj[] xs = new PointProj[3];
xs[0] = new PointProj(engine.params.NWORDS_FIELD);
xs[1] = new PointProj(engine.params.NWORDS_FIELD);
xs[2] = new PointProj(engine.params.NWORDS_FIELD);
// Generate x-only entangled basis
BuildEntangledXonly(A, xs, qnr, ind);
engine.fpx.fpcopy(engine.params.Montgomery_one,0, (xs[0].Z)[0]);
engine.fpx.fpcopy(engine.params.Montgomery_one,0, (xs[1].Z)[0]);
// Move them back to A = 6
for(i = 0; i < engine.params.MAX_Bob; i++)
{
engine.isogeny.eval_3_isog(xs[0], Ds[engine.params.MAX_Bob-1-i]);
engine.isogeny.eval_3_isog(xs[1], Ds[engine.params.MAX_Bob-1-i]);
engine.isogeny.eval_3_isog(xs[2], Ds[engine.params.MAX_Bob-1-i]);
}
// Recover y-coordinates with a single sqrt on A = 6
engine.fpx.fpcopy(engine.params.Montgomery_one,0, A6[0]);
engine.fpx.fpaddPRIME(A6[0], A6[0], t0);
engine.fpx.fpaddPRIME(t0, t0, A6[0]);
engine.fpx.fpaddPRIME(A6[0], t0, A6[0]); // A6 = 6
engine.isogeny.CompleteMPoint(A6, xs[0], Rs[0]);
RecoverY(A6, xs, Rs);
}
// Bob's ephemeral public key generation
// Input: a private key PrivateKeyB in the range [0, 2^Floor(Log(2,oB)) - 1].
// Output: the public key PublicKeyB consisting of 3 elements in GF(p^2) which are encoded by removing leading 0 bytes.
protected void FullIsogeny_B_dual(byte[] PrivateKeyB, long[][][][] Ds, long[][] A)
{
PointProj R = new PointProj(engine.params.NWORDS_FIELD),
Q3 = new PointProj(engine.params.NWORDS_FIELD);
PointProj[] pts = new PointProj[engine.params.MAX_INT_POINTS_BOB];
long[][] XPB = new long[2][engine.params.NWORDS_FIELD],
XQB = new long[2][engine.params.NWORDS_FIELD],
XRB = new long[2][engine.params.NWORDS_FIELD],
A24plus = new long[2][engine.params.NWORDS_FIELD],
A24minus = new long[2][engine.params.NWORDS_FIELD];
long[][][] coeff = new long[3][2][engine.params.NWORDS_FIELD];
int i, row, m, index = 0, npts = 0, ii = 0;
int[] pts_index = new int[engine.params.MAX_INT_POINTS_BOB];
long[] SecretKeyB = new long[engine.params.NWORDS_ORDER];
// Initialize basis points
init_basis(engine.params.B_gen, XPB, XQB, XRB);
engine.fpx.fpcopy(engine.params.XQB3, 0, Q3.X[0]);
engine.fpx.fpcopy(engine.params.XQB3, engine.params.NWORDS_FIELD, (Q3.X)[1]);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, Q3.Z[0]);
// Initialize constants: A24minus = A-2C, A24plus = A+2C, where A=6, C=1
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, A24plus[0]);
engine.fpx.fp2add(A24plus, A24plus, A24plus);
engine.fpx.fp2add(A24plus, A24plus, A24minus);
engine.fpx.fp2add(A24plus, A24minus, A);
engine.fpx.fp2add(A24minus, A24minus, A24plus);
// Retrieve kernel point
engine.fpx.decode_to_digits(PrivateKeyB, 0, SecretKeyB, engine.params.SECRETKEY_B_BYTES, engine.params.NWORDS_ORDER);
engine.isogeny.LADDER3PT(XPB, XQB, XRB, SecretKeyB, engine.params.BOB, R, A);
// Traverse tree
index = 0;
for (row = 1; row < engine.params.MAX_Bob; row++)
{
while (index < engine.params.MAX_Bob-row)
{
pts[npts] = new PointProj(engine.params.NWORDS_FIELD);
engine.fpx.fp2copy(R.X, pts[npts].X);
engine.fpx.fp2copy(R.Z, pts[npts].Z);
pts_index[npts++] = index;
m = engine.params.strat_Bob[ii++];
engine.isogeny.xTPLe(R, R, A24minus, A24plus, (int)m);
index += m;
}
engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
for (i = 0; i < npts; i++)
{
engine.isogeny.eval_3_isog(pts[i], coeff);
}
engine.isogeny.eval_3_isog(Q3, coeff); // Kernel of dual
engine.fpx.fp2sub(Q3.X,Q3.Z,Ds[row-1][0]);
engine.fpx.fp2add(Q3.X,Q3.Z,Ds[row-1][1]);
engine.fpx.fp2copy(pts[npts-1].X, R.X);
engine.fpx.fp2copy(pts[npts-1].Z, R.Z);
index = pts_index[npts-1];
npts -= 1;
}
engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
engine.isogeny.eval_3_isog(Q3, coeff); // Kernel of dual
engine.fpx.fp2sub(Q3.X, Q3.Z, Ds[engine.params.MAX_Bob-1][0]);
engine.fpx.fp2add(Q3.X, Q3.Z, Ds[engine.params.MAX_Bob-1][1]);
engine.fpx.fp2add(A24plus, A24minus, A);
engine.fpx.fp2sub(A24plus, A24minus, A24plus);
engine.fpx.fp2inv_mont_bingcd(A24plus);
engine.fpx.fp2mul_mont(A24plus, A, A);
engine.fpx.fp2add(A, A, A); // A = 2*(A24plus+A24mins)/(A24plus-A24minus)
}
protected void Dlogs2_dual(long[][][] f, int[] D, long[] d0, long[] c0, long[] d1, long[] c1)
{
solve_dlog(f[0], D, d0, 2);
solve_dlog(f[2], D, c0, 2);
solve_dlog(f[1], D, d1, 2);
solve_dlog(f[3], D, c1, 2);
engine.fpx.mp_sub(engine.params.Alice_order, c0, c0, engine.params.NWORDS_ORDER);
engine.fpx.mp_sub(engine.params.Alice_order, c1, c1, engine.params.NWORDS_ORDER);
}
protected void BuildEntangledXonly_Decomp(long[][] A, PointProj[] R, int qnr, int ind)
{
long[][] t_ptr, r = new long[2][engine.params.NWORDS_FIELD], t = new long[2][engine.params.NWORDS_FIELD];
// Select the correct table
if ( qnr == 1 )
{
t_ptr = engine.params.table_v_qnr;
}
else
{
t_ptr = engine.params.table_v_qr;
}
if (ind >= engine.params.TABLE_V_LEN/2)
{
ind = 0;
}
// Get x0
engine.fpx.fp2mul_mont(A, t_ptr, ind * 2, R[0].X); // x1 = A*v
engine.fpx.fp2neg(R[0].X); // R[0].X = -A*v
engine.fpx.fp2add(R[0].X, A, t);
engine.fpx.fp2mul_mont(R[0].X, t, t);
engine.fpx.fpaddPRIME(t[0], engine.params.Montgomery_one, t[0]);
engine.fpx.fp2mul_mont(R[0].X, t, t); // t = R[0].X^3 + A*R[0].X^2 + R[0].X
if (qnr == 1)
{
engine.fpx.fpcopy(engine.params.table_r_qnr[ind], 0, r[0]);
}
else
{
engine.fpx.fpcopy(engine.params.table_r_qr[ind], 0, r[0]);
}
// Get x1
engine.fpx.fp2add(R[0].X, A, R[1].X);
engine.fpx.fp2neg(R[1].X); // R[1].X = -R[0].X-A
// Get difference x2,z2
engine.fpx.fp2sub(R[0].X, R[1].X, R[2].Z);
engine.fpx.fp2sqr_mont(R[2].Z, R[2].Z);
engine.fpx.fpcopy(r[0], 0, r[1]); // (1+i)*ind
engine.fpx.fpaddPRIME(engine.params.Montgomery_one, r[0], r[0]);
engine.fpx.fp2sqr_mont(r, r);
engine.fpx.fp2mul_mont(t, r, R[2].X);
}
// Bob's PK decompression -- SIKE protocol
protected void PKBDecompression_extended(byte[] SecretKeyA, int SecretKeyAOffset, byte[] CompressedPKB, PointProj R, long[][] A, byte[] tphiBKA_t, int tphiBKA_tOffset)
{
long mask = -1L;
int qnr, ind;
long[][] A24 = new long[2][engine.params.NWORDS_FIELD],
Adiv2 = new long[2][engine.params.NWORDS_FIELD];
long[] tmp1 = new long[2*engine.params.NWORDS_ORDER],
tmp2 = new long[2*engine.params.NWORDS_ORDER],
inv = new long[engine.params.NWORDS_ORDER],
scal = new long[2*engine.params.NWORDS_ORDER],
SKin = new long[engine.params.NWORDS_ORDER],
a0 = new long[engine.params.NWORDS_ORDER],
a1 = new long[engine.params.NWORDS_ORDER],
b0 = new long[engine.params.NWORDS_ORDER],
b1 = new long[engine.params.NWORDS_ORDER];
PointProj[] Rs = new PointProj[3];
Rs[0] = new PointProj(engine.params.NWORDS_FIELD);
Rs[1] = new PointProj(engine.params.NWORDS_FIELD);
Rs[2] = new PointProj(engine.params.NWORDS_FIELD);
mask >>>= (engine.params.MAXBITS_ORDER - engine.params.OALICE_BITS);
engine.fpx.fp2_decode(CompressedPKB, A, 4*engine.params.ORDER_A_ENCODED_BYTES);
qnr = CompressedPKB[4*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] & 0x01;
ind = CompressedPKB[4*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES + 1];
BuildEntangledXonly_Decomp(A, Rs, qnr, ind);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, Rs[0].Z[0]);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, Rs[1].Z[0]);
engine.fpx.fpaddPRIME(A[0], engine.params.Montgomery_one, A24[0]);
engine.fpx.fpcopy(A[1], 0, A24[1]);
engine.fpx.fpaddPRIME(A24[0], engine.params.Montgomery_one, A24[0]);
engine.fpx.fp2div2(A24, A24);
engine.fpx.fp2div2(A24, A24);
engine.fpx.decode_to_digits(SecretKeyA, SecretKeyAOffset, SKin, engine.params.SECRETKEY_A_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.decode_to_digits(CompressedPKB, 0, a0, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.decode_to_digits(CompressedPKB, engine.params.ORDER_A_ENCODED_BYTES, b0, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.decode_to_digits(CompressedPKB, 2*engine.params.ORDER_A_ENCODED_BYTES, a1, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.decode_to_digits(CompressedPKB, 3*engine.params.ORDER_A_ENCODED_BYTES, b1, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
if ( (a0[0] & 1) == 1)
{
engine.fpx.multiply(SKin, b1, tmp1, engine.params.NWORDS_ORDER);
engine.fpx.mp_add(tmp1, b0, tmp1, engine.params.NWORDS_ORDER);
tmp1[engine.params.NWORDS_ORDER-1] &= mask;
engine.fpx.multiply(SKin, a1, tmp2, engine.params.NWORDS_ORDER);
engine.fpx.mp_add(tmp2, a0, tmp2, engine.params.NWORDS_ORDER);
tmp2[engine.params.NWORDS_ORDER-1] &= mask;
engine.fpx.inv_mod_orderA(tmp2, inv);
engine.fpx.multiply(tmp1, inv, scal, engine.params.NWORDS_ORDER);
scal[engine.params.NWORDS_ORDER-1] &= mask;
Ladder3pt_dual(Rs, scal, engine.params.ALICE, R, A24);
}
else
{
engine.fpx.multiply(SKin, a1, tmp1, engine.params.NWORDS_ORDER);
engine.fpx.mp_add(tmp1, a0, tmp1, engine.params.NWORDS_ORDER);
tmp1[engine.params.NWORDS_ORDER-1] &= (long)mask;
engine.fpx.multiply(SKin, b1, tmp2, engine.params.NWORDS_ORDER);
engine.fpx.mp_add(tmp2, b0, tmp2, engine.params.NWORDS_ORDER);
tmp2[engine.params.NWORDS_ORDER-1] &= (long)mask;
engine.fpx.inv_mod_orderA(tmp2, inv);
engine.fpx.multiply(inv, tmp1, scal, engine.params.NWORDS_ORDER);
scal[engine.params.NWORDS_ORDER-1] &= (long)mask;
engine.isogeny.swap_points(Rs[0], Rs[1], -1L);
Ladder3pt_dual(Rs, scal, engine.params.ALICE, R, A24);
}
engine.fpx.fp2div2(A,Adiv2);
engine.isogeny.xTPLe_fast(R, R, Adiv2, engine.params.OBOB_EXPON);
engine.fpx.fp2_encode(R.X, tphiBKA_t, tphiBKA_tOffset);
engine.fpx.fp2_encode(R.Z, tphiBKA_t, tphiBKA_tOffset + engine.params.FP2_ENCODED_BYTES);
engine.fpx.encode_to_bytes(inv, tphiBKA_t, tphiBKA_tOffset + 2*engine.params.FP2_ENCODED_BYTES, engine.params.ORDER_A_ENCODED_BYTES);
}
// Bob's PK compression -- SIKE protocol
protected void Compress_PKB_dual_extended(long[] d0, long[] c0, long[] d1, long[] c1, long[][] A, byte[] qnr, byte[] ind, byte[] CompressedPKB)
{
long mask = -1L;
long[] tmp = new long[2*engine.params.NWORDS_ORDER],
D = new long[2*engine.params.NWORDS_ORDER], Dinv = new long[2*engine.params.NWORDS_ORDER];
mask >>>= (engine.params.MAXBITS_ORDER - engine.params.OALICE_BITS);
engine.fpx.multiply(c0, d1, tmp, engine.params.NWORDS_ORDER);
engine.fpx.multiply(c1, d0, D, engine.params.NWORDS_ORDER);
engine.fpx.Montgomery_neg(D, engine.params.Alice_order);
engine.fpx.mp_add(tmp, D, D, engine.params.NWORDS_ORDER);
D[engine.params.NWORDS_ORDER-1] &= (long)mask;
engine.fpx.inv_mod_orderA(D, Dinv);
engine.fpx.multiply(d1, Dinv, tmp, engine.params.NWORDS_ORDER); // a0' = 3^n * d1 / D
tmp[engine.params.NWORDS_ORDER-1] &= mask;
engine.fpx.encode_to_bytes(tmp, CompressedPKB, 0, engine.params.ORDER_A_ENCODED_BYTES);
engine.fpx.Montgomery_neg(d0, engine.params.Alice_order);
engine.fpx.multiply(d0, Dinv, tmp, engine.params.NWORDS_ORDER); // b0' = 3^n * (- d0 / D)
tmp[engine.params.NWORDS_ORDER-1] &= (long)mask;
engine.fpx.encode_to_bytes(tmp,CompressedPKB, engine.params.ORDER_A_ENCODED_BYTES, engine.params.ORDER_A_ENCODED_BYTES);
engine.fpx.Montgomery_neg(c1, engine.params.Alice_order);
engine.fpx.multiply(c1, Dinv, tmp, engine.params.NWORDS_ORDER); // a1' = 3^n * (- c1 / D)
tmp[engine.params.NWORDS_ORDER-1] &= (long)mask;
engine.fpx.encode_to_bytes(tmp, CompressedPKB, 2*engine.params.ORDER_A_ENCODED_BYTES, engine.params.ORDER_A_ENCODED_BYTES);
engine.fpx.multiply(c0, Dinv, tmp, engine.params.NWORDS_ORDER); // b1' = 3^n * (c0 / D)
tmp[engine.params.NWORDS_ORDER-1] &= (long)mask;
engine.fpx.encode_to_bytes(tmp, CompressedPKB, 3*engine.params.ORDER_A_ENCODED_BYTES, engine.params.ORDER_A_ENCODED_BYTES);
engine.fpx.fp2_encode(A, CompressedPKB, 4*engine.params.ORDER_A_ENCODED_BYTES);
CompressedPKB[4*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] = qnr[0];
CompressedPKB[4*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES + 1] = ind[0];
}
// Bob's PK decompression -- SIDH protocol
protected void PKBDecompression(byte[] SecretKeyA, int SecretKeyAOffset, byte[] CompressedPKB, PointProj R, long[][] A)
{
long mask = -1L;
int bit,qnr,ind;
long[][] A24 = new long[2][engine.params.NWORDS_FIELD];
long[] tmp1 = new long[2*engine.params.NWORDS_ORDER],
tmp2 = new long[2*engine.params.NWORDS_ORDER],
vone = new long[2*engine.params.NWORDS_ORDER],
SKin = new long[engine.params.NWORDS_ORDER],
comp_temp = new long[engine.params.NWORDS_ORDER];
PointProj[] Rs = new PointProj[3];
mask >>>= (engine.params.MAXBITS_ORDER - engine.params.OALICE_BITS);
vone[0] = 1;
engine.fpx.fp2_decode(CompressedPKB, A, 3*engine.params.ORDER_A_ENCODED_BYTES);
bit = CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] >>> 7;
qnr = CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] & 0x1;
ind = CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES + 1];
// Rebuild the basis
BuildEntangledXonly_Decomp(A,Rs,qnr,ind);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, Rs[0].Z[0]);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, Rs[1].Z[0]);
engine.fpx.fpaddPRIME(A[0], engine.params.Montgomery_one, A24[0]);
engine.fpx.fpcopy(A[1], 0, A24[1]);
engine.fpx.fpaddPRIME(A24[0], engine.params.Montgomery_one, A24[0]);
engine.fpx.fp2div2(A24, A24);
engine.fpx.fp2div2(A24, A24);
engine.fpx.decode_to_digits(SecretKeyA, SecretKeyAOffset, SKin, engine.params.SECRETKEY_A_BYTES, engine.params.NWORDS_ORDER);
engine.isogeny.swap_points(Rs[0], Rs[1], 0-(long)bit);
if (bit == 0)
{
engine.fpx.decode_to_digits(CompressedPKB, engine.params.ORDER_A_ENCODED_BYTES, comp_temp, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.multiply(SKin, comp_temp, tmp1, engine.params.NWORDS_ORDER);
engine.fpx.mp_add(tmp1, vone, tmp1, engine.params.NWORDS_ORDER);
tmp1[engine.params.NWORDS_ORDER-1] &= (long)mask;
engine.fpx.inv_mod_orderA(tmp1, tmp2);
engine.fpx.decode_to_digits(CompressedPKB, 2*engine.params.ORDER_A_ENCODED_BYTES, comp_temp, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.multiply(SKin, comp_temp, tmp1, engine.params.NWORDS_ORDER);
engine.fpx.decode_to_digits(CompressedPKB, 0, comp_temp, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.mp_add(comp_temp, tmp1, tmp1, engine.params.NWORDS_ORDER);
engine.fpx.multiply(tmp1, tmp2, vone, engine.params.NWORDS_ORDER);
vone[engine.params.NWORDS_ORDER-1] &= mask;
Ladder3pt_dual(Rs, vone, engine.params.ALICE, R, A24);
}
else
{
engine.fpx.decode_to_digits(CompressedPKB, 2*engine.params.ORDER_A_ENCODED_BYTES, comp_temp, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.multiply(SKin, comp_temp, tmp1, engine.params.NWORDS_ORDER);
engine.fpx.mp_add(tmp1, vone, tmp1, engine.params.NWORDS_ORDER);
tmp1[engine.params.NWORDS_ORDER-1] &= mask;
engine.fpx.inv_mod_orderA(tmp1, tmp2);
engine.fpx.decode_to_digits(CompressedPKB, engine.params.ORDER_A_ENCODED_BYTES, comp_temp, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.multiply(SKin, comp_temp, tmp1, engine.params.NWORDS_ORDER);
engine.fpx.decode_to_digits(CompressedPKB, 0, comp_temp, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.fpx.mp_add(comp_temp, tmp1, tmp1, engine.params.NWORDS_ORDER);
engine.fpx.multiply(tmp1, tmp2, vone, engine.params.NWORDS_ORDER);
vone[engine.params.NWORDS_ORDER-1] &= mask;
Ladder3pt_dual(Rs, vone, engine.params.ALICE, R, A24);
}
engine.fpx.fp2div2(A, A24);
engine.isogeny.xTPLe_fast(R, R, A24, engine.params.OBOB_EXPON);
}
// Bob's PK compression -- SIDH protocol
protected void Compress_PKB_dual(long[] d0, long[] c0, long[] d1, long[] c1, long[][] A, byte qnr[], byte ind[], byte[] CompressedPKB)
{
long[] tmp = new long[2*engine.params.NWORDS_ORDER],
inv = new long[engine.params.NWORDS_ORDER];
if ((d1[0] & 1) == 1)
{ // Storing [-d0*d1^-1 = b1*a0^-1, -c1*d1^-1 = a1*a0^-1, c0*d1^-1 = b0*a0^-1] and setting bit384 to 0
engine.fpx.inv_mod_orderA(d1, inv);
engine.fpx.Montgomery_neg(d0, engine.params.Alice_order);
engine.fpx.multiply(d0, inv, tmp, engine.params.NWORDS_ORDER);
engine.fpx.encode_to_bytes(tmp, CompressedPKB, 0, engine.params.ORDER_A_ENCODED_BYTES);
CompressedPKB[engine.params.ORDER_A_ENCODED_BYTES-1] &= engine.params.MASK_ALICE;
engine.fpx.Montgomery_neg(c1, engine.params.Alice_order);
engine.fpx.multiply(c1, inv, tmp, engine.params.NWORDS_ORDER);
engine.fpx.encode_to_bytes(tmp, CompressedPKB,engine.params.ORDER_A_ENCODED_BYTES, engine.params.ORDER_A_ENCODED_BYTES);
CompressedPKB[2*engine.params.ORDER_A_ENCODED_BYTES-1] &= engine.params.MASK_ALICE;
engine.fpx.multiply(c0, inv, tmp, engine.params.NWORDS_ORDER);
engine.fpx.encode_to_bytes(tmp, CompressedPKB, 2*engine.params.ORDER_A_ENCODED_BYTES, engine.params.ORDER_A_ENCODED_BYTES);
CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES-1] &= engine.params.MASK_ALICE;
CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] = 0x00;
}
else
{ // Storing [ -d1*d0^-1 = b1*b0inv, c1*d0^-1 = a1*b0inv, -c0*d0^-1 = a0*b0inv] and setting bit384 to 1
engine.fpx.inv_mod_orderA(d0, inv);
engine.fpx.Montgomery_neg(d1, engine.params.Alice_order);
engine.fpx.multiply(d1, inv, tmp, engine.params.NWORDS_ORDER);
engine.fpx.encode_to_bytes(tmp, CompressedPKB,0, engine.params.ORDER_A_ENCODED_BYTES);
CompressedPKB[engine.params.ORDER_A_ENCODED_BYTES - 1] &= engine.params.MASK_ALICE;
engine.fpx.multiply(c1, inv, tmp, engine.params.NWORDS_ORDER);
engine.fpx.encode_to_bytes(tmp, CompressedPKB, engine.params.ORDER_A_ENCODED_BYTES, engine.params.ORDER_A_ENCODED_BYTES);
CompressedPKB[2*engine.params.ORDER_A_ENCODED_BYTES-1] &= engine.params.MASK_ALICE;
engine.fpx.Montgomery_neg(c0, engine.params.Alice_order);
engine.fpx.multiply(c0, inv, tmp, engine.params.NWORDS_ORDER);
engine.fpx.encode_to_bytes(tmp, CompressedPKB,2*engine.params.ORDER_A_ENCODED_BYTES, engine.params.ORDER_A_ENCODED_BYTES);
CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES-1] &= engine.params.MASK_ALICE;
CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] = (byte) 0x80;
}
engine.fpx.fp2_encode(A, CompressedPKB,3*engine.params.ORDER_A_ENCODED_BYTES);
CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES] |= qnr[0];
CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES + 1] = ind[0];
CompressedPKB[3*engine.params.ORDER_A_ENCODED_BYTES + engine.params.FP2_ENCODED_BYTES + 2] = 0;
}
// Bob's ephemeral public key generation using compression -- SIKE protocol
protected int EphemeralKeyGeneration_B_extended(byte[] PrivateKeyB, byte[] CompressedPKB, int sike)
{
byte[] qnr = new byte[1], ind = new byte[1];
int[] D = new int[engine.params.DLEN_2];
long[] c0 = new long[engine.params.NWORDS_ORDER],
d0 = new long[engine.params.NWORDS_ORDER],
c1 = new long[engine.params.NWORDS_ORDER],
d1 = new long[engine.params.NWORDS_ORDER];
long[][][][] Ds = new long[engine.params.MAX_Bob][2][2][engine.params.NWORDS_FIELD];
long[][][] f = new long[4][2][engine.params.NWORDS_FIELD];
long[][] A = new long[2][engine.params.NWORDS_FIELD];
PointProjFull[] Rs = new PointProjFull[2];
Rs[0] = new PointProjFull(engine.params.NWORDS_FIELD);
Rs[1] = new PointProjFull(engine.params.NWORDS_FIELD);
PointProj Pw = new PointProj(engine.params.NWORDS_FIELD),
Qw = new PointProj(engine.params.NWORDS_FIELD);
FullIsogeny_B_dual(PrivateKeyB, Ds, A);
BuildOrdinary2nBasis_dual(A, Ds, Rs, qnr, ind); // Generate a basis in E_A and pulls it back to E_A6. Rs[0] and Rs[1] affinized.
// Maps from y^2 = x^3 + 6x^2 + x into y^2 = x^3 -11x + 14
engine.fpx.fpaddPRIME(engine.params.Montgomery_one, Rs[0].X[0], Rs[0].X[0]);
engine.fpx.fpaddPRIME(engine.params.Montgomery_one, Rs[0].X[0], Rs[0].X[0]); // Weierstrass form
engine.fpx.fpaddPRIME(engine.params.Montgomery_one, Rs[1].X[0], Rs[1].X[0]);
engine.fpx.fpaddPRIME(engine.params.Montgomery_one, Rs[1].X[0], Rs[1].X[0]); // Weierstrass form
engine.fpx.fpcopy(engine.params.A_basis_zero, 0*engine.params.NWORDS_FIELD, Pw.X[0]);
engine.fpx.fpcopy(engine.params.A_basis_zero, 1*engine.params.NWORDS_FIELD, Pw.X[1]);
engine.fpx.fpcopy(engine.params.A_basis_zero, 2*engine.params.NWORDS_FIELD, Pw.Z[0]);//y
engine.fpx.fpcopy(engine.params.A_basis_zero, 3*engine.params.NWORDS_FIELD, Pw.Z[1]);//y
engine.fpx.fpcopy(engine.params.A_basis_zero, 4*engine.params.NWORDS_FIELD, Qw.X[0]);
engine.fpx.fpcopy(engine.params.A_basis_zero, 5*engine.params.NWORDS_FIELD, Qw.X[1]);
engine.fpx.fpcopy(engine.params.A_basis_zero, 6*engine.params.NWORDS_FIELD, Qw.Z[0]);//y
engine.fpx.fpcopy(engine.params.A_basis_zero, 7*engine.params.NWORDS_FIELD, Qw.Z[1]);//y
Tate2_pairings(Pw, Qw, Rs, f);
engine.fpx.fp2correction(f[0]);
engine.fpx.fp2correction(f[1]);
engine.fpx.fp2correction(f[2]);
engine.fpx.fp2correction(f[3]);
Dlogs2_dual(f, D, d0, c0, d1, c1);
if (sike == 1)
{
Compress_PKB_dual_extended(d0, c0, d1, c1, A, qnr, ind, CompressedPKB);
}
else
{
Compress_PKB_dual(d0, c0, d1, c1, A, qnr, ind, CompressedPKB);
}
return 0;
}
// Bob's ephemeral public key generation using compression -- SIDH protocol
protected int EphemeralKeyGeneration_B(byte[] PrivateKeyB, byte[] CompressedPKB)
{
return EphemeralKeyGeneration_B_extended(PrivateKeyB, CompressedPKB, 0);
}
// Alice's ephemeral shared secret computation using compression -- SIKE protocol
protected int EphemeralSecretAgreement_A_extended(byte[] PrivateKeyA, int PrivateKeyAOffset, byte[] PKB, byte[] SharedSecretA, int sike)
{
int i, ii = 0, row, m, index = 0, npts = 0;
int[] pts_index = new int[engine.params.MAX_INT_POINTS_ALICE];
long[][] A24plus = new long[2][engine.params.NWORDS_FIELD],
C24 = new long[2][engine.params.NWORDS_FIELD];
PointProj R = new PointProj(engine.params.NWORDS_FIELD);
PointProj[] pts = new PointProj[engine.params.MAX_INT_POINTS_ALICE];
long[][] jinv = new long[2][engine.params.NWORDS_FIELD],
A = new long[2][engine.params.NWORDS_FIELD],
param_A = new long[2][engine.params.NWORDS_FIELD];
long[][][] coeff = new long[5][2][engine.params.NWORDS_FIELD];
if (sike == 1)
{
PKBDecompression_extended(PrivateKeyA, PrivateKeyAOffset, PKB, R, param_A, SharedSecretA, engine.params.FP2_ENCODED_BYTES);
}
else
{
PKBDecompression(PrivateKeyA, PrivateKeyAOffset, PKB, R, param_A);
}
engine.fpx.fp2copy(param_A, A);
engine.fpx.fpaddPRIME(engine.params.Montgomery_one, engine.params.Montgomery_one, C24[0]);
engine.fpx.fp2add(A, C24, A24plus);
engine.fpx.fpaddPRIME(C24[0], C24[0], C24[0]);
if (engine.params.OALICE_BITS % 2 == 1)
{
PointProj S = new PointProj(engine.params.NWORDS_FIELD);
engine.isogeny.xDBLe(R, S, A24plus, C24, (engine.params.OALICE_BITS - 1));
engine.isogeny.get_2_isog(S, A24plus, C24);
engine.isogeny.eval_2_isog(R, S);
}
// Traverse tree
index = 0;
for (row = 1; row < engine.params.MAX_Alice; row++)
{
while (index < engine.params.MAX_Alice-row)
{
pts[npts] = new PointProj(engine.params.NWORDS_FIELD);
engine.fpx.fp2copy(R.X, pts[npts].X);
engine.fpx.fp2copy(R.Z, pts[npts].Z);
pts_index[npts++] = index;
m = engine.params.strat_Alice[ii++];
engine.isogeny.xDBLe(R, R, A24plus, C24, (int)(2*m));
index += m;
}
engine.isogeny.get_4_isog(R, A24plus, C24, coeff);
for (i = 0; i < npts; i++)
{
engine.isogeny.eval_4_isog(pts[i], coeff);
}
engine.fpx.fp2copy(pts[npts-1].X, R.X);
engine.fpx.fp2copy(pts[npts-1].Z, R.Z);
index = pts_index[npts-1];
npts -= 1;
}
engine.isogeny.get_4_isog(R, A24plus, C24, coeff);
engine.fpx.fp2add(A24plus, A24plus, A24plus);
engine.fpx.fp2sub(A24plus, C24, A24plus);
engine.fpx.fp2add(A24plus, A24plus, A24plus);
engine.isogeny.j_inv(A24plus, C24, jinv);
engine.fpx.fp2_encode(jinv, SharedSecretA, 0); // Format shared secret
return 0;
}
// Alice's ephemeral shared secret computation using compression -- SIDH protocol
// It produces a shared secret key SharedSecretA using her secret key PrivateKeyA and Bob's decompressed data point_R and param_A
// Inputs: Alice's PrivateKeyA is an even integer in the range [2, oA-2], where oA = 2^engine.params.OALICE_BITS.
// Bob's decompressed data consists of point_R in (X:Z) coordinates and the curve parameter param_A in GF(p^2).
// Output: a shared secret SharedSecretA that consists of one element in GF(p^2).
int EphemeralSecretAgreement_A(byte[] PrivateKeyA, int PrivateKeyAOffset, byte[] PKB, byte[] SharedSecretA)
{
return EphemeralSecretAgreement_A_extended(PrivateKeyA, PrivateKeyAOffset, PKB, SharedSecretA, 0);
}
protected byte validate_ciphertext(byte[] ephemeralsk_, byte[] CompressedPKB, byte[] xKA, int xKAOffset, byte[] tphiBKA_t, int tphiBKA_tOffset)
{ // If ct validation passes returns 0, otherwise returns -1.
PointProj[] phis = new PointProj[3],
pts = new PointProj[engine.params.MAX_INT_POINTS_BOB];
phis[0] = new PointProj(engine.params.NWORDS_FIELD);
phis[1] = new PointProj(engine.params.NWORDS_FIELD);
phis[2] = new PointProj(engine.params.NWORDS_FIELD);
PointProj R = new PointProj(engine.params.NWORDS_FIELD),
S = new PointProj(engine.params.NWORDS_FIELD);
long[][] XPB = new long[2][engine.params.NWORDS_FIELD],
XQB = new long[2][engine.params.NWORDS_FIELD],
XRB = new long[2][engine.params.NWORDS_FIELD],
A24plus = new long[2][engine.params.NWORDS_FIELD],
A24minus = new long[2][engine.params.NWORDS_FIELD],
A = new long[2][engine.params.NWORDS_FIELD],
comp1 = new long[2][engine.params.NWORDS_FIELD],
comp2 = new long[2][engine.params.NWORDS_FIELD],
one = new long[2][engine.params.NWORDS_FIELD];
long[][][] coeff = new long[3][2][engine.params.NWORDS_FIELD];
int i, row, m, index = 0, npts = 0, ii = 0;
int[] pts_index = new int[engine.params.MAX_INT_POINTS_BOB];
long[] temp = new long[engine.params.NWORDS_ORDER],
sk = new long[engine.params.NWORDS_ORDER];
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, one[0]);
// Initialize basis points
init_basis(engine.params.B_gen, XPB, XQB, XRB);
engine.fpx.fp2_decode(xKA, phis[0].X, xKAOffset);
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, phis[0].Z[0]); // phi[0] <- PA + skA*QA
// Initialize constants: A24minus = A-2C, A24plus = A+2C, where A=6, C=1
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, A24plus[0]);
engine.fpx.fp2add(A24plus, A24plus, A24plus);
engine.fpx.fp2add(A24plus, A24plus, A24minus); // A24minus = 4
engine.fpx.fp2add(A24plus, A24minus, A); // A = 6
engine.fpx.fp2add(A24minus, A24minus, A24plus); // A24plus = 8
// Retrieve kernel point
engine.fpx.decode_to_digits(ephemeralsk_, 0, sk, engine.params.SECRETKEY_B_BYTES, engine.params.NWORDS_ORDER);
engine.isogeny.LADDER3PT(XPB, XQB, XRB, sk, engine.params.BOB, R, A);
// Traverse tree
index = 0;
for (row = 1; row < engine.params.MAX_Bob; row++)
{
while (index < engine.params.MAX_Bob-row)
{
pts[npts] = new PointProj(engine.params.NWORDS_FIELD);
engine.fpx.fp2copy(R.X, pts[npts].X);
engine.fpx.fp2copy(R.Z, pts[npts].Z);
pts_index[npts++] = index;
m = engine.params.strat_Bob[ii++];
engine.isogeny.xTPLe(R, R, A24minus, A24plus, (int)m);
index += m;
}
engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
for (i = 0; i < npts; i++)
{
engine.isogeny.eval_3_isog(pts[i], coeff);
}
engine.isogeny.eval_3_isog(phis[0], coeff);
engine.fpx.fp2copy(pts[npts-1].X, R.X);
engine.fpx.fp2copy(pts[npts-1].Z, R.Z);
index = pts_index[npts-1];
npts -= 1;
}
engine.isogeny.get_3_isog(R, A24minus, A24plus, coeff);
engine.isogeny.eval_3_isog(phis[0], coeff); // phis[0] <- phiB(PA + skA*QA)
engine.fpx.fp2_decode(CompressedPKB, A, 4*engine.params.ORDER_A_ENCODED_BYTES);
// Single equation check: t*(phiP + skA*phiQ) =? t*3^n*((a0+skA*a1)*S1 + (b0+skA*b1)*S2) for t in {(a0+skA*a1)^-1, (b0+skA*b1)^-1}
engine.fpx.fp2_decode(tphiBKA_t, S.X, tphiBKA_tOffset);
engine.fpx.fp2_decode(tphiBKA_t, S.Z, tphiBKA_tOffset + engine.params.FP2_ENCODED_BYTES); // Recover t*3^n*((a0+skA*a1)*S1 + (b0+skA*b1)*S2)
engine.fpx.decode_to_digits(tphiBKA_t, tphiBKA_tOffset + 2*engine.params.FP2_ENCODED_BYTES, temp, engine.params.ORDER_A_ENCODED_BYTES, engine.params.NWORDS_ORDER);
engine.isogeny.Ladder(phis[0], temp, A, engine.params.OALICE_BITS, R); // t*(phiP + skA*phiQ)
engine.fpx.fp2mul_mont(R.X, S.Z, comp1);
engine.fpx.fp2mul_mont(R.Z, S.X, comp2);
return (engine.fpx.cmp_f2elm(comp1, comp2));
}
/// DLOG
// Computes the discrete log of input r = g^d where g = e(P,Q)^ell^e, and P,Q are torsion generators in the initial curve
// Return the integer d
void solve_dlog(long[][] r, int[] D, long[] d, int ell)
{
if (ell == 2)
{
if (engine.params.OALICE_BITS % engine.params.W_2 == 0)
{
Traverse_w_div_e_fullsigned(r, 0, 0, engine.params.PLEN_2 - 1, engine.params.ph2_path,
engine.params.ph2_T, D, engine.params.DLEN_2, engine.params.ELL2_W, engine.params.W_2);
}
else
{
Traverse_w_notdiv_e_fullsigned(r, 0, 0, engine.params.PLEN_2 - 1, engine.params.ph2_path,
engine.params.ph2_T1, engine.params.ph2_T2, D, engine.params.DLEN_2, ell, engine.params.ELL2_W,
engine.params.ELL2_EMODW, engine.params.W_2, engine.params.OALICE_BITS);
}
from_base(D, d, engine.params.DLEN_2, engine.params.ELL2_W);
}
else if (ell == 3)
{
if (engine.params.OBOB_EXPON % engine.params.W_3 == 0)
{
Traverse_w_div_e_fullsigned(r, 0, 0, engine.params.PLEN_3 - 1, engine.params.ph3_path,
engine.params.ph3_T, D, engine.params.DLEN_3, engine.params.ELL3_W, engine.params.W_3);
}
else
{
Traverse_w_notdiv_e_fullsigned(r, 0, 0, engine.params.PLEN_3 - 1, engine.params.ph3_path,
engine.params.ph3_T1, engine.params.ph3_T2, D, engine.params.DLEN_3, ell, engine.params.ELL3_W,
engine.params.ELL3_EMODW, engine.params.W_3, engine.params.OBOB_EXPON);
}
from_base(D, d, engine.params.DLEN_3, engine.params.ELL3_W);
}
}
// Convert a number in base "base" with signed digits: (D[k-1]D[k-2]...D[1]D[0])_base < 2^(NWORDS_ORDER*RADIX) into decimal
// Output: r = D[k-1]*base^(k-1) + ... + D[1]*base + D[0]
private void from_base(int[] D, long[] r, int Dlen, int base)
{
long[] ell = new long[engine.params.NWORDS_ORDER],
digit = new long[engine.params.NWORDS_ORDER],
temp = new long[engine.params.NWORDS_ORDER];
int ellw;
ell[0] = base;
if (D[Dlen-1] < 0)
{
digit[0] = (((int)-D[Dlen-1])*ell[0]);
if ((base & 1) == 0)
{
engine.fpx.Montgomery_neg(digit, engine.params.Alice_order);
engine.fpx.copy_words(digit, r, engine.params.NWORDS_ORDER);
}
else
{
engine.fpx.mp_sub(engine.params.Bob_order, digit, r, engine.params.NWORDS_ORDER);
}
}
else
{
r[0] = (D[Dlen-1]*ell[0]);
}
for (int i = Dlen-2; i >= 1; i--)
{
ellw = base;
Arrays.fill(digit, 0);
if (D[i] < 0)
{
digit[0] = (-D[i]);
if ((base & 1) == 0)
{
engine.fpx.Montgomery_neg(digit, engine.params.Alice_order);
}
else
{
engine.fpx.mp_sub(engine.params.Bob_order, digit, digit, engine.params.NWORDS_ORDER);
}
}
else
{
digit[0] = D[i];
}
engine.fpx.mp_add(r, digit, r, engine.params.NWORDS_ORDER);
if ((base & 1) != 0)
{
if (!engine.fpx.is_orderelm_lt(r, engine.params.Bob_order))
{
engine.fpx.mp_sub(r, engine.params.Bob_order, r, engine.params.NWORDS_ORDER);
}
}
if ((base & 1) == 0)
{
while (ellw > 1)
{
engine.fpx.mp_add(r, r, r, engine.params.NWORDS_ORDER);
ellw /= 2;
}
}
else
{
while (ellw > 1)
{
Arrays.fill(temp, 0);
engine.fpx.mp_add(r, r, temp, engine.params.NWORDS_ORDER);
if (!engine.fpx.is_orderelm_lt(temp, engine.params.Bob_order))
{
engine.fpx.mp_sub(temp, engine.params.Bob_order, temp, engine.params.NWORDS_ORDER);
}
engine.fpx.mp_add(r, temp, r, engine.params.NWORDS_ORDER);
if (!engine.fpx.is_orderelm_lt(r, engine.params.Bob_order))
{
engine.fpx.mp_sub(r, engine.params.Bob_order, r, engine.params.NWORDS_ORDER);
}
ellw /= 3;
}
}
}
Arrays.fill(digit, 0);
if (D[0] < 0)
{
digit[0] = (-D[0]);
if ((base & 1) == 0)
{
engine.fpx.Montgomery_neg(digit, engine.params.Alice_order);
}
else
{
engine.fpx.mp_sub(engine.params.Bob_order, digit, digit, engine.params.NWORDS_ORDER);
}
}
else
{
digit[0] = D[0];
}
engine.fpx.mp_add(r, digit, r, engine.params.NWORDS_ORDER);
if ((base & 1) != 0)
{
if (!engine.fpx.is_orderelm_lt(r, engine.params.Bob_order))
{
engine.fpx.mp_sub(r, engine.params.Bob_order, r, engine.params.NWORDS_ORDER);
}
}
}
// Traverse a Pohlig-Hellman optimal strategy to solve a discrete log in a group of order ell^e
// Leaves are used to recover the digits which are numbers from 0 to ell^w-1 except by the last leaf that gives a digit between 0 and ell^(e mod w)
// Assume w does not divide the exponent e
void Traverse_w_notdiv_e_fullsigned(long[][] r, int j, int k, int z, int[] P, long[] CT1, long[] CT2,
int[] D, int Dlen, int ell, int ellw, int ell_emodw, int w, int e)
{
long[][] rp = new long[2][engine.params.NWORDS_FIELD],
alpha = new long[2][engine.params.NWORDS_FIELD];
if (z > 1)
{
int t = P[z], goleft;
engine.fpx.fp2copy(r, rp);
goleft = (j > 0) ? w*(z-t) : (e % w) + w*(z-t-1);
for (int i = 0; i < goleft; i++)
{
if ((ell & 1) == 0)
{
engine.fpx.sqr_Fp2_cycl(rp, engine.params.Montgomery_one);
}
else
{
engine.fpx.cube_Fp2_cycl(rp, engine.params.Montgomery_one);
}
}
Traverse_w_notdiv_e_fullsigned(rp, j + (z - t), k, t, P, CT1, CT2, D, Dlen, ell, ellw, ell_emodw, w, e);
engine.fpx.fp2copy(r, rp);
for (int h = k; h < k + t; h++)
{
if (D[h] != 0)
{
if (j > 0)
{
if (D[h] < 0)
{
engine.fpx.fp2copy(CT2, engine.params.NWORDS_FIELD * (2*(j + h)*(ellw/2)+2*(-D[h]-1)), alpha);
engine.fpx.fpnegPRIME(alpha[1]);
engine.fpx.fp2mul_mont(rp, alpha, rp);
}
else
{
engine.fpx.fp2mul_mont(rp, CT2, engine.params.NWORDS_FIELD * (2*((j + h)*(ellw/2) + (D[h]-1))), rp);
}
}
else
{
if (D[h] < 0)
{
engine.fpx.fp2copy(CT1, engine.params.NWORDS_FIELD * (2*((j + h)*(ellw/2) + (-D[h]-1))), alpha);
engine.fpx.fpnegPRIME(alpha[1]);
engine.fpx.fp2mul_mont(rp, alpha, rp);
}
else
{
engine.fpx.fp2mul_mont(rp, CT1, engine.params.NWORDS_FIELD * (2*((j + h)*(ellw/2) + (D[h]-1))), rp);
}
}
}
}
Traverse_w_notdiv_e_fullsigned(rp, j, k + t, z - t, P, CT1, CT2, D, Dlen, ell, ellw, ell_emodw, w, e);
}
else
{
engine.fpx.fp2copy(r, rp);
engine.fpx.fp2correction(rp);
if (engine.fpx.is_felm_zero(rp[1]) && Fpx.subarrayEquals(rp[0],engine.params.Montgomery_one, engine.params.NWORDS_FIELD))
{
D[k] = 0;
}
else
{
if (!(j == 0 && k == Dlen - 1))
{
for (int t = 1; t <= (ellw/2); t++)
{
if (Fpx.subarrayEquals(rp, CT2, engine.params.NWORDS_FIELD * (2 *(ellw/2)*(Dlen-1) + 2*(t-1)), 2*engine.params.NWORDS_FIELD))
{
D[k] = -t;
break;
}
else
{
engine.fpx.fp2copy(CT2, engine.params.NWORDS_FIELD *(2*((ellw/2)*(Dlen-1) + (t-1))), alpha);
engine.fpx.fpnegPRIME(alpha[1]);
engine.fpx.fpcorrectionPRIME(alpha[1]);
if (Fpx.subarrayEquals(rp, alpha, 2*engine.params.NWORDS_FIELD))
{
D[k] = t;
break;
}
}
}
}
else
{
for (int t = 1; t <= ell_emodw/2; t++)
{
if (Fpx.subarrayEquals(rp, CT1,engine.params.NWORDS_FIELD * (2*(ellw/2)*(Dlen - 1) + 2*(t-1)), 2*engine.params.NWORDS_FIELD))
{
D[k] = -t;
break;
}
else
{
engine.fpx.fp2copy(CT1, engine.params.NWORDS_FIELD * (2*((ellw/2)*(Dlen-1) + (t-1))), alpha);
engine.fpx.fpnegPRIME(alpha[1]);
engine.fpx.fpcorrectionPRIME(alpha[1]);
if (Fpx.subarrayEquals(rp, alpha, 2*engine.params.NWORDS_FIELD))
{
D[k] = t;
break;
}
}
}
}
}
}
}
// Traverse a Pohlig-Hellman optimal strategy to solve a discrete log in a group of order ell^e
// The leaves of the tree will be used to recover the signed digits which are numbers from +/-{0,1... Ceil((ell^w-1)/2)}
// Assume the integer w divides the exponent e
void Traverse_w_div_e_fullsigned(long[][] r, int j, int k, int z, int[] P, long[] CT, int[] D, int Dlen, int ellw, int w)
{
long[][] rp = new long[2][engine.params.NWORDS_FIELD], alpha = new long[2][engine.params.NWORDS_FIELD];
if (z > 1)
{
int t = P[z];
engine.fpx.fp2copy(r, rp);
for (int i = 0; i < z-t; i++)
{
if ((ellw & 1) == 0)
{
for (int ii = 0; ii < w; ii++)
{
engine.fpx.sqr_Fp2_cycl(rp, engine.params.Montgomery_one);
}
}
else
{
for (int ii = 0; ii < w; ii++)
{
engine.fpx.cube_Fp2_cycl(rp, engine.params.Montgomery_one);
}
}
}
Traverse_w_div_e_fullsigned(rp, j + (z - t), k, t, P, CT, D, Dlen, ellw, w);
engine.fpx.fp2copy(r, rp);
for (int h = k; h < k + t; h++)
{
if (D[h] != 0)
{
if(D[h] < 0)
{
engine.fpx.fp2copy(CT, engine.params.NWORDS_FIELD * (2*((j + h)*(ellw/2) + (-D[h]-1))), alpha);
engine.fpx.fpnegPRIME(alpha[1]);
engine.fpx.fp2mul_mont(rp, alpha, rp);
}
else
{
engine.fpx.fp2mul_mont(rp, CT, engine.params.NWORDS_FIELD * (2*((j + h)*(ellw/2) + (D[h]-1))), rp);
}
}
}
Traverse_w_div_e_fullsigned(rp, j, k + t, z - t, P, CT, D, Dlen, ellw, w);
}
else
{
engine.fpx.fp2copy(r, rp);
engine.fpx.fp2correction(rp);
if (engine.fpx.is_felm_zero(rp[1]) && Fpx.subarrayEquals(rp[0], engine.params.Montgomery_one, engine.params.NWORDS_FIELD))
{
D[k] = 0;
}
else
{
for (int t = 1; t <= ellw/2; t++)
{
if (Fpx.subarrayEquals(rp, CT, engine.params.NWORDS_FIELD * (2*((Dlen - 1)*(ellw/2) + (t-1))), 2*engine.params.NWORDS_FIELD))
{
D[k] = -t;
break;
}
else
{
engine.fpx.fp2copy(CT, engine.params.NWORDS_FIELD * (2*((Dlen - 1)*(ellw/2) + (t-1))), alpha);
engine.fpx.fpnegPRIME(alpha[1]);
engine.fpx.fpcorrectionPRIME(alpha[1]);
if (Fpx.subarrayEquals(rp, alpha, 2*engine.params.NWORDS_FIELD ))
{
D[k] = t;
break;
}
}
}
}
}
}
///
//Pairing
private static final int t_points = 2;
private void Tate3_pairings(PointProjFull[] Qj, long[][][] f)
{
long[] x = new long[engine.params.NWORDS_FIELD],
y = new long[engine.params.NWORDS_FIELD],
l1 = new long[engine.params.NWORDS_FIELD],
l2 = new long[engine.params.NWORDS_FIELD],
n1 = new long[engine.params.NWORDS_FIELD],
n2 = new long[engine.params.NWORDS_FIELD],
x2 = new long[engine.params.NWORDS_FIELD],
x23 = new long[engine.params.NWORDS_FIELD],
x2p3 = new long[engine.params.NWORDS_FIELD];
long[][][] xQ2s = new long[t_points][2][engine.params.NWORDS_FIELD],
finv = new long[2*t_points][2][engine.params.NWORDS_FIELD];
long[][] one = new long[2][engine.params.NWORDS_FIELD],
t0 = new long[2][engine.params.NWORDS_FIELD],
t1 = new long[2][engine.params.NWORDS_FIELD],
t2 = new long[2][engine.params.NWORDS_FIELD],
t3 = new long[2][engine.params.NWORDS_FIELD],
t4 = new long[2][engine.params.NWORDS_FIELD],
t5 = new long[2][engine.params.NWORDS_FIELD],
g = new long[2][engine.params.NWORDS_FIELD],
h = new long[2][engine.params.NWORDS_FIELD],
tf = new long[2][engine.params.NWORDS_FIELD];
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, one[0]);
for (int j = 0; j < t_points; j++)
{
engine.fpx.fp2copy(one, f[j]);
engine.fpx.fp2copy(one, f[j+t_points]);
engine.fpx.fp2sqr_mont(Qj[j].X, xQ2s[j]);
}
for (int k = 0; k < engine.params.OBOB_EXPON - 1; k++)
{
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*k + 0), l1, 0, engine.params.NWORDS_FIELD);
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*k + 1), l2, 0, engine.params.NWORDS_FIELD);
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*k + 2), n1, 0, engine.params.NWORDS_FIELD);
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*k + 3), n2, 0, engine.params.NWORDS_FIELD);
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*k + 4), x23, 0, engine.params.NWORDS_FIELD);
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*k + 5), x2p3, 0, engine.params.NWORDS_FIELD);
for (int j = 0; j < t_points; j++)
{
engine.fpx.fpmul_mont(Qj[j].X[0], l1, t0[0]);
engine.fpx.fpmul_mont(Qj[j].X[1], l1, t0[1]);
engine.fpx.fpmul_mont(Qj[j].X[0], l2, t2[0]);
engine.fpx.fpmul_mont(Qj[j].X[1], l2, t2[1]);
engine.fpx.fpaddPRIME(xQ2s[j][0], x23, t4[0]);
engine.fpx.fpcopy(xQ2s[j][1], 0, t4[1]);
engine.fpx.fpmul_mont(Qj[j].X[0], x2p3, t5[0]);
engine.fpx.fpmul_mont(Qj[j].X[1], x2p3, t5[1]);
engine.fpx.fp2sub(t0, Qj[j].Y, t1);
engine.fpx.fpaddPRIME(t1[0], n1, t1[0]);
engine.fpx.fp2sub(t2, Qj[j].Y, t3);
engine.fpx.fpaddPRIME(t3[0], n2, t3[0]);
engine.fpx.fp2mul_mont(t1, t3, g);
engine.fpx.fp2sub(t4, t5, h);
engine.fpx.fp2_conj(h, h);
engine.fpx.fp2mul_mont(g, h, g);
engine.fpx.fp2sqr_mont(f[j], tf);
engine.fpx.fp2mul_mont(f[j], tf, f[j]);
engine.fpx.fp2mul_mont(f[j], g, f[j]);
engine.fpx.fpsubPRIME(t0[1], Qj[j].Y[0], t1[0]);
engine.fpx.fpaddPRIME(t0[0], Qj[j].Y[1], t1[1]);
engine.fpx.fpnegPRIME(t1[1]);
engine.fpx.fpaddPRIME(t1[1], n1, t1[1]);
engine.fpx.fpsubPRIME(t2[1], Qj[j].Y[0], t3[0]);
engine.fpx.fpaddPRIME(t2[0], Qj[j].Y[1], t3[1]);
engine.fpx.fpnegPRIME(t3[1]);
engine.fpx.fpaddPRIME(t3[1], n2, t3[1]);
engine.fpx.fp2mul_mont(t1, t3, g);
engine.fpx.fp2add(t4, t5, h);
engine.fpx.fp2_conj(h, h);
engine.fpx.fp2mul_mont(g, h, g);
engine.fpx.fp2sqr_mont(f[j+t_points], tf);
engine.fpx.fp2mul_mont(f[j+t_points], tf, f[j+t_points]);
engine.fpx.fp2mul_mont(f[j+t_points], g, f[j+t_points]);
}
}
for (int j = 0; j < t_points; j++)
{
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*(engine.params.OBOB_EXPON-1) + 0), x, 0, engine.params.NWORDS_FIELD);
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*(engine.params.OBOB_EXPON-1) + 1), y, 0, engine.params.NWORDS_FIELD);
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*(engine.params.OBOB_EXPON-1) + 2), l1, 0, engine.params.NWORDS_FIELD);
System.arraycopy(engine.params.T_tate3, engine.params.NWORDS_FIELD * (6*(engine.params.OBOB_EXPON-1) + 3), x2, 0, engine.params.NWORDS_FIELD);
engine.fpx.fpsubPRIME(Qj[j].X[0], x, t0[0]);
engine.fpx.fpcopy(Qj[j].X[1], 0, t0[1]);
engine.fpx.fpmul_mont(l1, t0[0], t1[0]);
engine.fpx.fpmul_mont(l1, t0[1], t1[1]);
engine.fpx.fp2sub(t1, Qj[j].Y, t2);
engine.fpx.fpaddPRIME(t2[0], y, t2[0]);
engine.fpx.fp2mul_mont(t0, t2, g);
engine.fpx.fpsubPRIME(Qj[j].X[0], x2, h[0]);
engine.fpx.fpcopy(Qj[j].X[1],0, h[1]);
engine.fpx.fpnegPRIME(h[1]);
engine.fpx.fp2mul_mont(g, h, g);
engine.fpx.fp2sqr_mont(f[j], tf);
engine.fpx.fp2mul_mont(f[j], tf, f[j]);
engine.fpx.fp2mul_mont(f[j], g, f[j]);
engine.fpx.fpaddPRIME(Qj[j].X[0], x, t0[0]);
engine.fpx.fpmul_mont(l1, t0[0], t1[0]);
engine.fpx.fpsubPRIME(Qj[j].Y[0], t1[1], t2[0]);
engine.fpx.fpaddPRIME(Qj[j].Y[1], t1[0], t2[1]);
engine.fpx.fpsubPRIME(t2[1], y, t2[1]);
engine.fpx.fp2mul_mont(t0, t2, g);
engine.fpx.fpaddPRIME(Qj[j].X[0], x2, h[0]);
engine.fpx.fp2mul_mont(g, h, g);
engine.fpx.fp2sqr_mont(f[j+t_points], tf);
engine.fpx.fp2mul_mont(f[j+t_points], tf, f[j+t_points]);
engine.fpx.fp2mul_mont(f[j+t_points], g, f[j+t_points]);
}
// Final exponentiation:
engine.fpx.mont_n_way_inv(f, 2*t_points, finv);
for (int j = 0; j < 2*t_points; j++)
{
final_exponentiation_3_torsion(f[j], finv[j], f[j]);
}
}
// The final exponentiation for pairings in the 3-torsion group. Raising the value f to the power (p^2-1)/3^eB.
private void final_exponentiation_3_torsion(long[][] f, long[][] finv, long[][] fout)
{
long[] one = new long[engine.params.NWORDS_FIELD];
long[][] temp = new long[2][engine.params.NWORDS_FIELD];
int i;
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, one);
// f = f^p
engine.fpx.fp2_conj(f, temp);
engine.fpx.fp2mul_mont(temp, finv, temp); // temp = f^(p-1)
for (i = 0; i < engine.params.OALICE_BITS; i++)
{
engine.fpx.sqr_Fp2_cycl(temp, one);
}
engine.fpx.fp2copy(temp, fout);
}
private void Tate2_pairings(PointProj P, PointProj Q, PointProjFull[] Qj, long[][][] f)
{
long[] x, y, x_, y_, l1;
long[][][] finv = new long[2*t_points][2][engine.params.NWORDS_FIELD];
long[][] x_first, y_first,
one = new long[2][engine.params.NWORDS_FIELD],
l1_first = new long[2][engine.params.NWORDS_FIELD],
t0 = new long[2][engine.params.NWORDS_FIELD],
t1 = new long[2][engine.params.NWORDS_FIELD],
g = new long[2][engine.params.NWORDS_FIELD],
h = new long[2][engine.params.NWORDS_FIELD];
int x_Offset, y_Offset, l1Offset, xOffset, yOffset;
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, one[0]);
for (int j = 0; j < t_points; j++)
{
engine.fpx.fp2copy(one, f[j]);
engine.fpx.fp2copy(one, f[j+t_points]);
}
// Pairings with P
x_first = P.X;
y_first = P.Z;
x_Offset = 0;
y_Offset = 1;
x_ = engine.params.T_tate2_firststep_P;
y_ = engine.params.T_tate2_firststep_P;
engine.fpx.fpcopy(engine.params.T_tate2_firststep_P, 2*engine.params.NWORDS_FIELD, l1_first[0]);
engine.fpx.fpcopy(engine.params.T_tate2_firststep_P, 3*engine.params.NWORDS_FIELD, l1_first[1]);
for (int j = 0; j < t_points; j++)
{
engine.fpx.fp2sub(Qj[j].X, x_first, t0);
engine.fpx.fp2sub(Qj[j].Y, y_first, t1);
engine.fpx.fp2mul_mont(l1_first, t0, t0);
engine.fpx.fp2sub(t0, t1, g);
engine.fpx.fpsubPRIME(Qj[j].X[0], engine.params.T_tate2_firststep_P, x_Offset, h[0]);
engine.fpx.fpcopy(Qj[j].X[1], 0, h[1]);
engine.fpx.fpnegPRIME(h[1]);
engine.fpx.fp2mul_mont(g, h, g);
engine.fpx.fp2sqr_mont(f[j], f[j]);
engine.fpx.fp2mul_mont(f[j], g, f[j]);
}
xOffset = 0;
yOffset = 1 * engine.params.NWORDS_FIELD;
x = x_;
y = y_;
for (int k = 0; k < engine.params.OALICE_BITS - 2; k++)
{
x_ = engine.params.T_tate2_P;
y_ = engine.params.T_tate2_P;
l1 = engine.params.T_tate2_P;
x_Offset = engine.params.NWORDS_FIELD * (3 * k + 0);
y_Offset = engine.params.NWORDS_FIELD * (3 * k + 1);
l1Offset = engine.params.NWORDS_FIELD * (3 * k + 2);
for (int j = 0; j < t_points; j++)
{
engine.fpx.fpsubPRIME(x, xOffset, Qj[j].X[0], t0[1]);
engine.fpx.fpmul_mont(l1, l1Offset, t0[1], t0[1]);
engine.fpx.fpmul_mont(l1, l1Offset, Qj[j].X[1], t0[0]);
engine.fpx.fpsubPRIME(Qj[j].Y[1], y, yOffset, t1[1]);
engine.fpx.fpsubPRIME(t0[1], t1[1], g[1]);
engine.fpx.fpsubPRIME(t0[0], Qj[j].Y[0], g[0]);
engine.fpx.fpsubPRIME(Qj[j].X[0], x_, x_Offset, h[0]);
engine.fpx.fpcopy(Qj[j].X[1], 0, h[1]);
engine.fpx.fpnegPRIME(h[1]);
engine.fpx.fp2mul_mont(g, h, g);
engine.fpx.fp2sqr_mont(f[j], f[j]);
engine.fpx.fp2mul_mont(f[j], g, f[j]);
}
x = x_;
y = y_;
yOffset = y_Offset;
xOffset = x_Offset;
}
for (int j = 0; j < t_points; j++)
{
engine.fpx.fpsubPRIME(Qj[j].X[0], x, xOffset, g[0]);
engine.fpx.fpcopy(Qj[j].X[1], 0, g[1]);
engine.fpx.fp2sqr_mont(f[j], f[j]);
engine.fpx.fp2mul_mont(f[j], g, f[j]);
}
// Pairings with Q
x_first = Q.X;
y_first = Q.Z;
x_ = engine.params.T_tate2_firststep_Q;
y_ = engine.params.T_tate2_firststep_Q;
x_Offset = 0;
y_Offset = 1 * engine.params.NWORDS_FIELD;
engine.fpx.fpcopy(engine.params.T_tate2_firststep_Q, 2 * engine.params.NWORDS_FIELD, l1_first[0]);
engine.fpx.fpcopy(engine.params.T_tate2_firststep_Q, 3 * engine.params.NWORDS_FIELD, l1_first[1]);
for (int j = 0; j < t_points; j++)
{
engine.fpx.fp2sub(Qj[j].X, x_first, t0);
engine.fpx.fp2sub(Qj[j].Y, y_first, t1);
engine.fpx.fp2mul_mont(l1_first, t0, t0);
engine.fpx.fp2sub(t0, t1, g);
engine.fpx.fpsubPRIME(Qj[j].X[0], x_, x_Offset, h[0]);
engine.fpx.fpcopy(Qj[j].X[1], 0, h[1]);
engine.fpx.fpnegPRIME(h[1]);
engine.fpx.fp2mul_mont(g, h, g);
engine.fpx.fp2sqr_mont(f[j+t_points], f[j+t_points]);
engine.fpx.fp2mul_mont(f[j+t_points], g, f[j+t_points]);
}
x = x_;
y = y_;
yOffset = y_Offset;
xOffset = x_Offset;
for (int k = 0; k < engine.params.OALICE_BITS - 2; k++)
{
x_ = engine.params.T_tate2_Q;
y_ = engine.params.T_tate2_Q;
l1 = engine.params.T_tate2_Q;
x_Offset = engine.params.NWORDS_FIELD * (3*k + 0);
y_Offset = engine.params.NWORDS_FIELD * (3*k + 1);
l1Offset = engine.params.NWORDS_FIELD * (3*k + 2);
for (int j = 0; j < t_points; j++)
{
engine.fpx.fpsubPRIME(Qj[j].X[0], x, xOffset, t0[0]);
engine.fpx.fpmul_mont(l1, l1Offset, t0[0], t0[0]);
engine.fpx.fpmul_mont(l1, l1Offset, Qj[j].X[1], t0[1]);
engine.fpx.fpsubPRIME(Qj[j].Y[0], y, yOffset, t1[0]);
engine.fpx.fpsubPRIME(t0[0], t1[0], g[0]);
engine.fpx.fpsubPRIME(t0[1], Qj[j].Y[1], g[1]);
engine.fpx.fpsubPRIME(Qj[j].X[0], x_, x_Offset, h[0]);
engine.fpx.fpcopy(Qj[j].X[1], 0, h[1]);
engine.fpx.fpnegPRIME(h[1]);
engine.fpx.fp2mul_mont(g, h, g);
engine.fpx.fp2sqr_mont(f[j+t_points], f[j+t_points]);
engine.fpx.fp2mul_mont(f[j+t_points], g, f[j+t_points]);
}
x = x_;
y = y_;
yOffset = y_Offset;
xOffset = x_Offset;
}
// Last iteration
for (int j = 0; j < t_points; j++)
{
engine.fpx.fpsubPRIME(Qj[j].X[0], x, xOffset, g[0]);
engine.fpx.fpcopy(Qj[j].X[1], 0, g[1]);
engine.fpx.fp2sqr_mont(f[j+t_points], f[j+t_points]);
engine.fpx.fp2mul_mont(f[j+t_points], g, f[j+t_points]);
}
// Final exponentiation:
engine.fpx.mont_n_way_inv(f, 2*t_points, finv);
for (int j = 0; j < 2*t_points; j++)
{
final_exponentiation_2_torsion(f[j], finv[j], f[j]);
}
}
// The final exponentiation for pairings in the 2^eA-torsion group. Raising the value f to the power (p^2-1)/2^eA.
private void final_exponentiation_2_torsion(long[][] f, long[][] finv, long[][] fout)
{
long[] one = new long[engine.params.NWORDS_FIELD];
long[][] temp = new long[2][engine.params.NWORDS_FIELD];
int i;
engine.fpx.fpcopy(engine.params.Montgomery_one, 0, one);
// f = f^p
engine.fpx.fp2_conj(f, temp);
engine.fpx.fp2mul_mont(temp, finv, temp); // temp = f^(p-1)
for (i = 0; i < engine.params.OBOB_EXPON; i++)
{
engine.fpx.cube_Fp2_cycl(temp, one);
}
engine.fpx.fp2copy(temp, fout);
}
}