blob: f80c41b2efda0f2e7ec123b1f33d20141c54c43e [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 */
package org.apache.milagro.amcl.BLS48;
public final class PAIR256 {
public static final boolean USE_GLV =true;
public static final boolean USE_GS_G2 =true;
public static final boolean USE_GS_GT =true;
public static final boolean GT_STRONG=false;
/* Line function */
public static FP48 line(ECP8 A,ECP8 B,FP Qx,FP Qy)
{
//System.out.println("Into line");
FP16 a,b,c; // Edits here
// c=new FP16(0);
if (A==B)
{ // Doubling
FP8 XX=new FP8(A.getx()); //X
FP8 YY=new FP8(A.gety()); //Y
FP8 ZZ=new FP8(A.getz()); //Z
FP8 YZ=new FP8(YY); //Y
YZ.mul(ZZ); //YZ
XX.sqr(); //X^2
YY.sqr(); //Y^2
ZZ.sqr(); //Z^2
YZ.imul(4);
YZ.neg(); YZ.norm(); //-2YZ
YZ.tmul(Qy); //-2YZ.Ys
XX.imul(6); //3X^2
XX.tmul(Qx); //3X^2.Xs
int sb=3*ROM.CURVE_B_I;
ZZ.imul(sb);
if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
{
ZZ.div_2i();
}
if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
{
ZZ.times_i();
ZZ.add(ZZ);
YZ.times_i();
YZ.norm();
}
ZZ.norm(); // 3b.Z^2
YY.add(YY);
ZZ.sub(YY); ZZ.norm(); // 3b.Z^2-Y^2
a=new FP16(YZ,ZZ); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
{
b=new FP16(XX); // L(0,1) | L(0,0) | L(1,0)
c=new FP16(0);
}
if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
{
b=new FP16(0);
c=new FP16(XX); c.times_i();
}
A.dbl();
}
else
{ // Addition - assume B is affine
FP8 X1=new FP8(A.getx()); // X1
FP8 Y1=new FP8(A.gety()); // Y1
FP8 T1=new FP8(A.getz()); // Z1
FP8 T2=new FP8(A.getz()); // Z1
T1.mul(B.gety()); // T1=Z1.Y2
T2.mul(B.getx()); // T2=Z1.X2
X1.sub(T2); X1.norm(); // X1=X1-Z1.X2
Y1.sub(T1); Y1.norm(); // Y1=Y1-Z1.Y2
T1.copy(X1); // T1=X1-Z1.X2
X1.tmul(Qy); // X1=(X1-Z1.X2).Ys
if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
{
X1.times_i();
X1.norm();
}
T1.mul(B.gety()); // T1=(X1-Z1.X2).Y2
T2.copy(Y1); // T2=Y1-Z1.Y2
T2.mul(B.getx()); // T2=(Y1-Z1.Y2).X2
T2.sub(T1); T2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
Y1.tmul(Qx); Y1.neg(); Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
a=new FP16(X1,T2); // (X1-Z1.X2).Ys | (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2 | - (Y1-Z1.Y2).Xs
if (ECP.SEXTIC_TWIST==ECP.D_TYPE)
{
b=new FP16(Y1);
c=new FP16(0);
}
if (ECP.SEXTIC_TWIST==ECP.M_TYPE)
{
b=new FP16(0);
c=new FP16(Y1); c.times_i();
}
A.add(B);
}
//System.out.println("Out of line");
return new FP48(a,b,c);
}
/* Optimal R-ate pairing */
public static FP48 ate(ECP8 P1,ECP Q1)
{
FP2 f;
BIG x=new BIG(ROM.CURVE_Bnx);
BIG n=new BIG(x);
FP48 lv;
int bt;
ECP8 P=new ECP8(P1);
ECP Q=new ECP(Q1);
P.affine();
Q.affine();
BIG n3=new BIG(n);
n3.pmul(3);
n3.norm();
FP Qx=new FP(Q.getx());
FP Qy=new FP(Q.gety());
ECP8 A=new ECP8();
FP48 r=new FP48(1);
A.copy(P);
ECP8 MP=new ECP8();
MP.copy(P); MP.neg();
int nb=n3.nbits();
for (int i=nb-2;i>=1;i--)
{
r.sqr();
lv=line(A,A,Qx,Qy);
r.smul(lv,ECP.SEXTIC_TWIST);
bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
if (bt==1)
{
lv=line(A,P,Qx,Qy);
r.smul(lv,ECP.SEXTIC_TWIST);
}
if (bt==-1)
{
//P.neg();
lv=line(A,MP,Qx,Qy);
r.smul(lv,ECP.SEXTIC_TWIST);
//P.neg();
}
}
if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
{
r.conj();
}
return r;
}
/* Optimal R-ate double pairing e(P,Q).e(R,S) */
public static FP48 ate2(ECP8 P1,ECP Q1,ECP8 R1,ECP S1)
{
FP2 f;
BIG x=new BIG(ROM.CURVE_Bnx);
BIG n=new BIG(x);
FP48 lv;
int bt;
ECP8 P=new ECP8(P1);
ECP Q=new ECP(Q1);
P.affine();
Q.affine();
ECP8 R=new ECP8(R1);
ECP S=new ECP(S1);
R.affine();
S.affine();
BIG n3=new BIG(n);
n3.pmul(3);
n3.norm();
FP Qx=new FP(Q.getx());
FP Qy=new FP(Q.gety());
FP Sx=new FP(S.getx());
FP Sy=new FP(S.gety());
ECP8 A=new ECP8();
ECP8 B=new ECP8();
FP48 r=new FP48(1);
A.copy(P);
B.copy(R);
ECP8 MP=new ECP8();
MP.copy(P); MP.neg();
ECP8 MR=new ECP8();
MR.copy(R); MR.neg();
int nb=n3.nbits();
for (int i=nb-2;i>=1;i--)
{
r.sqr();
lv=line(A,A,Qx,Qy);
r.smul(lv,ECP.SEXTIC_TWIST);
lv=line(B,B,Sx,Sy);
r.smul(lv,ECP.SEXTIC_TWIST);
bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
if (bt==1)
{
lv=line(A,P,Qx,Qy);
r.smul(lv,ECP.SEXTIC_TWIST);
lv=line(B,R,Sx,Sy);
r.smul(lv,ECP.SEXTIC_TWIST);
}
if (bt==-1)
{
//P.neg();
lv=line(A,MP,Qx,Qy);
r.smul(lv,ECP.SEXTIC_TWIST);
//P.neg();
//R.neg();
lv=line(B,MR,Sx,Sy);
r.smul(lv,ECP.SEXTIC_TWIST);
//R.neg();
}
}
if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
{
r.conj();
}
return r;
}
/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
public static FP48 fexp(FP48 m)
{
FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
BIG x=new BIG(ROM.CURVE_Bnx);
FP48 r=new FP48(m);
/* Easy part of final exp */
FP48 lv=new FP48(r);
lv.inverse();
r.conj();
r.mul(lv);
lv.copy(r);
r.frob(f,8);
r.mul(lv);
FP48 t0,t1,t2,t3,t4,t5,t6,t7;
/* Hard part of final exp */
// Ghamman & Fouotsa Method
t7=new FP48(r); t7.usqr();
t1=t7.pow(x);
x.fshr(1);
t2=t1.pow(x);
x.fshl(1);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3=new FP48(t1); t3.conj();
t2.mul(t3);
t2.mul(r);
r.mul(t7);
t1=t2.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,14);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,13);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,12);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,11);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,10);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,9);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,8);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t2); t3.conj();
t1.mul(t3);
t3.copy(t1);
t3.frob(f,7);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,6);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,5);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,4);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,3);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,2);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
t3.copy(t1);
t3.frob(f,1);
r.mul(t3);
t1=t1.pow(x);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX) {
t1.conj();
}
r.mul(t1);
t2.frob(f,15);
r.mul(t2);
r.reduce();
return r;
}
/* GLV method */
public static BIG[] glv(BIG e)
{
BIG[] u=new BIG[2];
// -(x^8).P = (Beta.x,y)
BIG q=new BIG(ROM.CURVE_Order);
BIG x=new BIG(ROM.CURVE_Bnx);
BIG x2=BIG.smul(x,x);
x=BIG.smul(x2,x2);
x2=BIG.smul(x,x);
u[0]=new BIG(e);
u[0].mod(x2);
u[1]=new BIG(e);
u[1].div(x2);
u[1].rsub(q);
return u;
}
/* Galbraith & Scott Method */
public static BIG[] gs(BIG e)
{
BIG[] u=new BIG[16];
BIG q=new BIG(ROM.CURVE_Order);
BIG x=new BIG(ROM.CURVE_Bnx);
BIG w=new BIG(e);
for (int i=0;i<15;i++)
{
u[i]=new BIG(w);
u[i].mod(x);
w.div(x);
}
u[15]=new BIG(w);
if (ECP.SIGN_OF_X==ECP.NEGATIVEX)
{
u[1].copy(BIG.modneg(u[1],q));
u[3].copy(BIG.modneg(u[3],q));
u[5].copy(BIG.modneg(u[5],q));
u[7].copy(BIG.modneg(u[7],q));
u[9].copy(BIG.modneg(u[9],q));
u[11].copy(BIG.modneg(u[11],q));
u[13].copy(BIG.modneg(u[13],q));
u[15].copy(BIG.modneg(u[15],q));
}
return u;
}
/* Multiply P by e in group G1 */
public static ECP G1mul(ECP P,BIG e)
{
ECP R;
if (USE_GLV)
{
//P.affine();
R=new ECP();
R.copy(P);
int i,np,nn;
ECP Q=new ECP();
Q.copy(P); Q.affine();
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();
}
u[0].norm();
u[1].norm();
R=R.mul2(u[0],Q,u[1]);
}
else
{
R=P.mul(e);
}
return R;
}
/* Multiply P by e in group G2 */
public static ECP8 G2mul(ECP8 P,BIG e)
{
ECP8 R;
if (USE_GS_G2)
{
ECP8[] Q=new ECP8[16];
FP2[] F=ECP8.frob_constants();
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 ECP8(); Q[0].copy(P);
for (i=1;i<16;i++)
{
Q[i]=new ECP8(); Q[i].copy(Q[i-1]);
Q[i].frob(F,1);
}
for (i=0;i<16;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();
}
u[i].norm();
//Q[i].affine();
}
R=ECP8.mul16(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 FP16.java */
public static FP48 GTpow(FP48 d,BIG e)
{
FP48 r;
if (USE_GS_GT)
{
FP48[] g=new FP48[16];
FP2 f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
BIG q=new BIG(ROM.CURVE_Order);
BIG t=new BIG(0);
int i,np,nn;
BIG[] u=gs(e);
g[0]=new FP48(d);
for (i=1;i<16;i++)
{
g[i]=new FP48(0); g[i].copy(g[i-1]);
g[i].frob(f,1);
}
for (i=0;i<16;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();
}
u[i].norm();
}
r=FP48.pow16(g,u);
}
else
{
r=d.pow(e);
}
return r;
}
}