You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@milagro.apache.org by br...@apache.org on 2018/11/07 23:49:41 UTC

[04/51] [partial] incubator-milagro-crypto git commit: update code

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto/blob/70e3a3a3/version22/c/mpin.c
----------------------------------------------------------------------
diff --git a/version22/c/mpin.c b/version22/c/mpin.c
new file mode 100644
index 0000000..5086ad5
--- /dev/null
+++ b/version22/c/mpin.c
@@ -0,0 +1,1212 @@
+/*
+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.
+*/
+
+/* MPIN Functions */
+
+/* Version 3.0 - supports Time Permits */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "mpin.h"
+
+#define ROUNDUP(a,b) ((a)-1)/(b)+1
+
+/* Special mpin hashing */
+static void mpin_hash(int sha,FP4 *f, ECP *P,octet *w)
+{
+    int i;
+    BIG x,y;
+    char h[64];
+    hash256 sha256;
+    hash512 sha512;
+    char t[6*MODBYTES];  // to hold 6 BIGs
+    int hlen=sha;
+    BIG_copy(x,f->a.a);
+    FP_redc(x);
+    BIG_toBytes(&t[0],x);
+    BIG_copy(x,f->a.b);
+    FP_redc(x);
+    BIG_toBytes(&t[MODBYTES],x);
+    BIG_copy(x,f->b.a);
+    FP_redc(x);
+    BIG_toBytes(&t[2*MODBYTES],x);
+    BIG_copy(x,f->b.b);
+    FP_redc(x);
+    BIG_toBytes(&t[3*MODBYTES],x);
+    ECP_get(x,y,P);
+    BIG_toBytes(&t[4*MODBYTES],x);
+    BIG_toBytes(&t[5*MODBYTES],y);
+
+    OCT_empty(w);
+    switch (sha)
+    {
+    case SHA256:
+        HASH256_init(&sha256);
+        for (i=0; i<6*MODBYTES; i++) HASH256_process(&sha256,t[i]);
+        HASH256_hash(&sha256,h);
+        break;
+    case SHA384:
+        HASH384_init(&sha512);
+        for (i=0; i<6*MODBYTES; i++) HASH384_process(&sha512,t[i]);
+        HASH384_hash(&sha512,h);
+        break;
+    case SHA512:
+        HASH512_init(&sha512);
+        for (i=0; i<6*MODBYTES; i++) HASH512_process(&sha512,t[i]);
+        HASH512_hash(&sha512,h);
+        break;
+    }
+
+    OCT_jbytes(w,h,PAS);
+    for (i=0; i<hlen; i++) h[i]=0;
+}
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+static void map(ECP *P,BIG u,int cb)
+{
+    BIG x,q;
+
+    BIG_rcopy(q,Modulus);
+    BIG_copy(x,u);
+    BIG_mod(x,q);
+
+    while (!ECP_setx(P,x,cb))
+        BIG_inc(x,1);
+}
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+static int unmap(BIG u,int *cb,ECP *P)
+{
+    int s,r=0;
+    BIG x;
+
+    s=ECP_get(x,x,P);
+    BIG_copy(u,x);
+    do
+    {
+        BIG_dec(u,1);
+        r++;
+    }
+    while (!ECP_setx(P,u,s));
+    ECP_setx(P,x,s);
+
+    *cb=s;
+
+    return r;
+}
+
+/* map octet string containing hash to point on curve of correct order */
+static void mapit(octet *h,ECP *P)
+{
+    BIG q,x,c;
+    BIG_fromBytes(x,h->val);
+    BIG_rcopy(q,Modulus);
+    BIG_mod(x,q);
+
+    while (!ECP_setx(P,x,0))
+        BIG_inc(x,1);
+
+    BIG_rcopy(c,CURVE_Cof);
+    ECP_mul(P,c);
+}
+
+/* needed for SOK */
+/* static void mapit2(octet *h,ECP2 *Q) */
+/* { */
+/* 	BIG q,one,Fx,Fy,x,hv; */
+/* 	FP2 X; */
+/* 	ECP2 T,K; */
+/* 	BIG_fromBytes(hv,h->val); */
+/* 	BIG_rcopy(q,Modulus); */
+/* 	BIG_one(one); */
+/* 	BIG_mod(hv,q); */
+
+/* 	for (;;) */
+/* 	{ */
+/* 		FP2_from_BIGs(&X,one,hv); */
+/* 		if (ECP2_setx(Q,&X)) break; */
+/* 		BIG_inc(hv,1);  */
+/* 	} */
+
+/* /\* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez *\/ */
+/* 	BIG_rcopy(Fx,CURVE_Fra); */
+/* 	BIG_rcopy(Fy,CURVE_Frb); */
+/* 	FP2_from_BIGs(&X,Fx,Fy); */
+/* 	BIG_rcopy(x,CURVE_Bnx); */
+
+/* 	ECP2_copy(&T,Q); */
+/* 	ECP2_mul(&T,x); */
+/* 	ECP2_neg(&T);  /\* our x is negative *\/ */
+/* 	ECP2_copy(&K,&T); */
+/* 	ECP2_dbl(&K); */
+/* 	ECP2_add(&K,&T); */
+/* 	ECP2_affine(&K); */
+
+/* 	ECP2_frob(&K,&X); */
+/* 	ECP2_frob(Q,&X); ECP2_frob(Q,&X); ECP2_frob(Q,&X);  */
+/* 	ECP2_add(Q,&T); */
+/* 	ECP2_add(Q,&K); */
+/* 	ECP2_frob(&T,&X); ECP2_frob(&T,&X); */
+/* 	ECP2_add(Q,&T); */
+/* 	ECP2_affine(Q); */
+/* } */
+
+
+
+/* general purpose hash function w=hash(p|n|x|y) */
+static void hashit(int sha,int n,octet *x,octet *w)
+{
+    int i,c[4],hlen;
+    hash256 sha256;
+    hash512 sha512;
+    char hh[64];
+
+    switch (sha)
+    {
+    case SHA256:
+        HASH256_init(&sha256);
+        break;
+    case SHA384:
+        HASH384_init(&sha512);
+        break;
+    case SHA512:
+        HASH512_init(&sha512);
+        break;
+    }
+
+    hlen=sha;
+
+    if (n>0)
+    {
+        c[0]=(n>>24)&0xff;
+        c[1]=(n>>16)&0xff;
+        c[2]=(n>>8)&0xff;
+        c[3]=(n)&0xff;
+        for (i=0; i<4; i++)
+        {
+            switch(sha)
+            {
+            case SHA256:
+                HASH256_process(&sha256,c[i]);
+                break;
+            case SHA384:
+                HASH384_process(&sha512,c[i]);
+                break;
+            case SHA512:
+                HASH512_process(&sha512,c[i]);
+                break;
+            }
+        }
+    }
+    if (x!=NULL) for (i=0; i<x->len; i++)
+        {
+            switch(sha)
+            {
+            case SHA256:
+                HASH256_process(&sha256,x->val[i]);
+                break;
+            case SHA384:
+                HASH384_process(&sha512,x->val[i]);
+                break;
+            case SHA512:
+                HASH512_process(&sha512,x->val[i]);
+                break;
+            }
+        }
+
+    for (i=0; i<hlen; i++) hh[i]=0;
+    switch (sha)
+    {
+    case SHA256:
+        HASH256_hash(&sha256,hh);
+        break;
+    case SHA384:
+        HASH384_hash(&sha512,hh);
+        break;
+    case SHA512:
+        HASH512_hash(&sha512,hh);
+        break;
+    }
+
+    OCT_empty(w);
+
+    if (hlen>=MODBYTES)
+        OCT_jbytes(w,hh,MODBYTES);
+    else
+    {
+        OCT_jbytes(w,hh,hlen);
+        OCT_jbyte(w,0,MODBYTES-hlen);
+    }
+}
+
+unsign32 MPIN_today(void)
+{
+    /* return time in slots since epoch */
+    unsign32 ti=(unsign32)time(NULL);
+    return (uint32_t)(ti/(60*TIME_SLOT_MINUTES));
+}
+
+/* Hash the M-Pin transcript - new */
+
+void MPIN_HASH_ALL(int sha,octet *HID,octet *xID,octet *xCID,octet *SEC,octet *Y,octet *R,octet *W,octet *H)
+{
+    char t[10*MODBYTES+4];
+    octet T= {0,sizeof(t),t};
+
+    OCT_joctet(&T,HID);
+    if (xCID!=NULL) OCT_joctet(&T,xCID);
+    else OCT_joctet(&T,xID);
+    OCT_joctet(&T,SEC);
+    OCT_joctet(&T,Y);
+    OCT_joctet(&T,R);
+    OCT_joctet(&T,W);
+
+    hashit(sha,0,&T,H);
+}
+
+void MPIN_HASH_ID(int sha,octet *ID,octet *HID)
+{
+    hashit(sha,0,ID,HID);
+}
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+int MPIN_ENCODING(csprng *RNG,octet *E)
+{
+    int rn,m,su,sv,res=0;
+
+    BIG q,u,v;
+    ECP P,W;
+
+    if (!ECP_fromOctet(&P,E)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        BIG_rcopy(q,Modulus);
+
+        BIG_randomnum(u,q,RNG);
+
+        su=RAND_byte(RNG);
+        if (su<0) su=-su;
+        su%=2;
+        map(&W,u,su);
+        ECP_sub(&P,&W);
+
+        rn=unmap(v,&sv,&P);
+        m=RAND_byte(RNG);
+        if (m<0) m=-m;
+        m%=rn;
+        BIG_inc(v,m+1);
+        E->val[0]=su+2*sv;
+        BIG_toBytes(&(E->val[1]),u);
+        BIG_toBytes(&(E->val[PFS+1]),v);
+    }
+
+    return res;
+}
+
+int MPIN_DECODING(octet *D)
+{
+    int su,sv;
+    BIG u,v;
+    ECP P,W;
+    int res=0;
+
+    if ((D->val[0]&0x04)!=0) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+
+        BIG_fromBytes(u,&(D->val[1]));
+        BIG_fromBytes(v,&(D->val[PFS+1]));
+
+        su=D->val[0]&1;
+        sv=(D->val[0]>>1)&1;
+
+        map(&W,u,su);
+        map(&P,v,sv);
+
+        ECP_add(&P,&W);
+        ECP_toOctet(D,&P);
+    }
+    return res;
+}
+
+/* R=R1+R2 in group G1 */
+int MPIN_RECOMBINE_G1(octet *R1,octet *R2,octet *R)
+{
+    ECP P,T;
+    int res=0;
+    if (res==0)
+    {
+        if (!ECP_fromOctet(&P,R1)) res=MPIN_INVALID_POINT;
+        if (!ECP_fromOctet(&T,R2)) res=MPIN_INVALID_POINT;
+    }
+    if (res==0)
+    {
+        ECP_add(&P,&T);
+        ECP_toOctet(R,&P);
+    }
+    return res;
+}
+
+/* W=W1+W2 in group G2 */
+int MPIN_RECOMBINE_G2(octet *W1,octet *W2,octet *W)
+{
+    ECP2 Q,T;
+    int res=0;
+    if (!ECP2_fromOctet(&Q,W1)) res=MPIN_INVALID_POINT;
+    if (!ECP2_fromOctet(&T,W2)) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        ECP2_add(&Q,&T);
+        ECP2_toOctet(W,&Q);
+    }
+    return res;
+}
+
+/* create random secret S */
+int MPIN_RANDOM_GENERATE(csprng *RNG,octet* S)
+{
+    BIG r,s;
+
+    BIG_rcopy(r,CURVE_Order);
+    BIG_randomnum(s,r,RNG);
+#ifdef AES_S
+    BIG_mod2m(s,2*AES_S);
+#endif
+    BIG_toBytes(S->val,s);
+    S->len=MODBYTES;
+    return 0;
+}
+
+/* Extract PIN from TOKEN for identity CID */
+int MPIN_EXTRACT_PIN(int sha,octet *CID,int pin,octet *TOKEN)
+{
+    ECP P,R;
+    int res=0;
+    char h[MODBYTES];
+    octet H= {0,sizeof(h),h};
+
+    if (!ECP_fromOctet(&P,TOKEN))  res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        hashit(sha,-1,CID,&H);
+        mapit(&H,&R);
+
+        pin%=MAXPIN;
+
+        ECP_pinmul(&R,pin,PBLEN);
+        ECP_sub(&P,&R);
+
+        ECP_toOctet(TOKEN,&P);
+    }
+    return res;
+}
+
+/* Implement step 2 on client side of MPin protocol - SEC=-(x+y)*SEC */
+int MPIN_CLIENT_2(octet *X,octet *Y,octet *SEC)
+{
+    BIG px,py,r;
+    ECP P;
+    int res=0;
+    BIG_rcopy(r,CURVE_Order);
+    if (!ECP_fromOctet(&P,SEC)) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        BIG_fromBytes(px,X->val);
+        BIG_fromBytes(py,Y->val);
+        BIG_add(px,px,py);
+        BIG_mod(px,r);
+        //	BIG_sub(px,r,px);
+        PAIR_G1mul(&P,px);
+        ECP_neg(&P);
+        ECP_toOctet(SEC,&P);
+    }
+    return res;
+}
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+
+int MPIN_GET_G1_MULTIPLE(csprng *RNG,int type,octet *X,octet *G,octet *W)
+{
+    ECP P;
+    BIG r,x;
+    int res=0;
+    if (RNG!=NULL)
+    {
+        BIG_rcopy(r,CURVE_Order);
+        BIG_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES;
+        BIG_toBytes(X->val,x);
+    }
+    else
+        BIG_fromBytes(x,X->val);
+
+    if (type==0)
+    {
+        if (!ECP_fromOctet(&P,G)) res=MPIN_INVALID_POINT;
+    }
+    else mapit(G,&P);
+
+    if (res==0)
+    {
+        PAIR_G1mul(&P,x);
+        ECP_toOctet(W,&P);
+    }
+    return res;
+}
+
+/*
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ W=x*G where G is point on the curve
+ if type==1 W=(x^-1)G
+*/
+
+int MPIN_GET_G2_MULTIPLE(csprng *RNG,int type,octet *X,octet *G,octet *W)
+{
+    ECP2 P;
+    BIG r,x;
+    int res=0;
+    BIG_rcopy(r,CURVE_Order);
+    if (RNG!=NULL)
+    {
+        BIG_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES;
+        BIG_toBytes(X->val,x);
+    }
+    else
+    {
+        BIG_fromBytes(x,X->val);
+        if (type==1) BIG_invmodp(x,x,r);
+    }
+
+    if (!ECP2_fromOctet(&P,G)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        PAIR_G2mul(&P,x);
+        ECP2_toOctet(W,&P);
+    }
+    return res;
+}
+
+
+
+/* Client secret CST=s*H(CID) where CID is client ID and s is master secret */
+/* CID is hashed externally */
+int MPIN_GET_CLIENT_SECRET(octet *S,octet *CID,octet *CST)
+{
+    return MPIN_GET_G1_MULTIPLE(NULL,1,S,CID,CST);
+}
+
+/* Implement step 1 on client side of MPin protocol */
+int MPIN_CLIENT_1(int sha,int date,octet *CLIENT_ID,csprng *RNG,octet *X,int pin,octet *TOKEN,octet *SEC,octet *xID,octet *xCID,octet *PERMIT)
+{
+    BIG r,x;
+    ECP P,T,W;
+    int res=0;
+    char h[MODBYTES];
+    octet H= {0,sizeof(h),h};
+
+    BIG_rcopy(r,CURVE_Order);
+    if (RNG!=NULL)
+    {
+        BIG_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES;
+        BIG_toBytes(X->val,x);
+    }
+    else
+        BIG_fromBytes(x,X->val);
+
+    hashit(sha,-1,CLIENT_ID,&H);
+    mapit(&H,&P);
+
+    if (!ECP_fromOctet(&T,TOKEN)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        pin%=MAXPIN;
+
+        ECP_copy(&W,&P);				// W=H(ID)
+        ECP_pinmul(&W,pin,PBLEN);			// W=alpha.H(ID)
+        ECP_add(&T,&W);					// T=Token+alpha.H(ID) = s.H(ID)
+
+        if (date)
+        {
+            if (PERMIT!=NULL)
+            {
+                if (!ECP_fromOctet(&W,PERMIT)) res=MPIN_INVALID_POINT;
+                ECP_add(&T,&W);					// SEC=s.H(ID)+s.H(T|ID)
+            }
+            hashit(sha,date,&H,&H);
+            mapit(&H,&W);
+            if (xID!=NULL)
+            {
+                PAIR_G1mul(&P,x);				// P=x.H(ID)
+                ECP_toOctet(xID,&P);  // xID
+                PAIR_G1mul(&W,x);               // W=x.H(T|ID)
+                ECP_add(&P,&W);
+            }
+            else
+            {
+                ECP_add(&P,&W);
+                PAIR_G1mul(&P,x);
+            }
+            if (xCID!=NULL) ECP_toOctet(xCID,&P);  // U
+        }
+        else
+        {
+            if (xID!=NULL)
+            {
+                PAIR_G1mul(&P,x);				// P=x.H(ID)
+                ECP_toOctet(xID,&P);  // xID
+            }
+        }
+    }
+
+    if (res==0)
+        ECP_toOctet(SEC,&T);  // V
+
+    return res;
+}
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+int MPIN_GET_SERVER_SECRET(octet *S,octet *SST)
+{
+    BIG r,s;
+    FP2 qx,qy;
+    ECP2 Q;
+    int res=0;
+
+    BIG_rcopy(r,CURVE_Order);
+    BIG_rcopy(qx.a,CURVE_Pxa);
+    FP_nres(qx.a);
+    BIG_rcopy(qx.b,CURVE_Pxb);
+    FP_nres(qx.b);
+    BIG_rcopy(qy.a,CURVE_Pya);
+    FP_nres(qy.a);
+    BIG_rcopy(qy.b,CURVE_Pyb);
+    FP_nres(qy.b);
+    ECP2_set(&Q,&qx,&qy);
+
+    if (res==0)
+    {
+        BIG_fromBytes(s,S->val);
+        PAIR_G2mul(&Q,s);
+        ECP2_toOctet(SST,&Q);
+    }
+
+    return res;
+}
+
+
+/* Time Permit CTT=s*H(date|H(CID)) where s is master secret */
+int MPIN_GET_CLIENT_PERMIT(int sha,int date,octet *S,octet *CID,octet *CTT)
+{
+    BIG s;
+    ECP P;
+    char h[MODBYTES];
+    octet H= {0,sizeof(h),h};
+
+    hashit(sha,date,CID,&H);
+
+    mapit(&H,&P);
+    BIG_fromBytes(s,S->val);
+    PAIR_G1mul(&P,s);
+
+    ECP_toOctet(CTT,&P);
+    return 0;
+}
+
+// if date=0 only use HID, set HCID=NULL
+// if date and PE, use HID and HCID
+
+/* Outputs H(CID) and H(CID)+H(T|H(CID)) for time permits. If no time permits set HTID=NULL */
+void MPIN_SERVER_1(int sha,int date,octet *CID,octet *HID,octet *HTID)
+{
+    char h[MODBYTES];
+    octet H= {0,sizeof(h),h};
+    ECP P,R;
+
+#ifdef USE_ANONYMOUS
+    mapit(CID,&P);
+#else
+    hashit(sha,-1,CID,&H);
+    mapit(&H,&P);
+#endif
+
+    ECP_toOctet(HID,&P);  // new
+
+    if (date)
+    {
+        //	if (HID!=NULL) ECP_toOctet(HID,&P);
+#ifdef USE_ANONYMOUS
+        hashit(sha,date,CID,&H);
+#else
+        hashit(sha,date,&H,&H);
+#endif
+        mapit(&H,&R);
+        ECP_add(&P,&R);
+        ECP_toOctet(HTID,&P);
+    }
+    //else ECP_toOctet(HID,&P);
+
+}
+
+/* Implement M-Pin on server side */
+int MPIN_SERVER_2(int date,octet *HID,octet *HTID,octet *Y,octet *SST,octet *xID,octet *xCID,octet *mSEC,octet *E,octet *F)
+{
+    BIG px,py,y;
+    FP2 qx,qy;
+    FP12 g;
+    ECP2 Q,sQ;
+    ECP P,R;
+    int res=0;
+
+    BIG_rcopy(qx.a,CURVE_Pxa);
+    FP_nres(qx.a);
+    BIG_rcopy(qx.b,CURVE_Pxb);
+    FP_nres(qx.b);
+    BIG_rcopy(qy.a,CURVE_Pya);
+    FP_nres(qy.a);
+    BIG_rcopy(qy.b,CURVE_Pyb);
+    FP_nres(qy.b);
+
+    if (!ECP2_set(&Q,&qx,&qy)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        if (!ECP2_fromOctet(&sQ,SST)) res=MPIN_INVALID_POINT;
+    }
+
+    if (res==0)
+    {
+        if (date)
+        {
+            BIG_fromBytes(px,&(xCID->val[1]));
+            BIG_fromBytes(py,&(xCID->val[PFS+1]));
+        }
+        else
+        {
+            BIG_fromBytes(px,&(xID->val[1]));
+            BIG_fromBytes(py,&(xID->val[PFS+1]));
+        }
+        if (!ECP_set(&R,px,py)) res=MPIN_INVALID_POINT; // x(A+AT)
+    }
+    if (res==0)
+    {
+        BIG_fromBytes(y,Y->val);
+        if (date)
+        {
+            if (!ECP_fromOctet(&P,HTID))  res=MPIN_INVALID_POINT;
+        }
+        else
+        {
+            if (!ECP_fromOctet(&P,HID))  res=MPIN_INVALID_POINT;
+        }
+    }
+    if (res==0)
+    {
+        PAIR_G1mul(&P,y);  // y(A+AT)
+        ECP_add(&P,&R); // x(A+AT)+y(A+T)
+        if (!ECP_fromOctet(&R,mSEC))  res=MPIN_INVALID_POINT; // V
+    }
+    if (res==0)
+    {
+        PAIR_double_ate(&g,&Q,&R,&sQ,&P);
+        PAIR_fexp(&g);
+
+        if (!FP12_isunity(&g))
+        {
+            if (HID!=NULL && xID!=NULL && E!=NULL && F !=NULL)
+            {
+                /* xID is set to NULL if there is no way to calculate PIN error */
+                FP12_toOctet(E,&g);
+
+                /* Note error is in the PIN, not in the time permit! Hence the need to exclude Time Permit from this check */
+
+                if (date)
+                {
+                    if (!ECP_fromOctet(&P,HID)) res=MPIN_INVALID_POINT;
+                    if (!ECP_fromOctet(&R,xID)) res=MPIN_INVALID_POINT; // U
+
+                    if (res==0)
+                    {
+                        PAIR_G1mul(&P,y);  // yA
+                        ECP_add(&P,&R); // yA+xA
+                    }
+                }
+                if (res==0)
+                {
+                    PAIR_ate(&g,&Q,&P);
+                    PAIR_fexp(&g);
+                    FP12_toOctet(F,&g);
+                }
+            }
+            res=MPIN_BAD_PIN;
+        }
+    }
+
+    return res;
+}
+
+#if MAXPIN==10000
+#define MR_TS 10  /* 2^10/10 approx = sqrt(MAXPIN) */
+#define TRAP 200  /* 2*sqrt(MAXPIN) */
+#endif
+
+#if MAXPIN==1000000
+#define MR_TS 14
+#define TRAP 2000
+#endif
+
+/* Pollards kangaroos used to return PIN error */
+int MPIN_KANGAROO(octet *E,octet *F)
+{
+    int i,j,m,s,dn,dm,steps;
+    int distance[MR_TS];
+    FP12 ge,gf,t,table[MR_TS];
+    int res=0;
+    // BIG w;
+
+    FP12_fromOctet(&ge,E);
+    FP12_fromOctet(&gf,F);
+
+    FP12_copy(&t,&gf);
+
+    for (s=1,m=0; m<MR_TS; m++)
+    {
+        distance[m]=s;
+        FP12_copy(&table[m],&t);
+        s*=2;
+        FP12_usqr(&t,&t);
+        FP12_reduce(&t);
+    }
+
+    FP12_one(&t);
+
+    for (dn=0,j=0; j<TRAP; j++)
+    {
+
+        //BIG_copy(w,t.a.a.a);
+        //FP_redc(w);
+        //i=BIG_lastbits(w,20)%MR_TS;
+
+        i=t.a.a.a[0]%MR_TS;
+
+        FP12_mul(&t,&table[i]);
+        FP12_reduce(&t);
+        dn+=distance[i];
+    }
+
+    FP12_conj(&gf,&t);
+    steps=0;
+    dm=0;
+    while (dm-dn<MAXPIN)
+    {
+        steps++;
+        if (steps>4*TRAP) break;
+
+        //BIG_copy(w,ge.a.a.a);
+        //FP_redc(w);
+        //i=BIG_lastbits(w,20)%MR_TS;
+
+        i=ge.a.a.a[0]%MR_TS;
+
+        FP12_mul(&ge,&table[i]);
+        FP12_reduce(&ge);
+        dm+=distance[i];
+        if (FP12_equals(&ge,&t))
+        {
+            res=dm-dn;
+            break;
+        }
+        if (FP12_equals(&ge,&gf))
+        {
+            res=dn-dm;
+            break;
+        }
+    }
+    if (steps>4*TRAP || dm-dn>=MAXPIN)
+    {
+        res=0;    /* Trap Failed  - probable invalid token */
+    }
+
+    return res;
+}
+
+/* Functions to support M-Pin Full */
+
+int MPIN_PRECOMPUTE(octet *TOKEN,octet *CID,octet *CP,octet *G1,octet *G2)
+{
+    ECP P,T;
+    ECP2 Q;
+    FP2 qx,qy;
+    FP12 g;
+    int res=0;
+
+    if (!ECP_fromOctet(&T,TOKEN)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        mapit(CID,&P);
+        if (CP!=NULL)
+        {
+            if (!ECP2_fromOctet(&Q,CP)) res=MPIN_INVALID_POINT;
+        }
+        else
+        {
+            BIG_rcopy(qx.a,CURVE_Pxa);
+            FP_nres(qx.a);
+            BIG_rcopy(qx.b,CURVE_Pxb);
+            FP_nres(qx.b);
+            BIG_rcopy(qy.a,CURVE_Pya);
+            FP_nres(qy.a);
+            BIG_rcopy(qy.b,CURVE_Pyb);
+            FP_nres(qy.b);
+            if (!ECP2_set(&Q,&qx,&qy)) res=MPIN_INVALID_POINT;
+        }
+    }
+    if (res==0)
+    {
+        PAIR_ate(&g,&Q,&T);
+        PAIR_fexp(&g);
+
+        FP12_toOctet(G1,&g);
+        if (G2!=NULL)
+        {
+            PAIR_ate(&g,&Q,&P);
+            PAIR_fexp(&g);
+            FP12_toOctet(G2,&g);
+        }
+    }
+    return res;
+}
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+int MPIN_CLIENT_KEY(int sha,octet *G1,octet *G2,int pin,octet *R,octet *X,octet *H,octet *wCID,octet *CK)
+{
+    FP12 g1,g2;
+    FP4 c,cp,cpm1,cpm2;
+    FP2 f;
+    ECP W;
+    int res=0;
+    BIG r,z,x,q,m,a,b,h;
+
+    FP12_fromOctet(&g1,G1);
+    FP12_fromOctet(&g2,G2);
+    BIG_fromBytes(z,R->val);
+    BIG_fromBytes(x,X->val);
+    BIG_fromBytes(h,H->val);
+
+    if (!ECP_fromOctet(&W,wCID)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        BIG_rcopy(r,CURVE_Order);
+        BIG_add(z,z,h);    // new
+        BIG_mod(z,r);
+
+        PAIR_G1mul(&W,x);
+
+        BIG_rcopy(a,CURVE_Fra);
+        BIG_rcopy(b,CURVE_Frb);
+        FP2_from_BIGs(&f,a,b);
+
+        BIG_rcopy(q,Modulus);
+        BIG_copy(m,q);
+        BIG_mod(m,r);
+
+        BIG_copy(a,z);
+        BIG_mod(a,m);
+
+        BIG_copy(b,z);
+        BIG_sdiv(b,m);
+
+        FP12_pinpow(&g2,pin,PBLEN);
+        FP12_mul(&g1,&g2);
+
+        FP12_trace(&c,&g1);
+
+        FP12_copy(&g2,&g1);
+        FP12_frob(&g2,&f);
+        FP12_trace(&cp,&g2);
+
+        FP12_conj(&g1,&g1);
+        FP12_mul(&g2,&g1);
+        FP12_trace(&cpm1,&g2);
+        FP12_mul(&g2,&g1);
+        FP12_trace(&cpm2,&g2);
+
+        FP4_xtr_pow2(&c,&cp,&c,&cpm1,&cpm2,a,b);
+        mpin_hash(sha,&c,&W,CK);
+
+    }
+    return res;
+}
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+int MPIN_SERVER_KEY(int sha,octet *Z,octet *SST,octet *W,octet *H,octet *HID,octet *xID,octet *xCID,octet *SK)
+{
+    int res=0;
+    FP12 g;
+    FP4 c;
+    ECP R,U,A;
+    ECP2 sQ;
+    BIG w,h;
+
+    if (!ECP2_fromOctet(&sQ,SST)) res=MPIN_INVALID_POINT;
+    if (!ECP_fromOctet(&R,Z)) res=MPIN_INVALID_POINT;
+
+
+    if (!ECP_fromOctet(&A,HID)) res=MPIN_INVALID_POINT;
+
+    // new
+    if (xCID!=NULL)
+    {
+        if (!ECP_fromOctet(&U,xCID)) res=MPIN_INVALID_POINT;
+    }
+    else
+    {
+        if (!ECP_fromOctet(&U,xID)) res=MPIN_INVALID_POINT;
+    }
+    BIG_fromBytes(w,W->val);
+    BIG_fromBytes(h,H->val);
+
+
+    PAIR_ate(&g,&sQ,&A);
+    PAIR_fexp(&g);
+
+    if (res==0)
+    {
+        PAIR_G1mul(&A,h);
+        ECP_add(&R,&A);  // new
+        PAIR_ate(&g,&sQ,&R);
+        PAIR_fexp(&g);
+        PAIR_G1mul(&U,w);
+        FP12_trace(&c,&g);
+        mpin_hash(sha,&c,&U,SK);
+    }
+    return res;
+}
+
+unsign32 MPIN_GET_TIME(void)
+{
+    return (unsign32)time(NULL);
+}
+
+/* Generate Y = H(TimeValue, xCID/xID) */
+void MPIN_GET_Y(int sha,int TimeValue,octet *xCID,octet *Y)
+{
+    BIG q,y;
+    char h[MODBYTES];
+    octet H= {0,sizeof(h),h};
+
+    hashit(sha,TimeValue,xCID,&H);
+    BIG_fromBytes(y,H.val);
+    BIG_rcopy(q,CURVE_Order);
+    BIG_mod(y,q);
+    BIG_toBytes(Y->val,y);
+    Y->len=PGS;
+}
+
+/* One pass MPIN Client */
+int MPIN_CLIENT(int sha,int date,octet *ID,csprng *RNG,octet *X,int pin,octet *TOKEN,octet *V,octet *U,octet *UT,octet *TP,octet *MESSAGE,int TimeValue,octet *Y)
+{
+    int rtn=0;
+    char m[M_SIZE];
+    octet M= {0,sizeof(m),m};
+
+    octet *pID;
+    if (date == 0)
+        pID = U;
+    else
+        pID = UT;
+
+    rtn = MPIN_CLIENT_1(sha,date,ID,RNG,X,pin,TOKEN,V,U,UT,TP);
+    if (rtn != 0)
+        return rtn;
+
+    OCT_joctet(&M,pID);
+    if (MESSAGE!=NULL)
+    {
+        OCT_joctet(&M,MESSAGE);
+    }
+
+    MPIN_GET_Y(sha,TimeValue,&M,Y);
+
+    rtn = MPIN_CLIENT_2(X,Y,V);
+    if (rtn != 0)
+        return rtn;
+
+    return 0;
+}
+
+/* One pass MPIN Server */
+int MPIN_SERVER(int sha,int date,octet *HID,octet *HTID,octet *Y,octet *sQ,octet *U,octet *UT,octet *V,octet *E,octet *F,octet *ID,octet *MESSAGE,int TimeValue)
+{
+    int rtn=0;
+    char m[M_SIZE];
+    octet M= {0,sizeof(m),m};
+
+    octet *pU;
+    if (date == 0)
+        pU = U;
+    else
+        pU = UT;
+
+    MPIN_SERVER_1(sha,date,ID,HID,HTID);
+
+    OCT_joctet(&M,pU);
+    if (MESSAGE!=NULL)
+    {
+        OCT_joctet(&M,MESSAGE);
+    }
+
+    MPIN_GET_Y(sha,TimeValue,&M,Y);
+
+    rtn = MPIN_SERVER_2(date,HID,HTID,Y,sQ,U,UT,V,E,F);
+    if (rtn != 0)
+        return rtn;
+
+    return 0;
+}
+
+/* AES-GCM Encryption of octets, K is key, H is header,
+   P is plaintext, C is ciphertext, T is authentication tag */
+void MPIN_AES_GCM_ENCRYPT(octet *K,octet *IV,octet *H,octet *P,octet *C,octet *T)
+{
+    gcm g;
+    GCM_init(&g,K->len,K->val,IV->len,IV->val);
+    GCM_add_header(&g,H->val,H->len);
+    GCM_add_plain(&g,C->val,P->val,P->len);
+    C->len=P->len;
+    GCM_finish(&g,T->val);
+    T->len=16;
+}
+
+/* AES-GCM Decryption of octets, K is key, H is header,
+   P is plaintext, C is ciphertext, T is authentication tag */
+void MPIN_AES_GCM_DECRYPT(octet *K,octet *IV,octet *H,octet *C,octet *P,octet *T)
+{
+    gcm g;
+    GCM_init(&g,K->len,K->val,IV->len,IV->val);
+    GCM_add_header(&g,H->val,H->len);
+    GCM_add_cipher(&g,P->val,C->val,C->len);
+    P->len=C->len;
+    GCM_finish(&g,T->val);
+    T->len=16;
+}
+
+/* Return the Field size */
+int MPIN_FS()
+{
+    return PFS;
+}
+
+/* Return the Group size */
+int MPIN_GS()
+{
+    return PGS;
+}
+
+/*
+int MPIN_TEST_PAIRING(octet *CID,octet *R)
+{
+    BIG b,px;
+	FP2 qx,qy;
+	FP12 g;
+    ECP2 Q;
+	ECP P;
+    int res=0;
+
+	hashit(-1,CID,&P);
+	BIG_rcopy(qx.a,CURVE_Pxa); FP_nres(qx.a);
+	BIG_rcopy(qx.b,CURVE_Pxb); FP_nres(qx.b);
+	BIG_rcopy(qy.a,CURVE_Pya); FP_nres(qy.a);
+	BIG_rcopy(qy.b,CURVE_Pyb); FP_nres(qy.b);
+
+	if (!ECP2_set(&Q,&qx,&qy))  res=MPIN_INVALID_POINT;
+
+	if (res==0)
+	{
+		PAIR_ate(&g,&Q,&P);
+        PAIR_fexp(&g);
+		FP12_trace(&(g.a),&g);
+
+		BIG_copy(b,g.a.a.a); FP_redc(b); printf("trace pairing= "); BIG_output(b); printf("\n");
+		BIG_copy(b,g.a.a.b); FP_redc(b); printf("trace pairing= "); BIG_output(b); printf("\n");
+		BIG_copy(b,g.a.b.a); FP_redc(b); printf("trace pairing= "); BIG_output(b); printf("\n");
+		BIG_copy(b,g.a.b.b); FP_redc(b); printf("trace pairing= "); BIG_output(b); printf("\n");
+
+	}
+
+    return res;
+}
+*/
+
+/*
+int main()
+{
+	ECP2 X;
+	FP2 x,y,rhs;
+	BIG r;
+	char hcid[HASH_BYTES],client_id[100];
+	octet HCID={0,sizeof(hcid),hcid};
+	octet CLIENT_ID={0,sizeof(client_id),client_id};
+
+	OCT_jstring(&CLIENT_ID,"testUser@certivox.com");
+	MPIN_HASH_ID(&CLIENT_ID,&HCID);
+
+	printf("Client ID= "); OCT_output_string(&CLIENT_ID); printf("\n");
+
+	mapit2(&HCID,&X);
+
+	ECP2_output(&X);
+
+	BIG_rcopy(r,CURVE_Order);
+
+	ECP2_mul(&X,r);
+
+	ECP2_output(&X);
+
+}
+*/

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto/blob/70e3a3a3/version22/c/mpin.h
----------------------------------------------------------------------
diff --git a/version22/c/mpin.h b/version22/c/mpin.h
new file mode 100644
index 0000000..9627eb8
--- /dev/null
+++ b/version22/c/mpin.h
@@ -0,0 +1,408 @@
+/*
+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.
+*/
+
+/**
+ * @file mpin.h
+ * @author Mike Scott and Kealan McCusker
+ * @date 2nd June 2015
+ * @brief M-Pin Header file
+ *
+ * Allows some user configuration
+ * defines structures
+ * declares functions
+ *
+ */
+
+#ifndef MPIN_H
+#define MPIN_H
+
+#include "amcl.h"
+
+/* Field size is assumed to be greater than or equal to group size */
+
+#define PGS MODBYTES  /**< MPIN Group Size */
+#define PFS MODBYTES  /**< MPIN Field Size */
+#define PAS 16        /**< MPIN Symmetric Key Size */
+
+#define MPIN_OK             0   /**< Function completed without error */
+#define MPIN_INVALID_POINT  -14	/**< Point is NOT on the curve */
+#define MPIN_BAD_PIN        -19 /**< Bad PIN number entered */
+
+
+/* Configure your PIN here */
+
+#ifdef CMAKE
+#define MAXPIN @AMCL_MAXPIN@ /**< max PIN */
+#define PBLEN @AMCL_PBLEN@   /**< max length of PIN in bits */
+#else
+#define MAXPIN 10000         /**< max PIN */
+#define PBLEN 14             /**< max length of PIN in bits */
+#endif
+
+#define TIME_SLOT_MINUTES 1440  /**< Time Slot = 1 day */
+#define HASH_TYPE_MPIN SHA256   /**< Choose Hash function */
+
+#define MESSAGE_SIZE 256  /**< Signature message size  */
+#define M_SIZE (MESSAGE_SIZE+2*PFS+1)   /**< Signature message size and G1 size */
+
+/* MPIN support functions */
+
+/* MPIN primitives */
+
+/**	@brief Hash an M-Pin Identity to an octet string
+ *
+ 	@param h is the hash type
+	@param ID an octet containing the identity
+	@param HID an octet containing the hashed identity
+ */
+void MPIN_HASH_ID(int h,octet *ID,octet *HID);
+/**	@brief Get epoch time as unsigned integer
+ *
+	@return current epoch time in seconds
+ */
+unsign32 MPIN_GET_TIME(void);
+/**	@brief Generate Y=H(s,O), where s is epoch time, O is an octet, and H(.) is a hash function
+ *
+  	@param h is the hash type
+	@param t is epoch time in seconds
+	@param O is an input octet
+	@param Y is the output octet
+ */
+void MPIN_GET_Y(int h,int t,octet *O,octet *Y);
+/**	@brief Extract a PIN number from a client secret
+ *
+  	@param h is the hash type
+	@param ID is the input client identity
+	@param pin is an input PIN number
+	@param CS is the client secret from which the PIN is to be extracted
+	@return 0 or an error code
+ */
+int MPIN_EXTRACT_PIN(int h,octet *ID,int pin,octet *CS);
+/**	@brief Perform client side of the one-pass version of the M-Pin protocol
+ *
+	If Time Permits are disabled, set d = 0, and UT is not generated and can be set to NULL.
+	If Time Permits are enabled, and PIN error detection is OFF, U is not generated and can be set to NULL.
+	If Time Permits are enabled, and PIN error detection is ON, U and UT are both generated.
+ 	@param h is the hash type
+	@param d is input date, in days since the epoch. Set to 0 if Time permits disabled
+	@param ID is the input client identity
+	@param R is a pointer to a cryptographically secure random number generator
+	@param x an output internally randomly generated if R!=NULL, otherwise must be provided as an input
+	@param pin is the input PIN number
+	@param T is the input M-Pin token (the client secret with PIN portion removed)
+	@param V is output = -(x+y)(CS+TP), where CS is the reconstructed client secret, and TP is the time permit
+	@param U is output = x.H(ID)
+	@param UT is output = x.(H(ID)+H(d|H(ID)))
+	@param TP is the input time permit
+	@param MESSAGE is the message to be signed
+	@param t is input epoch time in seconds - a timestamp
+	@param y is output H(t|U) or H(t|UT) if Time Permits enabled
+	@return 0 or an error code
+ */
+int MPIN_CLIENT(int h,int d,octet *ID,csprng *R,octet *x,int pin,octet *T,octet *V,octet *U,octet *UT,octet *TP, octet* MESSAGE, int t, octet *y);
+/**	@brief Perform first pass of the client side of the 3-pass version of the M-Pin protocol
+ *
+	If Time Permits are disabled, set d = 0, and UT is not generated and can be set to NULL.
+	If Time Permits are enabled, and PIN error detection is OFF, U is not generated and can be set to NULL.
+	If Time Permits are enabled, and PIN error detection is ON, U and UT are both generated.
+ 	@param h is the hash type
+	@param d is input date, in days since the epoch. Set to 0 if Time permits disabled
+	@param ID is the input client identity
+	@param R is a pointer to a cryptographically secure random number generator
+	@param x an output internally randomly generated if R!=NULL, otherwise must be provided as an input
+	@param pin is the input PIN number
+	@param T is the input M-Pin token (the client secret with PIN portion removed)
+	@param S is output = CS+TP, where CS=is the reconstructed client secret, and TP is the time permit
+	@param U is output = x.H(ID)
+	@param UT is output = x.(H(ID)+H(d|H(ID)))
+	@param TP is the input time permit
+	@return 0 or an error code
+ */
+int MPIN_CLIENT_1(int h,int d,octet *ID,csprng *R,octet *x,int pin,octet *T,octet *S,octet *U,octet *UT,octet *TP);
+/**	@brief Generate a random group element
+ *
+	@param R is a pointer to a cryptographically secure random number generator
+	@param S is the output random octet
+	@return 0 or an error code
+ */
+int MPIN_RANDOM_GENERATE(csprng *R,octet *S);
+/**	@brief Perform second pass of the client side of the 3-pass version of the M-Pin protocol
+ *
+	@param x an input, a locally generated random number
+	@param y an input random challenge from the server
+	@param V on output = -(x+y).V
+	@return 0 or an error code
+ */
+int MPIN_CLIENT_2(octet *x,octet *y,octet *V);
+/**	@brief Perform server side of the one-pass version of the M-Pin protocol
+ *
+	If Time Permits are disabled, set d = 0, and UT and HTID are not generated and can be set to NULL.
+	If Time Permits are enabled, and PIN error detection is OFF, U and HID are not needed and can be set to NULL.
+	If Time Permits are enabled, and PIN error detection is ON, U, UT, HID and HTID are all required.
+ 	@param h is the hash type
+	@param d is input date, in days since the epoch. Set to 0 if Time permits disabled
+	@param HID is output H(ID), a hash of the client ID
+	@param HTID is output H(ID)+H(d|H(ID))
+	@param y is output H(t|U) or H(t|UT) if Time Permits enabled
+	@param SS is the input server secret
+	@param U is input from the client = x.H(ID)
+	@param UT is input from the client= x.(H(ID)+H(d|H(ID)))
+	@param V is an input from the client
+	@param E is an output to help the Kangaroos to find the PIN error, or NULL if not required
+	@param F is an output to help the Kangaroos to find the PIN error, or NULL if not required
+	@param ID is the input claimed client identity
+	@param MESSAGE is the message to be signed
+	@param t is input epoch time in seconds - a timestamp
+	@return 0 or an error code
+ */
+int MPIN_SERVER(int h,int d,octet *HID,octet *HTID,octet *y,octet *SS,octet *U,octet *UT,octet *V,octet *E,octet *F,octet *ID,octet *MESSAGE, int t);
+/**	@brief Perform first pass of the server side of the 3-pass version of the M-Pin protocol
+ *
+ 	@param h is the hash type
+	@param d is input date, in days since the epoch. Set to 0 if Time permits disabled
+	@param ID is the input claimed client identity
+	@param HID is output H(ID), a hash of the client ID
+	@param HTID is output H(ID)+H(d|H(ID))
+	@return 0 or an error code
+ */
+void MPIN_SERVER_1(int h,int d,octet *ID,octet *HID,octet *HTID);
+/**	@brief Perform third pass on the server side of the 3-pass version of the M-Pin protocol
+ *
+	If Time Permits are disabled, set d = 0, and UT and HTID are not needed and can be set to NULL.
+	If Time Permits are enabled, and PIN error detection is OFF, U and HID are not needed and can be set to NULL.
+	If Time Permits are enabled, and PIN error detection is ON, U, UT, HID and HTID are all required.
+	@param d is input date, in days since the epoch. Set to 0 if Time permits disabled
+	@param HID is input H(ID), a hash of the client ID
+	@param HTID is input H(ID)+H(d|H(ID))
+	@param y is the input server's randomly generated challenge
+	@param SS is the input server secret
+	@param U is input from the client = x.H(ID)
+	@param UT is input from the client= x.(H(ID)+H(d|H(ID)))
+	@param V is an input from the client
+	@param E is an output to help the Kangaroos to find the PIN error, or NULL if not required
+	@param F is an output to help the Kangaroos to find the PIN error, or NULL if not required
+	@return 0 or an error code
+ */
+int MPIN_SERVER_2(int d,octet *HID,octet *HTID,octet *y,octet *SS,octet *U,octet *UT,octet *V,octet *E,octet *F);
+/**	@brief Add two members from the group G1
+ *
+	@param Q1 an input member of G1
+	@param Q2 an input member of G1
+	@param Q an output member of G1 = Q1+Q2
+	@return 0 or an error code
+ */
+int MPIN_RECOMBINE_G1(octet *Q1,octet *Q2,octet *Q);
+/**	@brief Add two members from the group G2
+ *
+	@param P1 an input member of G2
+	@param P2 an input member of G2
+	@param P an output member of G2 = P1+P2
+	@return 0 or an error code
+ */
+int MPIN_RECOMBINE_G2(octet *P1,octet *P2,octet *P);
+/**	@brief Use Kangaroos to find PIN error
+ *
+	@param E a member of the group GT
+	@param F a member of the group GT =  E^e
+	@return 0 if Kangaroos failed, or the PIN error e
+ */
+int MPIN_KANGAROO(octet *E,octet *F);
+/**	@brief Encoding of a Time Permit to make it indistinguishable from a random string
+ *
+	@param R is a pointer to a cryptographically secure random number generator
+	@param TP is the input time permit, obfuscated on output
+	@return 0 or an error code
+ */
+int MPIN_ENCODING(csprng *R,octet *TP);
+/**	@brief Encoding of an obfuscated Time Permit
+ *
+	@param TP is the input obfuscated time permit, restored on output
+	@return 0 or an error code
+ */
+int MPIN_DECODING(octet *TP);
+/**	@brief Supply today's date as days from the epoch
+ *
+	@return today's date, as number of days elapsed since the epoch
+ */
+unsign32 MPIN_today(void);
+
+/**	@brief Find a random multiple of a point in G1
+ *
+	@param R is a pointer to a cryptographically secure random number generator
+	@param type determines type of action to be taken
+	@param x an output internally randomly generated if R!=NULL, otherwise must be provided as an input
+	@param G if type=0 a point in G1, else an octet to be mapped to G1
+	@param W the output =x.G or x.M(G), where M(.) is a mapping
+	@return 0 or an error code
+ */
+int MPIN_GET_G1_MULTIPLE(csprng *R,int type,octet *x,octet *G,octet *W);
+/**	@brief Find a random multiple of a point in G1
+ *
+	@param R is a pointer to a cryptographically secure random number generator
+	@param type determines type of action to betaken
+	@param x an output internally randomly generated if R!=NULL, otherwise must be provided as an input
+	@param G a point in G2
+	@param W the output =x.G or (1/x).G
+	@return 0 or an error code
+ */
+int MPIN_GET_G2_MULTIPLE(csprng *R,int type,octet *x,octet *G,octet *W);
+/** @brief Hash the session transcript
+ 	@param h is the hash type
+	@param I is the hashed input client ID = H(ID)
+	@param U is the client output = x.H(ID)
+	@param CU is the client output = x.(H(ID)+H(T|H(ID)))
+	@param Y is the server challenge
+	@param V is the client part response
+	@param R is the client part response
+	@param W is the server part response
+	@param H the output is the hash of all of the above that apply
+*/
+void MPIN_HASH_ALL(int h,octet *I,octet *U,octet *CU,octet *Y,octet *V,octet *R,octet *W,octet *H);
+/**	@brief Create a client secret in G1 from a master secret and the client ID
+ *
+	@param S is an input master secret
+	@param ID is the input client identity
+	@param CS is the full client secret = s.H(ID)
+	@return 0 or an error code
+ */
+int MPIN_GET_CLIENT_SECRET(octet *S,octet *ID,octet *CS);
+/**	@brief Create a Time Permit in G1 from a master secret and the client ID
+ *
+  	@param h is the hash type
+	@param d is input date, in days since the epoch.
+	@param S is an input master secret
+	@param ID is the input client identity
+	@param TP is a Time Permit for the given date = s.H(d|H(ID))
+	@return 0 or an error code
+ */
+int MPIN_GET_CLIENT_PERMIT(int h,int d,octet *S,octet *ID,octet *TP);
+/**	@brief Create a server secret in G2 from a master secret
+ *
+	@param S is an input master secret
+	@param SS is the server secret = s.Q where Q is a fixed generator of G2
+	@return 0 or an error code
+ */
+int MPIN_GET_SERVER_SECRET(octet *S,octet *SS);
+/* int MPIN_TEST_PAIRING(octet *,octet *); */
+
+/* For M-Pin Full */
+/**	@brief Precompute values for use by the client side of M-Pin Full
+ *
+	@param T is the input M-Pin token (the client secret with PIN portion removed)
+	@param ID is the input client identity
+	@param CP is Public Key (or NULL)
+	@param g1 precomputed output
+	@param g2 precomputed output
+	@return 0 or an error code
+ */
+int MPIN_PRECOMPUTE(octet *T,octet *ID,octet *CP,octet *g1,octet *g2);
+/**	@brief Calculate Key on Server side for M-Pin Full
+ *
+	Uses UT internally for the key calculation, unless not available in which case U is used
+ 	@param h is the hash type
+	@param Z is the input Client-side Diffie-Hellman component
+	@param SS is the input server secret
+	@param w is an input random number generated by the server
+	@param p is an input, hash of the protocol transcript
+	@param I is the hashed input client ID = H(ID)
+	@param U is input from the client = x.H(ID)
+	@param UT is input from the client= x.(H(ID)+H(d|H(ID)))
+	@param K is the output calculated shared key
+	@return 0 or an error code
+ */
+int MPIN_SERVER_KEY(int h,octet *Z,octet *SS,octet *w,octet *p,octet *I,octet *U,octet *UT,octet *K);
+/**	@brief Calculate Key on Client side for M-Pin Full
+ *
+  	@param h is the hash type
+	@param g1 precomputed input
+	@param g2 precomputed input
+	@param pin is the input PIN number
+	@param r is an input, a locally generated random number
+	@param x is an input, a locally generated random number
+	@param p is an input, hash of the protocol transcript
+	@param T is the input Server-side Diffie-Hellman component
+	@param K is the output calculated shared key
+	@return 0 or an error code
+ */
+int MPIN_CLIENT_KEY(int h,octet *g1,octet *g2,int pin,octet *r,octet *x,octet *p,octet *T,octet *K);
+
+/**	@brief AES-GCM Encryption
+ *
+	@param K  AES key
+	@param IV Initialization vector
+	@param H Header
+	@param P Plaintext
+	@param C Ciphertext
+	@param T Checksum
+ */
+void MPIN_AES_GCM_ENCRYPT(octet *K,octet *IV,octet *H,octet *P,octet *C,octet *T);
+
+/**	@brief AES-GCM Decryption
+ *
+	@param K  AES key
+	@param IV Initialization vector
+	@param H Header
+	@param P Plaintext
+	@param C Ciphertext
+	@param T Checksum
+ */
+void MPIN_AES_GCM_DECRYPT(octet *K,octet *IV,octet *H,octet *C,octet *P,octet *T);
+
+/**
+ * @brief Return the field size
+ *
+ * Return the field size.
+ *
+ * @return Field size
+ */
+int MPIN_FS();
+
+/**
+ * @brief Return the group size
+ *
+ * Return the group size.
+ *
+ * @return Group size
+ */
+int MPIN_GS();
+
+/**	@brief HMAC of message M using key K to create tag of length len in octet tag
+ *
+	IEEE-1363 MAC1 function. Uses SHA256 internally.
+	@param M input message octet
+	@param K input encryption key
+	@param len is output desired length of HMAC tag
+	@param tag is the output HMAC
+	@return 0 for bad parameters, else 1
+ */
+//int HMAC(octet *M,octet *K,int len,octet *tag);
+
+/**	@brief Password Based Key Derivation Function - generates key K from password, salt and repeat counter
+ *
+	PBKDF2 Password Based Key Derivation Function. Uses SHA256 internally.
+	@param P input password
+	@param S input salt
+	@param rep Number of times to be iterated.
+	@param len is output desired length of key
+	@param K is the derived key
+ */
+//void PBKDF2(octet *P,octet *S,int rep,int len,octet *K);
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto/blob/70e3a3a3/version22/c/oct.c
----------------------------------------------------------------------
diff --git a/version22/c/oct.c b/version22/c/oct.c
new file mode 100644
index 0000000..8d5bdb2
--- /dev/null
+++ b/version22/c/oct.c
@@ -0,0 +1,428 @@
+/*
+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.
+*/
+
+/*** Basic Octet string maintainance routines  ***/
+/* SU=m, m is Stack Usage */
+
+#include <string.h>
+#include "amcl.h"
+
+/* Output an octet string (Debug Only) */
+
+/* SU= 16 */
+/* output octet */
+void OCT_output(octet *w)
+{
+    int i;
+    unsigned char ch;
+    for (i=0; i<w->len; i++)
+    {
+        ch=w->val[i];
+        printf("%02x",ch);
+    }
+    printf("\n");
+}
+
+/* SU= 16 */
+void OCT_output_string(octet *w)
+{
+    int i;
+    unsigned char ch;
+    for (i=0; i<w->len; i++)
+    {
+        ch=w->val[i];
+        printf("%c",ch);
+    }
+    /*  printf("\n"); */
+}
+
+/* Convert C string to octet format - truncates if no room  */
+void OCT_jstring(octet *y,char *s)
+{
+    int i,j;
+    i=y->len;
+    j=0;
+    while (s[j]!=0 && i<y->max)
+    {
+        y->val[i]=s[j];
+        y->len++;
+        i++;
+        j++;
+    }
+}
+
+/* compare 2 octet strings.
+ * If x==y return TRUE, else return FALSE */
+/* SU= 8 */
+int OCT_comp(octet *x,octet *y)
+{
+    int i;
+    if (x->len>y->len) return 0;
+    if (x->len<y->len) return 0;
+    for (i=0; i<x->len; i++)
+    {
+        if (x->val[i]!=y->val[i]) return 0;
+    }
+    return 1;
+}
+
+/* check are first n bytes the same */
+
+int OCT_ncomp(octet *x,octet *y,int n)
+{
+    int i;
+    if (n>y->len || n>x->len) return 0;
+    for (i=0; i<n; i++)
+    {
+        if (x->val[i]!=y->val[i]) return 0;
+    }
+    return 1;
+}
+
+/* Shift octet to the left by n bytes. Leftmost bytes disappear  */
+void OCT_shl(octet *x,int n)
+{
+    int i;
+    if (n>=x->len)
+    {
+        x->len=0;
+        return;
+    }
+    x->len-=n;
+    for (i=0; i<x->len; i++)
+        x->val[i]=x->val[i+n];
+}
+
+/* Append binary string to octet - truncates if no room */
+/* SU= 12 */
+void OCT_jbytes(octet *y,char *b,int len)
+{
+    int i,j;
+    i=y->len;
+    for (j=0; j<len && i<y->max; j++)
+    {
+        y->val[i]=b[j];
+        y->len++;
+        i++;
+    }
+}
+
+/* Concatenates two octet strings */
+/* SU= 8 */
+void OCT_joctet(octet *y,octet *x)
+{
+    /* y=y || x */
+    int i,j;
+    if (x==NULL) return;
+
+    for (i=0; i<x->len; i++)
+    {
+        j=y->len+i;
+        if (j>=y->max)
+        {
+            y->len=y->max;
+            return;
+        }
+        y->val[j]=x->val[i];
+    }
+    y->len+=x->len;
+}
+
+/* Append byte to octet rep times */
+/* SU= 8 */
+void OCT_jbyte(octet *y,int ch,int rep)
+{
+    int i,j;
+    i=y->len;
+    for (j=0; j<rep && i<y->max; j++)
+    {
+        y->val[i]=ch;
+        y->len++;
+        i++;
+    }
+}
+
+/* XOR common bytes of x with y */
+/* SU= 8 */
+void OCT_xor(octet *y,octet *x)
+{
+    /* xor first x->len bytes of y */
+
+    int i;
+    for (i=0; i<x->len && i<y->len; i++)
+    {
+        y->val[i]^=x->val[i];
+    }
+}
+
+/* clear an octet */
+void OCT_empty(octet *w)
+{
+    w->len=0;
+}
+
+/* Kill an octet string - Zeroise it for security */
+void OCT_clear(octet *w)
+{
+    int i;
+    for (i=0; i<w->max; i++) w->val[i]=0;
+    w->len=0;
+}
+
+/* appends int x of length len bytes to OCTET string */
+/* SU= 8 */
+void OCT_jint(octet *y,int x,int len)
+{
+    int i,n;
+    n=y->len+len;
+    if (n>y->max || len<=0) return;
+    for (i=y->len; i<n; i++) y->val[i]=0;
+    y->len=n;
+
+    i=y->len;
+    while (x>0 && i>0)
+    {
+        i--;
+        y->val[i]=x%256;
+        x/=256;
+    }
+}
+
+/* Pad an octet to a given length */
+/* SU= 8 */
+int OCT_pad(octet *w,int n)
+{
+    int i,d;
+    if (w->len>n || n>w->max) return 0;
+    if (n==w->len) return 1;
+    d=n-w->len;
+    for (i=n-1; i>=d; i--)
+        w->val[i]=w->val[i-d];
+    for (i=d-1; i>=0; i--)
+        w->val[i]=0;
+    w->len=n;
+    return 1;
+}
+
+
+/* Convert an octet string to base64 string */
+/* SU= 56 */
+void OCT_tobase64(char *b,octet *w)
+{
+    int i,j,k,rem,last;
+    int c,ch[4];
+    unsigned char ptr[3];
+    rem=w->len%3;
+    j=k=0;
+    last=4;
+    while (j<w->len)
+    {
+        for (i=0; i<3; i++)
+        {
+            if (j<w->len) ptr[i]=w->val[j++];
+            else
+            {
+                ptr[i]=0;
+                last--;
+            }
+        }
+        ch[0]=(ptr[0]>>2)&0x3f;
+        ch[1]=((ptr[0]<<4)|(ptr[1]>>4))&0x3f;
+        ch[2]=((ptr[1]<<2)|(ptr[2]>>6))&0x3f;
+        ch[3]=ptr[2]&0x3f;
+        for (i=0; i<last; i++)
+        {
+            c=ch[i];
+            if (c<26) c+=65;
+            if (c>=26 && c<52) c+=71;
+            if (c>=52 && c<62) c-=4;
+            if (c==62) c='+';
+            if (c==63) c='/';
+            b[k++]=c;
+        }
+    }
+    if (rem>0) for (i=rem; i<3; i++) b[k++]='=';
+    b[k]='\0';  /* dangerous! */
+}
+
+/* SU= 56 */
+void OCT_frombase64(octet *w,char *b)
+{
+    int i,j,k,pads,len=(int)strlen(b);
+    int c,ch[4],ptr[3];
+    /* int lead=1; */
+    j=k=0;
+    while (j<len && k<w->max)
+    {
+        pads=0;
+        for (i=0; i<4; i++)
+        {
+            c=80+b[j++];
+            if (c<=112) continue; /* ignore white space */
+            if (c>144 && c<171) c-=145;
+            if (c>176 && c<203) c-=151;
+            if (c>127 && c<138) c-=76;
+            if (c==123) c=62;
+            if (c==127) c=63;
+            if (c==141)
+            {
+                pads++;    /* ignore pads '=' */
+                continue;
+            }
+            ch[i]=c;
+        }
+        ptr[0]=(ch[0]<<2)|(ch[1]>>4);
+        ptr[1]=(ch[1]<<4)|(ch[2]>>2);
+        ptr[2]=(ch[2]<<6)|ch[3];
+        for (i=0; i<3-pads && k<w->max; i++)
+        {
+            /* don't put in leading zeros */
+            /* if (lead && ptr[i]==0) continue; */
+            w->val[k++]=ptr[i];
+            /* lead=0; */
+        }
+
+    }
+    w->len=k;
+}
+
+/* copy an octet string - truncates if no room */
+/* SU= 16 */
+void OCT_copy(octet *y,octet *x)
+{
+    int i;
+    OCT_clear(y);
+    y->len=x->len;
+    if (y->len>y->max) y->len=y->max;
+
+    for (i=0; i<y->len; i++)
+        y->val[i]=x->val[i];
+}
+
+/* XOR m with all of x */
+void OCT_xorbyte(octet *x,int m)
+{
+    int i;
+    for (i=0; i<x->len; i++) x->val[i]^=m;
+}
+
+/* truncates x to n bytes and places the rest in y (if y is not NULL) */
+/* SU= 8 */
+void OCT_chop(octet *x,octet *y,int n)
+{
+    int i;
+    if (n>=x->len)
+    {
+        if (y!=NULL) y->len=0;
+        return;
+    }
+    if (y!=NULL) y->len=x->len-n;
+    x->len=n;
+
+    if (y!=NULL)
+    {
+        for (i=0; i<y->len && i<y->max; i++) y->val[i]=x->val[i+n];
+    }
+}
+
+/* set x to len random bytes */
+void OCT_rand(octet *x,csprng *RNG,int len)
+{
+    int i;
+    if (len>x->max) len=x->max;
+    x->len=len;
+
+    for (i=0; i<len; i++) x->val[i]=RAND_byte(RNG);
+}
+
+/* Convert an octet to a hex string */
+void OCT_toHex(octet *src,char *dst)
+{
+    int i;
+    unsigned char ch;
+    for (i=0; i<src->len; i++)
+    {
+        ch=src->val[i];
+        sprintf(&dst[i*2],"%02x", ch);
+    }
+}
+
+static int char2int(char input)
+{
+    if(input >= '0' && input <= '9')
+        return input - '0';
+    if(input >= 'A' && input <= 'F')
+        return input - 'A' + 10;
+    if(input >= 'a' && input <= 'f')
+        return input - 'a' + 10;
+    return 0;
+}
+
+/* Convert from a hex string */
+void OCT_fromHex(octet *dst,char *src)
+{
+    int i=0;
+    int j=0;
+    OCT_clear(dst);
+
+    while(src[j]!=0)
+    {
+        dst->val[i++] = char2int(src[j])*16 + char2int(src[j+1]);
+        j += 2;
+    }
+    dst->len=i;
+}
+
+
+/* Convert an octet to a string */
+void OCT_toStr(octet *src,char *dst)
+{
+    int i;
+    unsigned char ch;
+    for (i=0; i<src->len; i++)
+    {
+        ch=src->val[i];
+        sprintf(&dst[i],"%c", ch);
+    }
+}
+
+/* Test program
+#include <stdio.h>
+#include "amcl.h"
+
+char test[]="abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+
+int main()
+{
+	char gm[100],gn[100],t[100];
+    octet m={0,sizeof(gm),gm};
+    octet n={0,sizeof(gn),gn};
+
+	OCT_jbytes(&m,test,strlen(test));
+	OCT_output(&m);
+
+	OCT_tobase64(t,&m);
+	printf(t); printf("\n");
+
+	OCT_frombase64(&n,t);
+	OCT_output(&n);
+
+    return 0;
+}
+*/

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto/blob/70e3a3a3/version22/c/pair.c
----------------------------------------------------------------------
diff --git a/version22/c/pair.c b/version22/c/pair.c
new file mode 100644
index 0000000..5b4e987
--- /dev/null
+++ b/version22/c/pair.c
@@ -0,0 +1,828 @@
+/*
+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 */
+
+//#define HAS_MAIN
+
+#include "amcl.h"
+
+/* Line function */
+static void PAIR_line(FP12 *v,ECP2 *A,ECP2 *B,BIG Qx,BIG Qy)
+{
+    ECP2 P;
+    FP2 Z3,X,Y,ZZ,T,NY;
+    FP4 a,b,c;
+    int D;
+    ECP2_copy(&P,A);
+    if (A==B)
+        D=ECP2_dbl(A);  // check these return numbers...
+    else
+        D=ECP2_add(A,B);
+    if (D<0)
+    {
+        /* Infinity */
+        FP12_one(v);
+        return;
+    }
+
+    FP2_copy(&Z3,&(A->z));
+    FP4_zero(&c);
+    FP2_sqr(&ZZ,&(P.z));    /* ZZ=Z^2 */
+    if (D==0)
+    {
+        /* addition */
+        ECP2_get(&X,&Y,B);
+        FP2_mul(&T,&(P.z),&Y);  /* T=Z*Y2 */
+
+        FP2_mul(&ZZ,&ZZ,&T);
+
+        FP2_neg(&NY,&(P.y));
+        FP2_add(&ZZ,&ZZ,&NY); /* ZZ=Z^3*Y2-Y (slope numerator) */
+        FP2_pmul(&Z3,&Z3,Qy);    /* Z3*Qy */
+        FP2_mul(&T,&T,&(P.x));
+        FP2_mul(&X,&X,&NY);
+        FP2_add(&T,&T,&X);       /* Z*Y2*X-X2*Y */
+        FP4_from_FP2s(&a,&Z3,&T); /* a=[Z3*Qy,Z*Y2*X-X2*Y] */
+        FP2_neg(&ZZ,&ZZ);
+        FP2_pmul(&ZZ,&ZZ,Qx);
+        FP4_from_FP2(&b,&ZZ);    /* b=-slope*Qx */
+    }
+    else
+    {
+        /* doubling */
+        FP2_sqr(&T,&(P.x));
+        FP2_imul(&T,&T,3);   /* T=3X^2 (slope numerator) */
+        FP2_sqr(&Y,&(P.y));
+
+        FP2_add(&Y,&Y,&Y);   /* Y=2Y^2 */
+        FP2_mul(&Z3,&Z3,&ZZ);   /* Z3=Z3*ZZ */
+        FP2_pmul(&Z3,&Z3,Qy);   /* Z3=Z3*ZZ*Qy */
+
+        FP2_mul(&X,&(P.x),&T);
+        FP2_sub(&X,&X,&Y);      /* X=X*slope-2Y^2 */
+        FP4_from_FP2s(&a,&Z3,&X); /* a=[Z3*ZZ*Qy , X*slope-2Y^2] */
+        FP2_neg(&T,&T);
+        FP2_mul(&ZZ,&ZZ,&T);
+        FP2_pmul(&ZZ,&ZZ,Qx);
+        FP4_from_FP2(&b,&ZZ);    /* b=-slope*ZZ*Qx */
+    }
+
+    FP12_from_FP4s(v,&a,&b,&c);
+}
+
+/* Optimal R-ate pairing r=e(P,Q) */
+void PAIR_ate(FP12 *r,ECP2 *P,ECP *Q)
+{
+    FP2 X;
+    BIG x,n,Qx,Qy;
+    int i,nb;
+    ECP2 A;
+    FP12 lv;
+#if CHOICE<BLS_CURVES
+    ECP2 KA;
+#endif
+
+    BIG_rcopy(Qx,CURVE_Fra);
+    BIG_rcopy(Qy,CURVE_Frb);
+    FP2_from_BIGs(&X,Qx,Qy);
+
+    BIG_rcopy(x,CURVE_Bnx);
+
+#if CHOICE<BLS_CURVES
+    BIG_pmul(n,x,6);
+    BIG_dec(n,2);
+#else
+    BIG_copy(n,x);
+#endif
+
+    BIG_norm(n);
+
+    ECP2_affine(P);
+    ECP_affine(Q);
+
+    BIG_copy(Qx,Q->x);
+    BIG_copy(Qy,Q->y);
+
+    ECP2_copy(&A,P);
+    FP12_one(r);
+    nb=BIG_nbits(n);
+
+    /* Main Miller Loop */
+    for (i=nb-2; i>=1; i--)
+    {
+        PAIR_line(&lv,&A,&A,Qx,Qy);
+        FP12_smul(r,&lv);
+        if (BIG_bit(n,i))
+        {
+
+            PAIR_line(&lv,&A,P,Qx,Qy);
+            FP12_smul(r,&lv);
+        }
+        FP12_sqr(r,r);
+    }
+
+    PAIR_line(&lv,&A,&A,Qx,Qy);
+    FP12_smul(r,&lv);
+
+    if (BIG_parity(n))
+    {
+        PAIR_line(&lv,&A,P,Qx,Qy);
+        FP12_smul(r,&lv);
+    }
+
+    /* R-ate fixup required for BN curves */
+#if CHOICE<BLS_CURVES
+    ECP2_copy(&KA,P);
+    ECP2_frob(&KA,&X);
+
+    ECP2_neg(&A);
+    FP12_conj(r,r);
+
+    PAIR_line(&lv,&A,&KA,Qx,Qy);
+    FP12_smul(r,&lv);
+    ECP2_frob(&KA,&X);
+    ECP2_neg(&KA);
+    PAIR_line(&lv,&A,&KA,Qx,Qy);
+    FP12_smul(r,&lv);
+#endif
+}
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+void PAIR_double_ate(FP12 *r,ECP2 *P,ECP *Q,ECP2 *R,ECP *S)
+{
+    FP2 X;
+    BIG x,n,Qx,Qy,Sx,Sy;
+    int i,nb;
+    ECP2 A,B;
+    FP12 lv;
+#if CHOICE<BLS_CURVES
+    ECP2 K;
+#endif
+    BIG_rcopy(Qx,CURVE_Fra);
+    BIG_rcopy(Qy,CURVE_Frb);
+    FP2_from_BIGs(&X,Qx,Qy);
+
+    BIG_rcopy(x,CURVE_Bnx);
+
+#if CHOICE<BLS_CURVES
+    BIG_pmul(n,x,6);
+    BIG_dec(n,2);
+#else
+    BIG_copy(n,x);
+#endif
+
+    BIG_norm(n);
+
+    ECP2_affine(P);
+    ECP_affine(Q);
+
+    ECP2_affine(R);
+    ECP_affine(S);
+
+    BIG_copy(Qx,Q->x);
+    BIG_copy(Qy,Q->y);
+
+    BIG_copy(Sx,S->x);
+    BIG_copy(Sy,S->y);
+
+    ECP2_copy(&A,P);
+    ECP2_copy(&B,R);
+    FP12_one(r);
+    nb=BIG_nbits(n);
+
+    /* Main Miller Loop */
+    for (i=nb-2; i>=1; i--)
+    {
+        PAIR_line(&lv,&A,&A,Qx,Qy);
+        FP12_smul(r,&lv);
+        PAIR_line(&lv,&B,&B,Sx,Sy);
+        FP12_smul(r,&lv);
+
+        if (BIG_bit(n,i))
+        {
+            PAIR_line(&lv,&A,P,Qx,Qy);
+            FP12_smul(r,&lv);
+
+            PAIR_line(&lv,&B,R,Sx,Sy);
+            FP12_smul(r,&lv);
+        }
+        FP12_sqr(r,r);
+    }
+
+    PAIR_line(&lv,&A,&A,Qx,Qy);
+    FP12_smul(r,&lv);
+
+    PAIR_line(&lv,&B,&B,Sx,Sy);
+    FP12_smul(r,&lv);
+
+    if (BIG_parity(n))
+    {
+        PAIR_line(&lv,&A,P,Qx,Qy);
+        FP12_smul(r,&lv);
+
+        PAIR_line(&lv,&B,R,Sx,Sy);
+        FP12_smul(r,&lv);
+    }
+
+    /* R-ate fixup required for BN curves */
+#if CHOICE<BLS_CURVES
+    FP12_conj(r,r);
+
+    ECP2_copy(&K,P);
+    ECP2_frob(&K,&X);
+    ECP2_neg(&A);
+    PAIR_line(&lv,&A,&K,Qx,Qy);
+    FP12_smul(r,&lv);
+    ECP2_frob(&K,&X);
+    ECP2_neg(&K);
+    PAIR_line(&lv,&A,&K,Qx,Qy);
+    FP12_smul(r,&lv);
+
+    ECP2_copy(&K,R);
+    ECP2_frob(&K,&X);
+    ECP2_neg(&B);
+    PAIR_line(&lv,&B,&K,Sx,Sy);
+    FP12_smul(r,&lv);
+    ECP2_frob(&K,&X);
+    ECP2_neg(&K);
+    PAIR_line(&lv,&B,&K,Sx,Sy);
+    FP12_smul(r,&lv);
+#endif
+}
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+void PAIR_fexp(FP12 *r)
+{
+    FP2 X;
+    BIG x,a,b;
+    FP12 t0,y0,y1,y2,y3;
+
+    BIG_rcopy(x,CURVE_Bnx);
+    BIG_rcopy(a,CURVE_Fra);
+    BIG_rcopy(b,CURVE_Frb);
+    FP2_from_BIGs(&X,a,b);
+
+    /* Easy part of final exp */
+
+    FP12_inv(&t0,r);
+    FP12_conj(r,r);
+
+    FP12_mul(r,&t0);
+    FP12_copy(&t0,r);
+
+    FP12_frob(r,&X);
+    FP12_frob(r,&X);
+    FP12_mul(r,&t0);
+
+    /* Hard part of final exp - see Duquesne & Ghamman eprint 2015/192.pdf */
+#if CHOICE<BLS_CURVES
+    FP12_pow(&t0,r,x); // t0=f^-u
+    FP12_usqr(&y3,&t0); // y3=t0^2
+    FP12_copy(&y0,&t0);
+    FP12_mul(&y0,&y3); // y0=t0*y3
+    FP12_copy(&y2,&y3);
+    FP12_frob(&y2,&X); // y2=y3^p
+    FP12_mul(&y2,&y3); //y2=y2*y3
+    FP12_usqr(&y2,&y2); //y2=y2^2
+    FP12_mul(&y2,&y3); // y2=y2*y3
+
+    FP12_pow(&t0,&y0,x);  //t0=y0^-u
+    FP12_conj(&y0,r);     //y0=~r
+    FP12_copy(&y1,&t0);
+    FP12_frob(&y1,&X);
+    FP12_frob(&y1,&X); //y1=t0^p^2
+    FP12_mul(&y1,&y0); // y1=y0*y1
+    FP12_conj(&t0,&t0); // t0=~t0
+    FP12_copy(&y3,&t0);
+    FP12_frob(&y3,&X); //y3=t0^p
+    FP12_mul(&y3,&t0); // y3=t0*y3
+    FP12_usqr(&t0,&t0); // t0=t0^2
+    FP12_mul(&y1,&t0); // y1=t0*y1
+
+    FP12_pow(&t0,&y3,x); // t0=y3^-u
+    FP12_usqr(&t0,&t0); //t0=t0^2
+    FP12_conj(&t0,&t0); //t0=~t0
+    FP12_mul(&y3,&t0); // y3=t0*y3
+
+    FP12_frob(r,&X);
+    FP12_copy(&y0,r);
+    FP12_frob(r,&X);
+    FP12_mul(&y0,r);
+    FP12_frob(r,&X);
+    FP12_mul(&y0,r);
+
+    FP12_usqr(r,&y3);  //r=y3^2
+    FP12_mul(r,&y2);   //r=y2*r
+    FP12_copy(&y3,r);
+    FP12_mul(&y3,&y0); // y3=r*y0
+    FP12_mul(r,&y1); // r=r*y1
+    FP12_usqr(r,r); // r=r^2
+    FP12_mul(r,&y3); // r=r*y3
+    FP12_reduce(r);
+#else
+// Ghamman & Fouotsa Method
+
+    FP12_usqr(&y0,r);
+    FP12_pow(&y1,&y0,x);
+    BIG_fshr(x,1);
+    FP12_pow(&y2,&y1,x);
+    BIG_fshl(x,1); // x must be even
+    FP12_conj(&y3,r);
+    FP12_mul(&y1,&y3);
+
+    FP12_conj(&y1,&y1);
+    FP12_mul(&y1,&y2);
+
+    FP12_pow(&y2,&y1,x);
+
+    FP12_pow(&y3,&y2,x);
+    FP12_conj(&y1,&y1);
+    FP12_mul(&y3,&y1);
+
+    FP12_conj(&y1,&y1);
+    FP12_frob(&y1,&X);
+    FP12_frob(&y1,&X);
+    FP12_frob(&y1,&X);
+    FP12_frob(&y2,&X);
+    FP12_frob(&y2,&X);
+    FP12_mul(&y1,&y2);
+
+    FP12_pow(&y2,&y3,x);
+    FP12_mul(&y2,&y0);
+    FP12_mul(&y2,r);
+
+    FP12_mul(&y1,&y2);
+    FP12_copy(&y2,&y3);
+    FP12_frob(&y2,&X);
+    FP12_mul(&y1,&y2);
+    FP12_copy(r,&y1);
+    FP12_reduce(r);
+
+// Aranha et al method as described by Ghamman & Fouotsa
+    /*
+    	FP12_usqr(&y0,r);  // t0=f^2
+    	FP12_conj(&y3,&y0); // t0=f^-2
+    	FP12_pow(&t0,r,x); // t5=f^u
+    	FP12_usqr(&y1,&t0); // t1=t5^2
+    	FP12_mul(&y3,&t0); // t3=t0*t5
+
+    	FP12_pow(&y0,&y3,x);
+
+    	FP12_pow(&y2,&y0,x);
+
+    	FP12_pow(&y4,&y2,x);
+
+    	FP12_mul(&y4,&y1);
+    	FP12_pow(&y1,&y4,x);
+    	FP12_conj(&y3,&y3);
+    	FP12_mul(&y1,&y3);
+    	FP12_mul(&y1,r);
+
+    	FP12_conj(&y3,r);
+    	FP12_mul(&y0,r);
+    	FP12_frob(&y0,&X); FP12_frob(&y0,&X); FP12_frob(&y0,&X);
+
+    	FP12_mul(&y4,&y3);
+    	FP12_frob(&y4,&X);
+
+    	FP12_mul(&t0,&y2);
+    	FP12_frob(&t0,&X); FP12_frob(&t0,&X);
+
+    	FP12_mul(&t0,&y0);
+    	FP12_mul(&t0,&y4);
+    	FP12_mul(&t0,&y1);
+    	FP12_copy(r,&t0);
+    	FP12_reduce(r);*/
+
+//-----------------------------------
+    /*
+    	FP12_copy(&y0,r);						// y0=r;
+    	FP12_copy(&y1,r);						// y1=r;
+    	FP12_copy(&t0,r); FP12_frob(&t0,&X);	// t0=Frobenius(r,X,1);
+    	FP12_conj(&y3,&t0); FP12_mul(&y1,&y3);	// y1*=inverse(t0);
+    	FP12_frob(&t0,&X); FP12_frob(&t0,&X);	// t0=Frobenius(t0,X,2);
+    	FP12_mul(&y1,&t0);						// y1*=t0;
+
+    	FP12_pow(r,r,x);						// r=pow(r,x);
+    	FP12_conj(&y3,r); FP12_mul(&y1,&y3);	// y1*=inverse(r);
+    	FP12_copy(&t0,r); FP12_frob(&t0,&X);	// t0=Frobenius(r,X,1);
+    	FP12_mul(&y0,&t0);						// y0*=t0;
+    	FP12_frob(&t0,&X);						// t0=Frobenius(t0,X,1);
+    	FP12_mul(&y1,&t0);						// y1*=t0;
+    	FP12_frob(&t0,&X);						// t0=Frobenius(t0,X,1);
+    	FP12_conj(&y3,&t0); FP12_mul(&y0,&y3);	// y0*=inverse(t0);
+
+    	FP12_pow(r,r,x);						// r=pow(r,x);
+    	FP12_mul(&y0,r);						// y0*=r;
+    	FP12_copy(&t0,r); FP12_frob(&t0,&X); FP12_frob(&t0,&X); // t0=Frobenius(r,X,2);
+    	FP12_conj(&y3,&t0); FP12_mul(&y0,&y3);	// y0*=inverse(t0);
+    	FP12_frob(&t0,&X);						// t0=Frobenius(t0,X,1);
+    	FP12_mul(&y1,&t0);						// y1*=t0;
+
+    	FP12_pow(r,r,x);						// r=pow(r,x);			// r^x3
+    	FP12_copy(&t0,r); FP12_frob(&t0,&X);	// t0=Frobenius(r,X,1);
+    	FP12_conj(&y3,&t0); FP12_mul(&y0,&y3);	// y0*=inverse(t0);
+    	FP12_frob(&t0,&X);						// t0=Frobenius(t0,X,1);
+    	FP12_mul(&y1,&t0);						// y1*=t0;
+
+    	FP12_pow(r,r,x);						// r=pow(r,x);			// r^x4
+    	FP12_conj(&y3,r); FP12_mul(&y0,&y3);	// y0*=inverse(r);
+    	FP12_copy(&t0,r); FP12_frob(&t0,&X);	// t0=Frobenius(r,X,1);
+    	FP12_mul(&y1,&t0);						//y1*=t0;
+
+    	FP12_pow(r,r,x);						// r=pow(r,x);			// r^x5
+    	FP12_mul(&y1,r);						// y1*=r;
+
+    	FP12_usqr(&y0,&y0);						// r=y0*y0*y1;
+    	FP12_mul(&y0,&y1);
+    	FP12_copy(r,&y0);
+    	FP12_reduce(r); */
+#endif
+}
+
+#ifdef USE_GLV
+/* GLV method */
+static void glv(BIG u[2],BIG e)
+{
+#if CHOICE<BLS_CURVES
+    int i,j;
+    BIG v[2],t,q;
+    DBIG d;
+    BIG_rcopy(q,CURVE_Order);
+    for (i=0; i<2; i++)
+    {
+        BIG_rcopy(t,CURVE_W[i]);
+//BIG_norm(t); BIG_norm(e);
+        BIG_mul(d,t,e);
+        BIG_ddiv(v[i],d,q);
+        BIG_zero(u[i]);
+    }
+    BIG_copy(u[0],e);
+    for (i=0; i<2; i++)
+        for (j=0; j<2; j++)
+        {
+            BIG_rcopy(t,CURVE_SB[j][i]);
+            BIG_modmul(t,v[j],t,q);
+            BIG_add(u[i],u[i],q);
+            BIG_sub(u[i],u[i],t);
+            BIG_mod(u[i],q);
+        }
+
+#else
+// -(x^2).P = (Beta.x,y)
+
+    BIG x,x2,q;
+    BIG_rcopy(x,CURVE_Bnx);
+    BIG_smul(x2,x,x);
+    BIG_copy(u[0],e);
+    BIG_mod(u[0],x2);
+    BIG_copy(u[1],e);
+    BIG_sdiv(u[1],x2);
+
+    BIG_rcopy(q,CURVE_Order);
+    BIG_sub(u[1],q,u[1]);
+
+#endif
+
+    return;
+}
+#endif // USE_GLV
+
+/* Galbraith & Scott Method */
+static void gs(BIG u[4],BIG e)
+{
+    int i;
+#if CHOICE<BLS_CURVES
+    int j;
+    BIG v[4],t,q;
+    DBIG d;
+    BIG_rcopy(q,CURVE_Order);
+    for (i=0; i<4; i++)
+    {
+        BIG_rcopy(t,CURVE_WB[i]);
+//BIG_norm(t); BIG_norm(e);
+        BIG_mul(d,t,e);
+        BIG_ddiv(v[i],d,q);
+        BIG_zero(u[i]);
+    }
+
+    BIG_copy(u[0],e);
+    for (i=0; i<4; i++)
+        for (j=0; j<4; j++)
+        {
+            BIG_rcopy(t,CURVE_BB[j][i]);
+            BIG_modmul(t,v[j],t,q);
+            BIG_add(u[i],u[i],q);
+            BIG_sub(u[i],u[i],t);
+            BIG_mod(u[i],q);
+        }
+
+#else
+
+    BIG x,w;
+    BIG_rcopy(x,CURVE_Bnx);
+    BIG_copy(w,e);
+
+    for (i=0; i<4; i++)
+    {
+        BIG_copy(u[i],w);
+        BIG_mod(u[i],x);
+        BIG_sdiv(w,x);
+    }
+
+#endif
+    return;
+}
+
+/* Multiply P by e in group G1 */
+void PAIR_G1mul(ECP *P,BIG e)
+{
+#ifdef USE_GLV   /* Note this method is patented */
+    int np,nn;
+    ECP Q;
+    BIG cru,t,q;
+    BIG u[2];
+
+    BIG_rcopy(q,CURVE_Order);
+    glv(u,e);
+
+    ECP_affine(P);
+    ECP_copy(&Q,P);
+    BIG_rcopy(cru,CURVE_Cru);
+    FP_nres(cru);
+    FP_mul(Q.x,Q.x,cru);
+
+    /* note that -a.B = a.(-B). Use a or -a depending on which is smaller */
+
+    np=BIG_nbits(u[0]);
+    BIG_modneg(t,u[0],q);
+    nn=BIG_nbits(t);
+    if (nn<np)
+    {
+        BIG_copy(u[0],t);
+        ECP_neg(P);
+    }
+
+    np=BIG_nbits(u[1]);
+    BIG_modneg(t,u[1],q);
+    nn=BIG_nbits(t);
+    if (nn<np)
+    {
+        BIG_copy(u[1],t);
+        ECP_neg(&Q);
+    }
+
+    ECP_mul2(P,&Q,u[0],u[1]);
+
+#else
+    ECP_mul(P,e);
+#endif
+}
+
+/* Multiply P by e in group G2 */
+void PAIR_G2mul(ECP2 *P,BIG e)
+{
+#ifdef USE_GS_G2   /* Well I didn't patent it :) */
+    int i,np,nn;
+    ECP2 Q[4];
+    FP2 X;
+    BIG x,y;
+    BIG u[4];
+
+    BIG_rcopy(x,CURVE_Fra);
+    BIG_rcopy(y,CURVE_Frb);
+    FP2_from_BIGs(&X,x,y);
+
+    BIG_rcopy(y,CURVE_Order);
+    gs(u,e);
+
+
+    ECP2_affine(P);
+
+    ECP2_copy(&Q[0],P);
+    for (i=1; i<4; i++)
+    {
+        ECP2_copy(&Q[i],&Q[i-1]);
+        ECP2_frob(&Q[i],&X);
+    }
+
+    for (i=0; i<4; i++)
+    {
+        np=BIG_nbits(u[i]);
+        BIG_modneg(x,u[i],y);
+        nn=BIG_nbits(x);
+        if (nn<np)
+        {
+            BIG_copy(u[i],x);
+            ECP2_neg(&Q[i]);
+        }
+    }
+
+    ECP2_mul4(P,Q,u);
+
+#else
+    ECP2_mul(P,e);
+#endif
+}
+
+/* f=f^e */
+void PAIR_GTpow(FP12 *f,BIG e)
+{
+#ifdef USE_GS_GT   /* Note that this option requires a lot of RAM! Maybe better to use compressed XTR method, see fp4.c */
+    int i,np,nn;
+    FP12 g[4];
+    FP2 X;
+    BIG t,q,x,y;
+    BIG u[4];
+
+    BIG_rcopy(x,CURVE_Fra);
+    BIG_rcopy(y,CURVE_Frb);
+    FP2_from_BIGs(&X,x,y);
+
+    BIG_rcopy(q,CURVE_Order);
+    gs(u,e);
+
+    FP12_copy(&g[0],f);
+    for (i=1; i<4; i++)
+    {
+        FP12_copy(&g[i],&g[i-1]);
+        FP12_frob(&g[i],&X);
+    }
+
+    for (i=0; i<4; i++)
+    {
+        np=BIG_nbits(u[i]);
+        BIG_modneg(t,u[i],q);
+        nn=BIG_nbits(t);
+        if (nn<np)
+        {
+            BIG_copy(u[i],t);
+            FP12_conj(&g[i],&g[i]);
+        }
+    }
+    FP12_pow4(f,g,u);
+
+#else
+    FP12_pow(f,f,e);
+#endif
+}
+
+/* test group membership test - no longer needed */
+/* with GT-Strong curve, now only check that m!=1, conj(m)*m==1, and m.m^{p^4}=m^{p^2} */
+
+/*
+int PAIR_GTmember(FP12 *m)
+{
+	BIG a,b;
+	FP2 X;
+	FP12 r,w;
+	if (FP12_isunity(m)) return 0;
+	FP12_conj(&r,m);
+	FP12_mul(&r,m);
+	if (!FP12_isunity(&r)) return 0;
+
+	BIG_rcopy(a,CURVE_Fra);
+	BIG_rcopy(b,CURVE_Frb);
+	FP2_from_BIGs(&X,a,b);
+
+
+	FP12_copy(&r,m); FP12_frob(&r,&X); FP12_frob(&r,&X);
+	FP12_copy(&w,&r); FP12_frob(&w,&X); FP12_frob(&w,&X);
+	FP12_mul(&w,m);
+
+
+#ifndef GT_STRONG
+	if (!FP12_equals(&w,&r)) return 0;
+
+	BIG_rcopy(a,CURVE_Bnx);
+
+	FP12_copy(&r,m); FP12_pow(&w,&r,a); FP12_pow(&w,&w,a);
+	FP12_sqr(&r,&w); FP12_mul(&r,&w); FP12_sqr(&r,&r);
+
+	FP12_copy(&w,m); FP12_frob(&w,&X);
+ #endif
+
+	return FP12_equals(&w,&r);
+}
+
+*/
+
+
+#ifdef HAS_MAIN
+/*
+#if CHOICE==BN254_T
+
+const BIG TEST_Gx={0x18AFF11A,0xF2EF406,0xAF68220,0x171F2E27,0x6BA0959,0x124C50E0,0x450BE27,0x7003EA8,0x8A914};
+const BIG TEST_Gy={0x6E010F4,0xA71D07E,0x7ECADA8,0x8260E8E,0x1F79C328,0x17A09412,0xBFAE690,0x1C57CBD1,0x17DF54};
+
+const BIG TEST_Pxa={0x1047D566,0xD83CD71,0x10322E9D,0x991FA93,0xA282C48,0x18AEBEC8,0xCB05850,0x13B4F669,0x21794A};
+const BIG TEST_Pxb={0x1E305936,0x16885BF1,0x327060,0xE26F794,0x1547D870,0x1963E5B2,0x1BEBB96C,0x988A33C,0x1A9B47};
+const BIG TEST_Pya={0x20FF876,0x4427E67,0x18732211,0xE88E45E,0x174D1A7E,0x17D877ED,0x343AB37,0x97EB453,0xB00D5};
+const BIG TEST_Pyb={0x1D746B7B,0x732F4C2,0x122A49B0,0x16267985,0x235DF56,0x10B1E4D,0x14D8F210,0x17A05C3E,0x5ECF8};
+
+#endif
+
+#if CHOICE==BN254_T2
+
+const BIG TEST_Gx={0x15488765,0x46790D7,0xD9900A,0x1DFB43F,0x9F2D307,0xC4724E8,0x5678E51,0x15C3E3A7,0x1BEC8E};
+const BIG TEST_Gy={0x3D3273C,0x1AFA5FF,0x1880A139,0xACD34DF,0x17493067,0x10FA4103,0x1D4C9766,0x1A73F3DB,0x2D148};
+
+const BIG TEST_Pxa={0xF8DC275,0xAC27FA,0x11815151,0x152691C8,0x5CDEBF1,0x7D5A965,0x1BF70CE3,0x679A1C8,0xD62CF};
+const BIG TEST_Pxb={0x1D17D7A8,0x6B28DF4,0x174A0389,0xFE67E5F,0x1FA97A3C,0x7F5F473,0xFFB5146,0x4BC19A5,0x227010};
+const BIG TEST_Pya={0x16CC1F90,0x5284627,0x171B91AB,0x11F843B9,0x1D468755,0x67E279C,0x19FE0EF8,0x1A0CAA6B,0x1CC6CB};
+const BIG TEST_Pyb={0x1FF0CF2A,0xBC83255,0x6DD6EE8,0xB8B752F,0x13E484EC,0x1809BE81,0x1A648AA1,0x8CEF3F3,0x86EE};
+
+
+#endif
+
+#if CHOICE==BN254
+
+const BIG TEST_Gx={0x14BEC4670E4EB7,0xEA2973860F6861,0x35C14B2FC3C28F,0x4402A0B63B9473,0x2074A81D};
+const BIG TEST_Gy={0xC284846631CBEB,0x34A6E8D871B3B,0x89FB94A82B2006,0x87B20038771FC,0x6A41108};
+
+const BIG TEST_Pxa={0xE4A00F52183C77,0x554E02DF4F8354,0xB65EB5CF1C2F89,0x8B71A87BFCFC9,0x49EEDB1};
+const BIG TEST_Pxb={0xCFB8FA9AA8845D,0x8A9CC76D966697,0x185BA05BF5EC08,0x76140E87D97226,0x1FB93AB6};
+const BIG TEST_Pya={0x3644CC1EDF208A,0xA637FB3FF8E257,0x4453DA2BB9E686,0xD14AD3CDF6A1FE,0xCD04A1E};
+const BIG TEST_Pyb={0x71BD7630A43C14,0x1CAA9F14EA264E,0x3C3C2DFC765DEF,0xCF59D1A1A7D6EE,0x11FF7795};
+
+
+#endif
+*/
+int main()
+{
+    int i;
+    char byt[32];
+    csprng rng;
+    BIG xa,xb,ya,yb,w,a,b,t1,q,u[2],v[4],m,r;
+    ECP2 P,G;
+    ECP Q,R;
+    FP12 g,gp;
+    FP4 t,c,cp,cpm1,cpm2;
+    FP2 x,y,X;
+
+
+    BIG_rcopy(a,CURVE_Fra);
+    BIG_rcopy(b,CURVE_Frb);
+    FP2_from_BIGs(&X,a,b);
+
+    BIG_rcopy(xa,CURVE_Gx);
+    BIG_rcopy(ya,CURVE_Gy);
+
+    ECP_set(&Q,xa,ya);
+    if (Q.inf) printf("Failed to set - point not on curve\n");
+    else printf("G1 set success\n");
+
+    printf("Q= ");
+    ECP_output(&Q);
+    printf("\n");
+
+//	BIG_rcopy(r,CURVE_Order); BIG_dec(r,7); BIG_norm(r);
+    BIG_rcopy(xa,CURVE_Pxa);
+    BIG_rcopy(xb,CURVE_Pxb);
+    BIG_rcopy(ya,CURVE_Pya);
+    BIG_rcopy(yb,CURVE_Pyb);
+
+    FP2_from_BIGs(&x,xa,xb);
+    FP2_from_BIGs(&y,ya,yb);
+
+    ECP2_set(&P,&x,&y);
+    if (P.inf) printf("Failed to set - point not on curve\n");
+    else printf("G2 set success\n");
+
+    printf("P= ");
+    ECP2_output(&P);
+    printf("\n");
+
+    for (i=0; i<1000; i++ )
+    {
+
+        PAIR_ate(&g,&P,&Q);
+        PAIR_fexp(&g);
+
+//	PAIR_GTpow(&g,xa);
+
+    }
+    printf("g= ");
+    FP12_output(&g);
+    printf("\n");
+
+}
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto/blob/70e3a3a3/version22/c/rand.c
----------------------------------------------------------------------
diff --git a/version22/c/rand.c b/version22/c/rand.c
new file mode 100644
index 0000000..4a2cd0a
--- /dev/null
+++ b/version22/c/rand.c
@@ -0,0 +1,172 @@
+/*
+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.
+*/
+
+/*
+ *   Cryptographic strong random number generator
+ *
+ *   Unguessable seed -> SHA -> PRNG internal state -> SHA -> random numbers
+ *   Slow - but secure
+ *
+ *   See ftp://ftp.rsasecurity.com/pub/pdfs/bull-1.pdf for a justification
+ */
+/* SU=m, m is Stack Usage */
+
+#include "amcl.h"
+
+/* SU= 20 */
+static unsign32 sbrand(csprng *rng)
+{
+    /* Marsaglia & Zaman random number generator */
+    int i,k;
+    unsign32 pdiff,t;
+    rng->rndptr++;
+    if (rng->rndptr<NK) return rng->ira[rng->rndptr];
+    rng->rndptr=0;
+    for (i=0,k=NK-NJ; i<NK; i++,k++)
+    {
+        /* calculate next NK values */
+        if (k==NK) k=0;
+        t=rng->ira[k];
+        pdiff=t - rng->ira[i] - rng->borrow;
+
+        if (pdiff<t) rng->borrow=0;
+        if (pdiff>t) rng->borrow=1;
+        rng->ira[i]=pdiff;
+    }
+    return rng->ira[0];
+}
+
+/* SU= 20 */
+static void sirand(csprng* rng,unsign32 seed)
+{
+    /* initialise random number system */
+    /* modified so that a subsequent call "stirs" in another seed value */
+    /* in this way as many seed bits as desired may be used */
+    int i,in;
+    unsign32 t,m=1;
+    rng->borrow=0L;
+    rng->rndptr=0;
+    rng->ira[0]^=seed;
+    for (i=1; i<NK; i++)
+    {
+        /* fill initialisation vector */
+        in=(NV*i)%NK;
+        rng->ira[in]^=m;      /* note XOR */
+        t=m;
+        m=seed-m;
+        seed=t;
+    }
+    for (i=0; i<10000; i++) sbrand(rng ); /* "warm-up" & stir the generator */
+}
+
+/* SU= 312 */
+static void fill_pool(csprng *rng)
+{
+    /* hash down output of RNG to re-fill the pool */
+    int i;
+    hash256 sh;
+    HASH256_init(&sh);
+    for (i=0; i<128; i++) HASH256_process(&sh,sbrand(rng));
+    HASH256_hash(&sh,rng->pool);
+    rng->pool_ptr=0;
+}
+
+static unsign32 pack(const uchar *b)
+{
+    /* pack bytes into a 32-bit Word */
+    return ((unsign32)b[3]<<24)|((unsign32)b[2]<<16)|((unsign32)b[1]<<8)|(unsign32)b[0];
+}
+
+/* SU= 360 */
+/* Initialize RNG with some real entropy from some external source */
+void RAND_seed(csprng *rng,int rawlen,char *raw)
+{
+    /* initialise from at least 128 byte string of raw  *
+     * random (keyboard?) input, and 32-bit time-of-day */
+    int i;
+    char digest[32];
+    uchar b[4];
+    hash256 sh;
+    rng->pool_ptr=0;
+    for (i=0; i<NK; i++) rng->ira[i]=0;
+    if (rawlen>0)
+    {
+        HASH256_init(&sh);
+        for (i=0; i<rawlen; i++)
+            HASH256_process(&sh,raw[i]);
+        HASH256_hash(&sh,digest);
+
+        /* initialise PRNG from distilled randomness */
+
+        for (i=0; i<8; i++)
+        {
+            b[0]=digest[4*i];
+            b[1]=digest[4*i+1];
+            b[2]=digest[4*i+2];
+            b[3]=digest[4*i+3];
+            //	printf("%08x\n",pack(b));
+            sirand(rng,pack(b));
+        }
+    }
+    fill_pool(rng);
+}
+
+/* Terminate and clean up */
+void RAND_clean(csprng *rng)
+{
+    /* kill internal state */
+    int i;
+    rng->pool_ptr=rng->rndptr=0;
+    for (i=0; i<32; i++) rng->pool[i]=0;
+    for (i=0; i<NK; i++) rng->ira[i]=0;
+    rng->borrow=0;
+}
+
+/* get random byte */
+/* SU= 8 */
+int RAND_byte(csprng *rng)
+{
+    int r;
+    r=rng->pool[rng->pool_ptr++];
+    if (rng->pool_ptr>=32) fill_pool(rng);
+    return (r&0xff);
+}
+
+/* test main program */
+/*
+#include <stdio.h>
+#include <string.h>
+
+void main()
+{
+    int i;
+    char raw[256];
+    csprng rng;
+
+	RAND_clean(&rng);
+
+
+	for (i=0;i<256;i++) raw[i]=(char)i;
+    RAND_seed(&rng,256,raw);
+
+	for (i=0;i<1000;i++)
+		printf("%02x ",(unsigned char)RAND_byte(&rng));
+}
+
+*/

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto/blob/70e3a3a3/version22/c/randapi.c
----------------------------------------------------------------------
diff --git a/version22/c/randapi.c b/version22/c/randapi.c
new file mode 100644
index 0000000..9b32efb
--- /dev/null
+++ b/version22/c/randapi.c
@@ -0,0 +1,15 @@
+#include "randapi.h"
+
+/* Initialise a Cryptographically Strong Random Number Generator from
+   an octet of raw random data */
+
+void CREATE_CSPRNG(csprng *RNG,octet *RAW)
+{
+    RAND_seed(RNG,RAW->len,RAW->val);
+}
+
+void KILL_CSPRNG(csprng *RNG)
+{
+    RAND_clean(RNG);
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto/blob/70e3a3a3/version22/c/randapi.h
----------------------------------------------------------------------
diff --git a/version22/c/randapi.h b/version22/c/randapi.h
new file mode 100644
index 0000000..631499d
--- /dev/null
+++ b/version22/c/randapi.h
@@ -0,0 +1,20 @@
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#include "amcl.h"
+
+/**	@brief Initialise a random number generator
+ *
+	@param R is a pointer to a cryptographically secure random number generator
+	@param S is an input truly random seed value
+ */
+extern void CREATE_CSPRNG(csprng *R,octet *S);
+/**	@brief Kill a random number generator
+ *
+	Deletes all internal state
+	@param R is a pointer to a cryptographically secure random number generator
+ */
+extern void KILL_CSPRNG(csprng *R);
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto/blob/70e3a3a3/version22/c/readme.txt
----------------------------------------------------------------------
diff --git a/version22/c/readme.txt b/version22/c/readme.txt
new file mode 100644
index 0000000..278770a
--- /dev/null
+++ b/version22/c/readme.txt
@@ -0,0 +1,77 @@
+AMCL is very simple to build.
+
+The examples here are for GCC under Linux and Windows (using MINGW).
+
+First indicate your computer/compiler architecture by setting the wordlength 
+in arch.h
+
+Next - decide what you want to do. Edit amcl.h - note there is only
+one area where USER CONFIGURABLE input is requested.
+
+Here choose your curve.
+
+Once this is done, build the library, and compile and link your program 
+with an API file and the ROM file rom.c that contains curve constants.
+
+Three example API files are provided, mpin.c which supports our M-Pin 
+(tm) protocol, ecdh.c which supports standard elliptic 
+curve key exchange, digital signature and public key crypto, and rsa.c 
+which supports the RSA method. The first 
+can be tested using the testmpin.c driver programs, the second can 
+be tested using testecdh.c, and the third can be tested using
+testrsa.c
+
+In the ROM file you must provide the curve constants. Several examples
+are provided there, and if you are willing to use one of these, simply
+select your curve of CHOICE in amcl.h
+
+Example (1), in amcl.h choose
+
+#define CHOICE BN254
+
+and
+
+#define CURVETYPE WEIERSTRASS
+
+Under windows run the batch file build_pair.bat to build the amcl.a library
+and the testmpin.exe applications.
+
+For linux execute "bash build_pair"
+
+Example (2), in amcl.h choose
+
+#define CHOICE C25519
+
+and
+
+#define CURVETYPE EDWARDS
+
+to select the Edwards curve ed25519.
+
+Under Windows run the batch file build_ec.bat to build the amcl.a library and
+the testecdh.exe application.
+
+For Linux execute "bash build_ec"
+
+
+To help generate the ROM constants for your own curve some MIRACL helper 
+programs are included. The programs bngen.cpp and blsgen.cpp generate ROM 
+data for a BN and BLS pairing friendly curves, and the program ecgen.cpp 
+generates ROM data for regular EC curves.
+
+The MIRACL based program check.cpp helps choose the best number base for
+big number representation, given the word-length and the size of the modulus.
+
+The program bigtobig.cpp converts a big number to the AMCL 
+BIG format.
+
+
+For quick jumpstart:-
+
+(Linux)
+bash build_pair
+./testmpin
+
+(Windows + MingW)
+build_pair
+testmpin