/***************************************************************************
*
Copyright 2013 CertiVox UK Ltd. *
*
This file is part of CertiVox MIRACL Crypto SDK. *
*
The CertiVox MIRACL Crypto SDK provides developers with an *
extensive and efficient set of cryptographic functions. *
For further information about its features and functionalities please *
refer to http://www.certivox.com *
*
* The CertiVox MIRACL Crypto SDK is free software: you can *
redistribute it and/or modify it under the terms of the *
GNU Affero General Public License as published by the *
Free Software Foundation, either version 3 of the License, *
or (at your option) any later version. *
*
* The CertiVox MIRACL Crypto SDK is distributed in the hope *
that it will be useful, but WITHOUT ANY WARRANTY; without even the *
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
See the GNU Affero General Public License for more details. *
*
* You should have received a copy of the GNU Affero General Public *
License along with CertiVox MIRACL Crypto SDK. *
If not, see . *
*
You can be released from the requirements of the license by purchasing *
a commercial license. Buying such a license is mandatory as soon as you *
develop commercial activities involving the CertiVox MIRACL Crypto SDK *
without disclosing the source code of your own applications, or shipping *
the CertiVox MIRACL Crypto SDK with a closed source product. *
*
***************************************************************************/
/*
* ss.cpp
*
* Super-Singular curve, eta_T pairing embedding degree 4
*
* Provides high level interface to pairing functions
*
* GT=pairing(G1,G1)
*
* This is calculated on a Pairing Friendly Curve (PFC), which must first be defined.
*
* G1 is a point over the base field
* GT is a finite field point over the 4-th extension, where 4 is the embedding degree.
*/
#define MR_PAIRING_SS2
#include "pairing_1.h"
// Using SHA256 as basic hash algorithm
//
// Hash function
//
#define HASH_LEN 32
Big H1(char *string)
{ // Hash a zero-terminated string to a number < modulus
Big h;
char s[HASH_LEN];
int i,j,M;
sha256 sh;
shs256_init(&sh);
for (i=0;;i++)
{
if (string[i]==0) break;
shs256_process(&sh,string[i]);
}
shs256_hash(&sh,s);
M=get_mip()->M;
h=1; j=0; i=1;
forever
{
h*=256;
if (j==HASH_LEN) {h+=i++; j=0;}
else h+=s[j++];
if (bits(h)>=M) break;
}
while (bits(h)>=M) h/=2;
return h;
}
// general hash
void PFC::start_hash(void)
{
shs256_init(&SH);
}
Big PFC::finish_hash_to_group(void)
{
Big hash;
Big o=pow((Big)2,2*S);
char s[HASH_LEN];
shs256_hash(&SH,s);
hash=from_binary(HASH_LEN,s);
return hash%o;
}
void PFC::add_to_hash(const GT& x)
{
int i,m;
GF2m xx[4];
Big a;
GF2m4x w=x.g;
w.get(xx[0],xx[1],xx[2],xx[3]);
for (i=0;i<4;i++)
{
a=(Big)xx[i];
while (a>0)
{
m=a%256;
shs256_process(&SH,m);
a/=256;
}
}
}
void PFC::add_to_hash(const G1& x)
{
Big a,X,Y;
int i,m;
x.g.get(X,Y);
a=X;
while (a>0)
{
m=a%256;
shs256_process(&SH,m);
a/=256;
}
a=Y;
while (a>0)
{
m=a%256;
shs256_process(&SH,m);
a/=256;
}
}
void PFC::add_to_hash(const Big& x)
{
int m;
Big a=x;
while (a>0)
{
m=a%256;
shs256_process(&SH,m);
a/=256;
}
}
// random multiplier
void PFC::random(Big& w)
{
if (RNG==NULL) w=rand(2*S,2);
else w=strong_rand(RNG,2*S,2);
}
// random AES key
void PFC::rankey(Big& k)
{
if (RNG==NULL) k=rand(S,2);
else k=strong_rand(RNG,S,2);
}
// Compress and hash a GF2m4x to a big number
Big H2(GF2m4x x)
{
sha256 sh;
Big a,hash;
GF2m xx[4];
char s[HASH_LEN];
int i,j,m;
shs256_init(&sh);
x.get(xx[0],xx[1],xx[2],xx[3]);
for (i=0;i<4;i++)
{
a=xx[i];
while (a>0)
{
m=a%256;
shs256_process(&sh,m);
a/=256;
}
}
shs256_hash(&sh,s);
hash=from_binary(HASH_LEN,s);
return hash;
}
//
// Extract ECn point in internal GF2m format
//
void extract(EC2& A,GF2m& x,GF2m& y)
{
x=(A.get_point())->X;
y=(A.get_point())->Y;
}
// Does nothing...
int PFC::precomp_for_pairing(G1& w)
{
return 0;
}
//
// eta_T Pairing G1 x G1 -> GT
//
// P is a point of order q in G1. Q is also a point of order q in G1.
//
// Note miller -> miller variable
// Loop unrolled x2 for speed
//
GT PFC::miller_loop(const G1& PP,const G1& QQ)
{
GF2m xp,yp,xq,yq,t;
GF2m4x miller,w,u,u0,u1,v,f,res;
EC2 P,Q;
GT mill;
int i,imod4,m=get_mip()->M;
P=PP.g; Q=QQ.g;
normalise(P); normalise(Q);
extract(P,xp,yp);
extract(Q,xq,yq);
imod4=((m+1)/2)%4;
if (imod4==1)
{ // (X=1) // (Y=0)
t=xp; // 0 (X+1)
f.set(t*(xp+xq+1)+yq+yp+B,t+xq+1,t+xq,0); // 0 (Y)
miller=1;
for (i=0;i<(m-3)/2;i+=2)
{
t=xp+1; xp=sqrt(xp); yp=sqrt(yp); // 1 (X)
u0.set(t*(xp+xq+1)+yp+yq,t+xq+1,t+xq,0); // 1 0 (X) ((X+1)*(xp+1)+Y)
xq*=xq; yq*=yq;
t=xp+1; xp=sqrt(xp); yp=sqrt(yp);
u1.set(t*(xp+xq+1)+yp+yq,t+xq+1,t+xq,0);
xq*=xq; yq*=yq;
u=mul(u0,u1);
miller*=u;
}
// final step
t=xp+1; xp=sqrt(xp); yp=sqrt(yp);
u.set(t*(xp+xq+1)+yp+yq,t+xq+1,t+xq,0);
miller*=u;
}
if (imod4==0)
{ // (X=0) // (Y=0)
t=xp+1; // 1 (X+1)
f.set(t*(xq+xp+1)+yq+yp+B,t+xq+1,t+xq,0); // 0 (Y)
miller=1;
for (i=0;i<(m-1)/2;i+=2)
{
// loop is unrolled x 2
t=xp; xp=sqrt(xp); yp=sqrt(yp); // 0 (X)
u0.set(t*(xp+xq)+yp+yq+xp+1,t+xq+1,t+xq,0); // 0 xp+1 (X) ((X+1)*(xp+1)+Y
xq*=xq; yq*=yq;
t=xp; xp=sqrt(xp); yp=sqrt(yp);
u1.set(t*(xp+xq)+yp+yq+xp+1,t+xq+1,t+xq,0);
xq*=xq; yq*=yq;
u=mul(u0,u1);
miller*=u;
}
}
if (imod4==2)
{ // (X=0) // (Y=1)
t=xp+1; // 1 (X+1)
f.set(t*(xq+xp+1)+yq+yp+B+1,t+xq+1,t+xq,0); // 1 (Y)
miller=1;
for (i=0;i<(m-1)/2;i+=2)
{
t=xp; xp=sqrt(xp); yp=sqrt(yp); // 0 (X)
u0.set(t*(xp+xq)+yp+yq+xp,t+xq+1,t+xq,0); // 0 xp+0 (X) ((X+1)*(xp+1)+Y)
xq*=xq; yq*=yq;
t=xp; xp=sqrt(xp); yp=sqrt(yp);
u1.set(t*(xp+xq)+yp+yq+xp,t+xq+1,t+xq,0);
xq*=xq; yq*=yq;
u=mul(u0,u1);
miller*=u;
}
}
if (imod4==3)
{ // (X=1) // (Y=1)
t=xp; // 0 (X+1)
f.set(t*(xq+xp+1)+yq+yp+B+1,t+xq+1,t+xq,0); // 1 (Y)
miller=1;
for (i=0;i<(m-3)/2;i+=2)
{
t=xp+1; xp=sqrt(xp); yp=sqrt(yp); // 1 (X)
u0.set(t*(xp+xq+1)+yp+yq+1,t+xq+1,t+xq,0); // 1 1 (X) ((X+1)*(xp+1)+Y)
xq*=xq; yq*=yq;
t=xp+1; xp=sqrt(xp); yp=sqrt(yp);
u1.set(t*(xp+xq+1)+yp+yq+1,t+xq+1,t+xq,0);
xq*=xq; yq*=yq;
u=mul(u0,u1);
miller*=u;
}
// final step
t=xp+1; xp=sqrt(xp); yp=sqrt(yp);
u.set(t*(xp+xq+1)+yp+yq+1,t+xq+1,t+xq,0);
miller*=u;
}
miller*=f;
mill.g=miller;
return mill;
}
GT PFC::final_exp(const GT& z)
{
int i,m,TYPE;
GT res;
GF2m4x r,u,v,w,y;
// raising to the power (2^m-2^[m+1)/2]+1)(2^[(m+1)/2]+1)(2^(2m)-1) or (2^m+2^[(m+1)/2]+1)(2^[(m+1)/2]-1)(2^(2m)-1)
// 6 Frobenius, 4 big field muls...
y=z.g;
if (B==0)
{
if (M%8==1 || M%8==7) TYPE=1;
else TYPE=2;
}
if (B==1)
{
if (M%8==3 || M%8==5) TYPE=1;
else TYPE=2;
}
u=v=w=y;
for (i=0;i<(M+1)/2;i++) u*=u;
if (TYPE==1)
{
u.powq();
w.powq();
v=w;
w.powq();
r=w;
w.powq();
w*=u;
w*=y;
r*=v;
u.powq();
u.powq();
r*=u;
}
else
{
u.powq();
v.powq();
w=u*v;
v.powq();
w*=v;
v.powq();
u.powq();
u.powq();
r=v*u;
r*=y;
}
r/=w;
res.g=r;
return res;
}
// initialise Pairing Friendly Curve
PFC::PFC(int s, csprng *rng)
{
int t,u,v,b,words,mod_bits;
if (s!=80 && s!=128)
{
cout << "No suitable curve available" << endl;
exit(0);
}
if (s==80) mod_bits=379;
if (s==128) mod_bits=1223;
words=(mod_bits/MIRACL)+1;
#ifdef MR_SIMPLE_BASE
miracl *mip=mirsys((MIRACL/4)*words,16);
#else
miracl *mip=mirsys(words,0);
mip->IOBASE=16;
#endif
ord=new Big;
S=s;
M=mod_bits;
if (s==80)
{
t=253; u=251; v=59; B=1; CF=1;
*ord=pow((Big)2,M)+pow((Big)2,(M+1)/2)+1; //TYPE=1
}
if (s==128)
{
t=255; u=0; v=0; B=0; CF=5;
*ord=pow((Big)2,M)+pow((Big)2,(M+1)/2)+1; //TYPE=1
}
*ord/=CF;
#ifdef MR_AFFINE_ONLY
ecurve2(-M,t,u,v,(Big)1,(Big)B,TRUE,MR_AFFINE);
#else
ecurve2(-M,t,u,v,(Big)1,(Big)B,TRUE,MR_PROJECTIVE);
#endif
RNG=rng;
}
G1 PFC::mult(const G1& w,const Big& k)
{
G1 z=w;
z.g*=k;
return z;
}
GT PFC::power(const GT& w,const Big& k)
{
GT z;
z.g=powu(w.g,k);
return z;
}
// Precomputation not used here
//
// Spill precomputation on pairing to byte array
//
int PFC::spill(G1& w,char *& bytes)
{
return 0;
}
//
// Restore precomputation on pairing to byte array
//
void PFC::restore(char * bytes,G1& w)
{
}
//
// spill precomputation on GT to byte array
//
int GT::spill(char *& bytes)
{
return 0;
}
//
// restore precomputation for GT from byte array
//
void GT::restore(char *bytes)
{
}
//
// spill precomputation on G1 to byte array
//
int G1::spill(char *& bytes)
{
return 0;
}
//
// restore precomputation for G1 from byte array
//
void G1::restore(char *bytes)
{
}
void PFC::hash_and_map(G1& w,char *ID)
{
Big x0=H1(ID);
while (!w.g.set(x0,x0)) x0+=1;
w.g*=CF;
}
void PFC::random(G1& w)
{
Big x0;
if (RNG==NULL) x0=rand(M,2);
else x0=strong_rand(RNG,M,2);
while (!w.g.set(x0,x0)) x0+=1;
w.g*=CF;
}
Big PFC::hash_to_aes_key(const GT& w)
{
Big m=pow((Big)2,S);
return H2(w.g)%m;
}
// hash to group multiplier
Big PFC::hash_to_group(char *ID)
{
Big m=H1(ID);
Big o=pow((Big)2,2*S);
return m%o;
}
GT operator*(const GT& x,const GT& y)
{
GT z=x;
z.g*=y.g;
return z;
}
// elements in GT are unitary
GT operator/(const GT& x,const GT& y)
{
GT z=x;
z.g*=conj(y.g);
return z;
}
G1 operator+(const G1& x,const G1& y)
{
G1 z=x;
z.g+=y.g;
return z;
}
G1 operator-(const G1& x)
{
G1 z=x;
z.g=-z.g;
return z;
}
// Fast PFC group membership check
BOOL PFC::member(const GT &z)
{
GF2m4x w=z.g;
GF2m4x r=z.g;
if (pow(r,CF)==1) return FALSE;
w.powq();
if (r*w==pow(r,pow((Big)2,(M+1)/2))) return TRUE;
return FALSE;
}
GT PFC::pairing(const G1& x,const G1& y)
{
GT z;
z=miller_loop(x,y);
z=final_exp(z);
return z;
}
GT PFC::multi_pairing(int n,G1 **y,G1 **x)
{
int i;
GT z=1;
for (i=0;i