blob: f69e528d14feda7a1ae7e522c65672bf032edc8a [file] [log] [blame]
/***************************************************************************
*
Copyright 2013 CertiVox UK Ltd. *
*
This file is part of CertiVox SkyKey XT Crypto SDK. *
*
The CertiVox SkyKey XT Crypto SDK provides developers with an *
extensive and efficient set of cryptographic functions. *
For further information about its features and functionalities please *
refer to http://www.certivox.com *
*
* The CertiVox SkyKey XT Crypto SDK is free software: you can *
redistribute it and/or modify it under the terms of the *
GNU Affero General Public License as published by the *
Free Software Foundation, either version 3 of the License, *
or (at your option) any later version. *
*
* The CertiVox SkyKey XT Crypto SDK is distributed in the hope *
that it will be useful, but WITHOUT ANY WARRANTY; without even the *
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
See the GNU Affero General Public License for more details. *
*
* You should have received a copy of the GNU Affero General Public *
License along with CertiVox MIRACL Crypto SDK. *
If not, see <http://www.gnu.org/licenses/>. *
*
You can be released from the requirements of the license by purchasing *
a commercial license. Buying such a license is mandatory as soon as you *
develop commercial activities involving the CertiVox SkyKey XT Crypto SDK *
without disclosing the source code of your own applications, or shipping *
the CertiVox SkyKey XT Crypto SDK with a closed source product. *
*
***************************************************************************/
/*
* MIRACL ECDH implementation file
* Author: M. Scott 2012
*/
/* ECDH/ECIES/ECDSA Functions - see main program below */
/* define this next to use Windows DLL */
/* #define ECDH_DLL */
/* Note that in production code miracl COMBA mechanism should be invoked for 2-3 times speed up */
/* Use this mirdef.h for 32-bit processor - note no assembly required
#define MR_LITTLE_ENDIAN
#define MIRACL 32
#define mr_utype int
#define mr_dltype long long
#define mr_unsign64 unsigned long long
#define MR_IBITS 32
#define MR_LBITS 32
#define mr_unsign32 unsigned int
#define MR_ALWAYS_BINARY
#define MR_STATIC 8
#define MR_GENERIC_MT
#define MR_STRIPPED_DOWN
#define MR_NOSUPPORT_COMPRESSION
#define MR_SIMPLE_BASE
#define MR_SIMPLE_IO
#define MR_NOASM
#define MAXBASE ((mr_small)1<<(MIRACL-1))
#define MR_BITSINCHAR 8
*/
/* Use this mirdef.h for 64-bit processor
#define MR_LITTLE_ENDIAN
#define MIRACL 64
#define mr_utype long long
#define mr_unsign64 unsigned long long
#define MR_IBITS 32
#define MR_LBITS 32
#define mr_unsign32 unsigned int
#define MR_ALWAYS_BINARY
#define MR_STATIC 4
#define MR_GENERIC_MT
#define MR_STRIPPED_DOWN
#define MR_NOSUPPORT_COMPRESSION
#define MR_SIMPLE_BASE
#define MR_SIMPLE_IO
#define MAXBASE ((mr_small)1<<(MIRACL-1))
#define MR_BITSINCHAR 8
*/
/* Link to these files
mrcore.c
mrarth0.c
mrarth1.c
mrarth2.c
mrio1.c
mrgcd.c
mrxgcd.c
mrarth3.c
mrbits.c
mrmonty.c
mrcurve.c
mraes.c
mrshs256.c
mrstrong.c
For 64-bit build using Microsoft compiler mrmuldv.w64 must be included as well
For 64-bit build using Linux and Intel chips, mrmuldv.g64 must be included as well
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "ecdh_c.h"
/* Internal - Hash function */
/* Input is taken from
1. octet p
2. non-negative 32-bit number n
3. octet x
*/
static void hash(octet *p,int n,octet *x,octet *w)
{
int i,hlen,c[4];
HASHFUNC sha;
char hh[HASH_BYTES];
hlen=HASH_BYTES;
SHS_INIT(&sha);
if (p!=NULL)
for (i=0;i<p->len;i++) SHS_PROCESS(&sha,p->val[i]);
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++) SHS_PROCESS(&sha,c[i]);
}
if (x!=NULL)
for (i=0;i<x->len;i++) SHS_PROCESS(&sha,x->val[i]);
SHS_HASH(&sha,hh);
OCTET_EMPTY(w);
OCTET_JOIN_BYTES(hh,hlen,w);
for (i=0;i<hlen;i++) hh[i]=0;
}
/* Hash octet p to octet w */
ECDH_API void ECDH_HASH(octet *p,octet *w)
{
hash(p,-1,NULL,w);
}
ECDH_API BOOL ECDH_HMAC(octet *m,octet *k,int olen,octet *tag)
{
/* Input is from an octet m *
* olen is requested output length in bytes. k is the key *
* The output is the calculated tag */
int hlen,b;
char h[HASH_BYTES],k0[HASH_BLOCK];
octet H={0,sizeof(h),h};
octet K0={0,sizeof(k0),k0};
hlen=HASH_BYTES; b=HASH_BLOCK;
if (olen<4 || olen>hlen) return FALSE;
if (k->len > b) hash(k,-1,NULL,&K0);
else OCTET_COPY(k,&K0);
OCTET_JOIN_BYTE(0,b-K0.len,&K0);
OCTET_XOR_BYTE(&K0,0x36);
hash(&K0,-1,m,&H);
OCTET_XOR_BYTE(&K0,0x6a); /* 0x6a = 0x36 ^ 0x5c */
hash(&K0,-1,&H,&H);
OCTET_EMPTY(tag);
OCTET_JOIN_BYTES(H.val,olen,tag);
return TRUE;
}
/* Key Derivation Functions */
/* Input octet z */
/* Output key of length olen */
ECDH_API void ECDH_KDF1(octet *z,int olen,octet *key)
{
char h[HASH_BYTES];
octet H={0,sizeof(h),h};
int counter,cthreshold;
int hlen=HASH_BYTES;
OCTET_EMPTY(key);
cthreshold=MR_ROUNDUP(olen,hlen);
for (counter=0;counter<cthreshold;counter++)
{
hash(z,counter,NULL,&H);
if (key->len+hlen>olen) OCTET_JOIN_BYTES(H.val,olen%hlen,key);
else OCTET_JOIN_OCTET(&H,key);
}
}
ECDH_API void ECDH_KDF2(octet *z,octet *p,int olen,octet *key)
{
/* NOTE: the parameter olen is the length of the output k in bytes */
char h[HASH_BYTES];
octet H={0,sizeof(h),h};
int counter,cthreshold;
int hlen=HASH_BYTES;
OCTET_EMPTY(key);
cthreshold=MR_ROUNDUP(olen,hlen);
for (counter=1;counter<=cthreshold;counter++)
{
hash(z,counter,p,&H);
if (key->len+hlen>olen) OCTET_JOIN_BYTES(H.val,olen%hlen,key);
else OCTET_JOIN_OCTET(&H,key);
}
}
/* Password based Key Derivation Function */
/* Input password p, salt s, and repeat count */
/* Output key of length olen */
ECDH_API void ECDH_PBKDF2(octet *p,octet *s,int rep,int olen,octet *key)
{
int i,j,len,d=MR_ROUNDUP(olen,HASH_BYTES);
char t[FS],f[FS],u[FS];
octet T={0,sizeof(t),t};
octet F={0,sizeof(f),f};
octet U={0,sizeof(u),u};
OCTET_EMPTY(key);
for (i=1;i<=d;i++)
{
len=s->len;
OCTET_JOIN_LONG(i,4,s);
ECDH_HMAC(s,p,FS,&F);
s->len=len;
OCTET_COPY(&F,&U);
for (j=2;j<=rep;j++)
{
ECDH_HMAC(&U,p,FS,&U);
OCTET_XOR(&U,&F);
}
OCTET_JOIN_OCTET(&F,key);
}
OCTET_CHOP(key,olen,NULL);
}
/* AES encryption/decryption */
ECDH_API void AES_CBC_IV0_ENCRYPT(octet *k,octet *m,octet *c)
{ /* AES CBC encryption, with Null IV and key k */
/* Input is from an octet string m, output is to an octet string c */
/* Input is padded as necessary to make up a full final block */
aes a;
BOOL fin;
int i,j,ipt,opt;
char buff[16];
int padlen;
OCTET_CLEAR(c);
if (m->len==0) return;
if (!aes_init(&a,MR_CBC,k->len,k->val,NULL)) return;
ipt=opt=0;
fin=FALSE;
forever
{
for (i=0;i<16;i++)
{
if (ipt<m->len) buff[i]=m->val[ipt++];
else {fin=TRUE; break;}
}
if (fin) break;
aes_encrypt(&a,buff);
for (i=0;i<16;i++)
if (opt<c->max) c->val[opt++]=buff[i];
}
/* last block, filled up to i-th index */
padlen=16-i;
for (j=i;j<16;j++) buff[j]=padlen;
aes_encrypt(&a,buff);
for (i=0;i<16;i++)
if (opt<c->max) c->val[opt++]=buff[i];
aes_end(&a);
c->len=opt;
}
/* returns TRUE if all consistent, else returns FALSE */
ECDH_API BOOL AES_CBC_IV0_DECRYPT(octet *k,octet *c,octet *m)
{ /* padding is removed */
aes a;
int i,ipt,opt,ch;
char buff[16];
BOOL fin,bad;
int padlen;
ipt=opt=0;
OCTET_CLEAR(m);
if (c->len==0) return TRUE;
ch=c->val[ipt++];
if (!aes_init(&a,MR_CBC,k->len,k->val,NULL)) return FALSE;
fin=FALSE;
forever
{
for (i=0;i<16;i++)
{
buff[i]=ch;
if (ipt>=c->len) {fin=TRUE; break;}
else ch=c->val[ipt++];
}
aes_decrypt(&a,buff);
if (fin) break;
for (i=0;i<16;i++)
if (opt<m->max) m->val[opt++]=buff[i];
}
aes_end(&a);
bad=FALSE;
padlen=buff[15];
if (i!=15 || padlen<1 || padlen>16) bad=TRUE;
if (padlen>=2 && padlen<=16)
for (i=16-padlen;i<16;i++) if (buff[i]!=padlen) bad=TRUE;
if (!bad) for (i=0;i<16-padlen;i++)
if (opt<m->max) m->val[opt++]=buff[i];
m->len=opt;
if (bad) return FALSE;
return TRUE;
}
/* Calculate a public/private EC GF(p) key pair. W=S.g mod EC(p),
* where S is the secret key and W is the public key
* If RNG is NULL then the private key is provided externally in S
* otherwise it is generated randomly internally */
ECDH_API int ECP_KEY_PAIR_GENERATE(ecp_domain *DOM,csprng *RNG,octet* S,octet *W)
{
#ifdef MR_GENERIC_AND_STATIC
miracl instance;
miracl *mr_mip=mirsys(&instance,DOM->nibbles,16);
#else
miracl *mr_mip=mirsys(DOM->nibbles,16);
#endif
big q,a,b,r,gx,gy,s,wx,wy;
epoint *G,*WP;
int err,res=0;
#ifndef MR_STATIC
char *mem=(char *)memalloc(_MIPP_ 9);
char *mem1=(char *)ecp_memalloc(_MIPP_ 2);
#else
char mem[MR_BIG_RESERVE(9)];
char mem1[MR_ECP_RESERVE(2)];
memset(mem,0,MR_BIG_RESERVE(9));
memset(mem1,0,MR_ECP_RESERVE(2));
#endif
if (mr_mip==NULL || mem==NULL || mem1==NULL) res= ECDH_OUT_OF_MEMORY;
mr_mip->ERCON=TRUE;
if (res==0)
{
q=mirvar_mem(_MIPP_ mem, 0);
a=mirvar_mem(_MIPP_ mem, 1);
b=mirvar_mem(_MIPP_ mem, 2);
r=mirvar_mem(_MIPP_ mem, 3);
gx=mirvar_mem(_MIPP_ mem, 4);
gy=mirvar_mem(_MIPP_ mem, 5);
s=mirvar_mem(_MIPP_ mem, 6);
wx=mirvar_mem(_MIPP_ mem, 7);
wy=mirvar_mem(_MIPP_ mem, 8);
bytes_to_big(_MIPP_ FS,DOM->Q,q);
bytes_to_big(_MIPP_ FS,DOM->A,a);
bytes_to_big(_MIPP_ FS,DOM->B,b);
bytes_to_big(_MIPP_ FS,DOM->R,r);
bytes_to_big(_MIPP_ FS,DOM->Gx,gx);
bytes_to_big(_MIPP_ FS,DOM->Gy,gy);
ecurve_init(_MIPP_ a,b,q,MR_PROJECTIVE);
G=epoint_init_mem(_MIPP_ mem1,0);
WP=epoint_init_mem(_MIPP_ mem1,1);
epoint_set(_MIPP_ gx,gy,0,G);
if (RNG!=NULL)
strong_bigrand(_MIPP_ RNG,r,s);
else
{
bytes_to_big(_MIPP_ S->len,S->val,s);
divide(_MIPP_ s,r,r);
}
ecurve_mult(_MIPP_ s,G,WP);
epoint_get(_MIPP_ WP,wx,wy);
if (RNG!=NULL) S->len=big_to_bytes(_MIPP_ 0,s,S->val,FALSE);
W->len=2*FS+1; W->val[0]=4;
big_to_bytes(_MIPP_ FS,wx,&(W->val[1]),TRUE);
big_to_bytes(_MIPP_ FS,wy,&(W->val[FS+1]),TRUE);
}
#ifndef MR_STATIC
memkill(_MIPP_ mem,9);
ecp_memkill(_MIPP_ mem1,2);
#else
memset(mem,0,MR_BIG_RESERVE(9));
memset(mem1,0,MR_ECP_RESERVE(2));
#endif
err=mr_mip->ERNUM;
mirexit(_MIPPO_ );
if (err==MR_ERR_OUT_OF_MEMORY) return ECDH_OUT_OF_MEMORY;
if (err==MR_ERR_DIV_BY_ZERO) return ECDH_DIV_BY_ZERO;
if (err!=0) return -(1000+err);
return res;
}
/* validate an EC GF(p) public key. Set full=TRUE for fuller,
* but more time-consuming test */
ECDH_API int ECP_PUBLIC_KEY_VALIDATE(ecp_domain *DOM,BOOL full,octet *W)
{
#ifdef MR_GENERIC_AND_STATIC
miracl instance;
miracl *mr_mip=mirsys(&instance,DOM->nibbles,16);
#else
miracl *mr_mip=mirsys(DOM->nibbles,16);
#endif
big q,a,b,r,wx,wy;
epoint *WP;
BOOL valid;
int err,res=0;
#ifndef MR_STATIC
char *mem=(char *)memalloc(_MIPP_ 6);
char *mem1=(char *)ecp_memalloc(_MIPP_ 1);
#else
char mem[MR_BIG_RESERVE(6)];
char mem1[MR_ECP_RESERVE(1)];
memset(mem,0,MR_BIG_RESERVE(6));
memset(mem1,0,MR_ECP_RESERVE(1));
#endif
if (mr_mip==NULL || mem==NULL || mem1==NULL) res= ECDH_OUT_OF_MEMORY;
mr_mip->ERCON=TRUE;
if (res==0)
{
q=mirvar_mem(_MIPP_ mem, 0);
a=mirvar_mem(_MIPP_ mem, 1);
b=mirvar_mem(_MIPP_ mem, 2);
r=mirvar_mem(_MIPP_ mem, 3);
wx=mirvar_mem(_MIPP_ mem, 4);
wy=mirvar_mem(_MIPP_ mem, 5);
bytes_to_big(_MIPP_ FS,DOM->Q,q);
bytes_to_big(_MIPP_ FS,DOM->A,a);
bytes_to_big(_MIPP_ FS,DOM->B,b);
bytes_to_big(_MIPP_ FS,DOM->R,r);
bytes_to_big(_MIPP_ FS,&(W->val[1]),wx);
bytes_to_big(_MIPP_ FS,&(W->val[FS+1]),wy);
if (mr_compare(wx,q)>=0 || mr_compare (wy,q)>=0) res=ECDH_INVALID_PUBLIC_KEY;
}
if (res==0)
{
ecurve_init(_MIPP_ a,b,q,MR_PROJECTIVE);
WP=epoint_init_mem(_MIPP_ mem1,0);
valid=epoint_set(_MIPP_ wx,wy,0,WP);
if (!valid || WP->marker==MR_EPOINT_INFINITY) res=ECDH_INVALID_PUBLIC_KEY;
if (res==0 && full)
{
ecurve_mult(_MIPP_ r,WP,WP);
if (WP->marker!=MR_EPOINT_INFINITY) res=ECDH_INVALID_PUBLIC_KEY;
}
}
#ifndef MR_STATIC
memkill(_MIPP_ mem,6);
ecp_memkill(_MIPP_ mem1,1);
#else
memset(mem,0,MR_BIG_RESERVE(6));
memset(mem1,0,MR_ECP_RESERVE(1));
#endif
err=mr_mip->ERNUM;
mirexit(_MIPPO_ );
if (err==MR_ERR_OUT_OF_MEMORY) return ECDH_OUT_OF_MEMORY;
if (err==MR_ERR_DIV_BY_ZERO) return ECDH_DIV_BY_ZERO;
if (err!=0) return -(1000+err);
return res;
}
/*** P1363 EC GF(p) primitives ***/
/* See P1363 documentation for specification */
ECDH_API int ECPSVDP_DH(ecp_domain *DOM,octet *S,octet *WD,octet *Z)
{
#ifdef MR_GENERIC_AND_STATIC
miracl instance;
miracl *mr_mip=mirsys(&instance,DOM->nibbles,16);
#else
miracl *mr_mip=mirsys(DOM->nibbles,16);
#endif
big q,a,b,s,wx,wy,z;
BOOL valid;
epoint *W;
int err,res=0;
#ifndef MR_STATIC
char *mem=(char *)memalloc(_MIPP_ 7);
char *mem1=(char *)ecp_memalloc(_MIPP_ 1);
#else
char mem[MR_BIG_RESERVE(7)];
char mem1[MR_ECP_RESERVE(1)];
memset(mem,0,MR_BIG_RESERVE(7));
memset(mem1,0,MR_ECP_RESERVE(1));
#endif
if (mr_mip==NULL || mem==NULL || mem1==NULL) res= ECDH_OUT_OF_MEMORY;
mr_mip->ERCON=TRUE;
if (res==0)
{
q=mirvar_mem(_MIPP_ mem, 0);
a=mirvar_mem(_MIPP_ mem, 1);
b=mirvar_mem(_MIPP_ mem, 2);
s=mirvar_mem(_MIPP_ mem, 3);
wx=mirvar_mem(_MIPP_ mem, 4);
wy=mirvar_mem(_MIPP_ mem, 5);
z=mirvar_mem(_MIPP_ mem, 6);
bytes_to_big(_MIPP_ FS,DOM->Q,q);
bytes_to_big(_MIPP_ FS,DOM->A,a);
bytes_to_big(_MIPP_ FS,DOM->B,b);
bytes_to_big(_MIPP_ S->len,S->val,s);
ecurve_init(_MIPP_ a,b,q,MR_PROJECTIVE);
W=epoint_init_mem(_MIPP_ mem1,0);
bytes_to_big(_MIPP_ FS,&(WD->val[1]),wx);
bytes_to_big(_MIPP_ FS,&(WD->val[FS+1]),wy);
valid=epoint_set(_MIPP_ wx,wy,0,W);
if (!valid) res=ECDH_ERROR;
}
if (res==0)
{
ecurve_mult(_MIPP_ s,W,W);
if (W->marker==MR_EPOINT_INFINITY) res=ECDH_ERROR;
else
{
epoint_get(_MIPP_ W,z,z);
Z->len=big_to_bytes(_MIPP_ FS,z,Z->val,TRUE);
}
}
#ifndef MR_STATIC
memkill(_MIPP_ mem,7);
ecp_memkill(_MIPP_ mem1,1);
#else
memset(mem,0,MR_BIG_RESERVE(7));
memset(mem1,0,MR_ECP_RESERVE(1));
#endif
err=mr_mip->ERNUM;
mirexit(_MIPPO_ );
if (err==MR_ERR_OUT_OF_MEMORY) return ECDH_OUT_OF_MEMORY;
if (err==MR_ERR_DIV_BY_ZERO) return ECDH_DIV_BY_ZERO;
if (err!=0) return -(1000+err);
return res;
}
/* Sign octet F using private key S. Signature in C and D. Must supply RNG */
ECDH_API int ECPSP_DSA(ecp_domain *DOM,csprng *RNG,octet *S,octet *F,octet *C,octet *D)
{
char h[HASH_BYTES];
octet H={0,sizeof(h),h};
#ifdef MR_GENERIC_AND_STATIC
miracl instance;
miracl *mr_mip=mirsys(&instance,DOM->nibbles,16);
#else
miracl *mr_mip=mirsys(DOM->nibbles,16);
#endif
big q,a,b,gx,gy,r,s,f,c,d,u,vx;
epoint *G,*V;
int err,res=0;
#ifndef MR_STATIC
char *mem=(char *)memalloc(_MIPP_ 12);
char *mem1=(char *)ecp_memalloc(_MIPP_ 2);
#else
char mem[MR_BIG_RESERVE(12)];
char mem1[MR_ECP_RESERVE(2)];
memset(mem,0,MR_BIG_RESERVE(12));
memset(mem1,0,MR_ECP_RESERVE(2));
#endif
if (mr_mip==NULL || mem==NULL || mem1==NULL) res= ECDH_OUT_OF_MEMORY;
mr_mip->ERCON=TRUE;
hash(F,-1,NULL,&H); /* hash message */
if (res==0)
{
q=mirvar_mem(_MIPP_ mem, 0);
a=mirvar_mem(_MIPP_ mem, 1);
b=mirvar_mem(_MIPP_ mem, 2);
gx=mirvar_mem(_MIPP_ mem, 3);
gy=mirvar_mem(_MIPP_ mem, 4);
r=mirvar_mem(_MIPP_ mem, 5);
s=mirvar_mem(_MIPP_ mem, 6);
f=mirvar_mem(_MIPP_ mem, 7);
c=mirvar_mem(_MIPP_ mem, 8);
d=mirvar_mem(_MIPP_ mem, 9);
u=mirvar_mem(_MIPP_ mem, 10);
vx=mirvar_mem(_MIPP_ mem,11);
bytes_to_big(_MIPP_ FS,DOM->Q,q);
bytes_to_big(_MIPP_ FS,DOM->R,r);
bytes_to_big(_MIPP_ FS,DOM->Gx,gx);
bytes_to_big(_MIPP_ FS,DOM->Gy,gy);
bytes_to_big(_MIPP_ FS,DOM->A,a);
bytes_to_big(_MIPP_ FS,DOM->B,b);
bytes_to_big(_MIPP_ S->len,S->val,s);
bytes_to_big(_MIPP_ H.len,H.val,f);
ecurve_init(_MIPP_ a,b,q,MR_PROJECTIVE);
G=epoint_init_mem(_MIPP_ mem1,0);
V=epoint_init_mem(_MIPP_ mem1,1);
epoint_set(_MIPP_ gx,gy,0,G);
do {
if (mr_mip->ERNUM) break;
strong_bigrand(_MIPP_ RNG,r,u);
ecurve_mult(_MIPP_ u,G,V);
epoint_get(_MIPP_ V,vx,vx);
copy(vx,c);
divide(_MIPP_ c,r,r);
if (size(c)==0) continue;
xgcd(_MIPP_ u,r,u,u,u);
mad(_MIPP_ s,c,f,r,r,d);
mad(_MIPP_ u,d,u,r,r,d);
} while (size(d)==0);
if (res==0)
{
C->len=big_to_bytes(_MIPP_ FS,c,C->val,TRUE);
D->len=big_to_bytes(_MIPP_ FS,d,D->val,TRUE);
}
}
#ifndef MR_STATIC
memkill(_MIPP_ mem,12);
ecp_memkill(_MIPP_ mem1,2);
#else
memset(mem,0,MR_BIG_RESERVE(12));
memset(mem1,0,MR_ECP_RESERVE(2));
#endif
err=mr_mip->ERNUM;
mirexit(_MIPPO_ );
if (err==MR_ERR_OUT_OF_MEMORY) return ECDH_OUT_OF_MEMORY;
if (err==MR_ERR_DIV_BY_ZERO) return ECDH_DIV_BY_ZERO;
if (err!=0) return -(1000+err);
return res;
}
/* Verify Signature (C, D) on F using public key W */
ECDH_API int ECPVP_DSA(ecp_domain *DOM,octet *W,octet *F, octet *C,octet *D)
{
char h[HASH_BYTES];
octet H={0,sizeof(h),h};
#ifdef MR_GENERIC_AND_STATIC
miracl instance;
miracl *mr_mip=mirsys(&instance,DOM->nibbles,16);
#else
miracl *mr_mip=mirsys(DOM->nibbles,16);
#endif
big q,r,a,b,gx,gy,wx,wy,f,c,d,h2;
int err,res=0;
epoint *G,*WP,*P;
BOOL valid;
#ifndef MR_STATIC
char *mem=(char *)memalloc(_MIPP_ 12);
char *mem1=(char *)ecp_memalloc(_MIPP_ 3);
#else
char mem[MR_BIG_RESERVE(12)];
char mem1[MR_ECP_RESERVE(3)];
memset(mem,0,MR_BIG_RESERVE(12));
memset(mem1,0,MR_ECP_RESERVE(3));
#endif
if (mr_mip==NULL || mem==NULL || mem1==NULL) res= ECDH_OUT_OF_MEMORY;
mr_mip->ERCON=TRUE;
hash(F,-1,NULL,&H); /* hash message */
if (res==0)
{
q=mirvar_mem(_MIPP_ mem, 0);
a=mirvar_mem(_MIPP_ mem, 1);
b=mirvar_mem(_MIPP_ mem, 2);
gx=mirvar_mem(_MIPP_ mem, 3);
gy=mirvar_mem(_MIPP_ mem, 4);
r=mirvar_mem(_MIPP_ mem, 5);
wx=mirvar_mem(_MIPP_ mem, 6);
wy=mirvar_mem(_MIPP_ mem, 7);
f=mirvar_mem(_MIPP_ mem, 8);
c=mirvar_mem(_MIPP_ mem, 9);
d=mirvar_mem(_MIPP_ mem, 10);
h2=mirvar_mem(_MIPP_ mem,11);
bytes_to_big(_MIPP_ FS,DOM->Q,q);
bytes_to_big(_MIPP_ FS,DOM->R,r);
bytes_to_big(_MIPP_ FS,DOM->Gx,gx);
bytes_to_big(_MIPP_ FS,DOM->Gy,gy);
bytes_to_big(_MIPP_ FS,DOM->A,a);
bytes_to_big(_MIPP_ FS,DOM->B,b);
bytes_to_big(_MIPP_ C->len,C->val,c);
bytes_to_big(_MIPP_ D->len,D->val,d);
bytes_to_big(_MIPP_ H.len,H.val,f);
if (size(c)<1 || mr_compare(c,r)>=0 || size(d)<1 || mr_compare(d,r)>=0)
res=ECDH_INVALID;
}
if (res==0)
{
xgcd(_MIPP_ d,r,d,d,d);
mad(_MIPP_ f,d,f,r,r,f);
mad(_MIPP_ c,d,c,r,r,h2);
ecurve_init(_MIPP_ a,b,q,MR_PROJECTIVE);
G=epoint_init_mem(_MIPP_ mem1,0);
WP=epoint_init_mem(_MIPP_ mem1,1);
P=epoint_init_mem(_MIPP_ mem1,2);
epoint_set(_MIPP_ gx,gy,0,G);
bytes_to_big(_MIPP_ FS,&(W->val[1]),wx);
bytes_to_big(_MIPP_ FS,&(W->val[FS+1]),wy);
valid=epoint_set(_MIPP_ wx,wy,0,WP);
if (!valid) res=ECDH_ERROR;
else
{
ecurve_mult2(_MIPP_ f,G,h2,WP,P);
if (P->marker==MR_EPOINT_INFINITY) res=ECDH_INVALID;
else
{
epoint_get(_MIPP_ P,d,d);
divide(_MIPP_ d,r,r);
if (mr_compare(d,c)!=0) res=ECDH_INVALID;
}
}
}
#ifndef MR_STATIC
memkill(_MIPP_ mem,12);
ecp_memkill(_MIPP_ mem1,3);
#else
memset(mem,0,MR_BIG_RESERVE(12));
memset(mem1,0,MR_ECP_RESERVE(3));
#endif
err=mr_mip->ERNUM;
mirexit(_MIPPO_ );
if (err==MR_ERR_OUT_OF_MEMORY) return ECDH_OUT_OF_MEMORY;
if (err==MR_ERR_DIV_BY_ZERO) return ECDH_DIV_BY_ZERO;
if (err!=0) return -(1000+err);
return res;
}
ECDH_API void ECP_ECIES_ENCRYPT(ecp_domain *DOM,octet *P1,octet *P2,csprng *RNG,octet *W,octet *M,int tlen,octet *V,octet *C,octet *T)
{ /* Inputs: Input params, random number generator, his public key, the message to be encrypted and the MAC length */
/* Outputs: my one-time public key, the ciphertext and the MAC tag */
int i,len;
char z[FS],vz[3*FS+2],k[32],k1[16],k2[16],l2[8],u[FS];
octet Z={0,sizeof(z),z};
octet VZ={0,sizeof(vz),vz};
octet K={0,sizeof(k),k};
octet K1={0,sizeof(k1),k1};
octet K2={0,sizeof(k2),k2};
octet L2={0,sizeof(l2),l2};
octet U={0,sizeof(u),u};
if (ECP_KEY_PAIR_GENERATE(DOM,RNG,&U,V)!=0) return; /* one time key pair */
if (ECPSVDP_DH(DOM,&U,W,&Z)!=0) return;
OCTET_COPY(V,&VZ);
OCTET_JOIN_OCTET(&Z,&VZ);
ECDH_KDF2(&VZ,P1,32,&K);
/* split key into AES encryption key and MAC key */
K1.len=K2.len=16;
for (i=0;i<16;i++) {K1.val[i]=K.val[i]; K2.val[i]=K.val[16+i];}
AES_CBC_IV0_ENCRYPT(&K1,M,C);
OCTET_JOIN_LONG((long)P2->len,8,&L2);
len=C->len;
OCTET_JOIN_OCTET(P2,C);
OCTET_JOIN_OCTET(&L2,C);
ECDH_HMAC(C,&K2,tlen,T);
C->len=len;
}
/* ECIES Decryption */
ECDH_API BOOL ECP_ECIES_DECRYPT(ecp_domain *DOM,octet *P1,octet *P2,octet *V,octet *C,octet *T,octet *U,octet *M)
{ /* Inputs: Input params, ciphertext triple V,C,T and recipients private key */
/* Output: recovered plaintext M */
int i,len;
char z[FS],vz[3*FS+2],k[32],k1[16],k2[16],l2[8],tag[32];
octet Z={0,sizeof(z),z};
octet VZ={0,sizeof(vz),vz};
octet K={0,sizeof(k),k};
octet K1={0,sizeof(k1),k1};
octet K2={0,sizeof(k2),k2};
octet L2={0,sizeof(l2),l2};
octet TAG={0,sizeof(tag),tag};
if (ECPSVDP_DH(DOM,U,V,&Z)!=0) return FALSE;
OCTET_COPY(V,&VZ);
OCTET_JOIN_OCTET(&Z,&VZ);
ECDH_KDF2(&VZ,P1,32,&K);
/* split key into AES decryption key and MAC key */
K1.len=K2.len=16;
for (i=0;i<16;i++) {K1.val[i]=K.val[i]; K2.val[i]=K.val[16+i];}
if (!AES_CBC_IV0_DECRYPT(&K1,C,M)) return FALSE;
OCTET_JOIN_LONG((long)P2->len,8,&L2);
len=C->len;
OCTET_JOIN_OCTET(P2,C);
OCTET_JOIN_OCTET(&L2,C);
ECDH_HMAC(C,&K2,T->len,&TAG);
C->len=len;
if (!OCTET_COMPARE(T,&TAG)) return FALSE;
return TRUE;
}
/* test driver and function exerciser for ECDH/ECIES/ECDSA Functions */
#ifdef __TEST__
int main(int argc,char **argv)
{
int i,res;
BOOL result;
char *pp="M0ng00se";
/* These octets are automatically protected against buffer overflow attacks */
/* Note salt must be big enough to include an appended word */
/* Note ECIES ciphertext C must be big enough to include at least 1 appended block */
/* Recall FS is field size in bytes. So FS=32 for 256-bit curve */
char s0[FS],s1[FS],w0[2*FS+1],w1[2*FS+1],z0[FS],z1[FS],raw[100],key[2*FS],salt[32],pw[20],b64[4+((8*FS+4)/3)],p1[30],p2[30],v[2*FS+1],m[32],c[64],t[32],cs[FS],ds[FS];
octet S0={0,sizeof(s0),s0};
octet S1={0,sizeof(s1),s1};
octet W0={0,sizeof(w0),w0};
octet W1={0,sizeof(w1),w1};
octet Z0={0,sizeof(z0),z0};
octet Z1={0,sizeof(z1),z1};
octet RAW={0,sizeof(raw),raw};
octet KEY={0,sizeof(key),key};
octet SALT={0,sizeof(salt),salt};
octet PW={0,sizeof(pw),pw};
octet P1={0,sizeof(p1),p1};
octet P2={0,sizeof(p2),p2};
octet V={0,sizeof(v),v};
octet M={0,sizeof(m),m};
octet C={0,sizeof(c),c};
octet T={0,sizeof(t),t};
octet CS={0,sizeof(cs),cs};
octet DS={0,sizeof(ds),ds};
ecp_domain epdom;
csprng RNG; /* Crypto Strong RNG */
RAW.len=100; /* fake random seed source */
for (i=0;i<100;i++) RAW.val[i]=i+1;
CREATE_CSPRNG(&RNG,&RAW); /* initialise strong RNG */
SALT.len=8;
for (i=0;i<8;i++) SALT.val[i]=i+1; // set Salt
printf("Alice's Passphrase= %s\n",pp);
OCTET_JOIN_STRING(pp,&PW); // set Password from string
/* First set up Elliptic Curve from ROM data */
ECP_DOMAIN_INIT(&epdom,rom);
/* private key S0 of size FS bytes derived from Password and Salt */
ECDH_PBKDF2(&PW,&SALT,1000,FS,&S0);
OCTET_TO_BASE64(&S0,b64);
printf("Alices private key= %s\n",b64);
/* Generate Key pair S/W */
ECP_KEY_PAIR_GENERATE(&epdom,NULL,&S0,&W0);
res=ECP_PUBLIC_KEY_VALIDATE(&epdom,TRUE,&W0);
if (res!=0)
{
printf("ECP Public Key is invalid!\n");
return 0;
}
OCTET_TO_BASE64(&W0,b64);
printf("Alice's public key= %s\n",b64);
OCTET_FROM_BASE64(b64,&W0);
/* Random private key for other party */
S1.len=3; S1.val[0]=0x01; S1.val[1]=0x23; S1.val[2]=0x45;
ECP_KEY_PAIR_GENERATE(&epdom,NULL,&S1,&W1);
res=ECP_PUBLIC_KEY_VALIDATE(&epdom,TRUE,&W1);
if (res!=0)
{
printf("ECP Public Key is invalid!\n");
return 0;
}
OCTET_TO_BASE64(&W1,b64);
printf("Servers public key= %s\n",b64);
OCTET_FROM_BASE64(b64,&W1);
/* Calculate common key using DH - IEEE 1363 method */
ECPSVDP_DH(&epdom,&S0,&W1,&Z0);
ECPSVDP_DH(&epdom,&S1,&W0,&Z1);
if (!OCTET_COMPARE(&Z0,&Z1))
{
printf("*** ECPSVDP-DH Failed\n");
return 0;
}
ECDH_KDF1(&Z0,16,&KEY);
printf("Alice's DH Key= "); for (i=0;i<16;i++) printf("%u ",KEY.val[i]&0xff);
printf("\n");
printf("Servers DH Key= "); for (i=0;i<16;i++) printf("%u ",KEY.val[i]&0xff);
printf("\n");
printf("Testing ECIES\n");
P1.len=3; P1.val[0]=0x0; P1.val[1]=0x1; P1.val[2]=0x2;
P2.len=4; P2.val[0]=0x0; P2.val[1]=0x1; P2.val[2]=0x2; P2.val[3]=0x3;
M.len=17;
for (i=0;i<=16;i++) M.val[i]=i; /* fake a message */
ECP_ECIES_ENCRYPT(&epdom,&P1,&P2,&RNG,&W1,&M,12,&V,&C,&T);
printf("Ciphertext= \n");
OCTET_TO_BASE64(&V,b64);
printf("V= %s\n",b64);
OCTET_FROM_BASE64(b64,&V);
OCTET_TO_BASE64(&C,b64);
printf("C= %s\n",b64);
OCTET_FROM_BASE64(b64,&C);
OCTET_TO_BASE64(&T,b64);
printf("V= %s\n",b64);
OCTET_FROM_BASE64(b64,&T);
if (!ECP_ECIES_DECRYPT(&epdom,&P1,&P2,&V,&C,&T,&S1,&M))
{
printf("*** ECIES Decryption Failed\n");
return 0;
}
else printf("Decryption succeeded\n");
printf("Message is "); OCTET_OUTPUT(&M);
printf("Testing ECDSA\n");
if (ECPSP_DSA(&epdom,&RNG,&S0,&M,&CS,&DS)!=0)
{
printf("***ECDSA Signature Failed\n");
return 0;
}
OCTET_TO_BASE64(&CS,b64);
printf("Signature C = %s\n",b64);
OCTET_FROM_BASE64(b64,&CS);
OCTET_TO_BASE64(&DS,b64);
printf("Signature D = %s\n",b64);
OCTET_FROM_BASE64(b64,&DS);
if (ECPVP_DSA(&epdom,&W0,&M,&CS,&DS)!=0)
{
printf("***ECDSA Verification Failed\n");
return 0;
}
else printf("ECDSA Signature/Verification succeeded\n");
ECP_DOMAIN_KILL(&epdom);
KILL_CSPRNG(&RNG);
return 0;
}
#endif // __TEST__