blob: 2b939a2b041d9bbf3c7ba22854ee606759112c6d [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.
*/
/* Finite Field arithmetic */
/* AMCL mod p functions */
var FP = function(ctx) {
"use strict";
/* General purpose Constructor */
var FP = function(x) {
if (x instanceof FP) {
this.f = new ctx.BIG(x.f);
this.XES = x.XES;
} else {
this.f = new ctx.BIG(x);
this.nres();
}
};
FP.NOT_SPECIAL = 0;
FP.PSEUDO_MERSENNE = 1;
FP.GENERALISED_MERSENNE = 2;
FP.MONTGOMERY_FRIENDLY = 3;
FP.ZERO = 0;
FP.ONE = 1;
FP.SPARSER = 2;
FP.SPARSE = 3;
FP.DENSE= 4;
FP.MODBITS = ctx.config["@NBT"];
FP.MOD8 = ctx.config["@M8"];
FP.MODTYPE = ctx.config["@MT"];
FP.FEXCESS = ((1 << ctx.config["@SH"])-1); // 2^(BASEBITS*NLEN-MODBITS)-1
FP.OMASK = (-1) << FP.TBITS;
FP.TBITS = FP.MODBITS % ctx.BIG.BASEBITS;
FP.TMASK = (1 << FP.TBITS) - 1;
FP.prototype = {
/* set this=0 */
zero: function() {
this.XES = 1;
this.f.zero();
},
/* copy from a ctx.BIG in ROM */
rcopy: function(y) {
this.f.rcopy(y);
this.nres();
},
/* copy from another ctx.BIG */
bcopy: function(y) {
this.f.copy(y);
this.nres();
},
/* copy from another FP */
copy: function(y) {
this.XES = y.XES;
this.f.copy(y.f);
},
/* conditional swap of a and b depending on d */
cswap: function(b, d) {
this.f.cswap(b.f, d);
var t, c = d;
c = ~(c - 1);
t = c & (this.XES ^ b.XES);
this.XES ^= t;
b.XES ^= t;
},
/* conditional copy of b to a depending on d */
cmove: function(b, d) {
var c = d;
c = ~(c - 1);
this.f.cmove(b.f, d);
this.XES ^= (this.XES ^ b.XES) & c;
},
/* convert to Montgomery n-residue form */
nres: function() {
var r, d;
if (FP.MODTYPE != FP.PSEUDO_MERSENNE && FP.MODTYPE != FP.GENERALISED_MERSENNE) {
r = new ctx.BIG();
r.rcopy(ctx.ROM_FIELD.R2modp);
d = ctx.BIG.mul(this.f, r);
this.f.copy(FP.mod(d));
this.XES = 2;
} else {
this.XES = 1;
}
return this;
},
/* convert back to regular form */
redc: function() {
var r = new ctx.BIG(0),
d, w;
r.copy(this.f);
if (FP.MODTYPE != FP.PSEUDO_MERSENNE && FP.MODTYPE != FP.GENERALISED_MERSENNE) {
d = new ctx.DBIG(0);
d.hcopy(this.f);
w = FP.mod(d);
r.copy(w);
}
return r;
},
/* convert this to string */
toString: function() {
var s = this.redc().toString();
return s;
},
/* test this=0 */
iszilch: function() {
var c=new FP(0); c.copy(this);
c.reduce();
return c.f.iszilch();
},
/* reduce this mod Modulus */
reduce: function() {
var q,carry,sr,sb,m = new ctx.BIG(0);
m.rcopy(ctx.ROM_FIELD.Modulus);
var r = new ctx.BIG(0);
r.rcopy(ctx.ROM_FIELD.Modulus);
this.f.norm();
if (this.XES>16)
{
q=FP.quo(this.f,m);
carry=r.pmul(q);
r.w[ctx.BIG.NLEN-1]+=(carry<<ctx.BIG.BASEBITS); // correction - put any carry out back in again
this.f.sub(r);
this.f.norm();
sb=2;
}
else {
sb=FP.logb2(this.XES-1);
}
m.fshl(sb);
while (sb>0)
{
// constant time...
sr=ctx.BIG.ssn(r,this.f,m); // optimized combined shift, subtract and norm
this.f.cmove(r,1-sr);
sb--;
}
this.XES = 1;
},
/* set this=1 */
one: function() {
this.f.one();
return this.nres();
},
/* normalise this */
norm: function() {
return this.f.norm();
},
/* this*=b mod Modulus */
mul: function(b) {
var d;
if (this.XES * b.XES > FP.FEXCESS) {
this.reduce();
}
d = ctx.BIG.mul(this.f, b.f);
this.f.copy(FP.mod(d));
this.XES = 2;
return this;
},
/* this*=c mod Modulus where c is an int */
imul: function(c) {
var s = false,
d, n;
if (c < 0) {
c = -c;
s = true;
}
if (FP.MODTYPE == FP.PSEUDO_MERSENNE || FP.MODTYPE == FP.GENERALISED_MERSENNE) {
d = this.f.pxmul(c);
this.f.copy(FP.mod(d));
this.XES = 2;
} else {
if (this.XES * c <= FP.FEXCESS) {
this.f.pmul(c);
this.XES *= c;
} else {
n = new FP(c);
this.mul(n);
}
}
if (s) {
this.neg();
this.norm();
}
return this;
},
/* this*=this mod Modulus */
sqr: function() {
var d, t;
if (this.XES * this.XES > FP.FEXCESS) {
this.reduce();
}
d = ctx.BIG.sqr(this.f);
t = FP.mod(d);
this.f.copy(t);
this.XES = 2;
return this;
},
/* this+=b */
add: function(b) {
this.f.add(b.f);
this.XES += b.XES;
if (this.XES > FP.FEXCESS) {
this.reduce();
}
return this;
},
/* this=-this mod Modulus */
neg: function() {
var m = new ctx.BIG(0),
sb;
m.rcopy(ctx.ROM_FIELD.Modulus);
sb = FP.logb2(this.XES - 1);
m.fshl(sb);
this.XES = (1 << sb)+1;
this.f.rsub(m);
if (this.XES > FP.FEXCESS) {
this.reduce();
}
return this;
},
/* this-=b */
sub: function(b) {
var n = new FP(0);
n.copy(b);
n.neg();
this.add(n);
return this;
},
rsub: function(b) {
var n = new FP(0);
n.copy(this);
n.neg();
this.copy(b);
this.add(n);
},
/* this/=2 mod Modulus */
div2: function() {
var p;
if (this.f.parity() === 0) {
this.f.fshr(1);
} else {
p = new ctx.BIG(0);
p.rcopy(ctx.ROM_FIELD.Modulus);
this.f.add(p);
this.f.norm();
this.f.fshr(1);
}
return this;
},
// return this^(p-3)/4 or this^(p-5)/8
// See https://eprint.iacr.org/2018/1038
fpow: function() {
var i,j,k,bw,w,c,nw,lo,m,n;
var xp=[];
var ac=[1,2,3,6,12,15,30,60,120,240,255];
// phase 1
xp[0]=new FP(this); // 1
xp[1]=new FP(this); xp[1].sqr(); // 2
xp[2]=new FP(xp[1]); xp[2].mul(this); //3
xp[3]=new FP(xp[2]); xp[3].sqr(); // 6
xp[4]=new FP(xp[3]); xp[4].sqr(); // 12
xp[5]=new FP(xp[4]); xp[5].mul(xp[2]); // 15
xp[6]=new FP(xp[5]); xp[6].sqr(); // 30
xp[7]=new FP(xp[6]); xp[7].sqr(); // 60
xp[8]=new FP(xp[7]); xp[8].sqr(); // 120
xp[9]=new FP(xp[8]); xp[9].sqr(); // 240
xp[10]=new FP(xp[9]); xp[10].mul(xp[5]); // 255
n=FP.MODBITS;
if (FP.MODTYPE == FP.GENERALISED_MERSENNE) // Goldilocks ONLY
n/=2;
if (FP.MOD8==5)
{
n-=3;
c=(ctx.ROM_FIELD.MConst+5)/8;
} else {
n-=2;
c=(ctx.ROM_FIELD.MConst+3)/4;
}
bw=0; w=1; while (w<c) {w*=2; bw+=1;}
k=w-c;
i=10; var key=new FP(0);
if (k!=0)
{
while (ac[i]>k) i--;
key.copy(xp[i]);
k-=ac[i];
}
while (k!=0)
{
i--;
if (ac[i]>k) continue;
key.mul(xp[i]);
k-=ac[i];
}
// phase 2
xp[1].copy(xp[2]);
xp[2].copy(xp[5]);
xp[3].copy(xp[10]);
j=3; m=8;
nw=n-bw;
var t=new FP(0);
while (2*m<nw)
{
t.copy(xp[j++]);
for (i=0;i<m;i++)
t.sqr();
xp[j].copy(xp[j-1]);
xp[j].mul(t);
m*=2;
}
lo=nw-m;
var r=new FP(xp[j]);
while (lo!=0)
{
m/=2; j--;
if (lo<m) continue;
lo-=m;
t.copy(r);
for (i=0;i<m;i++)
t.sqr();
r.copy(t);
r.mul(xp[j]);
}
// phase 3
if (bw!=0)
{
for (i=0;i<bw;i++ )
r.sqr();
r.mul(key);
}
if (FP.MODTYPE == FP.GENERALISED_MERSENNE) // Goldilocks ONLY
{
key.copy(r);
r.sqr();
r.mul(this);
for (i=0;i<n+1;i++)
r.sqr();
r.mul(key);
}
return r;
},
/* this=1/this mod Modulus */
inverse: function() {
if (FP.MODTYPE == FP.PSEUDO_MERSENNE || FP.MODTYPE == FP.GENERALISED_MERSENNE)
{
var y=this.fpow();
if (FP.MOD8==5)
{
var t=new FP(this);
t.sqr();
this.mul(t);
y.sqr();
}
y.sqr();
y.sqr();
this.mul(y);
return this;
} else {
var m2=new ctx.BIG(0);
m2.rcopy(ctx.ROM_FIELD.Modulus);
m2.dec(2); m2.norm();
this.copy(this.pow(m2));
return this;
}
},
/* return TRUE if this==a */
equals: function(a) {
var ft=new FP(0); ft.copy(this);
var sd=new FP(0); sd.copy(a);
ft.reduce();
sd.reduce();
if (ctx.BIG.comp(ft.f, sd.f) === 0) {
return true;
}
return false;
},
/* return this^e mod Modulus */
pow: function(e) {
var i,w=[],
tb=[],
t=new ctx.BIG(e),
nb, lsbs, r;
t.norm();
nb= 1 + Math.floor((t.nbits() + 3) / 4);
for (i=0;i<nb;i++) {
lsbs=t.lastbits(4);
t.dec(lsbs);
t.norm();
w[i]=lsbs;
t.fshr(4);
}
tb[0]=new FP(1);
tb[1]=new FP(this);
for (i=2;i<16;i++) {
tb[i]=new FP(tb[i-1]);
tb[i].mul(this);
}
r=new FP(tb[w[nb-1]]);
for (i=nb-2;i>=0;i--) {
r.sqr();
r.sqr();
r.sqr();
r.sqr();
r.mul(tb[w[i]]);
}
r.reduce();
return r;
},
/* return jacobi symbol (this/Modulus) */
jacobi: function() {
var p = new ctx.BIG(0),
w = this.redc();
p.rcopy(ctx.ROM_FIELD.Modulus);
return w.jacobi(p);
},
/* return sqrt(this) mod Modulus */
sqrt: function() {
var i, v, r;
this.reduce();
if (FP.MOD8 == 5) {
i = new FP(0);
i.copy(this);
i.f.shl(1);
if (FP.MODTYPE == FP.PSEUDO_MERSENNE || FP.MODTYPE == FP.GENERALISED_MERSENNE) {
v=i.fpow();
} else {
var b = new ctx.BIG(0);
b.rcopy(ctx.ROM_FIELD.Modulus);
b.dec(5);
b.norm();
b.shr(3);
v = i.pow(b);
}
i.mul(v);
i.mul(v);
i.f.dec(1);
r = new FP(0);
r.copy(this);
r.mul(v);
r.mul(i);
r.reduce();
return r;
} else {
if (FP.MODTYPE == FP.PSEUDO_MERSENNE || FP.MODTYPE == FP.GENERALISED_MERSENNE) {
var r=this.fpow();
r.mul(this);
return r;
} else {
var b = new ctx.BIG(0);
b.rcopy(ctx.ROM_FIELD.Modulus);
b.inc(1);
b.norm();
b.shr(2);
return this.pow(b);
}
}
}
};
FP.logb2 = function(v) {
var r;
v |= v >>> 1;
v |= v >>> 2;
v |= v >>> 4;
v |= v >>> 8;
v |= v >>> 16;
v = v - ((v >>> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
return r;
};
FP.quo = function(n,m) {
var num,den,hb=ctx.BIG.CHUNK>>1;
if (FP.TBITS<hb)
{
var sh=hb-FP.TBITS;
num=(n.w[ctx.BIG.NLEN-1]<<sh)|(n.w[ctx.BIG.NLEN-2]>>(ctx.BIG.BASEBITS-sh));
den=(m.w[ctx.BIG.NLEN-1]<<sh)|(m.w[ctx.BIG.NLEN-2]>>(ctx.BIG.BASEBITS-sh));
} else {
num=n.w[ctx.BIG.NLEN-1];
den=m.w[ctx.BIG.NLEN-1];
}
return Math.floor(num/(den+1))
};
/* reduce a ctx.DBIG to a ctx.BIG using a "special" modulus */
FP.mod = function(d) {
var b = new ctx.BIG(0),
i, t, v, tw, tt, lo, carry, m, dd;
if (FP.MODTYPE == FP.PSEUDO_MERSENNE) {
t = d.split(FP.MODBITS);
b.hcopy(d);
if (ctx.ROM_FIELD.MConst != 1) {
v = t.pmul(ctx.ROM_FIELD.MConst);
} else {
v = 0;
}
t.add(b);
t.norm();
tw = t.w[ctx.BIG.NLEN - 1];
t.w[ctx.BIG.NLEN - 1] &= FP.TMASK;
t.inc(ctx.ROM_FIELD.MConst * ((tw >> FP.TBITS) + (v << (ctx.BIG.BASEBITS - FP.TBITS))));
// b.add(t);
t.norm();
return t;
}
if (FP.MODTYPE == FP.MONTGOMERY_FRIENDLY) {
for (i = 0; i < ctx.BIG.NLEN; i++) {
d.w[ctx.BIG.NLEN + i] += d.muladd(d.w[i], ctx.ROM_FIELD.MConst - 1, d.w[i], ctx.BIG.NLEN + i - 1);
}
for (i = 0; i < ctx.BIG.NLEN; i++) {
b.w[i] = d.w[ctx.BIG.NLEN + i];
}
b.norm();
}
if (FP.MODTYPE == FP.GENERALISED_MERSENNE) { // GoldiLocks Only
t = d.split(FP.MODBITS);
b.hcopy(d);
b.add(t);
dd = new ctx.DBIG(0);
dd.hcopy(t);
dd.shl(FP.MODBITS / 2);
tt = dd.split(FP.MODBITS);
lo = new ctx.BIG();
lo.hcopy(dd);
b.add(tt);
b.add(lo);
//b.norm();
tt.shl(FP.MODBITS / 2);
b.add(tt);
carry = b.w[ctx.BIG.NLEN - 1] >> FP.TBITS;
b.w[ctx.BIG.NLEN - 1] &= FP.TMASK;
b.w[0] += carry;
b.w[Math.floor(224 / ctx.BIG.BASEBITS)] += carry << (224 % ctx.BIG.BASEBITS);
b.norm();
}
if (FP.MODTYPE == FP.NOT_SPECIAL) {
m = new ctx.BIG(0);
m.rcopy(ctx.ROM_FIELD.Modulus);
b.copy(ctx.BIG.monty(m, ctx.ROM_FIELD.MConst, d));
}
return b;
};
return FP;
};