blob: e9625a547fdc57957dc929e533996a8c1399cd9b [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.
*/
/* Elliptic Curve Point class */
/* Constructor */
var ECP = function()
{
this.x=new FP(0);
this.y=new FP(1);
this.z=new FP(1);
this.INF=true;
};
ECP.prototype={
/* test this=O point-at-infinity */
is_infinity: function()
{
if (ROM.CURVETYPE==ROM.EDWARDS)
{
this.x.reduce(); this.y.reduce(); this.z.reduce();
return (this.x.iszilch() && this.y.equals(this.z));
}
else return this.INF;
},
/* conditional swap of this and Q dependant on d */
cswap: function(Q,d)
{
this.x.cswap(Q.x,d);
if (ROM.CURVETYPE!=ROM.MONTGOMERY) this.y.cswap(Q.y,d);
this.z.cswap(Q.z,d);
if (ROM.CURVETYPE!=ROM.EDWARDS)
{
var bd=(d!==0)?true:false;
bd=bd&(this.INF^Q.INF);
this.INF^=bd;
Q.INF^=bd;
}
},
/* conditional move of Q to P dependant on d */
cmove: function(Q,d)
{
this.x.cmove(Q.x,d);
if (ROM.CURVETYPE!=ROM.MONTGOMERY) this.y.cmove(Q.y,d);
this.z.cmove(Q.z,d);
if (ROM.CURVETYPE!=ROM.EDWARDS)
{
var bd=(d!==0)?true:false;
this.INF^=(this.INF^Q.INF)&bd;
}
},
/* Constant time select from pre-computed table */
select: function(W,b)
{
var MP=new ECP();
var m=b>>31;
var babs=(b^m)-m;
babs=(babs-1)/2;
this.cmove(W[0],ECP.teq(babs,0)); // conditional move
this.cmove(W[1],ECP.teq(babs,1));
this.cmove(W[2],ECP.teq(babs,2));
this.cmove(W[3],ECP.teq(babs,3));
this.cmove(W[4],ECP.teq(babs,4));
this.cmove(W[5],ECP.teq(babs,5));
this.cmove(W[6],ECP.teq(babs,6));
this.cmove(W[7],ECP.teq(babs,7));
MP.copy(this);
MP.neg();
this.cmove(MP,(m&1));
},
/* Test P == Q */
equals: function(Q)
{
if (this.is_infinity() && Q.is_infinity()) return true;
if (this.is_infinity() || Q.is_infinity()) return false;
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{
var zs2=new FP(0); zs2.copy(this.z); zs2.sqr();
var zo2=new FP(0); zo2.copy(Q.z); zo2.sqr();
var zs3=new FP(0); zs3.copy(zs2); zs3.mul(this.z);
var zo3=new FP(0); zo3.copy(zo2); zo3.mul(Q.z);
zs2.mul(Q.x);
zo2.mul(this.x);
if (!zs2.equals(zo2)) return false;
zs3.mul(Q.y);
zo3.mul(this.y);
if (!zs3.equals(zo3)) return false;
}
else
{
var a=new FP(0);
var b=new FP(0);
a.copy(this.x); a.mul(Q.z); a.reduce();
b.copy(Q.x); b.mul(this.z); b.reduce();
if (!a.equals(b)) return false;
if (ROM.CURVETYPE==ROM.EDWARDS)
{
a.copy(this.y); a.mul(Q.z); a.reduce();
b.copy(Q.y); b.mul(this.z); b.reduce();
if (!a.equals(b)) return false;
}
}
return true;
},
/* copy this=P */
copy: function(P)
{
this.x.copy(P.x);
if (ROM.CURVETYPE!=ROM.MONTGOMERY) this.y.copy(P.y);
this.z.copy(P.z);
this.INF=P.INF;
},
/* this=-this */
neg: function()
{
if (this.is_infinity()) return;
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{
this.y.neg(); this.y.norm();
}
if (ROM.CURVETYPE==ROM.EDWARDS)
{
this.x.neg(); this.x.norm();
}
return;
},
/* set this=O */
inf: function()
{
this.INF=true;
this.x.zero();
this.y=new FP(1);
this.z=new FP(1);
},
/* set this=(x,y) where x and y are BIGs */
setxy: function(ix,iy)
{
this.x=new FP(0); this.x.bcopy(ix);
this.y=new FP(0); this.y.bcopy(iy);
this.z=new FP(1);
var rhs=ECP.RHS(this.x);
if (ROM.CURVETYPE==ROM.MONTGOMERY)
{
if (rhs.jacobi()==1) this.INF=false;
else this.inf();
}
else
{
var y2=new FP(0); y2.copy(this.y);
y2.sqr();
if (y2.equals(rhs)) this.INF=false;
else this.inf();
}
},
/* set this=x, where x is BIG, y is derived from sign s */
setxi: function(ix,s)
{
this.x=new FP(0); this.x.bcopy(ix);
var rhs=ECP.RHS(this.x);
this.z=new FP(1);
if (rhs.jacobi()==1)
{
var ny=rhs.sqrt();
if (ny.redc().parity()!=s) ny.neg();
this.y=ny;
this.INF=false;
}
else this.inf();
},
/* set this=x, y calcuated from curve equation */
setx: function(ix)
{
this.x=new FP(0); this.x.bcopy(ix);
var rhs=ECP.RHS(this.x);
this.z=new FP(1);
if (rhs.jacobi()==1)
{
if (ROM.CURVETYPE!=ROM.MONTGOMERY) this.y=rhs.sqrt();
this.INF=false;
}
else this.INF=true;
},
/* set this to affine - from (x,y,z) to (x,y) */
affine: function()
{
if (this.is_infinity()) return;
var one=new FP(1);
if (this.z.equals(one)) return;
this.z.inverse();
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{
var z2=new FP(0); z2.copy(this.z);
z2.sqr();
this.x.mul(z2); this.x.reduce();
this.y.mul(z2);
this.y.mul(this.z); this.y.reduce();
this.z=one;
}
if (ROM.CURVETYPE==ROM.EDWARDS)
{
this.x.mul(this.z); this.x.reduce();
this.y.mul(this.z); this.y.reduce();
this.z=one;
}
if (ROM.CURVETYPE==ROM.MONTGOMERY)
{
this.x.mul(this.z); this.x.reduce();
this.z=one;
}
},
/* extract x as BIG */
getX: function()
{
this.affine();
return this.x.redc();
},
/* extract y as BIG */
getY: function()
{
this.affine();
return this.y.redc();
},
/* get sign of Y */
getS: function()
{
this.affine();
var y=this.getY();
return y.parity();
},
/* extract x as FP */
getx: function()
{
return this.x;
},
/* extract y as FP */
gety: function()
{
return this.y;
},
/* extract z as FP */
getz: function()
{
return this.z;
},
/* convert to byte array */
toBytes: function(b)
{
var i,t=[];
if (ROM.CURVETYPE!=ROM.MONTGOMERY) b[0]=0x04;
else b[0]=0x02;
this.affine();
this.x.redc().toBytes(t);
for (i=0;i<ROM.MODBYTES;i++) b[i+1]=t[i];
if (ROM.CURVETYPE!=ROM.MONTGOMERY)
{
this.y.redc().toBytes(t);
for (i=0;i<ROM.MODBYTES;i++) b[i+ROM.MODBYTES+1]=t[i];
}
},
/* convert to hex string */
toString: function()
{
if (this.is_infinity()) return "infinity";
this.affine();
if (ROM.CURVETYPE==ROM.MONTGOMERY) return "("+this.x.redc().toString()+")";
else return "("+this.x.redc().toString()+","+this.y.redc().toString()+")";
},
/* this+=this */
dbl: function()
{
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{
if (this.INF) return;
if (this.y.iszilch())
{
this.inf();
return;
}
var w1=new FP(0); w1.copy(this.x);
var w6=new FP(0); w6.copy(this.z);
var w2=new FP(0);
var w3=new FP(0); w3.copy(this.x);
var w8=new FP(0); w8.copy(this.x);
if (ROM.CURVE_A==-3)
{
w6.sqr();
w1.copy(w6);
w1.neg();
w3.add(w1);
w8.add(w6);
w3.mul(w8);
w8.copy(w3);
w8.imul(3);
}
else
{
w1.sqr();
w8.copy(w1);
w8.imul(3);
}
w2.copy(this.y); w2.sqr();
w3.copy(this.x); w3.mul(w2);
w3.imul(4);
w1.copy(w3); w1.neg();
this.x.copy(w8); this.x.sqr();
this.x.add(w1);
this.x.add(w1);
this.x.norm();
this.z.mul(this.y);
this.z.add(this.z);
w2.add(w2);
w2.sqr();
w2.add(w2);
w3.sub(this.x);
this.y.copy(w8); this.y.mul(w3);
this.y.sub(w2);
this.y.norm();
this.z.norm();
}
if (ROM.CURVETYPE==ROM.EDWARDS)
{
var C=new FP(0); C.copy(this.x);
var D=new FP(0); D.copy(this.y);
var H=new FP(0); H.copy(this.z);
var J=new FP(0);
this.x.mul(this.y); this.x.add(this.x);
C.sqr();
D.sqr();
if (ROM.CURVE_A==-1) C.neg();
this.y.copy(C); this.y.add(D);
H.sqr(); H.add(H);
this.z.copy(this.y);
J.copy(this.y); J.sub(H);
this.x.mul(J);
C.sub(D);
this.y.mul(C);
this.z.mul(J);
this.x.norm();
this.y.norm();
this.z.norm();
}
if (ROM.CURVETYPE==ROM.MONTGOMERY)
{
var A=new FP(0); A.copy(this.x);
var B=new FP(0); B.copy(this.x);
var AA=new FP(0);
var BB=new FP(0);
var C=new FP(0);
if (this.INF) return;
A.add(this.z);
AA.copy(A); AA.sqr();
B.sub(this.z);
BB.copy(B); BB.sqr();
C.copy(AA); C.sub(BB);
this.x.copy(AA); this.x.mul(BB);
A.copy(C); A.imul((ROM.CURVE_A+2)>>2);
BB.add(A);
this.z.copy(BB); this.z.mul(C);
this.x.norm();
this.z.norm();
}
return;
},
/* this+=Q */
add: function(Q)
{
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{
if (this.INF)
{
this.copy(Q);
return;
}
if (Q.INF) return;
var aff=false;
var one=new FP(1);
if (Q.z.equals(one)) aff=true;
var A,C;
var B=new FP(this.z);
var D=new FP(this.z);
if (!aff)
{
A=new FP(Q.z);
C=new FP(Q.z);
A.sqr(); B.sqr();
C.mul(A); D.mul(B);
A.mul(this.x);
C.mul(this.y);
}
else
{
A=new FP(this.x);
C=new FP(this.y);
B.sqr();
D.mul(B);
}
B.mul(Q.x); B.sub(A);
D.mul(Q.y); D.sub(C);
if (B.iszilch())
{
if (D.iszilch())
{
this.dbl();
return;
}
else
{
this.INF=true;
return;
}
}
if (!aff) this.z.mul(Q.z);
this.z.mul(B);
var e=new FP(B); e.sqr();
B.mul(e);
A.mul(e);
e.copy(A);
e.add(A); e.add(B);
this.x.copy(D); this.x.sqr(); this.x.sub(e);
A.sub(this.x);
this.y.copy(A); this.y.mul(D);
C.mul(B); this.y.sub(C);
this.x.norm();
this.y.norm();
this.z.norm();
}
if (ROM.CURVETYPE==ROM.EDWARDS)
{
var b=new FP(0); b.rcopy(ROM.CURVE_B);
var A=new FP(0); A.copy(this.z);
var B=new FP(0);
var C=new FP(0); C.copy(this.x);
var D=new FP(0); D.copy(this.y);
var E=new FP(0);
var F=new FP(0);
var G=new FP(0);
A.mul(Q.z);
B.copy(A); B.sqr();
C.mul(Q.x);
D.mul(Q.y);
E.copy(C); E.mul(D); E.mul(b);
F.copy(B); F.sub(E);
G.copy(B); G.add(E);
C.add(D);
if (ROM.CURVE_A==1)
{
E.copy(D); D.sub(C);
}
B.copy(this.x); B.add(this.y);
D.copy(Q.x); D.add(Q.y);
B.mul(D);
B.sub(C);
B.mul(F);
this.x.copy(A); this.x.mul(B);
if (ROM.CURVE_A==1)
{
C.copy(E); C.mul(G);
}
if (ROM.CURVE_A==-1)
{
C.mul(G);
}
this.y.copy(A); this.y.mul(C);
this.z.copy(F); this.z.mul(G);
this.x.norm(); this.y.norm(); this.z.norm();
}
return;
},
/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
dadd: function(Q,W)
{
var A=new FP(0); A.copy(this.x);
var B=new FP(0); B.copy(this.x);
var C=new FP(0); C.copy(Q.x);
var D=new FP(0); D.copy(Q.x);
var DA=new FP(0);
var CB=new FP(0);
A.add(this.z);
B.sub(this.z);
C.add(Q.z);
D.sub(Q.z);
DA.copy(D); DA.mul(A);
CB.copy(C); CB.mul(B);
A.copy(DA); A.add(CB); A.sqr();
B.copy(DA); B.sub(CB); B.sqr();
this.x.copy(A);
this.z.copy(W.x); this.z.mul(B);
if (this.z.iszilch()) this.inf();
else this.INF=false;
this.x.norm();
},
/* this-=Q */
sub: function(Q) {
Q.neg();
this.add(Q);
Q.neg();
},
/* constant time multiply by small integer of length bts - use ladder */
pinmul: function(e,bts) {
if (ROM.CURVETYPE==ROM.MONTGOMERY)
return this.mul(new BIG(e));
else
{
var nb,i,b;
var P=new ECP();
var R0=new ECP();
var R1=new ECP(); R1.copy(this);
for (i=bts-1;i>=0;i--)
{
b=(e>>i)&1;
P.copy(R1);
P.add(R0);
R0.cswap(R1,b);
R1.copy(P);
R0.dbl();
R0.cswap(R1,b);
}
P.copy(R0);
P.affine();
return P;
}
},
/* return e.this - SPA immune, using Ladder */
mul: function(e)
{
if (e.iszilch() || this.is_infinity()) return new ECP();
var P=new ECP();
if (ROM.CURVETYPE==ROM.MONTGOMERY)
{ /* use ladder */
var nb,i,b;
var D=new ECP();
var R0=new ECP(); R0.copy(this);
var R1=new ECP(); R1.copy(this);
R1.dbl();
D.copy(this); D.affine();
nb=e.nbits();
for (i=nb-2;i>=0;i--)
{
b=e.bit(i);
P.copy(R1);
P.dadd(R0,D);
R0.cswap(R1,b);
R1.copy(P);
R0.dbl();
R0.cswap(R1,b);
}
P.copy(R0);
}
else
{
// fixed size windows
var i,b,nb,m,s,ns;
var mt=new BIG();
var t=new BIG();
var Q=new ECP();
var C=new ECP();
var W=[];
var w=[];
this.affine();
// precompute table
Q.copy(this);
Q.dbl();
W[0]=new ECP();
W[0].copy(this);
for (i=1;i<8;i++)
{
W[i]=new ECP();
W[i].copy(W[i-1]);
W[i].add(Q);
}
// convert the table to affine
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
ECP.multiaffine(8,W);
// make exponent odd - add 2P if even, P if odd
t.copy(e);
s=t.parity();
t.inc(1); t.norm(); ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm();
t.cmove(mt,s);
Q.cmove(this,ns);
C.copy(Q);
nb=1+Math.floor((t.nbits()+3)/4);
// convert exponent to signed 4-bit window
for (i=0;i<nb;i++)
{
w[i]=(t.lastbits(5)-16);
t.dec(w[i]); t.norm();
t.fshr(4);
}
w[nb]=t.lastbits(5);
P.copy(W[Math.floor((w[nb]-1)/2)]);
for (i=nb-1;i>=0;i--)
{
Q.select(W,w[i]);
P.dbl();
P.dbl();
P.dbl();
P.dbl();
P.add(Q);
}
P.sub(C);
}
P.affine();
return P;
},
/* Return e.this+f.Q */
mul2: function(e,Q,f) {
var te=new BIG();
var tf=new BIG();
var mt=new BIG();
var S=new ECP();
var T=new ECP();
var C=new ECP();
var W=[];
var w=[];
var i,s,ns,nb;
var a,b;
this.affine();
Q.affine();
te.copy(e);
tf.copy(f);
// precompute table
W[1]=new ECP(); W[1].copy(this); W[1].sub(Q);
W[2]=new ECP(); W[2].copy(this); W[2].add(Q);
S.copy(Q); S.dbl();
W[0]=new ECP(); W[0].copy(W[1]); W[0].sub(S);
W[3]=new ECP(); W[3].copy(W[2]); W[3].add(S);
T.copy(this); T.dbl();
W[5]=new ECP(); W[5].copy(W[1]); W[5].add(T);
W[6]=new ECP(); W[6].copy(W[2]); W[6].add(T);
W[4]=new ECP(); W[4].copy(W[5]); W[4].sub(S);
W[7]=new ECP(); W[7].copy(W[6]); W[7].add(S);
// convert the table to affine
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
ECP.multiaffine(8,W);
// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
s=te.parity();
te.inc(1); te.norm(); ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm();
te.cmove(mt,s);
T.cmove(this,ns);
C.copy(T);
s=tf.parity();
tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm();
tf.cmove(mt,s);
S.cmove(Q,ns);
C.add(S);
mt.copy(te); mt.add(tf); mt.norm();
nb=1+Math.floor((mt.nbits()+1)/2);
// convert exponent to signed 2-bit window
for (i=0;i<nb;i++)
{
a=(te.lastbits(3)-4);
te.dec(a); te.norm();
te.fshr(2);
b=(tf.lastbits(3)-4);
tf.dec(b); tf.norm();
tf.fshr(2);
w[i]=(4*a+b);
}
w[nb]=(4*te.lastbits(3)+tf.lastbits(3));
S.copy(W[Math.floor((w[nb]-1)/2)]);
for (i=nb-1;i>=0;i--)
{
T.select(W,w[i]);
S.dbl();
S.dbl();
S.add(T);
}
S.sub(C); /* apply correction */
S.affine();
return S;
}
};
ECP.multiaffine=function(m,P)
{
var i;
var t1=new FP(0);
var t2=new FP(0);
var work=[];
for (i=0;i<m;i++)
work[i]=new FP(0);
work[0].one();
work[1].copy(P[0].z);
for (i=2;i<m;i++)
{
work[i].copy(work[i-1]);
work[i].mul(P[i-1].z);
}
t1.copy(work[m-1]);
t1.mul(P[m-1].z);
t1.inverse();
t2.copy(P[m-1].z);
work[m-1].mul(t1);
for (i=m-2;;i--)
{
if (i==0)
{
work[0].copy(t1);
work[0].mul(t2);
break;
}
work[i].mul(t2);
work[i].mul(t1);
t2.mul(P[i].z);
}
/* now work[] contains inverses of all Z coordinates */
for (i=0;i<m;i++)
{
P[i].z.one();
t1.copy(work[i]);
t1.sqr();
P[i].x.mul(t1);
t1.mul(work[i]);
P[i].y.mul(t1);
}
};
/* return 1 if b==c, no branching */
ECP.teq=function(b,c)
{
var x=b^c;
x-=1; // if x=0, x now -1
return ((x>>31)&1);
};
/* convert from byte array to ECP */
ECP.fromBytes= function(b)
{
var i,t=[];
var P=new ECP();
var p=new BIG(0); p.rcopy(ROM.Modulus);
for (i=0;i<ROM.MODBYTES;i++) t[i]=b[i+1];
var px=BIG.fromBytes(t);
if (BIG.comp(px,p)>=0) return P;
if (b[0]==0x04)
{
for (i=0;i<ROM.MODBYTES;i++) t[i]=b[i+ROM.MODBYTES+1];
var py=BIG.fromBytes(t);
if (BIG.comp(py,p)>=0) return P;
P.setxy(px,py);
return P;
}
else
{
P.setx(px);
return P;
}
};
/* Calculate RHS of curve equation */
ECP.RHS= function(x)
{
x.norm();
var r=new FP(0); r.copy(x);
r.sqr();
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{ // x^3+Ax+B
var b=new FP(0); b.rcopy(ROM.CURVE_B);
r.mul(x);
if (ROM.CURVE_A==-3)
{
var cx=new FP(0); cx.copy(x);
cx.imul(3);
cx.neg(); cx.norm();
r.add(cx);
}
r.add(b);
}
if (ROM.CURVETYPE==ROM.EDWARDS)
{ // (Ax^2-1)/(Bx^2-1)
var b=new FP(0); b.rcopy(ROM.CURVE_B);
var one=new FP(1);
b.mul(r);
b.sub(one);
if (ROM.CURVE_A==-1) r.neg();
r.sub(one);
b.inverse();
r.mul(b);
}
if (ROM.CURVETYPE==ROM.MONTGOMERY)
{ // x^3+Ax^2+x
var x3=new FP(0);
x3.copy(r);
x3.mul(x);
r.imul(ROM.CURVE_A);
r.add(x3);
r.add(x);
}
r.reduce();
return r;
};