blob: 4ad1a5c58f5b5b6614026c995af10d7ac5fa5e1f [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 */
public sealed class ECP
{
private FP x;
private FP y;
private FP z;
private bool INF;
/* Constructor - set to O */
public ECP()
{
INF = true;
x = new FP(0);
y = new FP(1);
z = new FP(1);
}
/* test for O point-at-infinity */
public bool is_infinity()
{
if (ROM.CURVETYPE == ROM.EDWARDS)
{
x.reduce();
y.reduce();
z.reduce();
return (x.iszilch() && y.Equals(z));
}
else
{
return INF;
}
}
/* Conditional swap of P and Q dependant on d */
private void cswap(ECP Q, int d)
{
x.cswap(Q.x,d);
if (ROM.CURVETYPE != ROM.MONTGOMERY)
{
y.cswap(Q.y,d);
}
z.cswap(Q.z,d);
if (ROM.CURVETYPE != ROM.EDWARDS)
{
bool bd;
if (d == 0)
{
bd = false;
}
else
{
bd = true;
}
bd = bd & (INF ^ Q.INF);
INF ^= bd;
Q.INF ^= bd;
}
}
/* Conditional move of Q to P dependant on d */
private void cmove(ECP Q, int d)
{
x.cmove(Q.x,d);
if (ROM.CURVETYPE != ROM.MONTGOMERY)
{
y.cmove(Q.y,d);
}
z.cmove(Q.z,d);
if (ROM.CURVETYPE != ROM.EDWARDS)
{
bool bd;
if (d == 0)
{
bd = false;
}
else
{
bd = true;
}
INF ^= (INF ^ Q.INF) & bd;
}
}
/* return 1 if b==c, no branching */
private static int teq(int b, int c)
{
int x = b ^ c;
x -= 1; // if x=0, x now -1
return ((x >> 31) & 1);
}
/* Constant time select from pre-computed table */
private void select(ECP[] W, int b)
{
ECP MP = new ECP();
int m = b >> 31;
int babs = (b ^ m) - m;
babs = (babs - 1) / 2;
cmove(W[0],teq(babs,0)); // conditional move
cmove(W[1],teq(babs,1));
cmove(W[2],teq(babs,2));
cmove(W[3],teq(babs,3));
cmove(W[4],teq(babs,4));
cmove(W[5],teq(babs,5));
cmove(W[6],teq(babs,6));
cmove(W[7],teq(babs,7));
MP.copy(this);
MP.neg();
cmove(MP,(int)(m & 1));
}
/* Test P == Q */
public bool Equals(ECP Q)
{
if (is_infinity() && Q.is_infinity())
{
return true;
}
if (is_infinity() || Q.is_infinity())
{
return false;
}
if (ROM.CURVETYPE == ROM.WEIERSTRASS)
{
FP zs2 = new FP(z);
zs2.sqr();
FP zo2 = new FP(Q.z);
zo2.sqr();
FP zs3 = new FP(zs2);
zs3.mul(z);
FP zo3 = new FP(zo2);
zo3.mul(Q.z);
zs2.mul(Q.x);
zo2.mul(x);
if (!zs2.Equals(zo2))
{
return false;
}
zs3.mul(Q.y);
zo3.mul(y);
if (!zs3.Equals(zo3))
{
return false;
}
}
else
{
FP a = new FP(0);
FP b = new FP(0);
a.copy(x);
a.mul(Q.z);
a.reduce();
b.copy(Q.x);
b.mul(z);
b.reduce();
if (!a.Equals(b))
{
return false;
}
if (ROM.CURVETYPE == ROM.EDWARDS)
{
a.copy(y);
a.mul(Q.z);
a.reduce();
b.copy(Q.y);
b.mul(z);
b.reduce();
if (!a.Equals(b))
{
return false;
}
}
}
return true;
}
/* this=P */
public void copy(ECP P)
{
x.copy(P.x);
if (ROM.CURVETYPE != ROM.MONTGOMERY)
{
y.copy(P.y);
}
z.copy(P.z);
INF = P.INF;
}
/* this=-this */
public void neg()
{
if (is_infinity())
{
return;
}
if (ROM.CURVETYPE == ROM.WEIERSTRASS)
{
y.neg();
y.reduce();
}
if (ROM.CURVETYPE == ROM.EDWARDS)
{
x.neg();
x.reduce();
}
return;
}
/* set this=O */
public void inf()
{
INF = true;
x.zero();
y.one();
z.one();
// y=new FP(1);
// z=new FP(1);
}
/* Calculate RHS of curve equation */
public static FP RHS(FP x)
{
x.norm();
FP r = new FP(x);
r.sqr();
if (ROM.CURVETYPE == ROM.WEIERSTRASS)
{ // x^3+Ax+B
FP b = new FP(new BIG(ROM.CURVE_B));
r.mul(x);
if (ROM.CURVE_A == -3)
{
FP cx = new FP(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)
FP b = new FP(new BIG(ROM.CURVE_B));
FP 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
FP 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;
}
/* set (x,y) from two BIGs */
public ECP(BIG ix, BIG iy)
{
x = new FP(ix);
y = new FP(iy);
z = new FP(1);
FP rhs = RHS(x);
if (ROM.CURVETYPE == ROM.MONTGOMERY)
{
if (rhs.jacobi() == 1)
{
INF = false;
}
else
{
inf();
}
}
else
{
FP y2 = new FP(y);
y2.sqr();
if (y2.Equals(rhs))
{
INF = false;
}
else
{
inf();
}
}
}
/* set (x,y) from BIG and a bit */
public ECP(BIG ix, int s)
{
x = new FP(ix);
FP rhs = RHS(x);
y = new FP(0);
z = new FP(1);
if (rhs.jacobi() == 1)
{
FP ny = rhs.sqrt();
if (ny.redc().parity() != s)
{
ny.neg();
}
y.copy(ny);
INF = false;
}
else
{
inf();
}
}
/* set from x - calculate y from curve equation */
public ECP(BIG ix)
{
x = new FP(ix);
FP rhs = RHS(x);
y = new FP(0);
z = new FP(1);
if (rhs.jacobi() == 1)
{
if (ROM.CURVETYPE != ROM.MONTGOMERY)
{
y.copy(rhs.sqrt());
}
INF = false;
}
else
{
INF = true;
}
}
/* set to affine - from (x,y,z) to (x,y) */
public void affine()
{
if (is_infinity())
{
return;
}
FP one = new FP(1);
if (z.Equals(one))
{
return;
}
z.inverse();
if (ROM.CURVETYPE == ROM.WEIERSTRASS)
{
FP z2 = new FP(z);
z2.sqr();
x.mul(z2);
x.reduce();
y.mul(z2);
y.mul(z);
y.reduce();
}
if (ROM.CURVETYPE == ROM.EDWARDS)
{
x.mul(z);
x.reduce();
y.mul(z);
y.reduce();
}
if (ROM.CURVETYPE == ROM.MONTGOMERY)
{
x.mul(z);
x.reduce();
}
z.copy(one);
}
/* extract x as a BIG */
public BIG X
{
get
{
affine();
return x.redc();
}
}
/* extract y as a BIG */
public BIG Y
{
get
{
affine();
return y.redc();
}
}
/* get sign of Y */
public int S
{
get
{
affine();
BIG y = Y;
return y.parity();
}
}
/* extract x as an FP */
public FP getx()
{
return x;
}
/* extract y as an FP */
public FP gety()
{
return y;
}
/* extract z as an FP */
public FP getz()
{
return z;
}
/* convert to byte array */
public void toBytes(sbyte[] b)
{
sbyte[] t = new sbyte[ROM.MODBYTES];
if (ROM.CURVETYPE != ROM.MONTGOMERY)
{
b[0] = 0x04;
}
else
{
b[0] = 0x02;
}
affine();
x.redc().toBytes(t);
for (int i = 0;i < ROM.MODBYTES;i++)
{
b[i + 1] = t[i];
}
if (ROM.CURVETYPE != ROM.MONTGOMERY)
{
y.redc().toBytes(t);
for (int i = 0;i < ROM.MODBYTES;i++)
{
b[i + ROM.MODBYTES + 1] = t[i];
}
}
}
/* convert from byte array to point */
public static ECP fromBytes(sbyte[] b)
{
sbyte[] t = new sbyte[ROM.MODBYTES];
BIG p = new BIG(ROM.Modulus);
for (int i = 0;i < ROM.MODBYTES;i++)
{
t[i] = b[i + 1];
}
BIG px = BIG.fromBytes(t);
if (BIG.comp(px,p) >= 0)
{
return new ECP();
}
if (b[0] == 0x04)
{
for (int i = 0;i < ROM.MODBYTES;i++)
{
t[i] = b[i + ROM.MODBYTES + 1];
}
BIG py = BIG.fromBytes(t);
if (BIG.comp(py,p) >= 0)
{
return new ECP();
}
return new ECP(px,py);
}
else
{
return new ECP(px);
}
}
/* convert to hex string */
public override string ToString()
{
if (is_infinity())
{
return "infinity";
}
affine();
if (ROM.CURVETYPE == ROM.MONTGOMERY)
{
return "(" + x.redc().ToString() + ")";
}
else
{
return "(" + x.redc().ToString() + "," + y.redc().ToString() + ")";
}
}
/* this*=2 */
public void dbl()
{
if (ROM.CURVETYPE == ROM.WEIERSTRASS)
{
if (INF)
{
return;
}
if (y.iszilch())
{
inf();
return;
}
FP w1 = new FP(x);
FP w6 = new FP(z);
FP w2 = new FP(0);
FP w3 = new FP(x);
FP w8 = new FP(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(y);
w2.sqr();
w3.copy(x);
w3.mul(w2);
w3.imul(4);
w1.copy(w3);
w1.neg();
// w1.norm();
x.copy(w8);
x.sqr();
x.add(w1);
x.add(w1);
// x.reduce();
x.norm();
z.mul(y);
z.add(z);
w2.add(w2);
w2.sqr();
w2.add(w2);
w3.sub(x);
y.copy(w8);
y.mul(w3);
// w2.norm();
y.sub(w2);
// y.reduce();
// z.reduce();
y.norm();
z.norm();
}
if (ROM.CURVETYPE == ROM.EDWARDS)
{
FP C = new FP(x);
FP D = new FP(y);
FP H = new FP(z);
FP J = new FP(0);
x.mul(y);
x.add(x);
C.sqr();
D.sqr();
if (ROM.CURVE_A == -1)
{
C.neg();
}
y.copy(C);
y.add(D);
// y.norm();
H.sqr();
H.add(H);
z.copy(y);
J.copy(y);
J.sub(H);
x.mul(J);
C.sub(D);
y.mul(C);
z.mul(J);
x.norm();
y.norm();
z.norm();
}
if (ROM.CURVETYPE == ROM.MONTGOMERY)
{
FP A = new FP(x);
FP B = new FP(x);
FP AA = new FP(0);
FP BB = new FP(0);
FP C = new FP(0);
if (INF)
{
return;
}
A.add(z);
AA.copy(A);
AA.sqr();
B.sub(z);
BB.copy(B);
BB.sqr();
C.copy(AA);
C.sub(BB);
// C.norm();
x.copy(AA);
x.mul(BB);
A.copy(C);
A.imul((ROM.CURVE_A + 2) / 4);
BB.add(A);
z.copy(BB);
z.mul(C);
// x.reduce();
// z.reduce();
x.norm();
z.norm();
}
return;
}
/* this+=Q */
public void add(ECP Q)
{
if (ROM.CURVETYPE == ROM.WEIERSTRASS)
{
if (INF)
{
copy(Q);
return;
}
if (Q.INF)
{
return;
}
bool aff = false;
FP one = new FP(1);
if (Q.z.Equals(one))
{
aff = true;
}
FP A, C;
FP B = new FP(z);
FP D = new FP(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(x);
C.mul(y);
}
else
{
A = new FP(x);
C = new FP(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())
{
dbl();
return;
}
else
{
INF = true;
return;
}
}
if (!aff)
{
z.mul(Q.z);
}
z.mul(B);
FP e = new FP(B);
e.sqr();
B.mul(e);
A.mul(e);
e.copy(A);
e.add(A);
e.add(B);
x.copy(D);
x.sqr();
x.sub(e);
A.sub(x);
y.copy(A);
y.mul(D);
C.mul(B);
y.sub(C);
// x.reduce();
// y.reduce();
// z.reduce();
x.norm();
y.norm();
z.norm();
}
if (ROM.CURVETYPE == ROM.EDWARDS)
{
FP b = new FP(new BIG(ROM.CURVE_B));
FP A = new FP(z);
FP B = new FP(0);
FP C = new FP(x);
FP D = new FP(y);
FP E = new FP(0);
FP F = new FP(0);
FP G = new FP(0);
// FP H=new FP(0);
// FP I=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(x);
B.add(y);
D.copy(Q.x);
D.add(Q.y);
B.mul(D);
B.sub(C);
B.mul(F);
x.copy(A);
x.mul(B);
if (ROM.CURVE_A == 1)
{
C.copy(E);
C.mul(G);
}
if (ROM.CURVE_A == -1)
{
C.mul(G);
}
y.copy(A);
y.mul(C);
z.copy(F);
z.mul(G);
// x.reduce(); y.reduce(); z.reduce();
x.norm();
y.norm();
z.norm();
}
return;
}
/* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
public void dadd(ECP Q, ECP W)
{
FP A = new FP(x);
FP B = new FP(x);
FP C = new FP(Q.x);
FP D = new FP(Q.x);
FP DA = new FP(0);
FP CB = new FP(0);
A.add(z);
B.sub(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();
x.copy(A);
z.copy(W.x);
z.mul(B);
if (z.iszilch())
{
inf();
}
else
{
INF = false;
}
// x.reduce();
x.norm();
}
/* this-=Q */
public void sub(ECP Q)
{
Q.neg();
add(Q);
Q.neg();
}
public static void multiaffine(int m, ECP[] P)
{
int i;
FP t1 = new FP(0);
FP t2 = new FP(0);
FP[] work = new FP[m];
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);
}
}
/* constant time multiply by small integer of length bts - use ladder */
public ECP pinmul(int e, int bts)
{
if (ROM.CURVETYPE == ROM.MONTGOMERY)
{
return this.mul(new BIG(e));
}
else
{
int nb, i, b;
ECP P = new ECP();
ECP R0 = new ECP();
ECP 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 */
public ECP mul(BIG e)
{
if (e.iszilch() || is_infinity())
{
return new ECP();
}
ECP P = new ECP();
if (ROM.CURVETYPE == ROM.MONTGOMERY)
{
/* use Ladder */
int nb, i, b;
ECP D = new ECP();
ECP R0 = new ECP();
R0.copy(this);
ECP 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
int i, b, nb, m, s, ns;
BIG mt = new BIG();
BIG t = new BIG();
ECP Q = new ECP();
ECP C = new ECP();
ECP[] W = new ECP[8];
sbyte[] w = new sbyte[1 + (ROM.NLEN * ROM.BASEBITS + 3) / 4];
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)
{
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 + (t.nbits() + 3) / 4;
// convert exponent to signed 4-bit window
for (i = 0;i < nb;i++)
{
w[i] = (sbyte)(t.lastbits(5) - 16);
t.dec(w[i]);
t.norm();
t.fshr(4);
}
w[nb] = (sbyte)t.lastbits(5);
P.copy(W[(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); // apply correction
}
P.affine();
return P;
}
/* Return e.this+f.Q */
public ECP mul2(BIG e, ECP Q, BIG f)
{
BIG te = new BIG();
BIG tf = new BIG();
BIG mt = new BIG();
ECP S = new ECP();
ECP T = new ECP();
ECP C = new ECP();
ECP[] W = new ECP[8];
sbyte[] w = new sbyte[1 + (ROM.NLEN * ROM.BASEBITS + 1) / 2];
int i, s, ns, nb;
sbyte a, b;
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)
{
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 + (mt.nbits() + 1) / 2;
// convert exponent to signed 2-bit window
for (i = 0;i < nb;i++)
{
a = (sbyte)(te.lastbits(3) - 4);
te.dec(a);
te.norm();
te.fshr(2);
b = (sbyte)(tf.lastbits(3) - 4);
tf.dec(b);
tf.norm();
tf.fshr(2);
w[i] = (sbyte)(4 * a + b);
}
w[nb] = (sbyte)(4 * te.lastbits(3) + tf.lastbits(3));
S.copy(W[(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;
}
/*
public static void main(String[] args) {
BIG Gx=new BIG(ROM.CURVE_Gx);
BIG Gy;
ECP P;
if (ROM.CURVETYPE!=ROM.MONTGOMERY) Gy=new BIG(ROM.CURVE_Gy);
BIG r=new BIG(ROM.CURVE_Order);
//r.dec(7);
System.out.println("Gx= "+Gx.toString());
if (ROM.CURVETYPE!=ROM.MONTGOMERY) System.out.println("Gy= "+Gy.toString());
if (ROM.CURVETYPE!=ROM.MONTGOMERY) P=new ECP(Gx,Gy);
else P=new ECP(Gx);
System.out.println("P= "+P.toString());
ECP R=P.mul(r);
//for (int i=0;i<10000;i++)
// R=P.mul(r);
System.out.println("R= "+R.toString());
} */
}