org.bouncycastle.pqc.legacy.crypto.sike.Fpx Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-debug-jdk15to18 Show documentation
Show all versions of bcprov-debug-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.legacy.crypto.sike;
import org.bouncycastle.util.Pack;
class Fpx
{
private SIKEEngine engine;
Fpx(SIKEEngine engine)
{
this.engine = engine;
}
// Multiprecision left shift by one.
private void mp_shiftl1(long[] x, int nwords)
{
int i;
for (i = nwords-1; i > 0; i--)
{
//SHIFTL
x[i] = (x[i] << 1) ^ (x[i-1] >>> (Internal.RADIX - 1));
}
x[0] <<= 1;
}
// Cyclotomic squaring on elements of norm 1, using a^(p+1) = 1.
protected void sqr_Fp2_cycl(long[][] a, long[] one)
{
long[] t0 = new long[engine.params.NWORDS_FIELD];
fpaddPRIME(a[0], a[1], t0); // t0 = a0 + a1
fpsqr_mont(t0, t0); // t0 = t0^2
fpsubPRIME(t0, one, a[1]); // a1 = t0 - 1
fpsqr_mont(a[0], t0); // t0 = a0^2
fpaddPRIME(t0, t0, t0); // t0 = t0 + t0
fpsubPRIME(t0, one, a[0]); // a0 = t0 - 1
}
// n-way simultaneous inversion using Montgomery's trick.
// SECURITY NOTE: This function does not run in constant time.
// Also, vec and out CANNOT be the same variable!
protected void mont_n_way_inv(long[][][] vec, int n, long[][][] out)
{
long[][] t1 = new long[2][engine.params.NWORDS_FIELD];
int i;
fp2copy(vec[0], out[0]); // out[0] = vec[0]
for (i = 1; i < n; i++)
{
fp2mul_mont(out[i-1], vec[i], out[i]); // out[i] = out[i-1]*vec[i]
}
fp2copy(out[n-1], t1); // t1 = 1/out[n-1]
fp2inv_mont_bingcd(t1);
for (i = n-1; i >= 1; i--)
{
fp2mul_mont(out[i-1], t1, out[i]); // out[i] = t1*out[i-1]
fp2mul_mont(t1, vec[i], t1); // t1 = t1*vec[i]
}
fp2copy(t1, out[0]); // out[0] = t1
}
// Copy a field element, c = a.
protected void fpcopy(long[] a, int aOffset, long[] c)
{
int i;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
c[i] = a[i + aOffset];
}
}
// GF(p^2) addition without correction, c = a+b in GF(p^2).
protected void mp2_add(long[][] a, long[][] b, long[][] c)
{
mp_add(a[0], b[0], c[0], engine.params.NWORDS_FIELD);
mp_add(a[1], b[1], c[1], engine.params.NWORDS_FIELD);
}
// Modular correction, a = a in GF(p^2).
protected void fp2correction(long[][] a)
{
fpcorrectionPRIME(a[0]);
fpcorrectionPRIME(a[1]);
}
// Multiprecision addition, c = a+b, where lng(a) = lng(b) = nwords. Returns the carry bit.
protected int mp_add(long[] a, long[] b, long[] c, int nwords)
{
int i, carry = 0;
for (i = 0; i < nwords; i++)
{
//ADDC
long tempReg = a[i] + carry;
c[i] = b[i] + tempReg;
carry = (is_digit_lessthan_ct(tempReg, carry) | is_digit_lessthan_ct(c[i], tempReg));
}
return carry;
}
// Multiprecision addition, c = a+b, where lng(a) = lng(b) = nwords. Returns the carry bit.
private int mp_add(long[] a, int aOffset, long[] b, long[] c, int cOffset, int nwords)
{
int i, carry = 0;
for (i = 0; i < nwords; i++)
{
//ADDC
long tempReg = a[i + aOffset] + carry;
c[i + cOffset] = b[i] + tempReg;
carry = (is_digit_lessthan_ct(tempReg, carry) | is_digit_lessthan_ct(c[i + cOffset], tempReg));
}
return carry;
}
// Multiprecision addition, c = a+b, where lng(a) = lng(b) = nwords. Returns the carry bit.
private int mp_add(long[] a, int aOffset, long[] b, int bOffset, long[] c, int cOffset, int nwords)
{
int i, carry = 0;
for (i = 0; i < nwords; i++)
{
//ADDC
long tempReg = a[i + aOffset] + carry;
c[i + cOffset] = b[i + bOffset] + tempReg;
carry = (is_digit_lessthan_ct(tempReg, carry) | is_digit_lessthan_ct(c[i + cOffset], tempReg));
}
return carry;
}
// Is x < y?
private int is_digit_lessthan_ct(long x, long y)
{
return (int) ((x ^ ((x ^ y) | ((x - y) ^ y))) >>> (Internal.RADIX -1));
}
// Is x != 0?
private int is_digit_nonzero_ct(long x)
{
return (int)((x | (0-x)) >>> (Internal.RADIX -1));
}
// Is x = 0?
private int is_digit_zero_ct(long x)
{
return (1 ^ is_digit_nonzero_ct(x));
}
void fp2neg(long[][] a)
{ // GF(p^2) negation, a = -a in GF(p^2).
fpnegPRIME(a[0]);
fpnegPRIME(a[1]);
}
// Is x = 0? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise.
// SECURITY NOTE: This function does not run in constant-time.
protected boolean is_felm_zero(long[] x)
{
int i;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
if (x[i] != 0)
return false;
}
return true;
}
// Is x < y? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise.
// SECURITY NOTE: This function does not run in constant-time.
private boolean is_felm_lt(long[] x, long[] y)
{
for (int i = engine.params.NWORDS_FIELD-1; i >= 0; i--)
{
if (x[i] + Long.MIN_VALUE < y[i] + Long.MIN_VALUE)
{
return true;
}
else if (x[i] + Long.MIN_VALUE > y[i] + Long.MIN_VALUE )
{
return false;
}
}
return false;
}
// Is x even? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise.
private static boolean is_felm_even(long[] x)
{
return (x[0] & 1L) == 0L;
}
// Test if a is a square in GF(p^2) and return 1 if true, 0 otherwise
// If a is a quadratic residue, s will be assigned with a partially computed square root of a
protected boolean is_sqr_fp2(long[][] a, long[] s)
{
int i;
long[] a0 = new long[engine.params.NWORDS_FIELD],
a1 = new long[engine.params.NWORDS_FIELD],
z = new long[engine.params.NWORDS_FIELD],
temp = new long[engine.params.NWORDS_FIELD];
fpsqr_mont(a[0],a0);
fpsqr_mont(a[1],a1);
fpaddPRIME(a0,a1,z);
fpcopy(z, 0,s);
for (i = 0; i < engine.params.OALICE_BITS - 2; i++)
{
fpsqr_mont(s, s);
}
for (i = 0; i < engine.params.OBOB_EXPON; i++)
{
fpsqr_mont(s, temp);
fpmul_mont(s, temp, s);
}
fpsqr_mont(s,temp); // s = z^((p+1)/4)
fpcorrectionPRIME(temp);
fpcorrectionPRIME(z);
if (!subarrayEquals(temp, z, engine.params.NWORDS_FIELD)) // s^2 !=? z
{
return false;
}
return true;
}
// Partial Montgomery inversion via the binary GCD algorithm.
private void fpinv_mont_bingcd_partial(long[] a, long[] x1, int[] k)
{
long[] u = new long[engine.params.NWORDS_FIELD],
v = new long[engine.params.NWORDS_FIELD],
x2 = new long[engine.params.NWORDS_FIELD];
int cwords; // Number of words necessary for x1, x2
fpcopy(a, 0, u);
fpcopy(engine.params.PRIME, 0, v);
fpzero(x1); x1[0] = 1;
fpzero(x2);
k[0] = 0;
while (!is_felm_zero(v))
{
cwords = ((k[0] + 1) / Internal.RADIX) + 1;
if ((cwords < engine.params.NWORDS_FIELD))
{
if (is_felm_even(v))
{
mp_shiftr1(v);
mp_shiftl1(x1, cwords);
}
else if (is_felm_even(u))
{
mp_shiftr1(u);
mp_shiftl1(x2, cwords);
}
else if (!is_felm_lt(v, u))
{
mp_sub(v, u, v, engine.params.NWORDS_FIELD);
mp_shiftr1(v);
mp_add(x1, x2, x2, cwords);
mp_shiftl1(x1, cwords);
}
else
{
mp_sub(u, v, u, engine.params.NWORDS_FIELD);
mp_shiftr1(u);
mp_add(x1, x2, x1, cwords);
mp_shiftl1(x2, cwords);
}
}
else
{
if (is_felm_even(v))
{
mp_shiftr1(v);
mp_shiftl1(x1, engine.params.NWORDS_FIELD);
}
else if (is_felm_even(u))
{
mp_shiftr1(u);
mp_shiftl1(x2, engine.params.NWORDS_FIELD);
}
else if (!is_felm_lt(v, u))
{
mp_sub(v, u, v, engine.params.NWORDS_FIELD);
mp_shiftr1(v);
mp_add(x1, x2, x2, engine.params.NWORDS_FIELD);
mp_shiftl1(x1, engine.params.NWORDS_FIELD);
}
else
{
mp_sub(u, v, u, engine.params.NWORDS_FIELD);
mp_shiftr1(u);
mp_add(x1, x2, x1, engine.params.NWORDS_FIELD);
mp_shiftl1(x2, engine.params.NWORDS_FIELD);
}
}
k[0] += 1;
}
if (is_felm_lt(engine.params.PRIME, x1))
{
mp_sub(x1, engine.params.PRIME, x1, engine.params.NWORDS_FIELD);
}
}
// Set up the value 2^mark.
private void power2_setup(long[] x, int mark, int nwords)
{
int i;
for (i = 0; i < nwords; i++) x[i] = 0;
i = 0;
while (mark >= 0)
{
if (mark < Internal.RADIX)
{
x[i] = (long)1 << mark;
}
mark -= Internal.RADIX;
i += 1;
}
}
// Field inversion via the binary GCD using Montgomery arithmetic, a = a^-1*r' mod p.
// SECURITY NOTE: This function does not run in constant-time and is therefore only suitable for
// operations not involving any secret data.
private void fpinv_mont_bingcd(long[] a)
{
long[] x = new long[engine.params.NWORDS_FIELD],
t = new long[engine.params.NWORDS_FIELD];
int[] k = new int[1];
if (is_felm_zero(a))
return;
fpinv_mont_bingcd_partial(a, x, k);
if (k[0] <= engine.params.MAXBITS_FIELD)
{
fpmul_mont(x, engine.params.Montgomery_R2, x);
k[0] += engine.params.MAXBITS_FIELD;
}
fpmul_mont(x, engine.params.Montgomery_R2, x);
power2_setup(t, 2*engine.params.MAXBITS_FIELD - k[0], engine.params.NWORDS_FIELD);
fpmul_mont(x, t, a);
}
// GF(p^2) inversion using Montgomery arithmetic, a = (a0-i*a1)/(a0^2+a1^2)
// This uses the binary GCD for inversion in fp and is NOT constant time!!!
protected void fp2inv_mont_bingcd(long[][] a)
{
long[][] t1 = new long[2][engine.params.NWORDS_FIELD];
fpsqr_mont(a[0], t1[0]); // t10 = a0^2
fpsqr_mont(a[1], t1[1]); // t11 = a1^2
fpaddPRIME(t1[0], t1[1], t1[0]); // t10 = a0^2+a1^2
fpinv_mont_bingcd(t1[0]); // t10 = (a0^2+a1^2)^-1
fpnegPRIME(a[1]); // a = a0-i*a1
fpmul_mont(a[0], t1[0], a[0]);
fpmul_mont(a[1], t1[0], a[1]); // a = (a0-i*a1)*(a0^2+a1^2)^-1
}
// GF(p^2) division by two, c = a/2 in GF(p^2).
protected void fp2div2(long[][] a, long[][] c)
{
fpdiv2_PRIME(a[0], c[0]);
fpdiv2_PRIME(a[1], c[1]);
}
// Modular division by two, c = a/2 mod PRIME.
// Input : a in [0, 2*PRIME-1]
// Output: c in [0, 2*PRIME-1]
private void fpdiv2_PRIME(long[] a, long[] c)
{
int i, carry = 0;
long mask;
mask = 0 - (a[0] & 1); // If a is odd compute a+PRIME
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = a[i] + carry;
c[i] = (engine.params.PRIME[i] & mask) + tempReg;
carry = (is_digit_lessthan_ct(tempReg, carry) | is_digit_lessthan_ct(c[i], tempReg));
}
mp_shiftr1(c);
}
// Multiprecision subtraction with correction with 2*p, c = a-b+2p.
private void mp_subPRIME_p2(long [] a, long[] b, long[] c)
{
int i, borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//SUBC
long tempReg = a[i] - b[i];
int borrowReg = (is_digit_lessthan_ct(a[i], b[i]) | (borrow & is_digit_zero_ct(tempReg)));
c[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = c[i] + borrow;
c[i] = engine.params.PRIMEx2[i] + tempReg;
borrow = (is_digit_lessthan_ct(tempReg, borrow) | is_digit_lessthan_ct(c[i], tempReg));
}
}
// Multiprecision subtraction with correction with 4*p, c = a-b+4p.
private void mp_subPRIME_p4(long[] a, long[] b, long[] c)
{
int i, borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//SUBC
long tempReg = a[i] - b[i];
int borrowReg = (is_digit_lessthan_ct(a[i], b[i]) | (borrow & is_digit_zero_ct(tempReg)));
c[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = c[i] + borrow;
c[i] = engine.params.PRIMEx4[i] + tempReg;
borrow = (is_digit_lessthan_ct(tempReg, borrow) | is_digit_lessthan_ct(c[i], tempReg));
}
}
// Digit multiplication, digit * digit -> 2-digit result
private void digit_x_digit(long a, long b, long[] c)
{
long al, ah, bl, bh, temp;
long albl, albh, ahbl, ahbh, res1, res2, res3, carry;
long mask_low = ((long)(-1)) >>> (8*4), mask_high = ((-1L)) << (8*4);
al = a & mask_low; // Low part
ah = a >>> (8 * 4); // High part
bl = b & mask_low;
bh = b >>> (8 * 4);
albl = al*bl;
albh = al*bh;
ahbl = ah*bl;
ahbh = ah*bh;
c[0] = albl & mask_low; // C00
res1 = albl >>> (8 * 4);
res2 = ahbl & mask_low;
res3 = albh & mask_low;
temp = res1 + res2 + res3;
carry = temp >>> (8 * 4);
c[0] ^= temp << (8 * 4); // C01
res1 = ahbl >>> (8 * 4);
res2 = albh >>> (8 * 4);
res3 = ahbh & mask_low;
temp = res1 + res2 + res3 + carry;
c[1] = temp & mask_low; // C10
carry = temp & mask_high;
c[1] ^= (ahbh & mask_high) + carry; // C11
}
// Efficient Montgomery reduction using comba and exploiting the special form of the prime PRIME.
// mc = ma*R^-1 mod PRIMEx2, where R = 2^448.
// If ma < 2^448*PRIME, the output mc is in the range [0, 2*PRIME-1].
// ma is assumed to be in Montgomery representation.
private void rdc_mont(long[] ma, long[] mc)
{
int i, j, carry, count = engine.params.PRIME_ZERO_WORDS;
long t = 0, u = 0, v = 0, temp;
long[] UV = new long[2];
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
mc[i] = 0;
}
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
for (j = 0; j < i; j++)
{
if (j < (i-engine.params.PRIME_ZERO_WORDS+1))
{
//MUL
digit_x_digit(mc[j], engine.params.PRIMEp1[i - j], UV);
//ADDC
temp = UV[0];
v += temp;
temp = UV[1] + is_digit_lessthan_ct(v, temp); // No overflow possible; high part of product < Long.MAX_VALUE
//ADDC
u += temp;
t += is_digit_lessthan_ct(u, temp);
}
}
//ADDC
long tempReg = ma[i];
v += tempReg;
carry = is_digit_lessthan_ct(v, tempReg);
//ADDC
u += carry;
carry &= is_digit_zero_ct(u);
t += carry;
mc[i] = v;
v = u;
u = t;
t = 0;
}
for (i = engine.params.NWORDS_FIELD; i < 2*engine.params.NWORDS_FIELD-1; i++)
{
if (count > 0)
{
count -= 1;
}
for (j = i-engine.params.NWORDS_FIELD+1; j < engine.params.NWORDS_FIELD; j++)
{
if (j < (engine.params.NWORDS_FIELD-count))
{
//MUL
digit_x_digit(mc[j], engine.params.PRIMEp1[i - j], UV);
//ADDC
temp = UV[0];
v += temp;
temp = UV[1] + is_digit_lessthan_ct(v, temp); // No overflow possible; high part of product < Long.MAX_VALUE
//ADDC
u += temp;
t += is_digit_lessthan_ct(u, temp);
}
}
//ADDC
long tempReg = ma[i];
v += tempReg;
carry = is_digit_lessthan_ct(v, tempReg);
//ADDC
u += carry;
carry &= is_digit_zero_ct(u);
t += carry;
mc[i - engine.params.NWORDS_FIELD] = v;
v = u;
u = t;
t = 0;
}
//ADDC
long tempReg = ma[2*engine.params.NWORDS_FIELD-1];
v += tempReg;
carry = is_digit_lessthan_ct(v, tempReg);
// assert carry == 0;
mc[engine.params.NWORDS_FIELD-1] = v;
// assert u == 0;
// assert t == 0;
}
protected static boolean subarrayEquals(long[] a, long[] b, int length)
{
for (int i = 0; i < length; i++)
{
if(a[i] != b[i])
return false;
}
return true;
}
protected static boolean subarrayEquals(long[][] a, long[][] b, int length)
{
int nwords_feild = b[0].length;
for (int i = 0; i < length; i++)
{
if(a[i/nwords_feild][i%nwords_feild] != b[i/nwords_feild][i%nwords_feild])
return false;
}
return true;
}
protected static boolean subarrayEquals(long[][] a, long[][] b, int bOffset, int length)
{
int nwords_feild = b[0].length;
for (int i = 0; i < length; i++)
{
if(a[i/nwords_feild][i%nwords_feild] != b[(i + bOffset)/nwords_feild][(i+bOffset)%nwords_feild])
return false;
}
return true;
}
protected static boolean subarrayEquals(long[][] a, long[] b, int bOffset, int length)
{
int nwords_field = a[0].length;
for (int i = 0; i < length; i++)
{
if(a[i/nwords_field][i%nwords_field] != b[(i + bOffset)])
return false; //8316 -> 425529A64ABCAC1F
}
return true;
}
// Computes square roots of elements in (Fp2)^2 using Hamburg's trick.
void sqrt_Fp2(long[][] u, long[][] y)
{
long[] t0 = new long[engine.params.NWORDS_FIELD],
t1 = new long[engine.params.NWORDS_FIELD],
t2 = new long[engine.params.NWORDS_FIELD],
t3 = new long[engine.params.NWORDS_FIELD];
int i;
fpsqr_mont(u[0], t0); // t0 = a^2
fpsqr_mont(u[1], t1); // t1 = b^2
fpaddPRIME(t0, t1, t0); // t0 = t0+t1
fpcopy(t0, 0, t1);
for (i = 0; i < engine.params.OALICE_BITS - 2; i++)
{ // t = t3^((p+1)/4)
fpsqr_mont(t1, t1);
}
for (i = 0; i < engine.params.OBOB_EXPON; i++)
{
fpsqr_mont(t1, t0);
fpmul_mont(t1, t0, t1);
}
fpaddPRIME(u[0], t1, t0); // t0 = a+t1
fpdiv2_PRIME(t0, t0); // t0 = t0/2
fpcopy(t0, 0, t2);
fpinv_chain_mont(t2); // t2 = t0^((p-3)/4)
fpmul_mont(t0, t2, t1); // t1 = t2*t0
fpmul_mont(t2, u[1], t2); // t2 = t2*b
fpdiv2_PRIME(t2, t2); // t2 = t2/2
fpsqr_mont(t1, t3); // t3 = t1^2
fpcorrectionPRIME(t0);
fpcorrectionPRIME(t3);
if (subarrayEquals(t0, t3, engine.params.NWORDS_FIELD))
{
fpcopy(t1, 0, y[0]);
fpcopy(t2, 0, y[1]);
}
else
{
fpnegPRIME(t1);
fpcopy(t2, 0, y[0]);
fpcopy(t1, 0, y[1]);
}
}
// GF(p^2) squaring using Montgomery arithmetic, c = a^2 in GF(p^2).
// Inputs: a = a0+a1*i, where a0, a1 are in [0, 2*p-1]
// Output: c = c0+c1*i, where c0, c1 are in [0, 2*p-1]
protected void fp2sqr_mont(long[][] a, long[][] c)
{
long[] t1 = new long[engine.params.NWORDS_FIELD],
t2 = new long[engine.params.NWORDS_FIELD],
t3 = new long[engine.params.NWORDS_FIELD];
mp_add(a[0], a[1], t1, engine.params.NWORDS_FIELD); // t1 = a0+a1
mp_subPRIME_p4(a[0], a[1], t2); // t2 = a0-a1
mp_add(a[0], a[0], t3, engine.params.NWORDS_FIELD); // t3 = 2a0
fpmul_mont(t1, t2, c[0]); // c0 = (a0+a1)(a0-a1)
fpmul_mont(t3, a[1], c[1]); // c1 = 2a0*a1
}
// Modular addition, c = a+b mod PRIME.
// Inputs: a, b in [0, 2*PRIME-1]
// Output: c in [0, 2*PRIME-1]
protected void fpaddPRIME(long[] a, long[] b, long[] c)
{
int i, carry = 0;
long mask;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = a[i] + carry;
c[i] = b[i] + tempReg;
carry = (is_digit_lessthan_ct(tempReg, carry) | is_digit_lessthan_ct(c[i], tempReg));
}
carry = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//SUBC
long tempReg = c[i] - engine.params.PRIMEx2[i];
int borrowReg = (is_digit_lessthan_ct(c[i], engine.params.PRIMEx2[i]) | (carry & is_digit_zero_ct(tempReg)));
c[i] = tempReg - (long)(carry);
carry = borrowReg;
}
mask = 0 - carry;
carry = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = c[i] + carry;
c[i] = (engine.params.PRIMEx2[i] & mask) + tempReg;
carry = (is_digit_lessthan_ct(tempReg, carry) | is_digit_lessthan_ct(c[i], tempReg));
}
}
// Cyclotomic cubing on elements of norm 1, using a^(p+1) = 1.
protected void cube_Fp2_cycl(long[][] a, long[] one)
{
long[] t0 = new long[engine.params.NWORDS_FIELD];
fpaddPRIME(a[0], a[0], t0); // t0 = a0 + a0
fpsqr_mont(t0, t0); // t0 = t0^2
fpsubPRIME(t0, one, t0); // t0 = t0 - 1
fpmul_mont(a[1], t0, a[1]); // a1 = t0*a1
fpsubPRIME(t0, one, t0);
fpsubPRIME(t0, one, t0); // t0 = t0 - 2
fpmul_mont(a[0], t0, a[0]); // a0 = t0*a0
}
// Modular subtraction, c = a-b mod PRIME.
// Inputs: a, b in [0, 2*PRIME-1]
// Output: c in [0, 2*PRIME-1]
protected void fpsubPRIME(long[] a, long[] b, int bOffset, long[] c)
{
int i, borrow = 0;
long mask;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//SUBC
long tempReg = a[i] - b[i + bOffset];
int borrowReg = (is_digit_lessthan_ct(a[i], b[i + bOffset]) | (borrow & is_digit_zero_ct(tempReg)));
c[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
mask = 0 - borrow;
borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = c[i] + borrow;
c[i] = (engine.params.PRIMEx2[i] & mask) + tempReg;
borrow = (is_digit_lessthan_ct(tempReg, borrow) | is_digit_lessthan_ct(c[i], tempReg));
}
}
protected void fpsubPRIME(long[] a, int aOffset, long[] b, long[] c)
{
int i, borrow = 0;
long mask;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//SUBC
long tempReg = a[i + aOffset] - b[i];
int borrowReg = (is_digit_lessthan_ct(a[i + aOffset], b[i]) | (borrow & is_digit_zero_ct(tempReg)));
c[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
mask = 0 - borrow;
borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = c[i] + borrow;
c[i] = (engine.params.PRIMEx2[i] & mask) + tempReg;
borrow = (is_digit_lessthan_ct(tempReg, borrow) | is_digit_lessthan_ct(c[i], tempReg));
}
}
protected void fpsubPRIME(long[] a, long[] b, long[] c)
{
int i, borrow = 0;
long mask;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//SUBC
long tempReg = a[i] - b[i];
int borrowReg = (is_digit_lessthan_ct(a[i], b[i]) | (borrow & is_digit_zero_ct(tempReg)));
c[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
mask = 0 - borrow;
borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = c[i] + borrow;
c[i] = (engine.params.PRIMEx2[i] & mask) + tempReg;
borrow = (is_digit_lessthan_ct(tempReg, borrow) | is_digit_lessthan_ct(c[i], tempReg));
}
}
// Modular negation, a = -a mod PRIME.
// Input/output: a in [0, 2*PRIME-1]
protected void fpnegPRIME(long[] a)
{
int i, borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//SUBC
long tempReg = engine.params.PRIMEx2[i] - a[i];
int borrowReg = (is_digit_lessthan_ct(engine.params.PRIMEx2[i], a[i]) | (borrow & is_digit_zero_ct(tempReg)));
a[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
}
// Conversion of a GF(p^2) element from Montgomery representation to standard representation,
// c_i = ma_i*R^(-1) = a_i in GF(p^2).
protected void from_fp2mont(long[][] ma, long[][] c)
{
from_mont(ma[0], c[0]);
from_mont(ma[1], c[1]);
}
// Conversion of GF(p^2) element from Montgomery to standard representation, and encoding by removing leading 0 bytes
protected void fp2_encode(long[][] x, byte[] enc, int encOffset)
{
long[][] t = new long[2][engine.params.NWORDS_FIELD];
from_fp2mont(x, t);
encode_to_bytes(t[0], enc, encOffset,engine.params.FP2_ENCODED_BYTES / 2);
encode_to_bytes(t[1], enc, encOffset + engine.params.FP2_ENCODED_BYTES / 2, engine.params.FP2_ENCODED_BYTES / 2);
}
// Parse byte sequence back into GF(p^2) element, and conversion to Montgomery representation
protected void fp2_decode(byte[] x, long[][] dec, int xOffset)
{
decode_to_digits(x, xOffset, dec[0], engine.params.FP2_ENCODED_BYTES / 2, engine.params.NWORDS_FIELD);
decode_to_digits(x,xOffset + (engine.params.FP2_ENCODED_BYTES/2), dec[1], engine.params.FP2_ENCODED_BYTES / 2, engine.params.NWORDS_FIELD);
to_fp2mont(dec, dec);
}
// Conversion of elements in Z_r to Montgomery representation, where the order r is up to NBITS_ORDER bits.
protected void to_Montgomery_mod_order(long[] a, long[] mc, long[] order, long[] Montgomery_rprime, long[] Montgomery_Rprime)
{
Montgomery_multiply_mod_order(a, Montgomery_Rprime, mc, order, Montgomery_rprime);
}
// Montgomery multiplication modulo the group order, mc = ma*mb*r' mod order, where ma,mb,mc in [0, order-1].
// ma, mb and mc are assumed to be in Montgomery representation.
// The Montgomery constant r' = -r^(-1) mod 2^(log_2(r)) is the value "Montgomery_rprime", where r is the order.
// Assume log_2(r) is a multiple of RADIX bits
protected void Montgomery_multiply_mod_order(long[] ma, long[] mb, long[] mc, long[] order, long[] Montgomery_rprime)
{
int i, cout = 0, bout = 0;
long mask;
long[] P = new long[2*engine.params.NWORDS_ORDER],
Q = new long[2*engine.params.NWORDS_ORDER],
temp = new long[2*engine.params.NWORDS_ORDER];
multiply(ma, mb, P, engine.params.NWORDS_ORDER); // P = ma * mb
multiply(P, Montgomery_rprime, Q, engine.params.NWORDS_ORDER); // Q = P * r' mod 2^(log_2(r))
multiply(Q, order, temp, engine.params.NWORDS_ORDER); // temp = Q * r
cout = mp_add(P, temp, temp, 2*engine.params.NWORDS_ORDER); // (cout, temp) = P + Q * r
for (i = 0; i < engine.params.NWORDS_ORDER; i++) // (cout, mc) = (P + Q * r)/2^(log_2(r))
{
mc[i] = temp[engine.params.NWORDS_ORDER+i];
}
// Final, constant-time subtraction
bout = mp_sub(mc, order, mc, engine.params.NWORDS_ORDER); // (cout, mc) = (cout, mc) - r
mask = cout - bout; // if (cout, mc) >= 0 then mask = 0x00..0, else if (cout, mc) < 0 then mask = 0xFF..F
for (i = 0; i < engine.params.NWORDS_ORDER; i++)
{ // temp = mask & r
temp[i] = (order[i] & mask);
}
mp_add(mc, temp, mc, engine.params.NWORDS_ORDER); // mc = mc + (mask & r)
}
// Inversion of an odd integer modulo an even integer of the form 2^m.
// Algorithm 3: Explicit Quadratic Modular inverse modulo 2^m from Dumas'12: https://arxiv.org/pdf/1209.6626.pdf
// If the input is invalid (even), the function outputs c = a.
protected void inv_mod_orderA(long[] a, long[] c)
{
int i, f, s = 0;
long[] am1 = new long[engine.params.NWORDS_ORDER],
tmp1 = new long[engine.params.NWORDS_ORDER],
tmp2 = new long[2*engine.params.NWORDS_ORDER],
one = new long[engine.params.NWORDS_ORDER],
order = new long[engine.params.NWORDS_ORDER];
long mask = ((-1L) >>> (engine.params.NBITS_ORDER - engine.params.OALICE_BITS));
order[engine.params.NWORDS_ORDER-1] = (1L << (64 - (engine.params.NBITS_ORDER - engine.params.OALICE_BITS))); // Load most significant digit of Alice's order
one[0] = 1;
mp_sub(a, one, am1, engine.params.NWORDS_ORDER); // am1 = a-1
if (((a[0] & 1) == 0) || (is_zero(am1, engine.params.NWORDS_ORDER)))
{ // Check if the input is even or one
copy_words(a, c, engine.params.NWORDS_ORDER);
c[engine.params.NWORDS_ORDER-1] &= mask; // mod 2^m
}
else
{
mp_sub(order, am1, c, engine.params.NWORDS_ORDER);
mp_add(c, one, c, engine.params.NWORDS_ORDER); // c = 2^m - a + 2
copy_words(am1, tmp1, engine.params.NWORDS_ORDER);
while ((tmp1[0] & 1L) == 0)
{
s += 1;
mp_shiftr1(tmp1, engine.params.NWORDS_ORDER);
}
f = engine.params.OALICE_BITS / s;
for (i = 1; i < f; i <<= 1)
{
multiply(am1, am1, tmp2, engine.params.NWORDS_ORDER); // tmp2 = am1^2
copy_words(tmp2, am1, engine.params.NWORDS_ORDER);
am1[engine.params.NWORDS_ORDER-1] &= mask; // am1 = tmp2 mod 2^m
mp_add(am1, one, tmp1, engine.params.NWORDS_ORDER); // tmp1 = am1 + 1
tmp1[engine.params.NWORDS_ORDER-1] &= mask; // mod 2^m
multiply(c, tmp1, tmp2, engine.params.NWORDS_ORDER); // c = c*tmp1
copy_words(tmp2, c, engine.params.NWORDS_ORDER);
c[engine.params.NWORDS_ORDER-1] &= mask; // mod 2^m
}
}
}
// Multiprecision comba multiply, c = a*b, where lng(a) = lng(b) = nwords.
// NOTE: a and c CANNOT be the same variable!
protected void multiply(long[] a, long[] b, long[] c, int nwords)
{
int i, j;
long t = 0, u = 0, v = 0, temp;
long[] UV = new long[2];
for (i = 0; i < nwords; i++)
{
for (j = 0; j <= i; j++)
{
//MUL
digit_x_digit(a[j], b[i - j], UV);
//ADDC
temp = UV[0];
v += temp;
temp = UV[1] + is_digit_lessthan_ct(v, temp); // No overflow possible; high part of product < Long.MAX_VALUE
//ADDC
u += temp;
t += is_digit_lessthan_ct(u, temp);
}
c[i] = v;
v = u;
u = t;
t = 0;
}
for (i = nwords; i < 2*nwords-1; i++)
{
for (j = i-nwords+1; j < nwords; j++)
{
//MUL
digit_x_digit(a[j], b[i - j], UV);
//ADDC
temp = UV[0];
v += temp;
temp = UV[1] + is_digit_lessthan_ct(v, temp); // No overflow possible; high part of product < Long.MAX_VALUE
//ADDC
u += temp;
t += is_digit_lessthan_ct(u, temp);
}
c[i] = v;
v = u;
u = t;
t = 0;
}
c[2 * nwords - 1] = v;
// assert u == 0;
// assert t == 0;
}
// Is x = 0? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise
// SECURITY NOTE: This function does not run in constant time.
private boolean is_zero_mod_order(long[] x)
{
int i;
for (i = 0; i < engine.params.NWORDS_ORDER; i++)
{
if (x[i] != 0)
{
return false;
}
}
return true;
}
// Is x even? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise.
private boolean is_even_mod_order(long[] x)
{
return ((x[0] & 1) ^ 1) == 1;
}
// Is x < y? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise.
// SECURITY NOTE: This function does not run in constant time.
private boolean is_lt_mod_order(long[] x, long[] y)
{
int i;
for (i = engine.params.NWORDS_ORDER-1; i >= 0; i--)
{
if (x[i] + Long.MIN_VALUE < y[i] + Long.MIN_VALUE )
{
return true;
}
else if (x[i] + Long.MIN_VALUE > y[i] + Long.MIN_VALUE )
{
return false;
}
}
return false;
}
// Check if multiprecision element is zero.
// SECURITY NOTE: This function does not run in constant time.
private boolean is_zero(long[] a, int nwords)
{
for (int i = 0; i < nwords; i++)
{
if (a[i] != 0)
{
return false;
}
}
return true;
}
// Partial Montgomery inversion modulo order.
private void Montgomery_inversion_mod_order_bingcd_partial(long[] a, long[] x1, int[] k, long[] order)
{
long[] u = new long[engine.params.NWORDS_ORDER],
v = new long[engine.params.NWORDS_ORDER],
x2 = new long[engine.params.NWORDS_ORDER];
int cwords; // number of words necessary for x1, x2
copy_words(a, u, engine.params.NWORDS_ORDER);
copy_words(order, v, engine.params.NWORDS_ORDER);
copy_words(x2, x1, engine.params.NWORDS_ORDER);
x1[0] = 1;
k[0] = 0;
while (!is_zero_mod_order(v))
{
cwords = ((k[0] + 1) / Internal.RADIX) + 1;
if ((cwords < engine.params.NWORDS_ORDER))
{
if (is_even_mod_order(v))
{
mp_shiftr1(v, engine.params.NWORDS_ORDER);
mp_shiftl1(x1, cwords);
}
else if (is_even_mod_order(u))
{
mp_shiftr1(u, engine.params.NWORDS_ORDER);
mp_shiftl1(x2, cwords);
}
else if (!is_lt_mod_order(v, u))
{
mp_sub(v, u, v, engine.params.NWORDS_ORDER);
mp_shiftr1(v, engine.params.NWORDS_ORDER);
mp_add(x1, x2, x2, cwords);
mp_shiftl1(x1, cwords);
}
else
{
mp_sub(u, v, u, engine.params.NWORDS_ORDER);
mp_shiftr1(u, engine.params.NWORDS_ORDER);
mp_add(x1, x2, x1, cwords);
mp_shiftl1(x2, cwords);
}
}
else
{
if (is_even_mod_order(v))
{
mp_shiftr1(v, engine.params.NWORDS_ORDER);
mp_shiftl1(x1, engine.params.NWORDS_ORDER);
}
else if (is_even_mod_order(u))
{
mp_shiftr1(u, engine.params.NWORDS_ORDER);
mp_shiftl1(x2, engine.params.NWORDS_ORDER);
}
else if (!is_lt_mod_order(v, u))
{
mp_sub(v, u, v, engine.params.NWORDS_ORDER);
mp_shiftr1(v, engine.params.NWORDS_ORDER);
mp_add(x1, x2, x2, engine.params.NWORDS_ORDER);
mp_shiftl1(x1, engine.params.NWORDS_ORDER);
}
else
{
mp_sub(u, v, u, engine.params.NWORDS_ORDER);
mp_shiftr1(u, engine.params.NWORDS_ORDER);
mp_add(x1, x2, x1, engine.params.NWORDS_ORDER);
mp_shiftl1(x2, engine.params.NWORDS_ORDER);
}
}
k[0] += 1;
}
if (is_lt_mod_order(order, x1))
{
mp_sub(x1, order, x1, engine.params.NWORDS_ORDER);
}
}
// Montgomery inversion modulo order, c = a^(-1)*R mod order.
protected void Montgomery_inversion_mod_order_bingcd(long[] a, long[] c, long[] order, long[] Montgomery_rprime, long[] Montgomery_Rprime)
{
long[] x = new long[engine.params.NWORDS_ORDER],
t = new long[engine.params.NWORDS_ORDER];
int[] k = new int[1];
if (is_zero(a, engine.params.NWORDS_ORDER))
{
copy_words(t, c, engine.params.NWORDS_ORDER);
return;
}
Montgomery_inversion_mod_order_bingcd_partial(a, x, k, order);
if (k[0] <= engine.params.NBITS_ORDER)
{
Montgomery_multiply_mod_order(x, Montgomery_Rprime, x, order, Montgomery_rprime);
k[0] += engine.params.NBITS_ORDER;
}
Montgomery_multiply_mod_order(x, Montgomery_Rprime, x, order, Montgomery_rprime);
power2_setup(t, 2*engine.params.NBITS_ORDER - k[0], engine.params.NWORDS_ORDER);
Montgomery_multiply_mod_order(x, t, c, order, Montgomery_rprime);
}
// Conversion of elements in Z_r from Montgomery to standard representation, where the order is up to NBITS_ORDER bits.
protected void from_Montgomery_mod_order(long[] ma, long[] c, long[] order, long[] Montgomery_rprime)
{
long[] one = new long[engine.params.NWORDS_ORDER];
one[0] = 1;
Montgomery_multiply_mod_order(ma, one, c, order, Montgomery_rprime);
}
// Computes the input modulo 3
// The input is assumed to be NWORDS_ORDER long
protected int mod3(long[] a)
{
long temp;
int r = 0;
int[] val = Pack.littleEndianToInt(Pack.longToLittleEndian(a), 0, a.length*2);
for (int i = (2*engine.params.NWORDS_ORDER-1); i >= 0; i--)
{
temp = ((long)r << (4*8)) | ((long)val[i]) & 0x00000000ffffffffL;
r = (int)(temp % 3);
}
return r;
}
// Conversion of a GF(p^2) element to Montgomery representation,
// mc_i = a_i*R^2*R^(-1) = a_i*R in GF(p^2).
protected void to_fp2mont(long[][] a, long[][] mc)
{
to_mont(a[0], mc[0]);
to_mont(a[1], mc[1]);
}
// Conversion to Montgomery representation,
// mc = a*R^2*R^(-1) mod p = a*R mod p, where a in [0, p-1].
// The Montgomery constant R^2 mod p is the global value "Montgomery_R2".
private void to_mont(long[] a, long[] mc)
{
fpmul_mont(a, engine.params.Montgomery_R2, mc);
}
// Modular correction to reduce field element a in [0, 2*PRIME-1] to [0, PRIME-1].
protected void fpcorrectionPRIME(long[] a)
{
int i, borrow = 0;
long mask;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//SUBC
long tempReg = a[i] - engine.params.PRIME[i];
int borrowReg = (is_digit_lessthan_ct(a[i], engine.params.PRIME[i]) | (borrow & is_digit_zero_ct(tempReg)));
a[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
mask = 0 - borrow;
borrow = 0;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
//ADDC
long tempReg = a[i] + borrow;
a[i] = (engine.params.PRIME[i] & mask) + tempReg;
borrow = (is_digit_lessthan_ct(tempReg, borrow) | is_digit_lessthan_ct(a[i], tempReg));
}
}
protected byte cmp_f2elm(long[][] x, long[][] y)
{ // Comparison of two GF(p^2) elements in constant time.
// Is x != y? return -1 if condition is true, 0 otherwise.
long[][] a = new long[2][engine.params.NWORDS_FIELD],
b = new long[2][engine.params.NWORDS_FIELD];
byte r = 0;
fp2copy(x, a);
fp2copy(y, b);
fp2correction(a);
fp2correction(b);
for (int i = engine.params.NWORDS_FIELD-1; i >= 0; i--)
{
r |= (a[0][i] ^ b[0][i]) | (a[1][i] ^ b[1][i]);
}
return (byte) ((-(byte)r) >>> (8-1));
}
// Encoding digits to bytes according to endianness
protected void encode_to_bytes(long[] x, byte[] enc, int encOffset, int nbytes)
{
byte[] test = new byte[((nbytes*4+7)&~7)];
Pack.longToLittleEndian(x, test, 0);
System.arraycopy(test, 0, enc, encOffset, nbytes);
}
// Decoding bytes to digits according to endianness
protected void decode_to_digits(byte[] x, int xOffset, long[] dec, int nbytes, int ndigits)
{
// x -> dec
dec[ndigits - 1] = 0;
byte[] temp = new byte[(nbytes+7)&~7];
System.arraycopy(x, xOffset, temp, 0, nbytes);
Pack.littleEndianToLong(temp, 0, dec);
}
// r = a - b*i where v = a + b*i
protected void fp2_conj(long[][] v, long[][] r)
{
fpcopy(v[0], 0, r[0]);
fpcopy(v[1], 0, r[1]);
if(!is_felm_zero(r[1]))
{
fpnegPRIME(r[1]);
}
}
// Conversion from Montgomery representation to standard representation,
// c = ma*R^(-1) mod p = a mod p, where ma in [0, p-1].
private void from_mont(long[] ma, long[] c)
{
long[] one = new long[engine.params.NWORDS_FIELD];
one[0] = 1;
fpmul_mont(ma, one, c);
fpcorrectionPRIME(c);
}
// Multiprecision right shift by one.
private void mp_shiftr1(long[] x)
{
int i;
for (i = 0; i < engine.params.NWORDS_FIELD-1; i++)
{
//SHIFTR
x[i] = (x[i] >>> 1) ^ (x[i+1] << (Internal.RADIX - 1));
}
x[engine.params.NWORDS_FIELD-1] >>>= 1;
}
private void mp_shiftr1(long[] x, int nwords)
{
int i;
for (i = 0; i < nwords-1; i++)
{
//SHIFTR
x[i] = (x[i] >>> 1) ^ (x[i+1] << (Internal.RADIX - 1));
}
x[nwords-1] >>>= 1;
}
// Copy a GF(p^2) element, c = a.
protected void fp2copy(long[][] a, long[][] c)
{
fpcopy(a[0], 0, c[0]);
fpcopy(a[1], 0, c[1]);
}
// Copy a GF(p^2) element, c = a.
protected void fp2copy(long[][] a, int aOffset, long[][] c)
{
fpcopy(a[0 + aOffset], 0, c[0]);
fpcopy(a[1 + aOffset], 0, c[1]);
}
// Copy a GF(p^2) element, c = a.
protected void fp2copy(long[] a, int aOffset, long[][] c)
{
fpcopy(a, aOffset, c[0]);
fpcopy(a, aOffset + engine.params.NWORDS_FIELD, c[1]);
}
// Zero a field element, a = 0.
protected void fpzero(long[] a)
{
int i;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
a[i] = 0;
}
}
// GF(p^2) subtraction with correction with 2*p, c = a-b+2p in GF(p^2).
protected void mp2_sub_p2(long[][] a, long[][] b, long[][] c)
{
//todo/org : make fp class and change this to generic mp_sub_p2
mp_subPRIME_p2(a[0], b[0], c[0]);
mp_subPRIME_p2(a[1], b[1], c[1]);
}
// Multiprecision comba multiply, c = a*b, where lng(a) = lng(b) = nwords.
protected void mp_mul(long[] a, long[] b, long[] c, int nwords)
{
int i, j;
long t = 0, u = 0, v = 0, temp;
long[] UV = new long[2];
for (i = 0; i < nwords; i++)
{
for (j = 0; j <= i; j++)
{
//MUL
digit_x_digit(a[j], b[i - j], UV);
//ADDC
temp = UV[0];
v += temp;
temp = UV[1] + is_digit_lessthan_ct(v, temp); // No overflow possible; high part of product < Long.MAX_VALUE
//ADDC
u += temp;
t += is_digit_lessthan_ct(u, temp);
}
c[i] = v;
v = u;
u = t;
t = 0;
}
for (i = nwords; i < 2*nwords-1; i++)
{
for (j = i-nwords+1; j < nwords; j++)
{
//MUL
digit_x_digit(a[j], b[i - j], UV);
//ADDC
temp = UV[0];
v += temp;
temp = UV[1] + is_digit_lessthan_ct(v, temp); // No overflow possible; high part of product < Long.MAX_VALUE
//ADDC
u += temp;
t += is_digit_lessthan_ct(u, temp);
}
c[i] = v;
v = u;
u = t;
t = 0;
}
c[2 * nwords - 1] = v;
// assert u == 0;
// assert t == 0;
}
// Multiprecision comba multiply, c = a*b, where lng(a) = lng(b) = nwords.
protected void mp_mul(long[] a, int aOffset, long[] b, long[] c, int nwords)
{
int i, j;
long t = 0, u = 0, v = 0, temp;
long[] UV = new long[2];
for (i = 0; i < nwords; i++)
{
for (j = 0; j <= i; j++)
{
//MUL
digit_x_digit(a[j + aOffset], b[i - j], UV);
//ADDC
temp = UV[0];
v += temp;
temp = UV[1] + is_digit_lessthan_ct(v, temp); // No overflow possible; high part of product < Long.MAX_VALUE
//ADDC
u += temp;
t += is_digit_lessthan_ct(u, temp);
}
c[i] = v;
v = u;
u = t;
t = 0;
}
for (i = nwords; i < 2*nwords-1; i++)
{
for (j = i-nwords+1; j < nwords; j++)
{
//MUL
digit_x_digit(a[j + aOffset], b[i - j], UV);
//ADDC
temp = UV[0];
v += temp;
temp = UV[1] + is_digit_lessthan_ct(v, temp); // No overflow possible; high part of product < Long.MAX_VALUE
//ADDC
u += temp;
t += is_digit_lessthan_ct(u, temp);
}
c[i] = v;
v = u;
u = t;
t = 0;
}
c[2 * nwords - 1] = v;
// assert u == 0;
// assert t == 0;
}
// GF(p^2) multiplication using Montgomery arithmetic, c = a*b in GF(p^2).
// Inputs: a = a0+a1*i and b = b0+b1*i, where a0, a1, b0, b1 are in [0, 2*p-1]
// Output: c = c0+c1*i, where c0, c1 are in [0, 2*p-1]
protected void fp2mul_mont(long[][] a, long[][] b, long[][] c)
{
long[] t1 = new long[engine.params.NWORDS_FIELD],
t2 = new long[engine.params.NWORDS_FIELD];
long[] tt1 = new long[2*engine.params.NWORDS_FIELD],
tt2 = new long[2*engine.params.NWORDS_FIELD],
tt3 = new long[2*engine.params.NWORDS_FIELD];
mp_add(a[0], a[1], t1, engine.params.NWORDS_FIELD); // t1 = a0+a1
mp_add(b[0], b[1], t2, engine.params.NWORDS_FIELD); // t2 = b0+b1
mp_mul(a[0], b[0], tt1, engine.params.NWORDS_FIELD); // tt1 = a0*b0
mp_mul(a[1], b[1], tt2, engine.params.NWORDS_FIELD); // tt2 = a1*b1
mp_mul(t1, t2, tt3, engine.params.NWORDS_FIELD); // tt3 = (a0+a1)*(b0+b1)
mp_dblsubfast(tt1, tt2, tt3); // tt3 = (a0+a1)*(b0+b1) - a0*b0 - a1*b1
mp_subaddfast(tt1, tt2, tt1); // tt1 = a0*b0 - a1*b1 + p*2^MAXBITS_FIELD if a0*b0 - a1*b1 < 0, else tt1 = a0*b0 - a1*b1
rdc_mont(tt3, c[1]); // c[1] = (a0+a1)*(b0+b1) - a0*b0 - a1*b1
rdc_mont(tt1, c[0]); // c[0] = a0*b0 - a1*b1
}
protected void fp2mul_mont(long[][] a, long[][] b, int bOffset, long[][] c)
{
// System.out.print("b: ");
// for (int di = 0; di < 2; di++){for (int dj = 0; dj < engine.params.NWORDS_FIELD; dj++)
// {System.out.printf("%016x ", b[di + bOffset][dj] );}System.out.println();}
long[] t1 = new long[engine.params.NWORDS_FIELD],
t2 = new long[engine.params.NWORDS_FIELD];
long[] tt1 = new long[2*engine.params.NWORDS_FIELD],
tt2 = new long[2*engine.params.NWORDS_FIELD],
tt3 = new long[2*engine.params.NWORDS_FIELD];
mp_add(a[0], a[1], t1, engine.params.NWORDS_FIELD); // t1 = a0+a1
mp_add(b[0 + bOffset], b[bOffset + 1], t2, engine.params.NWORDS_FIELD); // t2 = b0+b1
mp_mul(a[0], b[bOffset + 0], tt1, engine.params.NWORDS_FIELD); // tt1 = a0*b0
mp_mul(a[1], b[bOffset + 1], tt2, engine.params.NWORDS_FIELD); // tt2 = a1*b1
mp_mul(t1, t2, tt3, engine.params.NWORDS_FIELD); // tt3 = (a0+a1)*(b0+b1)
mp_dblsubfast(tt1, tt2, tt3); // tt3 = (a0+a1)*(b0+b1) - a0*b0 - a1*b1
mp_subaddfast(tt1, tt2, tt1); // tt1 = a0*b0 - a1*b1 + p*2^MAXBITS_FIELD if a0*b0 - a1*b1 < 0, else tt1 = a0*b0 - a1*b1
rdc_mont(tt3, c[1]); // c[1] = (a0+a1)*(b0+b1) - a0*b0 - a1*b1
rdc_mont(tt1, c[0]); // c[0] = a0*b0 - a1*b1
}
protected void fp2mul_mont(long[][] a, long[] b, int bOffset, long[][] c)
{
long[] t1 = new long[engine.params.NWORDS_FIELD],
t2 = new long[engine.params.NWORDS_FIELD];
long[] tt1 = new long[2*engine.params.NWORDS_FIELD],
tt2 = new long[2*engine.params.NWORDS_FIELD],
tt3 = new long[2*engine.params.NWORDS_FIELD];
mp_add(a[0], a[1], t1, engine.params.NWORDS_FIELD); // t1 = a0+a1
mp_add(b, bOffset, b, bOffset + engine.params.NWORDS_FIELD, t2, 0,engine.params.NWORDS_FIELD); // t2 = b0+b1
mp_mul(b, bOffset, a[0], tt1, engine.params.NWORDS_FIELD); // tt1 = a0*b0
mp_mul(b,bOffset + engine.params.NWORDS_FIELD, a[1], tt2, engine.params.NWORDS_FIELD); // tt2 = a1*b1
mp_mul(t1, t2, tt3, engine.params.NWORDS_FIELD); // tt3 = (a0+a1)*(b0+b1)
mp_dblsubfast(tt1, tt2, tt3); // tt3 = (a0+a1)*(b0+b1) - a0*b0 - a1*b1
mp_subaddfast(tt1, tt2, tt1); // tt1 = a0*b0 - a1*b1 + p*2^MAXBITS_FIELD if a0*b0 - a1*b1 < 0, else tt1 = a0*b0 - a1*b1
rdc_mont(tt3, c[1]); // c[1] = (a0+a1)*(b0+b1) - a0*b0 - a1*b1
rdc_mont(tt1, c[0]); // c[0] = a0*b0 - a1*b1
}
// Multiprecision subtraction, c = c-a-b, where lng(a) = lng(b) = 2*engine.params.NWORDS_FIELD.
private void mp_dblsubfast(long[] a, long[] b, long[] c)
{
mp_sub(c, a, c, 2*engine.params.NWORDS_FIELD);
mp_sub(c, b, c, 2*engine.params.NWORDS_FIELD);
}
// Multiprecision subtraction, c = a-b, where lng(a) = lng(b) = nwords. Returns the borrow bit.
protected int mp_sub(long[] a, long[] b, long[] c, int nwords)
{
int i, borrow = 0;
for (i = 0; i < nwords; i++)
{
//SUBC
long tempReg = a[i] - b[i];
// System.out.printf("%016x ", tempReg);
int borrowReg = (is_digit_lessthan_ct(a[i], b[i]) | (borrow & is_digit_zero_ct(tempReg)));
c[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
// System.out.println();
return borrow;
}
// Is x < y? return 1 (TRUE) if condition is true, 0 (FALSE) otherwise.
// SECURITY NOTE: This function does not run in constant-time.
protected boolean is_orderelm_lt(long[] x, long[] y)
{
for (int i = engine.params.NWORDS_ORDER-1; i >= 0; i--)
{
if (x[i] + Long.MIN_VALUE < y[i] + Long.MIN_VALUE)
{
return true;
}
else if (x[i] + Long.MIN_VALUE > y[i] + Long.MIN_VALUE )
{
return false;
}
}
return false;
}
// Multiprecision subtraction followed by addition with p*2^MAXBITS_FIELD, c = a-b+(p*2^MAXBITS_FIELD) if a-b < 0, otherwise c=a-b.
private void mp_subaddfast(long[] a, long[] b, long[] c)
{
long[] t1 = new long[engine.params.NWORDS_FIELD];
long mask = 0 - (long) mp_sub(a, b, c, 2*engine.params.NWORDS_FIELD);
for (int i = 0; i < engine.params.NWORDS_FIELD; i++)
{
t1[i] = engine.params.PRIME[i] & mask;
}
mp_add(c, engine.params.NWORDS_FIELD, t1, c, engine.params.NWORDS_FIELD, engine.params.NWORDS_FIELD);
}
// Multiprecision squaring, c = a^2 mod p.
protected void fpsqr_mont(long[] ma, long[] mc)
{
long[] temp = new long[2*engine.params.NWORDS_FIELD];
mp_mul(ma, ma, temp, engine.params.NWORDS_FIELD);
rdc_mont(temp, mc);
}
// Field inversion using Montgomery arithmetic, a = a^(-1)*R mod p.
private void fpinv_mont(long[] a)
{
long[] tt = new long[engine.params.NWORDS_FIELD];
fpcopy(a, 0, tt);
fpinv_chain_mont(tt);
fpsqr_mont(tt, tt);
fpsqr_mont(tt, tt);
fpmul_mont(a, tt, a);
}
// GF(p^2) inversion using Montgomery arithmetic, a = (a0-i*a1)/(a0^2+a1^2).
protected void fp2inv_mont(long[][] a)
{
long[][] t1 = new long[2][engine.params.NWORDS_FIELD];
fpsqr_mont(a[0], t1[0]); // t10 = a0^2
fpsqr_mont(a[1], t1[1]); // t11 = a1^2
fpaddPRIME(t1[0], t1[1], t1[0]); // t10 = a0^2+a1^2
fpinv_mont(t1[0]); // t10 = (a0^2+a1^2)^-1
fpnegPRIME(a[1]); // a = a0-i*a1
fpmul_mont(a[0], t1[0], a[0]);
fpmul_mont(a[1], t1[0], a[1]); // a = (a0-i*a1)*(a0^2+a1^2)^-1
}
// Computes a = 3*a
// The input is assumed to be OBOB_BITS-2 bits long and stored in SECRETKEY_B_BYTES
protected void mul3(byte[] a)
{
long[] temp1 = new long[engine.params.NWORDS_ORDER],
temp2 = new long[engine.params.NWORDS_ORDER];
decode_to_digits(a, 0, temp1, engine.params.SECRETKEY_B_BYTES, engine.params.NWORDS_ORDER);
mp_add(temp1, temp1, temp2, engine.params.NWORDS_ORDER); // temp2 = 2*a
mp_add(temp1, temp2, temp1, engine.params.NWORDS_ORDER); // temp1 = 3*a
encode_to_bytes(temp1, a, 0, engine.params.SECRETKEY_B_BYTES);
}
// Compare two byte arrays in constant time.
// Returns 0 if the byte arrays are equal, -1 otherwise.
protected byte ct_compare(byte[] a, byte[] b, int len)
{
byte r = 0;
for (int i = 0; i < len; i++)
{
r |= a[i] ^ b[i];
}
return (byte)((-(byte)r) >>> 7);
}
// Conditional move in constant time.
// If selector = -1 then load r with a, else if selector = 0 then keep r.
protected void ct_cmov(byte[] r, byte[] a, int len, byte selector)
{
for (int i = 0; i < len; i++)
{
r[i] ^= selector & (a[i] ^ r[i]);
}
}
// Copy wordsize digits, c = a, where lng(a) = nwords.
protected void copy_words(long[] a, long[] c, int nwords)
{
int i;
for (i = 0; i < nwords; i++)
{
c[i] = a[i];
}
}
// c = (2^k)*a
protected void fp2shl(long[][] a, int k, long[][] c)
{
fp2copy(a, c);
for (int j = 0; j < k; j++)
{
fp2add(c, c, c);
}
}
// Copy wordsize digits, c = a, where lng(a) = nwords.
protected void copy_words(PointProj a, PointProj c)
{
int i;
for (i = 0; i < engine.params.NWORDS_FIELD; i++)
{
c.X[0][i] = a.X[0][i];
c.X[1][i] = a.X[1][i];
c.Z[0][i] = a.Z[0][i];
c.Z[1][i] = a.Z[1][i];
}
}
// Modular negation, a = -a mod p.
// Input/output: a in [0, 2*p-1]
void Montgomery_neg(long[] a, long[] order)
{
int i, borrow = 0;
for (i = 0; i < engine.params.NWORDS_ORDER; i++)
{
//SUBC
long tempReg = order[i] - a[i];
int borrowReg = (is_digit_lessthan_ct(order[i], a[i]) | (borrow & is_digit_zero_ct(tempReg)));
a[i] = tempReg - (long)(borrow);
borrow = borrowReg;
}
}
// GF(p^2) addition, c = a+b in GF(p^2).
protected void fp2add(long[][] a, long[][] b, long[][] c)
{
fpaddPRIME(a[0], b[0], c[0]);
fpaddPRIME(a[1], b[1], c[1]);
}
// GF(p^2) subtraction, c = a-b in GF(p^2).
protected void fp2sub(long[][] a, long[][] b, long[][] c)
{
fpsubPRIME(a[0], b[0], c[0]);
fpsubPRIME(a[1], b[1], c[1]);
}
// GF(p^2) subtraction with correction with 4*p, c = a-b+4p in GF(p^2).
// private void mp2_sub_p4(long[][] a, long[][] b, long[][] c)
// {
// mp_subPRIME_p4(a[0], b[0], c[0]);
// mp_subPRIME_p4(a[1], b[1], c[1]);
// }
// Multiprecision multiplication, c = a*b mod p.
protected void fpmul_mont(long[] ma, long[] mb, long[] mc)
{
long[] temp = new long[2*engine.params.NWORDS_FIELD];
mp_mul(ma, mb, temp, engine.params.NWORDS_FIELD);
rdc_mont(temp, mc);
}
// Multiprecision multiplication, c = a*b mod p.
protected void fpmul_mont(long[] ma, int maOffset, long[] mb, long[] mc)
{
long[] temp = new long[2*engine.params.NWORDS_FIELD];
mp_mul(ma, maOffset, mb, temp, engine.params.NWORDS_FIELD);
rdc_mont(temp, mc);
}
// Chain to compute a^(p-3)/4 using Montgomery arithmetic.
private void fpinv_chain_mont(long[] a)
{
int i, j;
if (engine.params.NBITS_FIELD == 434)
{
long[] tt = new long[engine.params.NWORDS_FIELD];
long[][] t = new long[31][engine.params.NWORDS_FIELD];
// Precomputed table
fpsqr_mont(a, tt);
fpmul_mont(a, tt, t[0]);
for (i = 0; i <= 29; i++) fpmul_mont(t[i], tt, t[i + 1]);
fpcopy(a, 0, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 10; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[14], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[3], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[23], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[13], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[24], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[7], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[12], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[30], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[1], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[30], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[21], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[19], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[1], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[24], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[26], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[16], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[0], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[20], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[9], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[25], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[30], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[26], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(a, tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[28], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[22], tt, tt);
for (j = 0; j < 35; j++)
{
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[30], tt, tt);
}
fpcopy(tt, 0, a);
}
if (engine.params.NBITS_FIELD == 503)
{
long[][] t = new long[15][engine.params.NWORDS_FIELD];
long[] tt = new long[engine.params.NWORDS_FIELD];
// Precomputed table
fpsqr_mont(a, tt);
fpmul_mont(a, tt, t[0]);
for (i = 0; i <= 13; i++) fpmul_mont(t[i], tt, t[i + 1]);
fpcopy(a,0, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(a, tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[8], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[9], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[0], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(a, tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[8], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(a, tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[0], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[3], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 12; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[12], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[8], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[12], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[11], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[14], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[14], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[8], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(a, tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[4], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[7], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(a, tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[0], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[11], tt, tt);
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[13], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[1], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (j = 0; j < 49; j++)
{
for (i = 0; i < 5; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[14], tt, tt);
}
fpcopy(tt, 0, a);
}
if (engine.params.NBITS_FIELD == 610)
{
long[][] t = new long[31][engine.params.NWORDS_FIELD];
long[] tt = new long[engine.params.NWORDS_FIELD];
// Precomputed table
fpsqr_mont(a, tt);
fpmul_mont(a, tt, t[0]);
for (i = 0; i <= 29; i++) fpmul_mont(t[i], tt, t[i + 1]);
fpcopy(a, 0, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[30], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[25], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[28], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[7], tt, tt);
for (i = 0; i < 11; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[11], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(a, tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[0], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[3], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[16], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[24], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[28], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[16], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[4], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[3], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[20], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[11], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[14], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[15], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[0], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[15], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[19], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[9], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[27], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[28], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[29], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[1], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[3], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[30], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[25], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[28], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[22], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[3], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[22], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[7], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[9], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[4], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[20], tt, tt);
for (i = 0; i < 11; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[26], tt, tt);
for (i = 0; i < 11; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (j = 0; j < 50; j++)
{
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[30], tt, tt);
}
fpcopy(tt, 0, a);
}
if (engine.params.NBITS_FIELD == 751)
{
long[][] t = new long[27][engine.params.NWORDS_FIELD];
long[] tt = new long[engine.params.NWORDS_FIELD];
// Precomputed table
fpsqr_mont(a, tt);
fpmul_mont(a, tt, t[0]);
fpmul_mont(t[0], tt, t[1]);
fpmul_mont(t[1], tt, t[2]);
fpmul_mont(t[2], tt, t[3]);
fpmul_mont(t[3], tt, t[3]);
for (i = 3; i <= 8; i++) fpmul_mont(t[i], tt, t[i + 1]);
fpmul_mont(t[9], tt, t[9]);
for (i = 9; i <= 20; i++) fpmul_mont(t[i], tt, t[i + 1]);
fpmul_mont(t[21], tt, t[21]);
for (i = 21; i <= 24; i++) fpmul_mont(t[i], tt, t[i + 1]);
fpmul_mont(t[25], tt, t[25]);
fpmul_mont(t[25], tt, t[26]);
fpcopy(a, 0, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[20], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[24], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[11], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[8], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[23], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 10; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[15], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[13], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[26], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[20], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[11], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[14], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[4], tt, tt);
for (i = 0; i < 10; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[18], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[1], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[22], tt, tt);
for (i = 0; i < 10; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[6], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[24], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[9], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[18], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[17], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(a, tt, tt);
for (i = 0; i < 10; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[16], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[7], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[0], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[12], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[19], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[22], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[25], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[10], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[22], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[18], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[4], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[14], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[13], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[23], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[21], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[23], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[12], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[9], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[3], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[13], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[17], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[26], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[5], tt, tt);
for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[8], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[2], tt, tt);
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[11], tt, tt);
for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[20], tt, tt);
for (j = 0; j < 61; j++)
{
for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
fpmul_mont(t[26], tt, tt);
}
fpcopy(tt,0, a);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy