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/08 00:12:55 UTC

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

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto-c/blob/8d28d2c3/src/mpin192.c.in
----------------------------------------------------------------------
diff --git a/src/mpin192.c.in b/src/mpin192.c.in
new file mode 100644
index 0000000..ee88145
--- /dev/null
+++ b/src/mpin192.c.in
@@ -0,0 +1,952 @@
+/*
+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 "mpin192_ZZZ.h"
+
+#define ROUNDUP(a,b) ((a)-1)/(b)+1
+
+/* Special mpin hashing */
+static void mpin_hash(int sha,FP8_YYY *f, ECP_ZZZ *P,octet *w)
+{
+    int i;
+    BIG_XXX x,y;
+    char h[64];
+    hash256 sha256;
+    hash512 sha512;
+    char t[10*MODBYTES_XXX];  // to hold 10 BIGs
+    int hlen=sha;
+
+
+    FP_YYY_redc(x,&(f->a.a.a));
+    BIG_XXX_toBytes(&t[0],x);
+    FP_YYY_redc(x,&(f->a.a.b));
+    BIG_XXX_toBytes(&t[MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->a.b.a));
+    BIG_XXX_toBytes(&t[2*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->a.b.b));
+    BIG_XXX_toBytes(&t[3*MODBYTES_XXX],x);
+
+    FP_YYY_redc(x,&(f->b.a.a));
+    BIG_XXX_toBytes(&t[4*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->b.a.b));
+    BIG_XXX_toBytes(&t[5*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->b.b.a));
+    BIG_XXX_toBytes(&t[6*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->b.b.b));
+    BIG_XXX_toBytes(&t[7*MODBYTES_XXX],x);
+
+    ECP_ZZZ_get(x,y,P);
+    BIG_XXX_toBytes(&t[8*MODBYTES_XXX],x);
+    BIG_XXX_toBytes(&t[9*MODBYTES_XXX],y);
+
+    OCT_empty(w);
+    switch (sha)
+    {
+    case SHA256:
+        HASH256_init(&sha256);
+        for (i=0; i<10*MODBYTES_XXX; i++) HASH256_process(&sha256,t[i]);
+        HASH256_hash(&sha256,h);
+        break;
+    case SHA384:
+        HASH384_init(&sha512);
+        for (i=0; i<10*MODBYTES_XXX; i++) HASH384_process(&sha512,t[i]);
+        HASH384_hash(&sha512,h);
+        break;
+    case SHA512:
+        HASH512_init(&sha512);
+        for (i=0; i<10*MODBYTES_XXX; i++) HASH512_process(&sha512,t[i]);
+        HASH512_hash(&sha512,h);
+        break;
+    }
+
+    OCT_jbytes(w,h,AESKEY_ZZZ);
+    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_ZZZ *P,BIG_XXX u,int cb)
+{
+    BIG_XXX x,q;
+
+    BIG_XXX_rcopy(q,Modulus_YYY);
+    BIG_XXX_copy(x,u);
+    BIG_XXX_mod(x,q);
+
+    while (!ECP_ZZZ_setx(P,x,cb))
+    {
+        BIG_XXX_inc(x,1);
+        BIG_XXX_norm(x);
+    }
+}
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+static int unmap(BIG_XXX u,int *cb,ECP_ZZZ *P)
+{
+    int s,r=0;
+    BIG_XXX x;
+
+    s=ECP_ZZZ_get(x,x,P);
+    BIG_XXX_copy(u,x);
+    do
+    {
+        BIG_XXX_dec(u,1);
+        BIG_XXX_norm(u);
+        r++;
+    }
+    while (!ECP_ZZZ_setx(P,u,s));
+    ECP_ZZZ_setx(P,x,s);
+
+    *cb=s;
+
+    return r;
+}
+
+/* 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_ZZZ_ENCODING(csprng *RNG,octet *E)
+{
+    int rn,m,su,sv,res=0;
+
+    BIG_XXX q,u,v;
+    ECP_ZZZ P,W;
+
+    if (!ECP_ZZZ_fromOctet(&P,E)) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        BIG_XXX_rcopy(q,Modulus_YYY);
+
+        BIG_XXX_randomnum(u,q,RNG);
+
+        su=RAND_byte(RNG);
+        if (su<0) su=-su;
+        su%=2;
+        map(&W,u,su);
+        ECP_ZZZ_sub(&P,&W);
+
+        rn=unmap(v,&sv,&P);
+        m=RAND_byte(RNG);
+        if (m<0) m=-m;
+        m%=rn;
+        BIG_XXX_inc(v,m+1);
+        E->val[0]=su+2*sv;
+        BIG_XXX_toBytes(&(E->val[1]),u);
+        BIG_XXX_toBytes(&(E->val[PFS_ZZZ+1]),v);
+    }
+    return res;
+}
+
+int MPIN_ZZZ_DECODING(octet *D)
+{
+    int su,sv;
+    BIG_XXX u,v;
+    ECP_ZZZ P,W;
+    int res=0;
+
+    if ((D->val[0]&0x04)!=0) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+
+        BIG_XXX_fromBytes(u,&(D->val[1]));
+        BIG_XXX_fromBytes(v,&(D->val[PFS_ZZZ+1]));
+
+        su=D->val[0]&1;
+        sv=(D->val[0]>>1)&1;
+        map(&W,u,su);
+        map(&P,v,sv);
+        ECP_ZZZ_add(&P,&W);
+        ECP_ZZZ_toOctet(D,&P);
+    }
+
+    return res;
+}
+
+/* R=R1+R2 in group G1 */
+int MPIN_ZZZ_RECOMBINE_G1(octet *R1,octet *R2,octet *R)
+{
+    ECP_ZZZ P,T;
+    int res=0;
+    if (res==0)
+    {
+        if (!ECP_ZZZ_fromOctet(&P,R1)) res=MPIN_INVALID_POINT;
+        if (!ECP_ZZZ_fromOctet(&T,R2)) res=MPIN_INVALID_POINT;
+    }
+    if (res==0)
+    {
+        ECP_ZZZ_add(&P,&T);
+        ECP_ZZZ_toOctet(R,&P);
+    }
+    return res;
+}
+
+/* W=W1+W2 in group G2 */
+int MPIN_ZZZ_RECOMBINE_G2(octet *W1,octet *W2,octet *W)
+{
+    ECP4_ZZZ Q,T;
+    int res=0;
+    if (!ECP4_ZZZ_fromOctet(&Q,W1)) res=MPIN_INVALID_POINT;
+    if (!ECP4_ZZZ_fromOctet(&T,W2)) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        ECP4_ZZZ_add(&Q,&T);
+        ECP4_ZZZ_toOctet(W,&Q);
+    }
+    return res;
+}
+
+/* create random secret S */
+int MPIN_ZZZ_RANDOM_GENERATE(csprng *RNG,octet* S)
+{
+    BIG_XXX r,s;
+
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+    BIG_XXX_randomnum(s,r,RNG);
+#ifdef AES_S
+    BIG_XXX_mod2m(s,2*AES_S);
+#endif
+    BIG_XXX_toBytes(S->val,s);
+    S->len=MODBYTES_XXX;
+    return 0;
+}
+
+/* Extract PIN from TOKEN for identity CID */
+int MPIN_ZZZ_EXTRACT_PIN(int sha,octet *CID,int pin,octet *TOKEN)
+{
+    pin%=MAXPIN;
+    return MPIN_ZZZ_EXTRACT_FACTOR(sha,CID,pin,PBLEN,TOKEN);
+}
+
+/* Extract a factor < 32 bits for identity CID */
+int MPIN_ZZZ_EXTRACT_FACTOR(int sha,octet *CID,int factor,int facbits,octet *TOKEN)
+{
+    ECP_ZZZ P,R;
+    int res=0;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    if (!ECP_ZZZ_fromOctet(&P,TOKEN))  res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        mhashit(sha,-1,CID,&H);
+        ECP_ZZZ_mapit(&R,&H);
+
+        ECP_ZZZ_pinmul(&R,factor,facbits);
+        ECP_ZZZ_sub(&P,&R);
+
+        ECP_ZZZ_toOctet(TOKEN,&P);
+    }
+    return res;
+}
+
+/* Extract a factor < 32 bits for identity CID */
+int MPIN_ZZZ_RESTORE_FACTOR(int sha,octet *CID,int factor,int facbits,octet *TOKEN)
+{
+    ECP_ZZZ P,R;
+    int res=0;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    if (!ECP_ZZZ_fromOctet(&P,TOKEN))  res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        mhashit(sha,-1,CID,&H);
+        ECP_ZZZ_mapit(&R,&H);
+
+        ECP_ZZZ_pinmul(&R,factor,facbits);
+        ECP_ZZZ_add(&P,&R);
+
+        ECP_ZZZ_toOctet(TOKEN,&P);
+    }
+    return res;
+}
+
+/* Implement step 2 on client side of MPin protocol - SEC=-(x+y)*SEC */
+int MPIN_ZZZ_CLIENT_2(octet *X,octet *Y,octet *SEC)
+{
+    BIG_XXX px,py,r;
+    ECP_ZZZ P;
+    int res=0;
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+    if (!ECP_ZZZ_fromOctet(&P,SEC)) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        BIG_XXX_fromBytes(px,X->val);
+        BIG_XXX_fromBytes(py,Y->val);
+        BIG_XXX_add(px,px,py);
+        BIG_XXX_mod(px,r);
+        //	BIG_XXX_sub(px,r,px);
+        PAIR_ZZZ_G1mul(&P,px);
+        ECP_ZZZ_neg(&P);
+        ECP_ZZZ_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_ZZZ_GET_G1_MULTIPLE(csprng *RNG,int type,octet *X,octet *G,octet *W)
+{
+    ECP_ZZZ P;
+    BIG_XXX r,x;
+    int res=0;
+    if (RNG!=NULL)
+    {
+        BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+        BIG_XXX_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_XXX_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES_XXX;
+        BIG_XXX_toBytes(X->val,x);
+    }
+    else
+        BIG_XXX_fromBytes(x,X->val);
+
+    if (type==0)
+    {
+        if (!ECP_ZZZ_fromOctet(&P,G)) res=MPIN_INVALID_POINT;
+    }
+    else
+    {
+        ECP_ZZZ_mapit(&P,G);
+    }
+
+    if (res==0)
+    {
+        PAIR_ZZZ_G1mul(&P,x);
+        ECP_ZZZ_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_ZZZ_GET_G2_MULTIPLE(csprng *RNG,int type,octet *X,octet *G,octet *W)
+{
+    ECP4_ZZZ P;
+    BIG_XXX r,x;
+    int res=0;
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+    if (RNG!=NULL)
+    {
+        BIG_XXX_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_XXX_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES_XXX;
+        BIG_XXX_toBytes(X->val,x);
+    }
+    else
+    {
+        BIG_XXX_fromBytes(x,X->val);
+        if (type==1) BIG_XXX_invmodp(x,x,r);
+    }
+
+    if (!ECP4_ZZZ_fromOctet(&P,G)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        PAIR_ZZZ_G2mul(&P,x);
+        ECP4_ZZZ_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_ZZZ_GET_CLIENT_SECRET(octet *S,octet *CID,octet *CST)
+{
+    return MPIN_ZZZ_GET_G1_MULTIPLE(NULL,1,S,CID,CST);
+}
+
+/* Implement step 1 on client side of MPin protocol */
+int MPIN_ZZZ_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_XXX r,x;
+    ECP_ZZZ P,T,W;
+    int res=0;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+    if (RNG!=NULL)
+    {
+        BIG_XXX_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_XXX_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES_XXX;
+        BIG_XXX_toBytes(X->val,x);
+    }
+    else
+        BIG_XXX_fromBytes(x,X->val);
+
+    mhashit(sha,-1,CLIENT_ID,&H);
+
+    ECP_ZZZ_mapit(&P,&H);
+
+    if (!ECP_ZZZ_fromOctet(&T,TOKEN)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        pin%=MAXPIN;
+
+        ECP_ZZZ_copy(&W,&P);				// W=H(ID)
+        ECP_ZZZ_pinmul(&W,pin,PBLEN);			// W=alpha.H(ID)
+        ECP_ZZZ_add(&T,&W);					// T=Token+alpha.H(ID) = s.H(ID)
+
+        if (date)
+        {
+            if (PERMIT!=NULL)
+            {
+                if (!ECP_ZZZ_fromOctet(&W,PERMIT)) res=MPIN_INVALID_POINT;
+                ECP_ZZZ_add(&T,&W);					// SEC=s.H(ID)+s.H(T|ID)
+            }
+            mhashit(sha,date,&H,&H);
+
+            ECP_ZZZ_mapit(&W,&H);
+            if (xID!=NULL)
+            {
+                PAIR_ZZZ_G1mul(&P,x);				// P=x.H(ID)
+                ECP_ZZZ_toOctet(xID,&P);  // xID
+                PAIR_ZZZ_G1mul(&W,x);               // W=x.H(T|ID)
+                ECP_ZZZ_add(&P,&W);
+            }
+            else
+            {
+                ECP_ZZZ_add(&P,&W);
+                PAIR_ZZZ_G1mul(&P,x);
+            }
+            if (xCID!=NULL) ECP_ZZZ_toOctet(xCID,&P);  // U
+        }
+        else
+        {
+            if (xID!=NULL)
+            {
+                PAIR_ZZZ_G1mul(&P,x);				// P=x.H(ID)
+                ECP_ZZZ_toOctet(xID,&P);  // xID
+            }
+        }
+    }
+
+    if (res==0)
+        ECP_ZZZ_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_ZZZ_GET_SERVER_SECRET(octet *S,octet *SST)
+{
+    BIG_XXX r,s;
+    ECP4_ZZZ Q;
+    int res=0;
+
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+
+    ECP4_ZZZ_generator(&Q);
+
+    if (res==0)
+    {
+        BIG_XXX_fromBytes(s,S->val);
+        PAIR_ZZZ_G2mul(&Q,s);
+        ECP4_ZZZ_toOctet(SST,&Q);
+    }
+
+    return res;
+}
+
+
+/* Time Permit CTT=s*H(date|H(CID)) where s is master secret */
+int MPIN_ZZZ_GET_CLIENT_PERMIT(int sha,int date,octet *S,octet *CID,octet *CTT)
+{
+    BIG_XXX s;
+    ECP_ZZZ P;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    mhashit(sha,date,CID,&H);
+
+    ECP_ZZZ_mapit(&P,&H);
+
+    BIG_XXX_fromBytes(s,S->val);
+
+    PAIR_ZZZ_G1mul(&P,s);
+
+    ECP_ZZZ_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_ZZZ_SERVER_1(int sha,int date,octet *CID,octet *HID,octet *HTID)
+{
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+    ECP_ZZZ P,R;
+
+#ifdef USE_ANONYMOUS
+    ECP_ZZZ_mapit(&P,CID);
+#else
+    mhashit(sha,-1,CID,&H);
+    ECP_ZZZ_mapit(&P,&H);
+#endif
+
+    ECP_ZZZ_toOctet(HID,&P);  // new
+
+    if (date)
+    {
+#ifdef USE_ANONYMOUS
+        mhashit(sha,date,CID,&H);
+#else
+        mhashit(sha,date,&H,&H);
+#endif
+        ECP_ZZZ_mapit(&R,&H);
+        ECP_ZZZ_add(&P,&R);
+        ECP_ZZZ_toOctet(HTID,&P);
+    }
+}
+
+/* Implement M-Pin on server side */
+int MPIN_ZZZ_SERVER_2(int date,octet *HID,octet *HTID,octet *Y,octet *SST,octet *xID,octet *xCID,octet *mSEC,octet *E,octet *F,octet *Pa)
+{
+    BIG_XXX px,py,y;
+    FP24_YYY g;
+    ECP4_ZZZ Q,sQ;
+    ECP_ZZZ P,R;
+    int res=0;
+
+    ECP4_ZZZ_generator(&Q);
+
+    // key-escrow less scheme: use Pa instead of Q in pairing computation
+    // Q left for backward compatiblity
+    if (Pa!=NULL)
+    {
+        if (!ECP4_ZZZ_fromOctet(&Q, Pa)) res=MPIN_INVALID_POINT;
+    }
+
+
+    if (res==0)
+    {
+        if (!ECP4_ZZZ_fromOctet(&sQ,SST)) res=MPIN_INVALID_POINT;
+    }
+
+    if (res==0)
+    {
+        if (date)
+        {
+            BIG_XXX_fromBytes(px,&(xCID->val[1]));
+            BIG_XXX_fromBytes(py,&(xCID->val[PFS_ZZZ+1]));
+        }
+        else
+        {
+            BIG_XXX_fromBytes(px,&(xID->val[1]));
+            BIG_XXX_fromBytes(py,&(xID->val[PFS_ZZZ+1]));
+        }
+        if (!ECP_ZZZ_set(&R,px,py)) res=MPIN_INVALID_POINT; // x(A+AT)
+    }
+    if (res==0)
+    {
+        BIG_XXX_fromBytes(y,Y->val);
+        if (date)
+        {
+            if (!ECP_ZZZ_fromOctet(&P,HTID))  res=MPIN_INVALID_POINT;
+        }
+        else
+        {
+            if (!ECP_ZZZ_fromOctet(&P,HID))  res=MPIN_INVALID_POINT;
+        }
+    }
+    if (res==0)
+    {
+        PAIR_ZZZ_G1mul(&P,y);  // y(A+AT)
+        ECP_ZZZ_add(&P,&R); // x(A+AT)+y(A+T)
+        ECP_ZZZ_affine(&P);
+        if (!ECP_ZZZ_fromOctet(&R,mSEC))  res=MPIN_INVALID_POINT; // V
+    }
+    if (res==0)
+    {
+
+        PAIR_ZZZ_double_ate(&g,&Q,&R,&sQ,&P);
+        PAIR_ZZZ_fexp(&g);
+
+        if (!FP24_YYY_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 */
+                FP24_YYY_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_ZZZ_fromOctet(&P,HID)) res=MPIN_INVALID_POINT;
+                    if (!ECP_ZZZ_fromOctet(&R,xID)) res=MPIN_INVALID_POINT; // U
+
+                    if (res==0)
+                    {
+                        PAIR_ZZZ_G1mul(&P,y);  // yA
+                        ECP_ZZZ_add(&P,&R); // yA+xA
+                        ECP_ZZZ_affine(&P);
+                    }
+                }
+                if (res==0)
+                {
+                    PAIR_ZZZ_ate(&g,&Q,&P);
+                    PAIR_ZZZ_fexp(&g);
+                    FP24_YYY_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_ZZZ_KANGAROO(octet *E,octet *F)
+{
+    int i,j,m,s,dn,dm,steps;
+    int distance[MR_TS];
+    FP24_YYY ge,gf,t,table[MR_TS];
+    int res=0;
+    // BIG_XXX w;
+
+    FP24_YYY_fromOctet(&ge,E);
+    FP24_YYY_fromOctet(&gf,F);
+
+    FP24_YYY_copy(&t,&gf);
+
+    for (s=1,m=0; m<MR_TS; m++)
+    {
+        distance[m]=s;
+        FP24_YYY_copy(&table[m],&t);
+        s*=2;
+        FP24_YYY_usqr(&t,&t);
+        FP24_YYY_reduce(&t);
+    }
+
+    FP24_YYY_one(&t);
+
+    for (dn=0,j=0; j<TRAP; j++)
+    {
+        i=t.a.a.a.a.g[0]%MR_TS;
+
+        FP24_YYY_mul(&t,&table[i]);
+        FP24_YYY_reduce(&t);
+        dn+=distance[i];
+    }
+
+    FP24_YYY_conj(&gf,&t);
+    steps=0;
+    dm=0;
+    while (dm-dn<MAXPIN)
+    {
+        steps++;
+        if (steps>4*TRAP) break;
+
+        i=ge.a.a.a.a.g[0]%MR_TS;
+
+        FP24_YYY_mul(&ge,&table[i]);
+        FP24_YYY_reduce(&ge);
+        dm+=distance[i];
+        if (FP24_YYY_equals(&ge,&t))
+        {
+            res=dm-dn;
+            break;
+        }
+        if (FP24_YYY_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_ZZZ_PRECOMPUTE(octet *TOKEN,octet *CID,octet *CP,octet *G1,octet *G2)
+{
+    ECP_ZZZ P,T;
+    ECP4_ZZZ Q;
+    FP24_YYY g;
+    int res=0;
+
+    if (!ECP_ZZZ_fromOctet(&T,TOKEN)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        ECP_ZZZ_mapit(&P,CID);
+        if (CP!=NULL)
+        {
+            if (!ECP4_ZZZ_fromOctet(&Q,CP)) res=MPIN_INVALID_POINT;
+        }
+        else
+        {
+            ECP4_ZZZ_generator(&Q);
+        }
+    }
+    if (res==0)
+    {
+        PAIR_ZZZ_ate(&g,&Q,&T);
+        PAIR_ZZZ_fexp(&g);
+
+        FP24_YYY_toOctet(G1,&g);
+        if (G2!=NULL)
+        {
+            PAIR_ZZZ_ate(&g,&Q,&P);
+            PAIR_ZZZ_fexp(&g);
+            FP24_YYY_toOctet(G2,&g);
+        }
+    }
+    return res;
+}
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+int MPIN_ZZZ_CLIENT_KEY(int sha,octet *G1,octet *G2,int pin,octet *R,octet *X,octet *H,octet *wCID,octet *CK)
+{
+    FP24_YYY g1,g2;
+    FP8_YYY c;
+
+    ECP_ZZZ W;
+    int res=0;
+    BIG_XXX r,z,x,h;
+
+    FP24_YYY_fromOctet(&g1,G1);
+    FP24_YYY_fromOctet(&g2,G2);
+    BIG_XXX_fromBytes(z,R->val);
+    BIG_XXX_fromBytes(x,X->val);
+    BIG_XXX_fromBytes(h,H->val);
+
+    if (!ECP_ZZZ_fromOctet(&W,wCID)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+        BIG_XXX_add(z,z,h);    // new
+        BIG_XXX_mod(z,r);
+
+        FP24_YYY_pinpow(&g2,pin,PBLEN);
+        FP24_YYY_mul(&g1,&g2);
+
+        PAIR_ZZZ_G1mul(&W,x);
+
+        FP24_YYY_compow(&c,&g1,z,r);
+        mpin_hash(sha,&c,&W,CK);
+
+    }
+    return res;
+}
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+int MPIN_ZZZ_SERVER_KEY(int sha,octet *Z,octet *SST,octet *W,octet *H,octet *HID,octet *xID,octet *xCID,octet *SK)
+{
+    int res=0;
+    FP24_YYY g;
+    FP8_YYY c;
+    ECP_ZZZ R,U,A;
+    ECP4_ZZZ sQ;
+    BIG_XXX w,h;
+
+    if (!ECP4_ZZZ_fromOctet(&sQ,SST)) res=MPIN_INVALID_POINT;
+    if (!ECP_ZZZ_fromOctet(&R,Z)) res=MPIN_INVALID_POINT;
+
+
+    if (!ECP_ZZZ_fromOctet(&A,HID)) res=MPIN_INVALID_POINT;
+
+    // new
+    if (xCID!=NULL)
+    {
+        if (!ECP_ZZZ_fromOctet(&U,xCID)) res=MPIN_INVALID_POINT;
+    }
+    else
+    {
+        if (!ECP_ZZZ_fromOctet(&U,xID)) res=MPIN_INVALID_POINT;
+    }
+    BIG_XXX_fromBytes(w,W->val);
+    BIG_XXX_fromBytes(h,H->val);
+
+
+    PAIR_ZZZ_ate(&g,&sQ,&A);
+    PAIR_ZZZ_fexp(&g);
+
+    if (res==0)
+    {
+        PAIR_ZZZ_G1mul(&A,h);
+        ECP_ZZZ_add(&R,&A);  // new
+        ECP_ZZZ_affine(&R);
+        PAIR_ZZZ_ate(&g,&sQ,&R);
+        PAIR_ZZZ_fexp(&g);
+        PAIR_ZZZ_G1mul(&U,w);
+        FP24_YYY_trace(&c,&g);
+        mpin_hash(sha,&c,&U,SK);
+    }
+    return res;
+}
+
+/* Generate Y = H(TimeValue, xCID/xID) */
+void MPIN_ZZZ_GET_Y(int sha,int TimeValue,octet *xCID,octet *Y)
+{
+    BIG_XXX q,y;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    mhashit(sha,TimeValue,xCID,&H);
+    BIG_XXX_fromBytes(y,H.val);
+    BIG_XXX_rcopy(q,CURVE_Order_ZZZ);
+    BIG_XXX_mod(y,q);
+    BIG_XXX_toBytes(Y->val,y);
+    Y->len=PGS_ZZZ;
+}
+
+/* One pass MPIN Client */
+int MPIN_ZZZ_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_ZZZ];
+    octet M= {0,sizeof(m),m};
+
+    octet *pID;
+    if (date == 0)
+        pID = U;
+    else
+        pID = UT;
+
+    rtn = MPIN_ZZZ_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_ZZZ_GET_Y(sha,TimeValue,&M,Y);
+
+    rtn = MPIN_ZZZ_CLIENT_2(X,Y,V);
+    if (rtn != 0)
+        return rtn;
+
+    return 0;
+}
+
+/* One pass MPIN Server */
+int MPIN_ZZZ_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, octet *Pa)
+{
+    int rtn=0;
+    char m[M_SIZE_ZZZ];
+    octet M= {0,sizeof(m),m};
+
+    octet *pU;
+    if (date == 0)
+        pU = U;
+    else
+        pU = UT;
+
+    MPIN_ZZZ_SERVER_1(sha,date,ID,HID,HTID);
+
+    OCT_joctet(&M,pU);
+    if (MESSAGE!=NULL)
+    {
+        OCT_joctet(&M,MESSAGE);
+    }
+
+    MPIN_ZZZ_GET_Y(sha,TimeValue,&M,Y);
+
+    rtn = MPIN_ZZZ_SERVER_2(date,HID,HTID,Y,sQ,U,UT,V,E,F,Pa);
+    if (rtn != 0)
+        return rtn;
+
+    return 0;
+}
+
+int MPIN_ZZZ_GET_DVS_KEYPAIR(csprng *R,octet *Z,octet *Pa)
+{
+    BIG_XXX z,r;
+    ECP4_ZZZ Q;
+    int res=0;
+
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+
+    if (R!=NULL)
+    {
+        BIG_XXX_randomnum(z,r,R);
+        Z->len=MODBYTES_XXX;
+        BIG_XXX_toBytes(Z->val,z);
+    }
+    else
+        BIG_XXX_fromBytes(z,Z->val);
+
+    BIG_XXX_invmodp(z,z,r);
+
+    ECP4_ZZZ_generator(&Q);
+
+    if (res==0)
+    {
+        PAIR_ZZZ_G2mul(&Q,z);
+        ECP4_ZZZ_toOctet(Pa,&Q);
+    }
+
+    return res;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto-c/blob/8d28d2c3/src/mpin256.c.in
----------------------------------------------------------------------
diff --git a/src/mpin256.c.in b/src/mpin256.c.in
new file mode 100644
index 0000000..7dd261b
--- /dev/null
+++ b/src/mpin256.c.in
@@ -0,0 +1,972 @@
+/*
+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 "mpin256_ZZZ.h"
+
+
+#define ROUNDUP(a,b) ((a)-1)/(b)+1
+
+
+/* Special mpin hashing */
+static void mpin_hash(int sha,FP16_YYY *f, ECP_ZZZ *P,octet *w)
+{
+    int i;
+    BIG_XXX x,y;
+    char h[64];
+    hash256 sha256;
+    hash512 sha512;
+    char t[18*MODBYTES_XXX];  // to hold 10 BIGs
+    int hlen=sha;
+
+    FP_YYY_redc(x,&(f->a.a.a.a));
+    BIG_XXX_toBytes(&t[0],x);
+    FP_YYY_redc(x,&(f->a.a.a.b));
+    BIG_XXX_toBytes(&t[MODBYTES_XXX],x);
+
+    FP_YYY_redc(x,&(f->a.a.b.a));
+    BIG_XXX_toBytes(&t[2*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->a.a.b.b));
+    BIG_XXX_toBytes(&t[3*MODBYTES_XXX],x);
+
+    FP_YYY_redc(x,&(f->a.b.a.a));
+    BIG_XXX_toBytes(&t[4*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->a.b.a.b));
+    BIG_XXX_toBytes(&t[5*MODBYTES_XXX],x);
+
+    FP_YYY_redc(x,&(f->a.b.b.a));
+    BIG_XXX_toBytes(&t[6*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->a.b.b.b));
+    BIG_XXX_toBytes(&t[7*MODBYTES_XXX],x);
+
+    FP_YYY_redc(x,&(f->b.a.a.a));
+    BIG_XXX_toBytes(&t[8*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->b.a.a.b));
+    BIG_XXX_toBytes(&t[9*MODBYTES_XXX],x);
+
+    FP_YYY_redc(x,&(f->b.a.b.a));
+    BIG_XXX_toBytes(&t[10*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->b.a.b.b));
+    BIG_XXX_toBytes(&t[11*MODBYTES_XXX],x);
+
+    FP_YYY_redc(x,&(f->b.b.a.a));
+    BIG_XXX_toBytes(&t[12*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->b.b.a.b));
+    BIG_XXX_toBytes(&t[13*MODBYTES_XXX],x);
+
+    FP_YYY_redc(x,&(f->b.b.b.a));
+    BIG_XXX_toBytes(&t[14*MODBYTES_XXX],x);
+    FP_YYY_redc(x,&(f->b.b.b.b));
+    BIG_XXX_toBytes(&t[15*MODBYTES_XXX],x);
+
+    ECP_ZZZ_get(x,y,P);
+    BIG_XXX_toBytes(&t[16*MODBYTES_XXX],x);
+    BIG_XXX_toBytes(&t[17*MODBYTES_XXX],y);
+
+    OCT_empty(w);
+    switch (sha)
+    {
+    case SHA256:
+        HASH256_init(&sha256);
+        for (i=0; i<18*MODBYTES_XXX; i++) HASH256_process(&sha256,t[i]);
+        HASH256_hash(&sha256,h);
+        break;
+    case SHA384:
+        HASH384_init(&sha512);
+        for (i=0; i<18*MODBYTES_XXX; i++) HASH384_process(&sha512,t[i]);
+        HASH384_hash(&sha512,h);
+        break;
+    case SHA512:
+        HASH512_init(&sha512);
+        for (i=0; i<18*MODBYTES_XXX; i++) HASH512_process(&sha512,t[i]);
+        HASH512_hash(&sha512,h);
+        break;
+    }
+
+    OCT_jbytes(w,h,AESKEY_ZZZ);
+    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_ZZZ *P,BIG_XXX u,int cb)
+{
+    BIG_XXX x,q;
+
+    BIG_XXX_rcopy(q,Modulus_YYY);
+    BIG_XXX_copy(x,u);
+    BIG_XXX_mod(x,q);
+
+    while (!ECP_ZZZ_setx(P,x,cb))
+    {
+        BIG_XXX_inc(x,1);
+        BIG_XXX_norm(x);
+    }
+}
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+static int unmap(BIG_XXX u,int *cb,ECP_ZZZ *P)
+{
+    int s,r=0;
+    BIG_XXX x;
+
+    s=ECP_ZZZ_get(x,x,P);
+    BIG_XXX_copy(u,x);
+    do
+    {
+        BIG_XXX_dec(u,1);
+        BIG_XXX_norm(u);
+        r++;
+    }
+    while (!ECP_ZZZ_setx(P,u,s));
+    ECP_ZZZ_setx(P,x,s);
+
+    *cb=s;
+
+    return r;
+}
+
+/* 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_ZZZ_ENCODING(csprng *RNG,octet *E)
+{
+    int rn,m,su,sv,res=0;
+
+    BIG_XXX q,u,v;
+    ECP_ZZZ P,W;
+
+    if (!ECP_ZZZ_fromOctet(&P,E)) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        BIG_XXX_rcopy(q,Modulus_YYY);
+
+        BIG_XXX_randomnum(u,q,RNG);
+
+        su=RAND_byte(RNG);
+        if (su<0) su=-su;
+        su%=2;
+        map(&W,u,su);
+        ECP_ZZZ_sub(&P,&W);
+
+        rn=unmap(v,&sv,&P);
+        m=RAND_byte(RNG);
+        if (m<0) m=-m;
+        m%=rn;
+        BIG_XXX_inc(v,m+1);
+        E->val[0]=su+2*sv;
+        BIG_XXX_toBytes(&(E->val[1]),u);
+        BIG_XXX_toBytes(&(E->val[PFS_ZZZ+1]),v);
+    }
+    return res;
+}
+
+int MPIN_ZZZ_DECODING(octet *D)
+{
+    int su,sv;
+    BIG_XXX u,v;
+    ECP_ZZZ P,W;
+    int res=0;
+
+    if ((D->val[0]&0x04)!=0) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+
+        BIG_XXX_fromBytes(u,&(D->val[1]));
+        BIG_XXX_fromBytes(v,&(D->val[PFS_ZZZ+1]));
+
+        su=D->val[0]&1;
+        sv=(D->val[0]>>1)&1;
+        map(&W,u,su);
+        map(&P,v,sv);
+        ECP_ZZZ_add(&P,&W);
+        ECP_ZZZ_toOctet(D,&P);
+    }
+
+    return res;
+}
+
+/* R=R1+R2 in group G1 */
+int MPIN_ZZZ_RECOMBINE_G1(octet *R1,octet *R2,octet *R)
+{
+    ECP_ZZZ P,T;
+    int res=0;
+    if (res==0)
+    {
+        if (!ECP_ZZZ_fromOctet(&P,R1)) res=MPIN_INVALID_POINT;
+        if (!ECP_ZZZ_fromOctet(&T,R2)) res=MPIN_INVALID_POINT;
+    }
+    if (res==0)
+    {
+        ECP_ZZZ_add(&P,&T);
+        ECP_ZZZ_toOctet(R,&P);
+    }
+    return res;
+}
+
+/* W=W1+W2 in group G2 */
+int MPIN_ZZZ_RECOMBINE_G2(octet *W1,octet *W2,octet *W)
+{
+    ECP8_ZZZ Q,T;
+    int res=0;
+    if (!ECP8_ZZZ_fromOctet(&Q,W1)) res=MPIN_INVALID_POINT;
+    if (!ECP8_ZZZ_fromOctet(&T,W2)) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        ECP8_ZZZ_add(&Q,&T);
+        ECP8_ZZZ_toOctet(W,&Q);
+    }
+    return res;
+}
+
+/* create random secret S */
+int MPIN_ZZZ_RANDOM_GENERATE(csprng *RNG,octet* S)
+{
+    BIG_XXX r,s;
+
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+    BIG_XXX_randomnum(s,r,RNG);
+#ifdef AES_S
+    BIG_XXX_mod2m(s,2*AES_S);
+#endif
+    BIG_XXX_toBytes(S->val,s);
+    S->len=MODBYTES_XXX;
+    return 0;
+}
+
+/* Extract PIN from TOKEN for identity CID */
+int MPIN_ZZZ_EXTRACT_PIN(int sha,octet *CID,int pin,octet *TOKEN)
+{
+    pin%=MAXPIN;
+    return MPIN_ZZZ_EXTRACT_FACTOR(sha,CID,pin,PBLEN,TOKEN);
+}
+
+/* Extract a factor < 32 bits for identity CID */
+int MPIN_ZZZ_EXTRACT_FACTOR(int sha,octet *CID,int factor,int facbits,octet *TOKEN)
+{
+    ECP_ZZZ P,R;
+    int res=0;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    if (!ECP_ZZZ_fromOctet(&P,TOKEN))  res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        mhashit(sha,-1,CID,&H);
+        ECP_ZZZ_mapit(&R,&H);
+
+        ECP_ZZZ_pinmul(&R,factor,facbits);
+        ECP_ZZZ_sub(&P,&R);
+
+        ECP_ZZZ_toOctet(TOKEN,&P);
+    }
+    return res;
+}
+
+/* Extract a factor < 32 bits for identity CID */
+int MPIN_ZZZ_RESTORE_FACTOR(int sha,octet *CID,int factor,int facbits,octet *TOKEN)
+{
+    ECP_ZZZ P,R;
+    int res=0;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    if (!ECP_ZZZ_fromOctet(&P,TOKEN))  res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        mhashit(sha,-1,CID,&H);
+        ECP_ZZZ_mapit(&R,&H);
+
+        ECP_ZZZ_pinmul(&R,factor,facbits);
+        ECP_ZZZ_add(&P,&R);
+
+        ECP_ZZZ_toOctet(TOKEN,&P);
+    }
+    return res;
+}
+
+/* Implement step 2 on client side of MPin protocol - SEC=-(x+y)*SEC */
+int MPIN_ZZZ_CLIENT_2(octet *X,octet *Y,octet *SEC)
+{
+    BIG_XXX px,py,r;
+    ECP_ZZZ P;
+    int res=0;
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+    if (!ECP_ZZZ_fromOctet(&P,SEC)) res=MPIN_INVALID_POINT;
+    if (res==0)
+    {
+        BIG_XXX_fromBytes(px,X->val);
+        BIG_XXX_fromBytes(py,Y->val);
+        BIG_XXX_add(px,px,py);
+        BIG_XXX_mod(px,r);
+        PAIR_ZZZ_G1mul(&P,px);
+        ECP_ZZZ_neg(&P);
+        ECP_ZZZ_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_ZZZ_GET_G1_MULTIPLE(csprng *RNG,int type,octet *X,octet *G,octet *W)
+{
+    ECP_ZZZ P;
+    BIG_XXX r,x;
+    int res=0;
+    if (RNG!=NULL)
+    {
+        BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+        BIG_XXX_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_XXX_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES_XXX;
+        BIG_XXX_toBytes(X->val,x);
+    }
+    else
+        BIG_XXX_fromBytes(x,X->val);
+
+    if (type==0)
+    {
+        if (!ECP_ZZZ_fromOctet(&P,G)) res=MPIN_INVALID_POINT;
+    }
+    else
+    {
+        ECP_ZZZ_mapit(&P,G);
+    }
+
+    if (res==0)
+    {
+        PAIR_ZZZ_G1mul(&P,x);
+        ECP_ZZZ_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_ZZZ_GET_G2_MULTIPLE(csprng *RNG,int type,octet *X,octet *G,octet *W)
+{
+    ECP8_ZZZ P;
+    BIG_XXX r,x;
+    int res=0;
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+    if (RNG!=NULL)
+    {
+        BIG_XXX_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_XXX_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES_XXX;
+        BIG_XXX_toBytes(X->val,x);
+    }
+    else
+    {
+        BIG_XXX_fromBytes(x,X->val);
+        if (type==1) BIG_XXX_invmodp(x,x,r);
+    }
+
+    if (!ECP8_ZZZ_fromOctet(&P,G)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        PAIR_ZZZ_G2mul(&P,x);
+        ECP8_ZZZ_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_ZZZ_GET_CLIENT_SECRET(octet *S,octet *CID,octet *CST)
+{
+    return MPIN_ZZZ_GET_G1_MULTIPLE(NULL,1,S,CID,CST);
+}
+
+/* Implement step 1 on client side of MPin protocol */
+int MPIN_ZZZ_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_XXX r,x;
+    ECP_ZZZ P,T,W;
+    int res=0;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+    if (RNG!=NULL)
+    {
+        BIG_XXX_randomnum(x,r,RNG);
+#ifdef AES_S
+        BIG_XXX_mod2m(x,2*AES_S);
+#endif
+        X->len=MODBYTES_XXX;
+        BIG_XXX_toBytes(X->val,x);
+    }
+    else
+        BIG_XXX_fromBytes(x,X->val);
+
+    mhashit(sha,-1,CLIENT_ID,&H);
+
+    ECP_ZZZ_mapit(&P,&H);
+
+    if (!ECP_ZZZ_fromOctet(&T,TOKEN)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        pin%=MAXPIN;
+
+        ECP_ZZZ_copy(&W,&P);				// W=H(ID)
+        ECP_ZZZ_pinmul(&W,pin,PBLEN);			// W=alpha.H(ID)
+        ECP_ZZZ_add(&T,&W);					// T=Token+alpha.H(ID) = s.H(ID)
+
+        if (date)
+        {
+            if (PERMIT!=NULL)
+            {
+                if (!ECP_ZZZ_fromOctet(&W,PERMIT)) res=MPIN_INVALID_POINT;
+                ECP_ZZZ_add(&T,&W);					// SEC=s.H(ID)+s.H(T|ID)
+            }
+            mhashit(sha,date,&H,&H);
+
+            ECP_ZZZ_mapit(&W,&H);
+            if (xID!=NULL)
+            {
+                PAIR_ZZZ_G1mul(&P,x);				// P=x.H(ID)
+                ECP_ZZZ_toOctet(xID,&P);  // xID
+                PAIR_ZZZ_G1mul(&W,x);               // W=x.H(T|ID)
+                ECP_ZZZ_add(&P,&W);
+            }
+            else
+            {
+                ECP_ZZZ_add(&P,&W);
+                PAIR_ZZZ_G1mul(&P,x);
+            }
+            if (xCID!=NULL) ECP_ZZZ_toOctet(xCID,&P);  // U
+        }
+        else
+        {
+            if (xID!=NULL)
+            {
+                PAIR_ZZZ_G1mul(&P,x);				// P=x.H(ID)
+                ECP_ZZZ_toOctet(xID,&P);  // xID
+            }
+        }
+    }
+
+    if (res==0)
+        ECP_ZZZ_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_ZZZ_GET_SERVER_SECRET(octet *S,octet *SST)
+{
+    BIG_XXX r,s;
+    ECP8_ZZZ Q;
+    int res=0;
+
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+
+    ECP8_ZZZ_generator(&Q);
+
+    if (res==0)
+    {
+        BIG_XXX_fromBytes(s,S->val);
+        PAIR_ZZZ_G2mul(&Q,s);
+        ECP8_ZZZ_toOctet(SST,&Q);
+    }
+
+    return res;
+}
+
+
+/* Time Permit CTT=s*H(date|H(CID)) where s is master secret */
+int MPIN_ZZZ_GET_CLIENT_PERMIT(int sha,int date,octet *S,octet *CID,octet *CTT)
+{
+    BIG_XXX s;
+    ECP_ZZZ P;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    mhashit(sha,date,CID,&H);
+
+    ECP_ZZZ_mapit(&P,&H);
+
+    BIG_XXX_fromBytes(s,S->val);
+
+    PAIR_ZZZ_G1mul(&P,s);
+    ECP_ZZZ_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_ZZZ_SERVER_1(int sha,int date,octet *CID,octet *HID,octet *HTID)
+{
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+    ECP_ZZZ P,R;
+
+#ifdef USE_ANONYMOUS
+    ECP_ZZZ_mapit(&P,CID);
+#else
+    mhashit(sha,-1,CID,&H);
+    ECP_ZZZ_mapit(&P,&H);
+#endif
+
+    ECP_ZZZ_toOctet(HID,&P);  // new
+
+    if (date)
+    {
+#ifdef USE_ANONYMOUS
+        mhashit(sha,date,CID,&H);
+#else
+        mhashit(sha,date,&H,&H);
+#endif
+        ECP_ZZZ_mapit(&R,&H);
+        ECP_ZZZ_add(&P,&R);
+        ECP_ZZZ_toOctet(HTID,&P);
+    }
+}
+
+/* Implement M-Pin on server side */
+int MPIN_ZZZ_SERVER_2(int date,octet *HID,octet *HTID,octet *Y,octet *SST,octet *xID,octet *xCID,octet *mSEC,octet *E,octet *F,octet *Pa)
+{
+    BIG_XXX px,py,y;
+    FP48_YYY g;
+    ECP8_ZZZ Q,sQ;
+    ECP_ZZZ P,R;
+    int res=0;
+
+    ECP8_ZZZ_generator(&Q);
+
+    // key-escrow less scheme: use Pa instead of Q in pairing computation
+    // Q left for backward compatiblity
+    if (Pa!=NULL)
+    {
+        if (!ECP8_ZZZ_fromOctet(&Q, Pa)) res=MPIN_INVALID_POINT;
+    }
+
+
+    if (res==0)
+    {
+        if (!ECP8_ZZZ_fromOctet(&sQ,SST)) res=MPIN_INVALID_POINT;
+    }
+
+    if (res==0)
+    {
+        if (date)
+        {
+            BIG_XXX_fromBytes(px,&(xCID->val[1]));
+            BIG_XXX_fromBytes(py,&(xCID->val[PFS_ZZZ+1]));
+        }
+        else
+        {
+            BIG_XXX_fromBytes(px,&(xID->val[1]));
+            BIG_XXX_fromBytes(py,&(xID->val[PFS_ZZZ+1]));
+        }
+        if (!ECP_ZZZ_set(&R,px,py)) res=MPIN_INVALID_POINT; // x(A+AT)
+    }
+    if (res==0)
+    {
+        BIG_XXX_fromBytes(y,Y->val);
+        if (date)
+        {
+            if (!ECP_ZZZ_fromOctet(&P,HTID))  res=MPIN_INVALID_POINT;
+        }
+        else
+        {
+            if (!ECP_ZZZ_fromOctet(&P,HID))  res=MPIN_INVALID_POINT;
+        }
+    }
+    if (res==0)
+    {
+        PAIR_ZZZ_G1mul(&P,y);  // y(A+AT)
+        ECP_ZZZ_add(&P,&R); // x(A+AT)+y(A+T)
+        ECP_ZZZ_affine(&P);
+        if (!ECP_ZZZ_fromOctet(&R,mSEC))  res=MPIN_INVALID_POINT; // V
+    }
+    if (res==0)
+    {
+
+        PAIR_ZZZ_double_ate(&g,&Q,&R,&sQ,&P);
+        PAIR_ZZZ_fexp(&g);
+
+        if (!FP48_YYY_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 */
+                FP48_YYY_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_ZZZ_fromOctet(&P,HID)) res=MPIN_INVALID_POINT;
+                    if (!ECP_ZZZ_fromOctet(&R,xID)) res=MPIN_INVALID_POINT; // U
+
+                    if (res==0)
+                    {
+                        PAIR_ZZZ_G1mul(&P,y);  // yA
+                        ECP_ZZZ_add(&P,&R); // yA+xA
+                        ECP_ZZZ_affine(&P);
+                    }
+                }
+                if (res==0)
+                {
+                    PAIR_ZZZ_ate(&g,&Q,&P);
+                    PAIR_ZZZ_fexp(&g);
+                    FP48_YYY_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_ZZZ_KANGAROO(octet *E,octet *F)
+{
+    int i,j,m,s,dn,dm,steps;
+    int distance[MR_TS];
+    FP48_YYY ge,gf,t,table[MR_TS];
+    int res=0;
+
+    FP48_YYY_fromOctet(&ge,E);
+    FP48_YYY_fromOctet(&gf,F);
+
+    FP48_YYY_copy(&t,&gf);
+
+    for (s=1,m=0; m<MR_TS; m++)
+    {
+        distance[m]=s;
+        FP48_YYY_copy(&table[m],&t);
+        s*=2;
+        FP48_YYY_usqr(&t,&t);
+        FP48_YYY_reduce(&t);
+    }
+
+    FP48_YYY_one(&t);
+
+    for (dn=0,j=0; j<TRAP; j++)
+    {
+        i=t.a.a.a.a.a.g[0]%MR_TS;
+
+        FP48_YYY_mul(&t,&table[i]);
+        FP48_YYY_reduce(&t);
+        dn+=distance[i];
+    }
+
+    FP48_YYY_conj(&gf,&t);
+    steps=0;
+    dm=0;
+    while (dm-dn<MAXPIN)
+    {
+        steps++;
+        if (steps>4*TRAP) break;
+
+        i=ge.a.a.a.a.a.g[0]%MR_TS;
+
+        FP48_YYY_mul(&ge,&table[i]);
+        FP48_YYY_reduce(&ge);
+        dm+=distance[i];
+        if (FP48_YYY_equals(&ge,&t))
+        {
+            res=dm-dn;
+            break;
+        }
+        if (FP48_YYY_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_ZZZ_PRECOMPUTE(octet *TOKEN,octet *CID,octet *CP,octet *G1,octet *G2)
+{
+    ECP_ZZZ P,T;
+    ECP8_ZZZ Q;
+    FP48_YYY g;
+    int res=0;
+
+    if (!ECP_ZZZ_fromOctet(&T,TOKEN)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        ECP_ZZZ_mapit(&P,CID);
+        if (CP!=NULL)
+        {
+            if (!ECP8_ZZZ_fromOctet(&Q,CP)) res=MPIN_INVALID_POINT;
+        }
+        else
+        {
+            ECP8_ZZZ_generator(&Q);
+        }
+    }
+    if (res==0)
+    {
+        PAIR_ZZZ_ate(&g,&Q,&T);
+        PAIR_ZZZ_fexp(&g);
+
+        FP48_YYY_toOctet(G1,&g);
+        if (G2!=NULL)
+        {
+            PAIR_ZZZ_ate(&g,&Q,&P);
+            PAIR_ZZZ_fexp(&g);
+            FP48_YYY_toOctet(G2,&g);
+        }
+    }
+    return res;
+}
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+int MPIN_ZZZ_CLIENT_KEY(int sha,octet *G1,octet *G2,int pin,octet *R,octet *X,octet *H,octet *wCID,octet *CK)
+{
+    FP48_YYY g1,g2;
+    FP16_YYY c;
+
+    ECP_ZZZ W;
+    int res=0;
+    BIG_XXX r,z,x,h;
+
+    FP48_YYY_fromOctet(&g1,G1);
+    FP48_YYY_fromOctet(&g2,G2);
+    BIG_XXX_fromBytes(z,R->val);
+    BIG_XXX_fromBytes(x,X->val);
+    BIG_XXX_fromBytes(h,H->val);
+
+    if (!ECP_ZZZ_fromOctet(&W,wCID)) res=MPIN_INVALID_POINT;
+
+    if (res==0)
+    {
+        BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+        BIG_XXX_add(z,z,h);    // new
+        BIG_XXX_mod(z,r);
+
+        FP48_YYY_pinpow(&g2,pin,PBLEN);
+        FP48_YYY_mul(&g1,&g2);
+
+        PAIR_ZZZ_G1mul(&W,x);
+
+        FP48_YYY_compow(&c,&g1,z,r);
+        mpin_hash(sha,&c,&W,CK);
+
+    }
+    return res;
+}
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+
+int MPIN_ZZZ_SERVER_KEY(int sha,octet *Z,octet *SST,octet *W,octet *H,octet *HID,octet *xID,octet *xCID,octet *SK)
+{
+    int res=0;
+    FP48_YYY g;
+    FP16_YYY c;
+    ECP_ZZZ R,U,A;
+    ECP8_ZZZ sQ;
+    BIG_XXX w,h;
+
+    if (!ECP8_ZZZ_fromOctet(&sQ,SST)) res=MPIN_INVALID_POINT;
+    if (!ECP_ZZZ_fromOctet(&R,Z)) res=MPIN_INVALID_POINT;
+
+
+    if (!ECP_ZZZ_fromOctet(&A,HID)) res=MPIN_INVALID_POINT;
+
+    // new
+    if (xCID!=NULL)
+    {
+        if (!ECP_ZZZ_fromOctet(&U,xCID)) res=MPIN_INVALID_POINT;
+    }
+    else
+    {
+        if (!ECP_ZZZ_fromOctet(&U,xID)) res=MPIN_INVALID_POINT;
+    }
+    BIG_XXX_fromBytes(w,W->val);
+    BIG_XXX_fromBytes(h,H->val);
+
+
+    PAIR_ZZZ_ate(&g,&sQ,&A);
+    PAIR_ZZZ_fexp(&g);
+
+    if (res==0)
+    {
+        PAIR_ZZZ_G1mul(&A,h);
+        ECP_ZZZ_add(&R,&A);  // new
+        ECP_ZZZ_affine(&R);
+        PAIR_ZZZ_ate(&g,&sQ,&R);
+        PAIR_ZZZ_fexp(&g);
+        PAIR_ZZZ_G1mul(&U,w);
+        FP48_YYY_trace(&c,&g);
+        mpin_hash(sha,&c,&U,SK);
+    }
+    return res;
+}
+
+/* Generate Y = H(TimeValue, xCID/xID) */
+void MPIN_ZZZ_GET_Y(int sha,int TimeValue,octet *xCID,octet *Y)
+{
+    BIG_XXX q,y;
+    char h[MODBYTES_XXX];
+    octet H= {0,sizeof(h),h};
+
+    mhashit(sha,TimeValue,xCID,&H);
+    BIG_XXX_fromBytes(y,H.val);
+    BIG_XXX_rcopy(q,CURVE_Order_ZZZ);
+    BIG_XXX_mod(y,q);
+    BIG_XXX_toBytes(Y->val,y);
+    Y->len=PGS_ZZZ;
+}
+
+/* One pass MPIN Client */
+int MPIN_ZZZ_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_ZZZ];
+    octet M= {0,sizeof(m),m};
+
+    octet *pID;
+    if (date == 0)
+        pID = U;
+    else
+        pID = UT;
+
+    rtn = MPIN_ZZZ_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_ZZZ_GET_Y(sha,TimeValue,&M,Y);
+
+    rtn = MPIN_ZZZ_CLIENT_2(X,Y,V);
+    if (rtn != 0)
+        return rtn;
+
+    return 0;
+}
+
+/* One pass MPIN Server */
+int MPIN_ZZZ_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, octet *Pa)
+{
+    int rtn=0;
+    char m[M_SIZE_ZZZ];
+    octet M= {0,sizeof(m),m};
+
+    octet *pU;
+    if (date == 0)
+        pU = U;
+    else
+        pU = UT;
+
+    MPIN_ZZZ_SERVER_1(sha,date,ID,HID,HTID);
+
+    OCT_joctet(&M,pU);
+    if (MESSAGE!=NULL)
+    {
+        OCT_joctet(&M,MESSAGE);
+    }
+
+    MPIN_ZZZ_GET_Y(sha,TimeValue,&M,Y);
+
+    rtn = MPIN_ZZZ_SERVER_2(date,HID,HTID,Y,sQ,U,UT,V,E,F,Pa);
+    if (rtn != 0)
+        return rtn;
+
+    return 0;
+}
+
+int MPIN_ZZZ_GET_DVS_KEYPAIR(csprng *R,octet *Z,octet *Pa)
+{
+    BIG_XXX z,r;
+    ECP8_ZZZ Q;
+    int res=0;
+
+    BIG_XXX_rcopy(r,CURVE_Order_ZZZ);
+
+    if (R!=NULL)
+    {
+        BIG_XXX_randomnum(z,r,R);
+        Z->len=MODBYTES_XXX;
+        BIG_XXX_toBytes(Z->val,z);
+    }
+    else
+        BIG_XXX_fromBytes(z,Z->val);
+
+    BIG_XXX_invmodp(z,z,r);
+
+    ECP8_ZZZ_generator(&Q);
+
+    if (res==0)
+    {
+        PAIR_ZZZ_G2mul(&Q,z);
+        ECP8_ZZZ_toOctet(Pa,&Q);
+    }
+
+    return res;
+}

http://git-wip-us.apache.org/repos/asf/incubator-milagro-crypto-c/blob/8d28d2c3/src/oct.c
----------------------------------------------------------------------
diff --git a/src/oct.c b/src/oct.c
new file mode 100644
index 0000000..abaece3
--- /dev/null
+++ b/src/oct.c
@@ -0,0 +1,424 @@
+/*
+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);
+    }
+}
+
+/* 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];
+    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 */
+            w->val[k++]=ptr[i];
+        }
+
+    }
+    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-c/blob/8d28d2c3/src/pair.c.in
----------------------------------------------------------------------
diff --git a/src/pair.c.in b/src/pair.c.in
new file mode 100644
index 0000000..26b479a
--- /dev/null
+++ b/src/pair.c.in
@@ -0,0 +1,781 @@
+/*
+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 "pair_ZZZ.h"
+
+/* Line function */
+static void PAIR_ZZZ_line(FP12_YYY *v,ECP2_ZZZ *A,ECP2_ZZZ *B,FP_YYY *Qx,FP_YYY *Qy)
+{
+    FP2_YYY X1,Y1,T1,T2;
+    FP2_YYY XX,YY,ZZ,YZ;
+    FP4_YYY a,b,c;
+
+    if (A==B)
+    {
+        /* doubling */
+        FP2_YYY_copy(&XX,&(A->x));	//FP2 XX=new FP2(A.getx());  //X
+        FP2_YYY_copy(&YY,&(A->y));	//FP2 YY=new FP2(A.gety());  //Y
+        FP2_YYY_copy(&ZZ,&(A->z));	//FP2 ZZ=new FP2(A.getz());  //Z
+
+
+        FP2_YYY_copy(&YZ,&YY);		//FP2 YZ=new FP2(YY);        //Y
+        FP2_YYY_mul(&YZ,&YZ,&ZZ);		//YZ.mul(ZZ);                //YZ
+        FP2_YYY_sqr(&XX,&XX);		//XX.sqr();	               //X^2
+        FP2_YYY_sqr(&YY,&YY);		//YY.sqr();	               //Y^2
+        FP2_YYY_sqr(&ZZ,&ZZ);		//ZZ.sqr();			       //Z^2
+
+        FP2_YYY_imul(&YZ,&YZ,4);	//YZ.imul(4);
+        FP2_YYY_neg(&YZ,&YZ);		//YZ.neg();
+        FP2_YYY_norm(&YZ);			//YZ.norm();       //-4YZ
+
+        FP2_YYY_imul(&XX,&XX,6);					//6X^2
+        FP2_YYY_pmul(&XX,&XX,Qx);	               //6X^2.Xs
+
+        FP2_YYY_imul(&ZZ,&ZZ,3*CURVE_B_I_ZZZ);	//3Bz^2
+
+        FP2_YYY_pmul(&YZ,&YZ,Qy);	//-4YZ.Ys
+
+#if SEXTIC_TWIST_ZZZ==D_TYPE
+        FP2_YYY_div_ip2(&ZZ);		//6(b/i)z^2
+#endif
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+        FP2_YYY_mul_ip(&ZZ);
+        FP2_YYY_add(&ZZ,&ZZ,&ZZ);  // 6biz^2
+        FP2_YYY_mul_ip(&YZ);
+        FP2_YYY_norm(&YZ);
+#endif
+        FP2_YYY_norm(&ZZ);			// 6bi.Z^2
+
+        FP2_YYY_add(&YY,&YY,&YY);	// 2y^2
+        FP2_YYY_sub(&ZZ,&ZZ,&YY);	//
+        FP2_YYY_norm(&ZZ);			// 6b.Z^2-2Y^2
+
+        FP4_YYY_from_FP2s(&a,&YZ,&ZZ); // -4YZ.Ys | 6b.Z^2-2Y^2 | 6X^2.Xs
+#if SEXTIC_TWIST_ZZZ==D_TYPE
+        FP4_YYY_from_FP2(&b,&XX);
+        FP4_YYY_zero(&c);
+#endif
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+        FP4_YYY_zero(&b);
+        FP4_YYY_from_FP2H(&c,&XX);
+#endif
+
+        ECP2_ZZZ_dbl(A);				//A.dbl();
+    }
+    else
+    {
+        /* addition */
+
+        FP2_YYY_copy(&X1,&(A->x));		//FP2 X1=new FP2(A.getx());    // X1
+        FP2_YYY_copy(&Y1,&(A->y));		//FP2 Y1=new FP2(A.gety());    // Y1
+        FP2_YYY_copy(&T1,&(A->z));		//FP2 T1=new FP2(A.getz());    // Z1
+
+        FP2_YYY_copy(&T2,&T1);		//FP2 T2=new FP2(A.getz());    // Z1
+
+        FP2_YYY_mul(&T1,&T1,&(B->y));	//T1.mul(B.gety());    // T1=Z1.Y2
+        FP2_YYY_mul(&T2,&T2,&(B->x));	//T2.mul(B.getx());    // T2=Z1.X2
+
+        FP2_YYY_sub(&X1,&X1,&T2);		//X1.sub(T2);
+        FP2_YYY_norm(&X1);				//X1.norm();  // X1=X1-Z1.X2
+        FP2_YYY_sub(&Y1,&Y1,&T1);		//Y1.sub(T1);
+        FP2_YYY_norm(&Y1);				//Y1.norm();  // Y1=Y1-Z1.Y2
+
+        FP2_YYY_copy(&T1,&X1);			//T1.copy(X1);            // T1=X1-Z1.X2
+
+        FP2_YYY_pmul(&X1,&X1,Qy);		//X1.pmul(Qy);            // X1=(X1-Z1.X2).Ys
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+        FP2_YYY_mul_ip(&X1);
+        FP2_YYY_norm(&X1);
+#endif
+
+        FP2_YYY_mul(&T1,&T1,&(B->y));	//T1.mul(B.gety());       // T1=(X1-Z1.X2).Y2
+
+        FP2_YYY_copy(&T2,&Y1);			//T2.copy(Y1);            // T2=Y1-Z1.Y2
+        FP2_YYY_mul(&T2,&T2,&(B->x));	//T2.mul(B.getx());       // T2=(Y1-Z1.Y2).X2
+        FP2_YYY_sub(&T2,&T2,&T1);		//T2.sub(T1);
+        FP2_YYY_norm(&T2);				//T2.norm();          // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+        FP2_YYY_pmul(&Y1,&Y1,Qx);		//Y1.pmul(Qx);
+        FP2_YYY_neg(&Y1,&Y1);			//Y1.neg();
+        FP2_YYY_norm(&Y1);				//Y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+        FP4_YYY_from_FP2s(&a,&X1,&T2);	// (X1-Z1.X2).Ys  |  (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2  | - (Y1-Z1.Y2).Xs
+#if SEXTIC_TWIST_ZZZ==D_TYPE
+        FP4_YYY_from_FP2(&b,&Y1);		//b=new FP4(Y1);
+        FP4_YYY_zero(&c);
+#endif
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+        FP4_YYY_zero(&b);
+        FP4_YYY_from_FP2H(&c,&Y1);		//b=new FP4(Y1);
+#endif
+        ECP2_ZZZ_add(A,B);			//A.add(B);
+    }
+
+    FP12_YYY_from_FP4s(v,&a,&b,&c);
+}
+
+/* Optimal R-ate pairing r=e(P,Q) */
+void PAIR_ZZZ_ate(FP12_YYY *r,ECP2_ZZZ *P,ECP_ZZZ *Q)
+{
+
+    BIG_XXX x,n,n3;
+    FP_YYY Qx,Qy;
+    int i,nb,bt;
+    ECP2_ZZZ A;
+    FP12_YYY lv;
+#if PAIRING_FRIENDLY_ZZZ==BN
+    ECP2_ZZZ KA;
+    FP2_YYY X;
+
+    FP_YYY_rcopy(&Qx,Fra_YYY);
+    FP_YYY_rcopy(&Qy,Frb_YYY);
+    FP2_YYY_from_FPs(&X,&Qx,&Qy);
+
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+    FP2_YYY_inv(&X,&X);
+    FP2_YYY_norm(&X);
+#endif
+#endif
+
+    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
+
+#if PAIRING_FRIENDLY_ZZZ==BN
+    BIG_XXX_pmul(n,x,6);
+#if SIGN_OF_X_ZZZ==POSITIVEX
+    BIG_XXX_inc(n,2);
+#else
+    BIG_XXX_dec(n,2);
+#endif
+#else
+    BIG_XXX_copy(n,x);
+#endif
+
+    BIG_XXX_norm(n);
+    BIG_XXX_pmul(n3,n,3);
+    BIG_XXX_norm(n3);
+
+    FP_YYY_copy(&Qx,&(Q->x));
+    FP_YYY_copy(&Qy,&(Q->y));
+
+    ECP2_ZZZ_copy(&A,P);
+    FP12_YYY_one(r);
+    nb=BIG_XXX_nbits(n3);  //n
+
+    /* Main Miller Loop */
+    for (i=nb-2; i>=1; i--)   //0
+    {
+        FP12_YYY_sqr(r,r);
+        PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
+        FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+        bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i);
+        if (bt==1)
+        {
+
+            PAIR_ZZZ_line(&lv,&A,P,&Qx,&Qy);
+            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+        }
+        if (bt==-1)
+        {
+            ECP2_ZZZ_neg(P);
+            PAIR_ZZZ_line(&lv,&A,P,&Qx,&Qy);
+            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            ECP2_ZZZ_neg(P);
+        }
+    }
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_YYY_conj(r,r);
+#endif
+
+    /* R-ate fixup required for BN curves */
+#if PAIRING_FRIENDLY_ZZZ==BN
+    ECP2_ZZZ_copy(&KA,P);
+    ECP2_ZZZ_frob(&KA,&X);
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    ECP2_ZZZ_neg(&A);
+#endif
+    PAIR_ZZZ_line(&lv,&A,&KA,&Qx,&Qy);
+    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+    ECP2_ZZZ_frob(&KA,&X);
+    ECP2_ZZZ_neg(&KA);
+    PAIR_ZZZ_line(&lv,&A,&KA,&Qx,&Qy);
+    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+#endif
+}
+
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+void PAIR_ZZZ_double_ate(FP12_YYY *r,ECP2_ZZZ *P,ECP_ZZZ *Q,ECP2_ZZZ *R,ECP_ZZZ *S)
+{
+    BIG_XXX x,n,n3;
+    FP_YYY Qx,Qy,Sx,Sy;
+    int i,nb,bt;
+    ECP2_ZZZ A,B;
+    FP12_YYY lv;
+#if PAIRING_FRIENDLY_ZZZ==BN
+    FP2_YYY X;
+    ECP2_ZZZ K;
+
+    FP_YYY_rcopy(&Qx,Fra_YYY);
+    FP_YYY_rcopy(&Qy,Frb_YYY);
+    FP2_YYY_from_FPs(&X,&Qx,&Qy);
+
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+    FP2_YYY_inv(&X,&X);
+    FP2_YYY_norm(&X);
+#endif
+#endif
+
+    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
+
+#if PAIRING_FRIENDLY_ZZZ==BN
+    BIG_XXX_pmul(n,x,6);
+#if SIGN_OF_X_ZZZ==POSITIVEX
+    BIG_XXX_inc(n,2);
+#else
+    BIG_XXX_dec(n,2);
+#endif
+#else
+    BIG_XXX_copy(n,x);
+#endif
+
+    BIG_XXX_norm(n);
+    BIG_XXX_pmul(n3,n,3);
+    BIG_XXX_norm(n3);
+
+    FP_YYY_copy(&Qx,&(Q->x));
+    FP_YYY_copy(&Qy,&(Q->y));
+
+    FP_YYY_copy(&Sx,&(S->x));
+    FP_YYY_copy(&Sy,&(S->y));
+
+    ECP2_ZZZ_copy(&A,P);
+    ECP2_ZZZ_copy(&B,R);
+    FP12_YYY_one(r);
+    nb=BIG_XXX_nbits(n3);
+
+    /* Main Miller Loop */
+    for (i=nb-2; i>=1; i--)
+    {
+        FP12_YYY_sqr(r,r);
+        PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
+        FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+
+        PAIR_ZZZ_line(&lv,&B,&B,&Sx,&Sy);
+        FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+
+        bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i);
+        if (bt==1)
+        {
+            PAIR_ZZZ_line(&lv,&A,P,&Qx,&Qy);
+            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+
+            PAIR_ZZZ_line(&lv,&B,R,&Sx,&Sy);
+            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+        }
+
+        if (bt==-1)
+        {
+            ECP2_ZZZ_neg(P);
+            PAIR_ZZZ_line(&lv,&A,P,&Qx,&Qy);
+            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            ECP2_ZZZ_neg(P);
+
+            ECP2_ZZZ_neg(R);
+            PAIR_ZZZ_line(&lv,&B,R,&Sx,&Sy);
+            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            ECP2_ZZZ_neg(R);
+        }
+    }
+
+    /* R-ate fixup required for BN curves */
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_YYY_conj(r,r);
+#endif
+
+#if PAIRING_FRIENDLY_ZZZ==BN
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    ECP2_ZZZ_neg(&A);
+    ECP2_ZZZ_neg(&B);
+#endif
+
+    ECP2_ZZZ_copy(&K,P);
+    ECP2_ZZZ_frob(&K,&X);
+
+    PAIR_ZZZ_line(&lv,&A,&K,&Qx,&Qy);
+    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+    ECP2_ZZZ_frob(&K,&X);
+    ECP2_ZZZ_neg(&K);
+    PAIR_ZZZ_line(&lv,&A,&K,&Qx,&Qy);
+    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+
+    ECP2_ZZZ_copy(&K,R);
+    ECP2_ZZZ_frob(&K,&X);
+
+    PAIR_ZZZ_line(&lv,&B,&K,&Sx,&Sy);
+    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+    ECP2_ZZZ_frob(&K,&X);
+    ECP2_ZZZ_neg(&K);
+    PAIR_ZZZ_line(&lv,&B,&K,&Sx,&Sy);
+    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+
+#endif
+}
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+void PAIR_ZZZ_fexp(FP12_YYY *r)
+{
+    FP2_YYY X;
+    BIG_XXX x;
+    FP_YYY a,b;
+    FP12_YYY t0,y0,y1,y2,y3;
+
+    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
+    FP_YYY_rcopy(&a,Fra_YYY);
+    FP_YYY_rcopy(&b,Frb_YYY);
+    FP2_YYY_from_FPs(&X,&a,&b);
+
+    /* Easy part of final exp */
+
+    FP12_YYY_inv(&t0,r);
+    FP12_YYY_conj(r,r);
+
+    FP12_YYY_mul(r,&t0);
+    FP12_YYY_copy(&t0,r);
+
+    FP12_YYY_frob(r,&X);
+    FP12_YYY_frob(r,&X);
+    FP12_YYY_mul(r,&t0);
+
+    /* Hard part of final exp - see Duquesne & Ghamman eprint 2015/192.pdf */
+#if PAIRING_FRIENDLY_ZZZ==BN
+    FP12_YYY_pow(&t0,r,x); // t0=f^-u
+#if SIGN_OF_X_ZZZ==POSITIVEX
+    FP12_YYY_conj(&t0,&t0);
+#endif
+    FP12_YYY_usqr(&y3,&t0); // y3=t0^2
+    FP12_YYY_copy(&y0,&t0);
+    FP12_YYY_mul(&y0,&y3); // y0=t0*y3
+    FP12_YYY_copy(&y2,&y3);
+    FP12_YYY_frob(&y2,&X); // y2=y3^p
+    FP12_YYY_mul(&y2,&y3); //y2=y2*y3
+    FP12_YYY_usqr(&y2,&y2); //y2=y2^2
+    FP12_YYY_mul(&y2,&y3); // y2=y2*y3
+
+    FP12_YYY_pow(&t0,&y0,x);  //t0=y0^-u
+#if SIGN_OF_X_ZZZ==POSITIVEX
+    FP12_YYY_conj(&t0,&t0);
+#endif
+    FP12_YYY_conj(&y0,r);     //y0=~r
+    FP12_YYY_copy(&y1,&t0);
+    FP12_YYY_frob(&y1,&X);
+    FP12_YYY_frob(&y1,&X); //y1=t0^p^2
+    FP12_YYY_mul(&y1,&y0); // y1=y0*y1
+    FP12_YYY_conj(&t0,&t0); // t0=~t0
+    FP12_YYY_copy(&y3,&t0);
+    FP12_YYY_frob(&y3,&X); //y3=t0^p
+    FP12_YYY_mul(&y3,&t0); // y3=t0*y3
+    FP12_YYY_usqr(&t0,&t0); // t0=t0^2
+    FP12_YYY_mul(&y1,&t0); // y1=t0*y1
+
+    FP12_YYY_pow(&t0,&y3,x); // t0=y3^-u
+#if SIGN_OF_X_ZZZ==POSITIVEX
+    FP12_YYY_conj(&t0,&t0);
+#endif
+    FP12_YYY_usqr(&t0,&t0); //t0=t0^2
+    FP12_YYY_conj(&t0,&t0); //t0=~t0
+    FP12_YYY_mul(&y3,&t0); // y3=t0*y3
+
+    FP12_YYY_frob(r,&X);
+    FP12_YYY_copy(&y0,r);
+    FP12_YYY_frob(r,&X);
+    FP12_YYY_mul(&y0,r);
+    FP12_YYY_frob(r,&X);
+    FP12_YYY_mul(&y0,r);
+
+    FP12_YYY_usqr(r,&y3);  //r=y3^2
+    FP12_YYY_mul(r,&y2);   //r=y2*r
+    FP12_YYY_copy(&y3,r);
+    FP12_YYY_mul(&y3,&y0); // y3=r*y0
+    FP12_YYY_mul(r,&y1); // r=r*y1
+    FP12_YYY_usqr(r,r); // r=r^2
+    FP12_YYY_mul(r,&y3); // r=r*y3
+    FP12_YYY_reduce(r);
+#else
+    // Ghamman & Fouotsa Method
+    FP12_YYY_usqr(&y0,r);
+    FP12_YYY_pow(&y1,&y0,x);
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_YYY_conj(&y1,&y1);
+#endif
+
+
+    BIG_XXX_fshr(x,1);
+    FP12_YYY_pow(&y2,&y1,x);
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_YYY_conj(&y2,&y2);
+#endif
+
+
+    BIG_XXX_fshl(x,1); // x must be even
+    FP12_YYY_conj(&y3,r);
+    FP12_YYY_mul(&y1,&y3);
+
+    FP12_YYY_conj(&y1,&y1);
+    FP12_YYY_mul(&y1,&y2);
+
+    FP12_YYY_pow(&y2,&y1,x);
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_YYY_conj(&y2,&y2);
+#endif
+
+    FP12_YYY_pow(&y3,&y2,x);
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_YYY_conj(&y3,&y3);
+#endif
+    FP12_YYY_conj(&y1,&y1);
+    FP12_YYY_mul(&y3,&y1);
+
+    FP12_YYY_conj(&y1,&y1);
+    FP12_YYY_frob(&y1,&X);
+    FP12_YYY_frob(&y1,&X);
+    FP12_YYY_frob(&y1,&X);
+    FP12_YYY_frob(&y2,&X);
+    FP12_YYY_frob(&y2,&X);
+    FP12_YYY_mul(&y1,&y2);
+
+    FP12_YYY_pow(&y2,&y3,x);
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_YYY_conj(&y2,&y2);
+#endif
+    FP12_YYY_mul(&y2,&y0);
+    FP12_YYY_mul(&y2,r);
+
+    FP12_YYY_mul(&y1,&y2);
+    FP12_YYY_copy(&y2,&y3);
+    FP12_YYY_frob(&y2,&X);
+    FP12_YYY_mul(&y1,&y2);
+    FP12_YYY_copy(r,&y1);
+    FP12_YYY_reduce(r);
+#endif
+}
+
+#ifdef USE_GLV_ZZZ
+/* GLV method */
+static void glv(BIG_XXX u[2],BIG_XXX e)
+{
+#if PAIRING_FRIENDLY_ZZZ==BN
+    int i,j;
+    BIG_XXX v[2],t,q;
+    DBIG_XXX d;
+    BIG_XXX_rcopy(q,CURVE_Order_ZZZ);
+    for (i=0; i<2; i++)
+    {
+        BIG_XXX_rcopy(t,CURVE_W_ZZZ[i]);
+        BIG_XXX_mul(d,t,e);
+        BIG_XXX_ddiv(v[i],d,q);
+        BIG_XXX_zero(u[i]);
+    }
+    BIG_XXX_copy(u[0],e);
+    for (i=0; i<2; i++)
+        for (j=0; j<2; j++)
+        {
+            BIG_XXX_rcopy(t,CURVE_SB_ZZZ[j][i]);
+            BIG_XXX_modmul(t,v[j],t,q);
+            BIG_XXX_add(u[i],u[i],q);
+            BIG_XXX_sub(u[i],u[i],t);
+            BIG_XXX_mod(u[i],q);
+        }
+
+#else
+// -(x^2).P = (Beta.x,y)
+
+    BIG_XXX x,x2,q;
+    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
+    BIG_XXX_smul(x2,x,x);
+    BIG_XXX_copy(u[0],e);
+    BIG_XXX_mod(u[0],x2);
+    BIG_XXX_copy(u[1],e);
+    BIG_XXX_sdiv(u[1],x2);
+
+    BIG_XXX_rcopy(q,CURVE_Order_ZZZ);
+    BIG_XXX_sub(u[1],q,u[1]);
+
+#endif
+
+    return;
+}
+#endif // USE_GLV
+
+/* Galbraith & Scott Method */
+static void gs(BIG_XXX u[4],BIG_XXX e)
+{
+    int i;
+#if PAIRING_FRIENDLY_ZZZ==BN
+    int j;
+    BIG_XXX v[4],t,q;
+    DBIG_XXX d;
+    BIG_XXX_rcopy(q,CURVE_Order_ZZZ);
+    for (i=0; i<4; i++)
+    {
+        BIG_XXX_rcopy(t,CURVE_WB_ZZZ[i]);
+        BIG_XXX_mul(d,t,e);
+        BIG_XXX_ddiv(v[i],d,q);
+        BIG_XXX_zero(u[i]);
+    }
+
+    BIG_XXX_copy(u[0],e);
+    for (i=0; i<4; i++)
+        for (j=0; j<4; j++)
+        {
+            BIG_XXX_rcopy(t,CURVE_BB_ZZZ[j][i]);
+            BIG_XXX_modmul(t,v[j],t,q);
+            BIG_XXX_add(u[i],u[i],q);
+            BIG_XXX_sub(u[i],u[i],t);
+            BIG_XXX_mod(u[i],q);
+        }
+
+#else
+
+    BIG_XXX x,w,q;
+    BIG_XXX_rcopy(q,CURVE_Order_ZZZ);
+    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
+    BIG_XXX_copy(w,e);
+
+    for (i=0; i<3; i++)
+    {
+        BIG_XXX_copy(u[i],w);
+        BIG_XXX_mod(u[i],x);
+        BIG_XXX_sdiv(w,x);
+    }
+    BIG_XXX_copy(u[3],w);
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    BIG_XXX_modneg(u[1],u[1],q);
+    BIG_XXX_modneg(u[3],u[3],q);
+#endif
+
+#endif
+
+
+
+    return;
+}
+
+/* Multiply P by e in group G1 */
+void PAIR_ZZZ_G1mul(ECP_ZZZ *P,BIG_XXX e)
+{
+#ifdef USE_GLV_ZZZ   /* Note this method is patented */
+    int np,nn;
+    ECP_ZZZ Q;
+    FP_YYY cru;
+    BIG_XXX t,q;
+    BIG_XXX u[2];
+
+    BIG_XXX_rcopy(q,CURVE_Order_ZZZ);
+    glv(u,e);
+
+    ECP_ZZZ_affine(P);
+    ECP_ZZZ_copy(&Q,P);
+    FP_YYY_rcopy(&cru,CURVE_Cru_ZZZ);
+    FP_YYY_mul(&(Q.x),&(Q.x),&cru);
+
+    /* note that -a.B = a.(-B). Use a or -a depending on which is smaller */
+
+    np=BIG_XXX_nbits(u[0]);
+    BIG_XXX_modneg(t,u[0],q);
+    nn=BIG_XXX_nbits(t);
+    if (nn<np)
+    {
+        BIG_XXX_copy(u[0],t);
+        ECP_ZZZ_neg(P);
+    }
+
+    np=BIG_XXX_nbits(u[1]);
+    BIG_XXX_modneg(t,u[1],q);
+    nn=BIG_XXX_nbits(t);
+    if (nn<np)
+    {
+        BIG_XXX_copy(u[1],t);
+        ECP_ZZZ_neg(&Q);
+    }
+    BIG_XXX_norm(u[0]);
+    BIG_XXX_norm(u[1]);
+    ECP_ZZZ_mul2(P,&Q,u[0],u[1]);
+
+#else
+    ECP_ZZZ_mul(P,e);
+#endif
+}
+
+/* Multiply P by e in group G2 */
+void PAIR_ZZZ_G2mul(ECP2_ZZZ *P,BIG_XXX e)
+{
+#ifdef USE_GS_G2_ZZZ   /* Well I didn't patent it :) */
+    int i,np,nn;
+    ECP2_ZZZ Q[4];
+    FP2_YYY X;
+    FP_YYY fx,fy;
+    BIG_XXX x,y,u[4];
+
+    FP_YYY_rcopy(&fx,Fra_YYY);
+    FP_YYY_rcopy(&fy,Frb_YYY);
+    FP2_YYY_from_FPs(&X,&fx,&fy);
+
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+    FP2_YYY_inv(&X,&X);
+    FP2_YYY_norm(&X);
+#endif
+
+    BIG_XXX_rcopy(y,CURVE_Order_ZZZ);
+    gs(u,e);
+
+
+    ECP2_ZZZ_affine(P);
+    ECP2_ZZZ_copy(&Q[0],P);
+    for (i=1; i<4; i++)
+    {
+        ECP2_ZZZ_copy(&Q[i],&Q[i-1]);
+        ECP2_ZZZ_frob(&Q[i],&X);
+    }
+    for (i=0; i<4; i++)
+    {
+        np=BIG_XXX_nbits(u[i]);
+        BIG_XXX_modneg(x,u[i],y);
+        nn=BIG_XXX_nbits(x);
+        if (nn<np)
+        {
+            BIG_XXX_copy(u[i],x);
+            ECP2_ZZZ_neg(&Q[i]);
+        }
+        BIG_XXX_norm(u[i]);
+    }
+
+    ECP2_ZZZ_mul4(P,Q,u);
+
+#else
+    ECP2_ZZZ_mul(P,e);
+#endif
+}
+
+/* f=f^e */
+void PAIR_ZZZ_GTpow(FP12_YYY *f,BIG_XXX e)
+{
+#ifdef USE_GS_GT_ZZZ   /* Note that this option requires a lot of RAM! Maybe better to use compressed XTR method, see fp4.c */
+    int i,np,nn;
+    FP12_YYY g[4];
+    FP2_YYY X;
+    BIG_XXX t,q;
+    FP_YYY fx,fy;
+    BIG_XXX u[4];
+
+    FP_YYY_rcopy(&fx,Fra_YYY);
+    FP_YYY_rcopy(&fy,Frb_YYY);
+    FP2_YYY_from_FPs(&X,&fx,&fy);
+
+    BIG_XXX_rcopy(q,CURVE_Order_ZZZ);
+    gs(u,e);
+
+    FP12_YYY_copy(&g[0],f);
+    for (i=1; i<4; i++)
+    {
+        FP12_YYY_copy(&g[i],&g[i-1]);
+        FP12_YYY_frob(&g[i],&X);
+    }
+
+    for (i=0; i<4; i++)
+    {
+        np=BIG_XXX_nbits(u[i]);
+        BIG_XXX_modneg(t,u[i],q);
+        nn=BIG_XXX_nbits(t);
+        if (nn<np)
+        {
+            BIG_XXX_copy(u[i],t);
+            FP12_YYY_conj(&g[i],&g[i]);
+        }
+        BIG_XXX_norm(u[i]);
+    }
+    FP12_YYY_pow4(f,g,u);
+
+#else
+    FP12_YYY_pow(f,f,e);
+#endif
+}
+
+#ifdef HAS_MAIN
+
+int main()
+{
+    int i;
+    char byt[32];
+    csprng rng;
+    BIG_XXX xa,xb,ya,yb,w,a,b,t1,q,u[2],v[4],m,r;
+    ECP2_ZZZ P,G;
+    ECP_ZZZ Q,R;
+    FP12_YYY g,gp;
+    FP4_YYY t,c,cp,cpm1,cpm2;
+    FP2_YYY x,y,X;
+
+    BIG_XXX_rcopy(a,CURVE_Fra);
+    BIG_XXX_rcopy(b,CURVE_Frb);
+    FP2_YYY_from_BIGs(&X,a,b);
+
+    BIG_XXX_rcopy(xa,CURVE_Gx);
+    BIG_XXX_rcopy(ya,CURVE_Gy);
+
+    ECP_ZZZ_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_ZZZ_output(&Q);
+    printf("\n");
+
+    BIG_XXX_rcopy(xa,CURVE_Pxa);
+    BIG_XXX_rcopy(xb,CURVE_Pxb);
+    BIG_XXX_rcopy(ya,CURVE_Pya);
+    BIG_XXX_rcopy(yb,CURVE_Pyb);
+
+    FP2_YYY_from_BIGs(&x,xa,xb);
+    FP2_YYY_from_BIGs(&y,ya,yb);
+
+    ECP2_ZZZ_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_ZZZ_output(&P);
+    printf("\n");
+
+    for (i=0; i<1000; i++ )
+    {
+        PAIR_ZZZ_ate(&g,&P,&Q);
+        PAIR_ZZZ_fexp(&g);
+    }
+    printf("g= ");
+    FP12_YYY_output(&g);
+    printf("\n");
+
+}
+
+#endif