blob: ad93b6123b067351892cc892b895cca85c0c5dae [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 BN Curve Pairing functions */
public final class PAIR {
/* Line function */
public static FP12 line(ECP2 A,ECP2 B,FP Qx,FP Qy)
{
ECP2 P=new ECP2();
FP4 a,b,c;
P.copy(A);
FP2 ZZ=new FP2(P.getz());
ZZ.sqr();
int D;
if (A==B) D=A.dbl(); /* Check this return value in amcl_ec2.c */
else D=A.add(B);
if (D<0)
return new FP12(1);
FP2 Z3=new FP2(A.getz());
c=new FP4(0);
if (D==0)
{ /* Addition */
FP2 X=new FP2(B.getx());
FP2 Y=new FP2(B.gety());
FP2 T=new FP2(P.getz());
T.mul(Y);
ZZ.mul(T);
FP2 NY=new FP2(P.gety()); NY.neg();
ZZ.add(NY);
Z3.pmul(Qy);
T.mul(P.getx());
X.mul(NY);
T.add(X);
a=new FP4(Z3,T);
ZZ.neg();
ZZ.pmul(Qx);
b=new FP4(ZZ);
}
else
{ /* Doubling */
FP2 X=new FP2(P.getx());
FP2 Y=new FP2(P.gety());
FP2 T=new FP2(P.getx());
T.sqr();
T.imul(3);
Y.sqr();
Y.add(Y);
Z3.mul(ZZ);
Z3.pmul(Qy);
X.mul(T);
X.sub(Y);
a=new FP4(Z3,X);
T.neg();
ZZ.mul(T);
ZZ.pmul(Qx);
b=new FP4(ZZ);
}
return new FP12(a,b,c);
}
/* Optimal R-ate pairing */
public static FP12 ate(ECP2 P,ECP Q)
{
FP2 f=new FP2(new BIG(ROM.CURVE_Fra),new BIG(ROM.CURVE_Frb));
BIG x=new BIG(ROM.CURVE_Bnx);
BIG n=new BIG(x);
ECP2 K=new ECP2();
FP12 lv;
n.pmul(6); n.dec(2); n.norm();
P.affine();
Q.affine();
FP Qx=new FP(Q.getx());
FP Qy=new FP(Q.gety());
ECP2 A=new ECP2();
FP12 r=new FP12(1);
A.copy(P);
int nb=n.nbits();
for (int i=nb-2;i>=1;i--)
{
lv=line(A,A,Qx,Qy);
r.smul(lv);
if (n.bit(i)==1)
{
lv=line(A,P,Qx,Qy);
r.smul(lv);
}
r.sqr();
}
lv=line(A,A,Qx,Qy);
r.smul(lv);
/* R-ate fixup */
r.conj();
K.copy(P);
K.frob(f);
A.neg();
lv=line(A,K,Qx,Qy);
r.smul(lv);
K.frob(f);
K.neg();
lv=line(A,K,Qx,Qy);
r.smul(lv);
return r;
}
/* Optimal R-ate double pairing e(P,Q).e(R,S) */
public static FP12 ate2(ECP2 P,ECP Q,ECP2 R,ECP S)
{
FP2 f=new FP2(new BIG(ROM.CURVE_Fra),new BIG(ROM.CURVE_Frb));
BIG x=new BIG(ROM.CURVE_Bnx);
BIG n=new BIG(x);
ECP2 K=new ECP2();
FP12 lv;
n.pmul(6); n.dec(2); n.norm();
P.affine();
Q.affine();
R.affine();
S.affine();
FP Qx=new FP(Q.getx());
FP Qy=new FP(Q.gety());
FP Sx=new FP(S.getx());
FP Sy=new FP(S.gety());
ECP2 A=new ECP2();
ECP2 B=new ECP2();
FP12 r=new FP12(1);
A.copy(P);
B.copy(R);
int nb=n.nbits();
for (int i=nb-2;i>=1;i--)
{
lv=line(A,A,Qx,Qy);
r.smul(lv);
lv=line(B,B,Sx,Sy);
r.smul(lv);
if (n.bit(i)==1)
{
lv=line(A,P,Qx,Qy);
r.smul(lv);
lv=line(B,R,Sx,Sy);
r.smul(lv);
}
r.sqr();
}
lv=line(A,A,Qx,Qy);
r.smul(lv);
lv=line(B,B,Sx,Sy);
r.smul(lv);
/* R-ate fixup */
r.conj();
K.copy(P);
K.frob(f);
A.neg();
lv=line(A,K,Qx,Qy);
r.smul(lv);
K.frob(f);
K.neg();
lv=line(A,K,Qx,Qy);
r.smul(lv);
K.copy(R);
K.frob(f);
B.neg();
lv=line(B,K,Sx,Sy);
r.smul(lv);
K.frob(f);
K.neg();
lv=line(B,K,Sx,Sy);
r.smul(lv);
return r;
}
/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
public static FP12 fexp(FP12 m)
{
FP2 f=new FP2(new BIG(ROM.CURVE_Fra),new BIG(ROM.CURVE_Frb));
BIG x=new BIG(ROM.CURVE_Bnx);
FP12 r=new FP12(m);
FP12 x0,x1,x2,x3,x4,x5;
/* Easy part of final exp */
FP12 lv=new FP12(r);
lv.inverse();
r.conj();
r.mul(lv);
lv.copy(r);
r.frob(f);
r.frob(f);
r.mul(lv);
/* Hard part of final exp */
lv.copy(r);
lv.frob(f);
x0=new FP12(lv);
x0.frob(f);
lv.mul(r);
x0.mul(lv);
x0.frob(f);
x1=new FP12(r);
x1.conj();
x4=r.pow(x);
x3=new FP12(x4);
x3.frob(f);
x2=x4.pow(x);
x5=new FP12(x2); x5.conj();
lv=x2.pow(x);
x2.frob(f);
r.copy(x2); r.conj();
x4.mul(r);
x2.frob(f);
r.copy(lv);
r.frob(f);
lv.mul(r);
lv.usqr();
lv.mul(x4);
lv.mul(x5);
r.copy(x3);
r.mul(x5);
r.mul(lv);
lv.mul(x2);
r.usqr();
r.mul(lv);
r.usqr();
lv.copy(r);
lv.mul(x1);
r.mul(x0);
lv.usqr();
r.mul(lv);
r.reduce();
return r;
}
/* GLV method */
public static BIG[] glv(BIG e)
{
int i,j;
BIG t=new BIG(0);
BIG q=new BIG(ROM.CURVE_Order);
BIG[] u=new BIG[2];
BIG[] v=new BIG[2];
for (i=0;i<2;i++)
{
t.copy(new BIG(ROM.CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
DBIG d=BIG.mul(t,e);
v[i]=new BIG(d.div(q));
u[i]=new BIG(0);
}
u[0].copy(e);
for (i=0;i<2;i++)
for (j=0;j<2;j++)
{
t.copy(new BIG(ROM.CURVE_SB[j][i]));
t.copy(BIG.modmul(v[j],t,q));
u[i].add(q);
u[i].sub(t);
u[i].mod(q);
}
return u;
}
/* Galbraith & Scott Method */
public static BIG[] gs(BIG e)
{
int i,j;
BIG t=new BIG(0);
BIG q=new BIG(ROM.CURVE_Order);
BIG[] u=new BIG[4];
BIG[] v=new BIG[4];
for (i=0;i<4;i++)
{
t.copy(new BIG(ROM.CURVE_WB[i]));
DBIG d=BIG.mul(t,e);
v[i]=new BIG(d.div(q));
u[i]=new BIG(0);
}
u[0].copy(e);
for (i=0;i<4;i++)
for (j=0;j<4;j++)
{
t.copy(new BIG(ROM.CURVE_BB[j][i]));
t.copy(BIG.modmul(v[j],t,q));
u[i].add(q);
u[i].sub(t);
u[i].mod(q);
}
return u;
}
/* Multiply P by e in group G1 */
public static ECP G1mul(ECP P,BIG e)
{
ECP R;
if (ROM.USE_GLV)
{
P.affine();
R=new ECP();
R.copy(P);
int i,np,nn;
ECP Q=new ECP();
Q.copy(P);
BIG q=new BIG(ROM.CURVE_Order);
FP cru=new FP(new BIG(ROM.CURVE_Cru));
BIG t=new BIG(0);
BIG[] u=glv(e);
Q.getx().mul(cru);
np=u[0].nbits();
t.copy(BIG.modneg(u[0],q));
nn=t.nbits();
if (nn<np)
{
u[0].copy(t);
R.neg();
}
np=u[1].nbits();
t.copy(BIG.modneg(u[1],q));
nn=t.nbits();
if (nn<np)
{
u[1].copy(t);
Q.neg();
}
R=R.mul2(u[0],Q,u[1]);
}
else
{
R=P.mul(e);
}
return R;
}
/* Multiply P by e in group G2 */
public static ECP2 G2mul(ECP2 P,BIG e)
{
ECP2 R;
if (ROM.USE_GS_G2)
{
ECP2[] Q=new ECP2[4];
FP2 f=new FP2(new BIG(ROM.CURVE_Fra),new BIG(ROM.CURVE_Frb));
BIG q=new BIG(ROM.CURVE_Order);
BIG[] u=gs(e);
BIG t=new BIG(0);
int i,np,nn;
P.affine();
Q[0]=new ECP2(); Q[0].copy(P);
for (i=1;i<4;i++)
{
Q[i]=new ECP2(); Q[i].copy(Q[i-1]);
Q[i].frob(f);
}
for (i=0;i<4;i++)
{
np=u[i].nbits();
t.copy(BIG.modneg(u[i],q));
nn=t.nbits();
if (nn<np)
{
u[i].copy(t);
Q[i].neg();
}
}
R=ECP2.mul4(Q,u);
}
else
{
R=P.mul(e);
}
return R;
}
/* f=f^e */
/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
public static FP12 GTpow(FP12 d,BIG e)
{
FP12 r;
if (ROM.USE_GS_GT)
{
FP12[] g=new FP12[4];
FP2 f=new FP2(new BIG(ROM.CURVE_Fra),new BIG(ROM.CURVE_Frb));
BIG q=new BIG(ROM.CURVE_Order);
BIG t=new BIG(0);
int i,np,nn;
BIG[] u=gs(e);
g[0]=new FP12(d);
for (i=1;i<4;i++)
{
g[i]=new FP12(0); g[i].copy(g[i-1]);
g[i].frob(f);
}
for (i=0;i<4;i++)
{
np=u[i].nbits();
t.copy(BIG.modneg(u[i],q));
nn=t.nbits();
if (nn<np)
{
u[i].copy(t);
g[i].conj();
}
}
r=FP12.pow4(g,u);
}
else
{
r=d.pow(e);
}
return r;
}
/* test group membership */
/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
public static boolean GTmember(FP12 m)
{
if (m.isunity()) return false;
FP12 r=new FP12(m);
r.conj();
r.mul(m);
if (!r.isunity()) return false;
FP2 f=new FP2(new BIG(ROM.CURVE_Fra),new BIG(ROM.CURVE_Frb));
r.copy(m); r.frob(f); r.frob(f);
FP12 w=new FP12(r); w.frob(f); w.frob(f);
w.mul(m);
if (!ROM.GT_STRONG)
{
if (!w.equals(r)) return false;
BIG x=new BIG(ROM.CURVE_Bnx);
r.copy(m); w=r.pow(x); w=w.pow(x);
r.copy(w); r.sqr(); r.mul(w); r.sqr();
w.copy(m); w.frob(f);
}
return w.equals(r);
}
/*
public static void main(String[] args) {
ECP Q=new ECP(new BIG(ROM.CURVE_Gx),new BIG(ROM.CURVE_Gy));
ECP2 P=new ECP2(new FP2(new BIG(ROM.CURVE_Pxa),new BIG(ROM.CURVE_Pxb)),new FP2(new BIG(ROM.CURVE_Pya),new BIG(ROM.CURVE_Pyb)));
BIG r=new BIG(ROM.CURVE_Order);
BIG xa=new BIG(ROM.CURVE_Pxa);
System.out.println("P= "+P.toString());
System.out.println("Q= "+Q.toString());
BIG m=new BIG(17);
FP12 e=ate(P,Q);
System.out.println("\ne= "+e.toString());
e=fexp(e);
// e=GTpow(e,m);
System.out.println("\ne= "+e.toString());
BIG [] GLV=glv(r);
System.out.println("GLV[0]= "+GLV[0].toString());
System.out.println("GLV[0]= "+GLV[1].toString());
ECP G=new ECP(); G.copy(Q);
ECP2 R=new ECP2(); R.copy(P);
e=ate(R,Q);
e=fexp(e);
e=GTpow(e,xa);
System.out.println("\ne= "+e.toString());
R=G2mul(R,xa);
e=ate(R,G);
e=fexp(e);
System.out.println("\ne= "+e.toString());
G=G1mul(G,xa);
e=ate(P,G);
e=fexp(e);
System.out.println("\ne= "+e.toString());
} */
}