564 lines
14 KiB
C++
564 lines
14 KiB
C++
//
|
|
// Program to find Brezing & Weng pairing friendly curves
|
|
// http://eprint.iacr.org/2003/143
|
|
//
|
|
// Finds families of elliptic curves with groups of points of order r over the prime field p with
|
|
// embedding degree k, where k>2. The program tries to find curves with minimum rho=log(p)/log(r),
|
|
// as such curves may be optimal for fast implementation. The total number of points on the curve is
|
|
// p+1-t, where t is the trace.
|
|
//
|
|
// To get an actual curve, substitute for x, (ensure p(x) is prime and that r(x) has a large prime factor)
|
|
// and use the CM program to find actual curve parameters.
|
|
//
|
|
// This method is based on the fact that (t(x)-1) should be a k-th root of unity mod r(x), and r(x)
|
|
// only has k-th roots of unity if it is a divisor of phi_k(t(x)-1), as x^k=1 mod Phi_k(x)
|
|
// A simple way to generate t(x) and r(x) is to choose r(x) as Phi_{nk}(x) and then t(x)-1=x^n is a
|
|
// k-th root of unity. However this method is not exhaustive, and there could be better solutions.
|
|
// (That is there may be suitable r(x) which are not of the form Phi_{nk}(x), so these curves are not
|
|
// necessarily optimal).
|
|
//
|
|
// Omega indicates the degree of loop reduction possible compared to the Tate pairing, with the Ate
|
|
// or Eta pairing
|
|
//
|
|
// This program uses the NTL number theoretic library available from http://www.shoup.net/ntl/
|
|
//
|
|
// Nov. 2007 - modified to search for the best Ate or Eta Omega
|
|
//
|
|
// Mike Scott (2007)
|
|
//
|
|
|
|
#include <fstream>
|
|
#include "NTL/ZZXFactoring.h"
|
|
|
|
NTL_CLIENT
|
|
using namespace std;
|
|
|
|
#define POWER
|
|
#define MAXK 64
|
|
#define BM 10 // these limits have been tested as sufficient for K<=64
|
|
#define BD 20
|
|
|
|
void output(ZZX& p)
|
|
{
|
|
int i,leading=1;
|
|
ZZ c;
|
|
if (p==0)
|
|
{
|
|
cout << "0" << endl;
|
|
return;
|
|
}
|
|
int len=p.rep.length();
|
|
|
|
if (p.rep[0]!=0)
|
|
{
|
|
leading=0;
|
|
cout << p.rep[0];
|
|
}
|
|
|
|
if (len==1) return;
|
|
|
|
c=p.rep[1];
|
|
if (c<0)
|
|
{
|
|
c=-c;
|
|
cout << "-";
|
|
}
|
|
else if (c>0 && !leading)
|
|
cout << "+";
|
|
if (c!=0)
|
|
{
|
|
leading=0;
|
|
if (c==1)
|
|
cout << "x";
|
|
else
|
|
cout << c << "*x";
|
|
}
|
|
|
|
if (len==2) return;
|
|
|
|
for (i=2;i<len;i++)
|
|
{
|
|
c=p.rep[i];
|
|
if (c<0)
|
|
{
|
|
c=-c;
|
|
cout << "-";
|
|
}
|
|
else if (c>0 && !leading)
|
|
cout << "+";
|
|
if (c!=0)
|
|
{
|
|
leading=0;
|
|
if (c!=1) cout << c << "*";
|
|
if (i==2) cout << "x*x";
|
|
else
|
|
#ifdef POWER
|
|
cout << "pow(x," << i << ")";
|
|
#else
|
|
cout << "x^" << i;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
int outputfactors(ZZX &f)
|
|
{
|
|
int i;
|
|
ZZ c;
|
|
vec_pair_ZZX_long factors;
|
|
factor(c,factors,f);
|
|
if (c!=1) cout << c << ".";
|
|
for (i=0;i<factors.length();i++)
|
|
{
|
|
cout << "("; output(factors[i].a); cout << ")";
|
|
if (factors[i].b>1) cout << "^" << factors[i].b;
|
|
}
|
|
return factors.length();
|
|
}
|
|
|
|
//
|
|
// Composition. Give g(x) return f(x)=g(b(x))
|
|
//
|
|
|
|
ZZX compose(ZZX &g,ZZX &b)
|
|
{
|
|
ZZX c;
|
|
int i,d=deg(g);
|
|
|
|
// vec_ZZX table(INIT_SIZE,d+1);
|
|
ZZX table[100];
|
|
table[0]=1;
|
|
for (i=1;i<=d;i++) table[i]=(table[i-1]*b);
|
|
clear(c);
|
|
|
|
for (i=0;i<=d;i++)
|
|
{
|
|
c+=g.rep[i]*table[i];
|
|
// table(i).kill();
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
int omega(ZZX &p,ZZX &pkr)
|
|
{ // input phi(k)/r
|
|
ZZX t,f=pkr;
|
|
int biggest=0;
|
|
|
|
while (deg(f)>=0)
|
|
{
|
|
t=f%p;
|
|
if (deg(t)>biggest) biggest=deg(t);
|
|
f/=p;
|
|
}
|
|
return biggest;
|
|
}
|
|
|
|
// evaluate f(x)
|
|
|
|
ZZ eval(ZZX &f,int x)
|
|
{
|
|
ZZ y,xx;
|
|
y=0;
|
|
xx=1;
|
|
for (int i=0;i<=deg(f);i++)
|
|
{
|
|
y+=coeff(f,i)*xx;
|
|
xx*=x;
|
|
}
|
|
return y;
|
|
}
|
|
|
|
unsigned int igcd(unsigned int x,unsigned int y)
|
|
{ /* integer GCD, returns GCD of x and y */
|
|
unsigned int r;
|
|
if (y==0) return x;
|
|
while ((r=x%y)!=0)
|
|
x=y,y=r;
|
|
return y;
|
|
}
|
|
|
|
// for given x evaluate f(x) mod m
|
|
|
|
int evalmod(ZZX &f,int x,int m)
|
|
{
|
|
int y,xx;
|
|
y=0;
|
|
xx=1;
|
|
for (int i=0;i<=deg(f);i++)
|
|
{
|
|
y+=(to_long(coeff(f,i))%m)*xx;
|
|
y%=m;
|
|
xx=(x*xx)%m;
|
|
}
|
|
if (y<0) y+=m;
|
|
|
|
return y;
|
|
}
|
|
|
|
// check if f(x)/m has integer solutions
|
|
|
|
int solutions(ZZX &f,int m)
|
|
{
|
|
int s=0;
|
|
for (int x=0;x<m;x+=1)
|
|
if (evalmod(f,x,m)==0) s++;
|
|
|
|
return s;
|
|
}
|
|
|
|
// check p(x)/W for irreducibility
|
|
|
|
int irreducible(ZZX &p,long W)
|
|
{
|
|
int x;
|
|
ZZ pp,sr;
|
|
|
|
// This is a little dodgy....
|
|
|
|
for (x=10;x<500;x++)
|
|
{
|
|
pp=eval(p,x);
|
|
if (pp%W!=0) continue;
|
|
pp/=W;
|
|
sr=SqrRoot(pp);
|
|
if (sr*sr==pp) continue;
|
|
if (ProbPrime(pp,4)) return 1;
|
|
|
|
pp=eval(p,-x);
|
|
if (pp%W!=0) continue;
|
|
pp/=W;
|
|
sr=SqrRoot(pp);
|
|
if (sr*sr==pp) continue;
|
|
if (ProbPrime(pp,4)) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int negative(ZZX &f)
|
|
{
|
|
if (LeadCoeff(f)<0 && deg(f)%2==0) return 1;
|
|
return 0;
|
|
}
|
|
|
|
// check a polynomial p(x)/W for a probable prime generator
|
|
// and remove any common factor between p and W
|
|
|
|
int isap(ZZX& p,long& W)
|
|
{
|
|
int couldbe,mpj,cc;
|
|
ZZ c,pp;
|
|
|
|
c=content(p);
|
|
p/=c;
|
|
cc=to_long(c);
|
|
|
|
if (W%cc!=0) return 0;
|
|
W/=cc;
|
|
|
|
if (!solutions(p,W)) return 0;
|
|
if (!irreducible(p,W)) return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Check d for square-free
|
|
|
|
int squarefree(int d)
|
|
{
|
|
int sqr=0;
|
|
for (int i=2;;i++)
|
|
{
|
|
if (d%(i*i)==0)
|
|
{
|
|
sqr=1;
|
|
break;
|
|
}
|
|
if (i*i>d) break;
|
|
}
|
|
if (sqr) return 0;
|
|
return 1;
|
|
}
|
|
|
|
// basis for prime p
|
|
|
|
ZZX rib(long p,long n,ZZX& phi)
|
|
{
|
|
int j;
|
|
ZZ r;
|
|
ZZX s,t,b;
|
|
ZZX zeta,z,iz,izeta,f,inf;
|
|
|
|
b=1;
|
|
SetCoeff(zeta,n/p,1);
|
|
z=zeta;
|
|
|
|
t=InvTrunc(phi,n/p);
|
|
izeta=(1-phi*t)/zeta;
|
|
iz=izeta;
|
|
|
|
for (j=1;j<=(p-1)/2;j++)
|
|
{
|
|
b=MulMod(b,(z-iz)%phi,phi);
|
|
z*=zeta;
|
|
iz*=izeta;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
//
|
|
// square root of -d in Q(x), D is negative
|
|
// Murphy & Fitzpatrick
|
|
// http://eprint.iacr.org/2005/302
|
|
//
|
|
|
|
int sqrt(ZZX& r,long n,long d,ZZX& phi)
|
|
{
|
|
long p,dd;
|
|
ZZX zeta4,zeta8;
|
|
if (d<=0 || (d>1 && n%d!=0)) return 0;
|
|
if (d%2==0 && n%8!=0) return 0;
|
|
if (d%4!=3 && n%4!=0) return 0;
|
|
|
|
if (n%4==0) {SetCoeff(zeta4,n/4,1);zeta4%=phi;}
|
|
if (n%8==0) {SetCoeff(zeta8,n/8,1);zeta8%=phi;}
|
|
|
|
if (d==1)
|
|
{
|
|
r=zeta4;
|
|
return 1;
|
|
}
|
|
|
|
r=1;
|
|
dd=d;
|
|
for (p=2;p<=dd;p++)
|
|
{
|
|
if (d%p!=0) continue;
|
|
dd/=p;
|
|
if (p==2)
|
|
{
|
|
r=MulMod(zeta8,r,phi);
|
|
r=MulMod((1+zeta4),r,phi);
|
|
}
|
|
else
|
|
{
|
|
r=MulMod(rib(p,n,phi),r,phi);
|
|
}
|
|
}
|
|
if (d%4==1) r=MulMod(zeta4,r,phi);
|
|
if (d%2==0 && (4-(d/2)%4)%4==1) r=MulMod(zeta4,r,phi);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int main(int argc,char **argv)
|
|
{
|
|
int nn,i,j,m,K,mode,min_K,max_K,fp,fast;
|
|
long ww,d,W,x,bd,bn;
|
|
// ZZ m;
|
|
ZZX h,g,a,b,p,r,ff,q,kg;
|
|
ZZX pru,w,T,lambda;
|
|
int twist,small_ate,small_eta;
|
|
unsigned int rho_n,rho_d,best_rho_n,best_rho_d,omega_n,omega_d,best_omega_n,best_omega_d,gcd;
|
|
unsigned int delta_n,delta_d,best_delta_n,best_delta_d;
|
|
argv++; argc--;
|
|
|
|
fast=0;
|
|
if (argc!=1) {K=0; mode=0;}
|
|
else {K=atoi(argv[0]); mode=1; if (K<0) {K=-K; fast=1;}}
|
|
|
|
if (K!=0 && (K<3 || K>MAXK)) return 0;
|
|
|
|
// generate cyclotomic polynomials
|
|
|
|
vec_ZZX phi(INIT_SIZE, BM*MAXK);
|
|
for (i = 1; i <= BM*MAXK; i++)
|
|
{
|
|
ZZX t;
|
|
t = 1;
|
|
for (j = 1; j <= i-1; j++)
|
|
if (i % j == 0)
|
|
t *= phi(j);
|
|
phi(i) = (ZZX(i, 1) - 1)/t; // ZZX(i, a) == X^i * a
|
|
}
|
|
/*
|
|
ZZX bnp=ZZX(4,36)+ZZX(3,36)+ZZX(2,24)+ZZX(1,6)+ZZX(0,1);
|
|
ZZX bntm1=ZZX(2,6);
|
|
ZZX bnq=bnp-bntm1;
|
|
|
|
ZZX cof=(bnp*bnp*bnp*bnp-bnp*bnp+ZZX(0,1))/bnq;
|
|
|
|
cout << "cof= ";outputfactors(cof); cout << endl;
|
|
exit(0);
|
|
*/
|
|
if (mode==0)
|
|
{
|
|
cout << "Finds best Brezing and Weng families of pairing friendly elliptic curves" << endl;
|
|
cout << "To find all individual curves - bandw K, where 3<=K<=" << MAXK << endl;
|
|
cout << "To just see best curves (smallest rho found so far) = bandw -K" << endl;
|
|
cout << "A smaller omega means a shorter Miller loop for ETA or ATE pairing" << endl;
|
|
cout << "Note that sometimes the ETA pairing is possible, as well as ATE" << endl;
|
|
// cout << "A smaller delta means a faster Ate pairing" << endl;
|
|
min_K=3;
|
|
max_K=MAXK;
|
|
}
|
|
else {min_K=max_K=K;}
|
|
|
|
for (K=min_K;K<=max_K; K++)
|
|
{
|
|
best_rho_n=2; best_rho_d=1; bd=0; bn=0;
|
|
best_omega_n=1; best_omega_d=1;
|
|
// best_delta_n=1; best_delta_d=1;
|
|
// Try r(x) as Phi_{nK}(x) - why? Because thats what B&W did.
|
|
for (nn=1;nn<=BM;nn++)
|
|
{
|
|
r=phi(nn*K);
|
|
|
|
// set K-th root of unity
|
|
|
|
clear(g);
|
|
SetCoeff(g,nn,1);
|
|
kg=g%r;
|
|
|
|
// Try for small discriminants...
|
|
|
|
for (d=1;d<=BD;d++)
|
|
{
|
|
if (!squarefree(d)) continue;
|
|
W=4*d;
|
|
|
|
// find square root of -d mod r
|
|
|
|
if (!sqrt(h,nn*K,d,r)) continue;
|
|
|
|
// try for all the other K-th roots...
|
|
g=kg;
|
|
for (j=1;j<K;j++)
|
|
{
|
|
if (igcd(j,K)!=1)
|
|
{ // wrong order...
|
|
g=MulMod(g,kg,r);
|
|
continue;
|
|
}
|
|
|
|
a=(g+1);
|
|
b=MulMod((a-2)%r,h,r);
|
|
p=d*a*a+b*b;
|
|
ww=W;
|
|
rho_n=deg(p); rho_d=deg(r);
|
|
gcd=igcd(rho_n,rho_d);
|
|
rho_n/=gcd; rho_d/=gcd;
|
|
|
|
if ((!fast && mode==1) || rho_n*best_rho_d<best_rho_n*rho_d)
|
|
{
|
|
|
|
if (isap(p,ww))
|
|
{
|
|
// omega_n=omega(p,compose(phi(K),p)/r); omega_d=deg(p); gcd=igcd(omega_n,omega_d);
|
|
// omega_n/=gcd; omega_d/=gcd;
|
|
omega_n=deg(a); omega_d=deg(r); gcd=igcd(omega_n,omega_d);
|
|
omega_n/=gcd; omega_d/=gcd;
|
|
|
|
if (mode==1)
|
|
{
|
|
cout << "\nK= " << K << " D= " << d << " zeta_{" << K*nn << "}" << endl;
|
|
cout << "#define TRACE(x) ";output(a);cout << endl;
|
|
cout << "#define PRIME(x) (";output(p);cout << ")/" << ww << endl;
|
|
cout << "#define ORDER(x) ";output(r);cout << endl;
|
|
cout << "#define WW " << ww << endl;
|
|
cout << "rho= " << rho_n << "/" << rho_d << endl;
|
|
int fl;
|
|
cout << "factors= ";fl=outputfactors(compose(phi(K),p)/r);cout << endl;
|
|
cout << "Number of factors= " << fl << endl;
|
|
}
|
|
|
|
w=g;
|
|
small_ate=deg(r);
|
|
|
|
for (m=1;m<K;m++)
|
|
{
|
|
// cout << "m= " << m << "w= ";output(w);cout << endl;
|
|
if (!negative(w) && deg(w)<small_ate)
|
|
{
|
|
small_ate=deg(w);
|
|
T=w;
|
|
}
|
|
w=MulMod(w,g,r);
|
|
}
|
|
omega_n=small_ate; omega_d=deg(r); gcd=igcd(omega_n,omega_d);
|
|
omega_n/=gcd; omega_d/=gcd;
|
|
|
|
if (small_ate<deg(r) && mode==1)
|
|
{
|
|
cout << "Best ATE Loop=";output(T);
|
|
cout << ", omega= " << omega_d << "/" << omega_n << endl;
|
|
}
|
|
|
|
twist=0;
|
|
if (K%3==0 && d==3) twist=3;
|
|
if (K%4==0 && d==1) twist=4;
|
|
if (K%6==0 && d==3) twist=6;
|
|
|
|
if (twist)
|
|
{
|
|
lambda=g;
|
|
for (m=1;m<(K/twist);m++) lambda=MulMod(lambda,g,r);
|
|
w=lambda;
|
|
small_eta=deg(r);
|
|
|
|
for (m=1;m<twist;m++)
|
|
{
|
|
if (!negative(w) && deg(w)<small_eta)
|
|
{
|
|
small_eta=deg(w);
|
|
T=w;
|
|
}
|
|
w=MulMod(w,lambda,r);
|
|
}
|
|
if (small_eta<deg(r) && mode==1)
|
|
{
|
|
cout << "Best ETA Loop=";output(T);
|
|
cout << ", omega= " << omega_d/igcd(small_eta,omega_d) << "/" << small_eta/igcd(small_eta,omega_d) << endl;
|
|
}
|
|
if (small_eta<=small_ate)
|
|
{
|
|
omega_n=small_eta; omega_d=deg(r); gcd=igcd(omega_n,omega_d);
|
|
omega_n/=gcd; omega_d/=gcd;
|
|
}
|
|
// cout << "delta= " << delta_n << "/" << delta_d << endl << endl;
|
|
}
|
|
if (rho_n*best_rho_d<best_rho_n*rho_d)
|
|
{
|
|
best_rho_n=rho_n;
|
|
best_rho_d=rho_d;
|
|
best_omega_n=omega_n;
|
|
best_omega_d=omega_d;
|
|
// best_delta_n=delta_n;
|
|
// best_delta_d=delta_d;
|
|
|
|
bd=d;
|
|
bn=nn;
|
|
}
|
|
else if (rho_n*best_rho_d==best_rho_n*rho_d && omega_n*best_omega_d < best_omega_n*omega_d)
|
|
{
|
|
best_rho_n=rho_n;
|
|
best_rho_d=rho_d;
|
|
best_omega_n=omega_n;
|
|
best_omega_d=omega_d;
|
|
// best_delta_n=delta_n;
|
|
// best_delta_d=delta_d;
|
|
|
|
bd=d;
|
|
bn=nn;
|
|
}
|
|
}
|
|
}
|
|
g=MulMod(g,kg,r);
|
|
}
|
|
}
|
|
}
|
|
cout << "For K= " << K << " with D= " << bd << " best rho= " << best_rho_n << "/" << best_rho_d << " omega= " << best_omega_d << "/" << best_omega_n << /* " multiplier= " << bn << */ endl;
|
|
}
|
|
return 0;
|
|
}
|