blob: 40e2e6a5d37b6b1a701dbd94e82f1017b13a7c4b [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
/* AMCL BIG number class */
public class BIG
{
private long[] w = new long[ROM.NLEN];
/* Constructors */
public BIG()
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] = 0;
}
}
public BIG(int x)
{
w[0] = x;
for (int i = 1;i < ROM.NLEN;i++)
{
w[i] = 0;
}
}
public BIG(BIG x)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] = x.w[i];
}
}
public BIG(DBIG x)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] = x.w[i];
}
}
public BIG(long[] x)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] = x[i];
}
}
public virtual long get(int i)
{
return w[i];
}
public virtual void set(int i, long x)
{
w[i] = x;
}
public virtual void xortop(long x)
{
w[ROM.NLEN - 1] ^= x;
}
public virtual void ortop(long x)
{
w[ROM.NLEN - 1] |= x;
}
/* calculate Field Excess */
public static long EXCESS(BIG a)
{
return ((a.w[ROM.NLEN - 1] & ROM.OMASK) >> (ROM.MODBITS % ROM.BASEBITS));
}
/* test for zero */
public virtual bool iszilch()
{
for (int i = 0;i < ROM.NLEN;i++)
{
if (w[i] != 0)
{
return false;
}
}
return true;
}
/* set to zero */
public virtual void zero()
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] = 0;
}
}
/* set to one */
public virtual void one()
{
w[0] = 1;
for (int i = 1;i < ROM.NLEN;i++)
{
w[i] = 0;
}
}
/* Test for equal to one */
public virtual bool isunity()
{
for (int i = 1;i < ROM.NLEN;i++)
{
if (w[i] != 0)
{
return false;
}
}
if (w[0] != 1)
{
return false;
}
return true;
}
/* Copy from another BIG */
public virtual void copy(BIG x)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] = x.w[i];
}
}
public virtual void copy(DBIG x)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] = x.w[i];
}
}
/* Conditional swap of two bigs depending on d using XOR - no branches */
public virtual void cswap(BIG b, int d)
{
int i;
long t , c = (long)d;
c = ~(c - 1);
for (i = 0;i < ROM.NLEN;i++)
{
t = c & (w[i] ^ b.w[i]);
w[i] ^= t;
b.w[i] ^= t;
}
}
public virtual void cmove(BIG g, int d)
{
int i;
long b = -d;
for (i = 0;i < ROM.NLEN;i++)
{
w[i] ^= (w[i] ^ g.w[i]) & b;
}
}
/* normalise BIG - force all digits < 2^BASEBITS */
public virtual long norm()
{
long d , carry = 0;
for (int i = 0;i < ROM.NLEN - 1;i++)
{
d = w[i] + carry;
w[i] = d & ROM.MASK;
carry = d >> ROM.BASEBITS;
}
w[ROM.NLEN - 1] = (w[ROM.NLEN - 1] + carry);
return (w[ROM.NLEN - 1] >> ((8 * ROM.MODBYTES) % ROM.BASEBITS));
}
/* Shift right by less than a word */
public virtual long fshr(int k)
{
long r = w[0] & (((long)1 << k) - 1); // shifted out part
for (int i = 0;i < ROM.NLEN - 1;i++)
{
w[i] = (w[i] >> k) | ((w[i + 1] << (ROM.BASEBITS - k)) & ROM.MASK);
}
w[ROM.NLEN - 1] = w[ROM.NLEN - 1] >> k;
return r;
}
/* general shift right */
public virtual void shr(int k)
{
int n = k % ROM.BASEBITS;
int m = k / ROM.BASEBITS;
for (int i = 0;i < ROM.NLEN - m - 1;i++)
{
w[i] = (w[m + i] >> n) | ((w[m + i + 1] << (ROM.BASEBITS - n)) & ROM.MASK);
}
w[ROM.NLEN - m - 1] = w[ROM.NLEN - 1] >> n;
for (int i = ROM.NLEN - m;i < ROM.NLEN;i++)
{
w[i] = 0;
}
}
/* Shift right by less than a word */
public virtual long fshl(int k)
{
w[ROM.NLEN - 1] = ((w[ROM.NLEN - 1] << k)) | (w[ROM.NLEN - 2]>>(ROM.BASEBITS - k));
for (int i = ROM.NLEN - 2;i > 0;i--)
{
w[i] = ((w[i] << k) & ROM.MASK) | (w[i - 1]>>(ROM.BASEBITS - k));
}
w[0] = (w[0] << k) & ROM.MASK;
return (w[ROM.NLEN - 1] >> ((8 * ROM.MODBYTES) % ROM.BASEBITS)); // return excess - only used in ff.c
}
/* general shift left */
public virtual void shl(int k)
{
int n = k % ROM.BASEBITS;
int m = k / ROM.BASEBITS;
w[ROM.NLEN - 1] = ((w[ROM.NLEN - 1 - m] << n)) | (w[ROM.NLEN - m - 2]>>(ROM.BASEBITS - n));
for (int i = ROM.NLEN - 2;i > m;i--)
{
w[i] = ((w[i - m] << n) & ROM.MASK) | (w[i - m - 1]>>(ROM.BASEBITS - n));
}
w[m] = (w[0] << n) & ROM.MASK;
for (int i = 0;i < m;i++)
{
w[i] = 0;
}
}
/* return number of bits */
public virtual int nbits()
{
int bts , k = ROM.NLEN - 1;
long c;
norm();
while (k >= 0 && w[k] == 0)
{
k--;
}
if (k < 0)
{
return 0;
}
bts = ROM.BASEBITS * k;
c = w[k];
while (c != 0)
{
c /= 2;
bts++;
}
return bts;
}
public virtual string toRawString()
{
BIG b = new BIG(this);
string s = "(";
for (int i = 0;i < ROM.NLEN - 1;i++)
{
s += b.w[i].ToString("x");
s += ",";
}
s += b.w[ROM.NLEN - 1].ToString("x");
s += ")";
return s;
}
/* Convert to Hex String */
public override string ToString()
{
BIG b;
string s = "";
int len = nbits();
if (len % 4 == 0)
{
len /= 4;
}
else
{
len /= 4;
len++;
}
if (len < ROM.MODBYTES * 2)
{
len = ROM.MODBYTES * 2;
}
for (int i = len - 1;i >= 0;i--)
{
b = new BIG(this);
b.shr(i * 4);
s += (b.w[0] & 15).ToString("x");
}
return s;
}
/* return this+x */
public virtual BIG plus(BIG x)
{
BIG s = new BIG(0);
for (int i = 0;i < ROM.NLEN;i++)
{
s.w[i] = w[i] + x.w[i];
}
return s;
}
/* this+=x */
public virtual void add(BIG x)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] += x.w[i];
}
}
/* this+=x, where x is int */
public virtual void inc(int x)
{
norm();
w[0] += x;
}
/* return this.x */
public virtual BIG minus(BIG x)
{
BIG d = new BIG(0);
for (int i = 0;i < ROM.NLEN;i++)
{
d.w[i] = w[i] - x.w[i];
}
return d;
}
/* this-=x */
public virtual void sub(BIG x)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] -= x.w[i];
}
}
/* reverse subtract this=x-this */
public virtual void rsub(BIG x)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] = x.w[i] - w[i];
}
}
/* this-=x where x is int */
public virtual void dec(int x)
{
norm();
w[0] -= (long)x;
}
/* this*=x, where x is small int<NEXCESS */
public virtual void imul(int c)
{
for (int i = 0;i < ROM.NLEN;i++)
{
w[i] *= c;
}
}
/* convert this BIG to byte array */
public virtual void tobytearray(sbyte[] b, int n)
{
norm();
BIG c = new BIG(this);
for (int i = ROM.MODBYTES - 1;i >= 0;i--)
{
b[i + n] = (sbyte)c.w[0];
c.fshr(8);
}
}
/* convert from byte array to BIG */
public static BIG frombytearray(sbyte[] b, int n)
{
BIG m = new BIG(0);
for (int i = 0;i < ROM.MODBYTES;i++)
{
m.fshl(8);
m.w[0] += (int)b[i + n] & 0xff;
//m.inc((int)b[i]&0xff);
}
return m;
}
public virtual void toBytes(sbyte[] b)
{
tobytearray(b,0);
}
public static BIG fromBytes(sbyte[] b)
{
return frombytearray(b,0);
}
/* set this[i]+=x*y+c, and return high part */
public virtual long muladd(long a, long b, long c, int i)
{
long x0, x1, y0, y1;
x0 = a & ROM.HMASK;
x1 = (a >> ROM.HBITS);
y0 = b & ROM.HMASK;
y1 = (b >> ROM.HBITS);
long bot = x0 * y0;
long top = x1 * y1;
long mid = x0 * y1 + x1 * y0;
x0 = mid & ROM.HMASK;
x1 = (mid >> ROM.HBITS);
bot += x0 << ROM.HBITS;
bot += c;
bot += w[i];
top += x1;
long carry = bot >> ROM.BASEBITS;
bot &= ROM.MASK;
top += carry;
w[i] = bot;
return top;
}
/* this*=x, where x is >NEXCESS */
public virtual long pmul(int c)
{
long ak , carry = 0;
norm();
for (int i = 0;i < ROM.NLEN;i++)
{
ak = w[i];
w[i] = 0;
carry = muladd(ak,(long)c,carry,i);
}
return carry;
}
/* this*=c and catch overflow in DBIG */
public virtual DBIG pxmul(int c)
{
DBIG m = new DBIG(0);
long carry = 0;
for (int j = 0;j < ROM.NLEN;j++)
{
carry = m.muladd(w[j],(long)c,carry,j);
}
m.w[ROM.NLEN] = carry;
return m;
}
/* divide by 3 */
public virtual int div3()
{
long ak , @base , carry = 0;
norm();
@base = ((long)1 << ROM.BASEBITS);
for (int i = ROM.NLEN - 1;i >= 0;i--)
{
ak = (carry * @base + w[i]);
w[i] = ak / 3;
carry = ak % 3;
}
return (int)carry;
}
/* return a*b where result fits in a BIG */
public static BIG smul(BIG a, BIG b)
{
long carry;
BIG c = new BIG(0);
for (int i = 0;i < ROM.NLEN;i++)
{
carry = 0;
for (int j = 0;j < ROM.NLEN;j++)
{
if (i + j < ROM.NLEN)
{
carry = c.muladd(a.w[i],b.w[j],carry,i + j);
}
}
}
return c;
}
/* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
public static int comp(BIG a, BIG b)
{
for (int i = ROM.NLEN - 1;i >= 0;i--)
{
if (a.w[i] == b.w[i])
{
continue;
}
if (a.w[i] > b.w[i])
{
return 1;
}
else
{
return -1;
}
}
return 0;
}
/* set x = x mod 2^m */
public virtual void mod2m(int m)
{
int i, wd, bt;
long msk;
wd = m / ROM.BASEBITS;
bt = m % ROM.BASEBITS;
msk = ((long)1 << bt) - 1;
w[wd] &= msk;
for (i = wd + 1;i < ROM.NLEN;i++)
{
w[i] = 0;
}
}
/* Arazi and Qi inversion mod 256 */
public static int invmod256(int a)
{
int U, t1, t2, b, c;
t1 = 0;
c = (a >> 1) & 1;
t1 += c;
t1 &= 1;
t1 = 2 - t1;
t1 <<= 1;
U = t1 + 1;
// i=2
b = a & 3;
t1 = U * b;
t1 >>= 2;
c = (a >> 2) & 3;
t2 = (U * c) & 3;
t1 += t2;
t1 *= U;
t1 &= 3;
t1 = 4 - t1;
t1 <<= 2;
U += t1;
// i=4
b = a & 15;
t1 = U * b;
t1 >>= 4;
c = (a >> 4) & 15;
t2 = (U * c) & 15;
t1 += t2;
t1 *= U;
t1 &= 15;
t1 = 16 - t1;
t1 <<= 4;
U += t1;
return U;
}
/* a=1/a mod 2^256. This is very fast! */
public virtual void invmod2m()
{
int i;
BIG U = new BIG(0);
BIG b = new BIG(0);
BIG c = new BIG(0);
U.inc(invmod256(lastbits(8)));
for (i = 8;i < 256;i <<= 1)
{
b.copy(this);
b.mod2m(i);
BIG t1 = BIG.smul(U,b);
t1.shr(i);
c.copy(this);
c.shr(i);
c.mod2m(i);
BIG t2 = BIG.smul(U,c);
t2.mod2m(i);
t1.add(t2);
b = BIG.smul(t1,U);
t1.copy(b);
t1.mod2m(i);
t2.one();
t2.shl(i);
t1.rsub(t2);
t1.norm();
t1.shl(i);
U.add(t1);
}
this.copy(U);
}
/* reduce this mod m */
public virtual void mod(BIG m)
{
int k = 0;
norm();
if (comp(this,m) < 0)
{
return;
}
do
{
m.fshl(1);
k++;
} while (comp(this,m) >= 0);
while (k > 0)
{
m.fshr(1);
if (comp(this,m) >= 0)
{
sub(m);
norm();
}
k--;
}
}
/* divide this by m */
public virtual void div(BIG m)
{
int k = 0;
norm();
BIG e = new BIG(1);
BIG b = new BIG(this);
zero();
while (comp(b,m) >= 0)
{
e.fshl(1);
m.fshl(1);
k++;
}
while (k > 0)
{
m.fshr(1);
e.fshr(1);
if (comp(b,m) >= 0)
{
add(e);
norm();
b.sub(m);
b.norm();
}
k--;
}
}
/* return parity */
public virtual int parity()
{
return (int)(w[0] % 2);
}
/* return n-th bit */
public virtual int bit(int n)
{
if ((w[n / ROM.BASEBITS] & ((long)1 << (n % ROM.BASEBITS)))>0)
{
return 1;
}
else
{
return 0;
}
}
/* return n last bits */
public virtual int lastbits(int n)
{
int msk = (1 << n) - 1;
norm();
return ((int)w[0]) & msk;
}
/* get 8*MODBYTES size random number */
public static BIG random(RAND rng)
{
BIG m = new BIG(0);
int i , b , j = 0, r = 0;
/* generate random BIG */
for (i = 0;i < 8 * ROM.MODBYTES;i++)
{
if (j == 0)
{
r = rng.Byte;
}
else
{
r >>= 1;
}
b = r & 1;
m.shl(1);
m.w[0] += b; // m.inc(b);
j++;
j &= 7;
}
return m;
}
/* Create random BIG in portable way, one bit at a time */
public static BIG randomnum(BIG q, RAND rng)
{
DBIG d = new DBIG(0);
int i , b , j = 0, r = 0;
for (i = 0;i < 2 * ROM.MODBITS;i++)
{
if (j == 0)
{
r = rng.Byte;
}
else
{
r >>= 1;
}
b = r & 1;
d.shl(1);
d.w[0] += b; // m.inc(b);
j++;
j &= 7;
}
BIG m = d.mod(q);
return m;
}
/* return NAF value as +/- 1, 3 or 5. x and x3 should be normed.
nbs is number of bits processed, and nzs is number of trailing 0s detected */
public static int[] nafbits(BIG x, BIG x3, int i)
{
int[] n = new int[3];
int nb = x3.bit(i) - x.bit(i);
int j;
n[1] = 1;
n[0] = 0;
if (nb == 0)
{
n[0] = 0;
return n;
}
if (i == 0)
{
n[0] = nb;
return n;
}
if (nb > 0)
{
n[0] = 1;
}
else
{
n[0] = (-1);
}
for (j = i - 1;j > 0;j--)
{
n[1]++;
n[0] *= 2;
nb = x3.bit(j) - x.bit(j);
if (nb > 0)
{
n[0] += 1;
}
if (nb < 0)
{
n[0] -= 1;
}
if (n[0] > 5 || n[0] < -5)
{
break;
}
}
if (n[0] % 2 != 0 && j != 0)
{ // backtrack
if (nb > 0)
{
n[0] = (n[0] - 1) / 2;
}
if (nb < 0)
{
n[0] = (n[0] + 1) / 2;
}
n[1]--;
}
while (n[0] % 2 == 0)
{ // remove trailing zeros
n[0] /= 2;
n[2]++;
n[1]--;
}
return n;
}
/* return a*b as DBIG */
public static DBIG mul(BIG a, BIG b)
{
DBIG c = new DBIG(0);
long carry;
a.norm();
b.norm();
for (int i = 0;i < ROM.NLEN;i++)
{
carry = 0;
for (int j = 0;j < ROM.NLEN;j++)
{
carry = c.muladd(a.w[i],b.w[j],carry,i + j);
}
c.w[ROM.NLEN + i] = carry;
}
return c;
}
/* return a^2 as DBIG */
public static DBIG sqr(BIG a)
{
DBIG c = new DBIG(0);
long carry;
a.norm();
for (int i = 0;i < ROM.NLEN;i++)
{
carry = 0;
for (int j = i + 1;j < ROM.NLEN;j++)
{
carry = c.muladd(2 * a.w[i],a.w[j],carry,i + j);
}
c.w[ROM.NLEN + i] = carry;
}
for (int i = 0;i < ROM.NLEN;i++)
{
c.w[2 * i + 1] += c.muladd(a.w[i],a.w[i],0,2 * i);
}
c.norm();
return c;
}
/* reduce a DBIG to a BIG using the appropriate form of the modulus */
public static BIG mod(DBIG d)
{
BIG b;
if (ROM.MODTYPE == ROM.PSEUDO_MERSENNE)
{
long v, tw;
BIG t = d.Split(ROM.MODBITS);
b = new BIG(d);
unchecked
{
v = t.pmul((int)ROM.MConst);
}
tw = t.w[ROM.NLEN - 1];
t.w[ROM.NLEN - 1] &= ROM.TMASK;
t.w[0] += (ROM.MConst * ((tw >> ROM.TBITS) + (v << (ROM.BASEBITS - ROM.TBITS))));
b.add(t);
b.norm();
}
if (ROM.MODTYPE == ROM.MONTGOMERY_FRIENDLY)
{
for (int i = 0;i < ROM.NLEN;i++)
{
d.w[ROM.NLEN + i] += d.muladd(d.w[i],ROM.MConst - 1,d.w[i],ROM.NLEN + i - 1);
}
b = new BIG(0);
for (int i = 0;i < ROM.NLEN;i++)
{
b.w[i] = d.w[ROM.NLEN + i];
}
b.norm();
}
if (ROM.MODTYPE == ROM.NOT_SPECIAL)
{
BIG md = new BIG(ROM.Modulus);
long m, carry;
for (int i = 0;i < ROM.NLEN;i++)
{
if (ROM.MConst == -1)
{
m = (-d.w[i]) & ROM.MASK;
}
else
{
if (ROM.MConst == 1)
{
m = d.w[i];
}
else
{
m = (ROM.MConst * d.w[i]) & ROM.MASK;
}
}
carry = 0;
for (int j = 0;j < ROM.NLEN;j++)
{
carry = d.muladd(m,md.w[j],carry,i + j);
}
d.w[ROM.NLEN + i] += carry;
}
b = new BIG(0);
for (int i = 0;i < ROM.NLEN;i++)
{
b.w[i] = d.w[ROM.NLEN + i];
}
b.norm();
}
return b;
}
/* return a*b mod m */
public static BIG modmul(BIG a, BIG b, BIG m)
{
a.mod(m);
b.mod(m);
DBIG d = mul(a,b);
return d.mod(m);
}
/* return a^2 mod m */
public static BIG modsqr(BIG a, BIG m)
{
a.mod(m);
DBIG d = sqr(a);
return d.mod(m);
}
/* return -a mod m */
public static BIG modneg(BIG a, BIG m)
{
a.mod(m);
return m.minus(a);
}
/* return this^e mod m */
public virtual BIG powmod(BIG e, BIG m)
{
int bt;
norm();
e.norm();
BIG a = new BIG(1);
BIG z = new BIG(e);
BIG s = new BIG(this);
while (true)
{
bt = z.parity();
z.fshr(1);
if (bt == 1)
{
a = modmul(a,s,m);
}
if (z.iszilch())
{
break;
}
s = modsqr(s,m);
}
return a;
}
/* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
public virtual int jacobi(BIG p)
{
int n8 , k , m = 0;
BIG t = new BIG(0);
BIG x = new BIG(0);
BIG n = new BIG(0);
BIG zilch = new BIG(0);
BIG one = new BIG(1);
if (p.parity() == 0 || comp(this,zilch) == 0 || comp(p,one) <= 0)
{
return 0;
}
norm();
x.copy(this);
n.copy(p);
x.mod(p);
while (comp(n,one) > 0)
{
if (comp(x,zilch) == 0)
{
return 0;
}
n8 = n.lastbits(3);
k = 0;
while (x.parity() == 0)
{
k++;
x.shr(1);
}
if (k % 2 == 1)
{
m += (n8 * n8 - 1) / 8;
}
m += (n8 - 1) * (x.lastbits(2) - 1) / 4;
t.copy(n);
t.mod(x);
n.copy(x);
x.copy(t);
m %= 2;
}
if (m == 0)
{
return 1;
}
else
{
return -1;
}
}
/* this=1/this mod p. Binary method */
public virtual void invmodp(BIG p)
{
mod(p);
BIG u = new BIG(this);
BIG v = new BIG(p);
BIG x1 = new BIG(1);
BIG x2 = new BIG(0);
BIG t = new BIG(0);
BIG one = new BIG(1);
while (comp(u,one) != 0 && comp(v,one) != 0)
{
while (u.parity() == 0)
{
u.shr(1);
if (x1.parity() != 0)
{
x1.add(p);
x1.norm();
}
x1.shr(1);
}
while (v.parity() == 0)
{
v.shr(1);
if (x2.parity() != 0)
{
x2.add(p);
x2.norm();
}
x2.shr(1);
}
if (comp(u,v) >= 0)
{
u.sub(v);
u.norm();
if (comp(x1,x2) >= 0)
{
x1.sub(x2);
}
else
{
t.copy(p);
t.sub(x2);
x1.add(t);
}
x1.norm();
}
else
{
v.sub(u);
v.norm();
if (comp(x2,x1) >= 0)
{
x2.sub(x1);
}
else
{
t.copy(p);
t.sub(x1);
x2.add(t);
}
x2.norm();
}
}
if (comp(u,one) == 0)
{
copy(x1);
}
else
{
copy(x2);
}
}
}