/* * Copyright (c) 2012-2020 MIRACL UK Ltd. * * This file is part of MIRACL Core * (see https://github.com/miracl/core). * * Licensed 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. */ /* HMAC functions */ #include "arch.h" #include "core.h" using namespace core; #define ROUNDUP(a,b) ((a)-1)/(b)+1 #define CEIL(a,b) (((a)-1)/(b)+1) /* General Purpose hash function, padding with zeros, optional input octets p and x, optional integer n,hash to octet w of length olen */ /* hash is the Hash family, either MC_SHA2 or MC_SHA3 */ /* hlen should be 32,48 or 64 for MC_SHA2 (that is SHA256/384/512) */ /* hlen should be 24,32,48,64 for MC_SHA3 */ /* olen=0 - output = hlen bytes */ /* olen<=hlen - output = olen bytes */ /* olen>hlen - output is padded with leading zeros and then hlen bytes */ void core::GPhash(int hash,int hlen,octet *w,int olen,int pad,octet *p,int n,octet *x) { hash256 sh256; hash384 sh384; hash512 sh512; sha3 sh3; int i,c[4]; char hh[64]; if (n>=0) { c[0] = (n >> 24) & 0xff; c[1] = (n >> 16) & 0xff; c[2] = (n >> 8) & 0xff; c[3] = (n) & 0xff; } switch (hash) { case MC_SHA2 : switch (hlen) { case SHA256 : HASH256_init(&sh256); for (i=0;ilen;i++) HASH256_process(&sh256,p->val[i]); if (n>=0) for (i=0;i<4;i++) HASH256_process(&sh256,c[i]); if (x!=NULL) for (i=0;ilen;i++) HASH256_process(&sh256,x->val[i]); HASH256_hash(&sh256,hh); break; case SHA384 : HASH384_init(&sh384); for (i=0;ilen;i++) HASH384_process(&sh384,p->val[i]); if (n>=0) for (i=0;i<4;i++) HASH384_process(&sh384,c[i]); if (x!=NULL) for (i=0;ilen;i++) HASH384_process(&sh384,x->val[i]); HASH384_hash(&sh384,hh); break; case SHA512 : HASH512_init(&sh512); for (i=0;ilen;i++) HASH512_process(&sh512,p->val[i]); if (n>=0) for (i=0;i<4;i++) HASH512_process(&sh512,c[i]); if (x!=NULL) for (i=0;ilen;i++) HASH512_process(&sh512,x->val[i]); HASH512_hash(&sh512,hh); break; } break; case MC_SHA3 : SHA3_init(&sh3,hlen); for (i=0;ilen;i++) SHA3_process(&sh3,p->val[i]); if (n>=0) for (i=0;i<4;i++) SHA3_process(&sh3,c[i]); if (x!=NULL) for (i=0;x->len;i++) SHA3_process(&sh3,x->val[i]); SHA3_hash(&sh3,hh); break; default: return; } OCT_empty(w); if (!olen) OCT_jbytes(w,hh,hlen); else { if (olen<=hlen) { OCT_jbytes(w,hh,olen); } else { OCT_jbyte(w, 0, olen - hlen); OCT_jbytes(w, hh, hlen); } } } /* Simple hash octet p to octet w of length hlen */ void core::SPhash(int hash, int hlen,octet *w, octet *p) { GPhash(hash, hlen, w, 0, 0, p, -1, NULL); } static int blksize(int hash,int hlen) { int blk=0; switch (hash) { case MC_SHA2 : blk=64; if (hlen>32) blk=128; break; case MC_SHA3 : blk=200-2*hlen; break; default: break; } return blk; } /* RFC 2104 */ void core::HMAC(int hash,int hlen,octet *TAG,int olen,octet *K,octet *M) { int blk; char h[128],k0[200]; // assumes max block sizes octet K0 = {0, sizeof(k0), k0}; octet H={0,sizeof(h),h}; blk=blksize(hash,hlen); if (blk==0) return; if (K->len > blk) SPhash(hash,hlen,&K0,K); else OCT_copy(&K0,K); OCT_jbyte(&K0,0,blk-K0.len); OCT_xorbyte(&K0,0x36); GPhash(hash,hlen,&H,0,0,&K0,-1,M); OCT_xorbyte(&K0,0x6a); /* 0x6a = 0x36 ^ 0x5c */ GPhash(hash,hlen,&H,0,0,&K0,-1,&H); OCT_empty(TAG); OCT_jbytes(TAG,H.val,olen); OCT_clear(&H); OCT_clear(&K0); } /* RFC 5869 */ void core::HKDF_Extract(int hash,int hlen,octet *PRK,octet *SALT,octet *IKM) { char h[64]; octet H={0,sizeof(h),h}; if (SALT==NULL) { OCT_jbyte(&H,0,hlen); HMAC(hash,hlen,PRK,hlen,&H,IKM); } else { HMAC(hash,hlen,PRK,hlen,SALT,IKM); } } void core::HKDF_Expand(int hash,int hlen,octet *OKM,int olen,octet *PRK,octet *INFO) { int i; char t[1024]; // >= info.length+hlen+1 octet T={0,sizeof(t),t}; int n=olen/hlen; int flen=olen%hlen; OCT_empty(OKM); for (i=1;i<=n;i++) { OCT_joctet(&T,INFO); OCT_jbyte(&T,i,1); HMAC(hash,hlen,&T,hlen,PRK,&T); OCT_joctet(OKM,&T); } if (flen>0) { OCT_joctet(&T,INFO); OCT_jbyte(&T,n+1,1); HMAC(hash,hlen,&T,flen,PRK,&T); OCT_joctet(OKM,&T); } } /* https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ */ void core::XOF_Expand(int hlen,octet *OKM,int olen,octet *DST,octet *M) { int i; sha3 SHA3; SHA3_init(&SHA3,hlen); for (i=0;ilen;i++) SHA3_process(&SHA3,M->val[i]); SHA3_process(&SHA3,olen/256); SHA3_process(&SHA3,olen%256); for (i=0;ilen;i++) SHA3_process(&SHA3,DST->val[i]); SHA3_process(&SHA3,DST->len); SHA3_shake(&SHA3,OKM->val,olen); OKM->len=olen; } static void XMD_Expand_Short_DST(int hash, int hlen,octet *OKM,int olen,octet *DST,octet *M) { int i,blk; int ell=CEIL(olen,hlen); char tmp[260]; octet TMP={0,sizeof(tmp),tmp}; char h0[64]; octet H0 = {0, sizeof(h0), h0}; char h1[64]; octet H1 = {0, sizeof(h1), h1}; blk=blksize(hash,hlen); OCT_jint(&TMP,olen,2); OCT_jint(&TMP,0,1); OCT_joctet(&TMP,DST); OCT_jint(&TMP,DST->len,1); GPhash(hash,hlen,&H0,0,blk,M,-1,&TMP); OCT_empty(&TMP); OCT_jint(&TMP,1,1); OCT_joctet(&TMP,DST); OCT_jint(&TMP,DST->len,1); GPhash(hash,hlen,&H1,0,0,&H0,-1,&TMP); OCT_empty(OKM); OCT_joctet(OKM,&H1); for (i=2;i<=ell;i++) { OCT_xor(&H1,&H0); OCT_empty(&TMP); OCT_jint(&TMP,i,1); OCT_joctet(&TMP,DST); OCT_jint(&TMP,DST->len,1); GPhash(hash,hlen,&H1,0,0,&H1,-1,&TMP); OCT_joctet(OKM,&H1); } OKM->len=olen; } void core::XMD_Expand(int hash, int hlen,octet *OKM,int olen,octet *DST,octet *M) { char w[64]; octet W = {0, sizeof(w), w}; char os[20]; octet OS={0,sizeof(os),os}; OCT_jstring(&OS,(char *)"H2C-OVERSIZE-DST-"); if (DST->len>=256) { GPhash(hash,hlen,&W,0,0,&OS,-1,DST); XMD_Expand_Short_DST(hash,hlen,OKM,olen,&W,M); } else { XMD_Expand_Short_DST(hash,hlen,OKM,olen,DST,M); } } /* Key Derivation Function */ void core::KDF2(int hash, int hlen, octet *key, int olen, octet *z, octet *p) { /* NOTE: the parameter olen is the length of the output k in bytes */ char h[64]; octet H = {0, sizeof(h), h}; int counter, cthreshold; OCT_empty(key); cthreshold = ROUNDUP(olen, hlen); for (counter = 1; counter <= cthreshold; counter++) { GPhash(hash,hlen, &H, 0, 0, z, counter, p); if (key->len + hlen > olen) OCT_jbytes(key, H.val, olen % hlen); else OCT_joctet(key, &H); } } /* Password based Key Derivation Function */ /* Input password p, salt s, and repeat count */ /* Output key of length olen */ void core::PBKDF2(int hash, int hlen, octet *key, int olen, octet *p, octet *s, int rep) { int i, j, len, d = ROUNDUP(olen, hlen); char f[64], u[64]; octet F = {0, sizeof(f), f}; octet U = {0, sizeof(u), u}; OCT_empty(key); for (i = 1; i <= d; i++) { len = s->len; OCT_jint(s, i, 4); HMAC(hash, hlen, &F, hlen, s, p); s->len = len; OCT_copy(&U, &F); for (j = 2; j <= rep; j++) { HMAC(hash, hlen, &U, hlen, &U, p); OCT_xor(&F, &U); } OCT_joctet(key, &F); } OCT_chop(key, NULL, olen); } /* RSA Auxiliary Functions */ #define MAX_RSA_BYTES 512 /**< Maximum of 4096 */ /* Mask Generation Function */ static void MGF1(int sha, octet *z, int olen, octet *mask) { char h[64]; octet H = {0, sizeof(h), h}; int hlen = sha; int counter, cthreshold; OCT_empty(mask); cthreshold = ROUNDUP(olen, hlen); for (counter = 0; counter < cthreshold; counter++) { GPhash(MC_SHA2,sha,&H,0,0,z,counter,NULL); //hashit(sha, z, counter, &H); if (mask->len + hlen > olen) OCT_jbytes(mask, H.val, olen % hlen); else OCT_joctet(mask, &H); } OCT_clear(&H); } /* MGF1 plus masking */ static void MGF1XOR(int sha, octet *z, octet *w) { char h[64]; octet H = {0, sizeof(h), h}; int i,len,wlen,hlen = sha; int counter, cthreshold; wlen=0; cthreshold = ROUNDUP(w->len, hlen); for (counter = 0; counter < cthreshold; counter++) { GPhash(MC_SHA2,sha,&H,0,0,z,counter,NULL); if (wlen+hlen <= w->len) len=hlen; else len=w->len%hlen; for (i=0;ival[wlen+i]^=H.val[i]; wlen+=len; } OCT_clear(&H); } /* SHAXXX identifier strings */ const unsigned char SHA256ID[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; const unsigned char SHA384ID[] = {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; const unsigned char SHA512ID[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}; /* PKCS 1.5 padding of a message to be signed */ int core::PKCS15(int sha, octet *m, octet *w) { int olen = w->max; int hlen = sha; int idlen = 19; char h[64]; octet H = {0, sizeof(h), h}; if (olen < idlen + hlen + 10) return 0; GPhash(MC_SHA2,sha,&H,0,0,m,-1,NULL); OCT_empty(w); OCT_jbyte(w, 0x00, 1); OCT_jbyte(w, 0x01, 1); OCT_jbyte(w, 0xff, olen - idlen - hlen - 3); OCT_jbyte(w, 0x00, 1); if (hlen == 32) OCT_jbytes(w, (char *)SHA256ID, idlen); if (hlen == 48) OCT_jbytes(w, (char *)SHA384ID, idlen); if (hlen == 64) OCT_jbytes(w, (char *)SHA512ID, idlen); OCT_joctet(w, &H); return 1; } /* Alternate form, without the NULL 0500 */ /* SHAXXX identifier strings */ const unsigned char SHA256IDb[] = {0x30, 0x2f, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x04, 0x20}; const unsigned char SHA384IDb[] = {0x30, 0x3f, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x04, 0x30}; const unsigned char SHA512IDb[] = {0x30, 0x4f, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x04, 0x40}; /* PKCS 1.5 padding of a message to be signed */ int core::PKCS15b(int sha, octet *m, octet *w) { int olen = w->max; int hlen = sha; int idlen = 17; char h[64]; octet H = {0, sizeof(h), h}; if (olen < idlen + hlen + 10) return 0; GPhash(MC_SHA2,sha,&H,0,0,m,-1,NULL); OCT_empty(w); OCT_jbyte(w, 0x00, 1); OCT_jbyte(w, 0x01, 1); OCT_jbyte(w, 0xff, olen - idlen - hlen - 3); OCT_jbyte(w, 0x00, 1); if (hlen == 32) OCT_jbytes(w, (char *)SHA256IDb, idlen); if (hlen == 48) OCT_jbytes(w, (char *)SHA384IDb, idlen); if (hlen == 64) OCT_jbytes(w, (char *)SHA512IDb, idlen); OCT_joctet(w, &H); return 1; } /* PSS Encoding of message to be signed. Salt is hlen */ int core::PSS_ENCODE(int sha, octet *m, csprng *RNG, octet *w) { unsigned char mask; char h[64]; octet H = {0, sizeof(h), h}; char md[136]; octet MD={0,sizeof(md),md}; char salt[64]; octet SALT={0,sizeof(salt),salt}; int hlen=sha; int emlen=w->max; int embits=8*emlen-1; OCT_rand(&SALT, RNG, hlen); mask=(0xff)>>(8*emlen-embits); GPhash(MC_SHA2,sha,&H,0,0,m,-1,NULL); if (emlen < hlen + hlen + 2) return 0; OCT_jbyte(&MD,0,8); OCT_joctet(&MD,&H); OCT_joctet(&MD,&SALT); GPhash(MC_SHA2,sha,&H,0,0,&MD,-1,NULL); OCT_clear(w); OCT_jbyte(w,0,emlen-hlen-hlen-2); OCT_jbyte(w,0x01,1); OCT_joctet(w,&SALT); MGF1XOR(sha,&H,w); w->val[0]&=mask; OCT_joctet(w,&H); OCT_jbyte(w,0xbc,1); return 1; } int core::PSS_VERIFY(int sha, octet *m,octet *w) { int i,k; unsigned char mask; char hmask[64]; octet HMASK = {0, sizeof(hmask), hmask}; char h[64]; octet H = {0, sizeof(h), h}; char db[MAX_RSA_BYTES]; octet DB = {0, sizeof(db), db}; char salt[64]; octet SALT={0,sizeof(salt),salt}; int hlen=sha; int emlen=w->len; int embits=8*emlen-1; mask=(0xff)>>(8*emlen-embits); GPhash(MC_SHA2,sha,&HMASK,0,0,m,-1,NULL); if (emlen < hlen + hlen + 2) return 0; if (w->val[emlen-1]!=(char)0xbc) return 0; if ((w->val[0]&(~mask))!=0) return 0; OCT_jbytes(&DB,w->val,emlen-hlen-1); OCT_jbytes(&H,&w->val[emlen-hlen-1],hlen); MGF1XOR(sha,&H,&DB); DB.val[0]&=mask; k=0; for (i=0;imax - 1; int mlen = m->len; int hlen, seedlen; char dbmask[MAX_RSA_BYTES], seed[64]; octet DBMASK = {0, sizeof(dbmask), dbmask}; octet SEED = {0, sizeof(seed), seed}; hlen = seedlen = sha; if (mlen > olen - hlen - seedlen - 1) return 0; if (m == f) return 0; /* must be distinct octets */ GPhash(MC_SHA2,sha,f,0,0,p,-1,NULL); //hashit(sha, p, -1, f); slen = olen - mlen - hlen - seedlen - 1; OCT_jbyte(f, 0, slen); OCT_jbyte(f, 0x1, 1); OCT_joctet(f, m); OCT_rand(&SEED, RNG, seedlen); MGF1(sha, &SEED, olen - seedlen, &DBMASK); OCT_xor(&DBMASK, f); MGF1(sha, &DBMASK, seedlen, f); OCT_xor(f, &SEED); OCT_joctet(f, &DBMASK); OCT_pad(f, f->max); OCT_clear(&SEED); OCT_clear(&DBMASK); return 1; } /* OAEP Message Decoding for Decryption */ int core::OAEP_DECODE(int sha, octet *p, octet *f) { int comp, x, t; int i, k, olen = f->max - 1; int hlen, seedlen; char dbmask[MAX_RSA_BYTES], seed[64], chash[64]; octet DBMASK = {0, sizeof(dbmask), dbmask}; octet SEED = {0, sizeof(seed), seed}; octet CHASH = {0, sizeof(chash), chash}; seedlen = hlen = sha; if (olen < seedlen + hlen + 1) return 0; if (!OCT_pad(f, olen + 1)) return 0; GPhash(MC_SHA2,sha,&CHASH,0,0,p,-1,NULL); //hashit(sha, p, -1, &CHASH); x = f->val[0]; for (i = seedlen; i < olen; i++) DBMASK.val[i - seedlen] = f->val[i + 1]; DBMASK.len = olen - seedlen; MGF1(sha, &DBMASK, seedlen, &SEED); for (i = 0; i < seedlen; i++) SEED.val[i] ^= f->val[i + 1]; MGF1(sha, &SEED, olen - seedlen, f); OCT_xor(&DBMASK, f); comp = OCT_ncomp(&CHASH, &DBMASK, hlen); OCT_shl(&DBMASK, hlen); OCT_clear(&SEED); OCT_clear(&CHASH); // find first non-zero t in array t=k=0; for (i=0;i