/* Copyright (C) 2003 Robert Laughlin

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Contact Information:  Robert Laughlin, Digital Systems, PO Box 158
Walkersville, MD 21791        robert@dscinet.com
*/ 



/*** Function: hmac_md5 ***/

#include <stdlib.h>
#include <string.h>
#include "md5.h"
#include "hmac_md5.h"


/************************************************************************************
 *   Call hmac_md5_key()   one time to specify the key for subsequent               *
 *   calls to hmac_md5_digest().  it can be called again to change keys.            *
 ************************************************************************************/
void hmac_md5_key(HMAC_CTX *ctx, unsigned char* key, int key_len)
/* unsigned char*  key;                  pointer to authentication key */
/* int             key_len;              length of authentication key */
{
MD5_CTX       context;
unsigned char k_ipad[65];    /* inner padding - key XORd with ipad */
unsigned char k_opad[65];    /* outer padding - key XORd with opad */
unsigned char tk[16];        /* storage for MD5'd key, if key_len > 64 */
int  i;



/* if key is longer than 64 bytes reset it to key=MD5(key) */
if (key_len > 64) {
    MD5Init(&context);
    MD5Update(&context, key, key_len);
    MD5Final(&context);
    memcpy(tk, context.digest, 16 );
    key = tk;
    key_len = 16;
    }

/* start out by storing key in pads, note calloc cleared k_ipad & k_opad */
memset( k_ipad, 0, sizeof(k_ipad));
memcpy( k_ipad, key, key_len);
memset( k_opad, 0, sizeof(k_opad));
memcpy( k_opad, key, key_len);

/* XOR key with ipad and opad values */
for (i=0; i<64; i++) {
    k_ipad[i] ^= 0x36;
    k_opad[i] ^= 0x5c;
    }

/* precompute inner MD5: MD5(key XOR ipad) */
MD5Init(&context);                   /* init context for 1st pass */
MD5Update(&context, k_ipad, 64);     /* start with inner pad */
memcpy( &ctx->context_inner, &context, sizeof( ctx->context_inner ) - 16 );  /* save partial result */

/* precompute outer MD5: MD5(key XOR opad) */
MD5Init(&context);                   /* init context for 2nd pass */
MD5Update(&context, k_opad, 64);     /* start with outer pad */
memcpy( &ctx->context_outer, &context, sizeof( ctx->context_outer ) - 16 );
}




void hmac_md5_digest(HMAC_CTX *ctx, unsigned char* text, int text_len, unsigned char* digest)
/* unsigned char*  text;                 pointer to data stream */
/* int             text_len;             length of data stream */
/* unsigned char*  digest;               caller digest to be filled in */

{
MD5_CTX context;
        
/* perform inner MD5  */
memcpy( &context, &ctx->context_inner, sizeof( context ) - 16 );  /* get partial result */
MD5Update(&context, text, text_len); /* text of datagram */
MD5Final(&context);                  /* finish up 1st pass */
memcpy(digest, context.digest, 16);
        
/* perform outer MD5 */
memcpy( &context, &ctx->context_outer, sizeof( context )- 16 );  /* get partial result */
MD5Update(&context, digest, 16);     /* results of 1st hash */
MD5Final(&context);                  /* finish up 2nd pass */
memcpy(digest, context.digest, 16);
}




void hmac_md5(unsigned char* text, int text_len, unsigned char* key, int key_len, unsigned char* digest)
/* unsigned char*  text;                 pointer to data stream */
/* int             text_len;             length of data stream */
/* unsigned char*  key;                  pointer to authentication key */
/* int             key_len;              length of authentication key */
/* unsigned char*  digest;               caller digest to be filled in */

{
        MD5_CTX context;
        unsigned char k_ipad[65];    /* inner padding - key XORd with ipad */
        unsigned char k_opad[65];    /* outer padding - key XORd with opad */
        unsigned char tk[16];
        int i;
        /* if key is longer than 64 bytes reset it to key=MD5(key) */
        if (key_len > 64) {

                MD5_CTX      tctx;

                MD5Init(&tctx);
                MD5Update(&tctx, key, key_len);
                MD5Final(&tctx);
                memcpy(&tk, tctx.digest, 16 );
                key = (unsigned char*) tk;
                key_len = 16;
        }

        /*
         * the HMAC_MD5 transform looks like:
         *
         * MD5(K XOR opad, MD5(K XOR ipad, text))
         *
         * where K is an n byte key
         * ipad is the byte 0x36 repeated 64 times

         * opad is the byte 0x5c repeated 64 times
         * and text is the data being protected
         */

        /* start out by storing key in pads */
        memset( k_ipad, 0, sizeof k_ipad);
        memset( k_opad, 0, sizeof k_opad);
        memcpy( k_ipad, key, key_len);
        memcpy( k_opad, key, key_len);

        /* XOR key with ipad and opad values */
        for (i=0; i<64; i++) {
                k_ipad[i] ^= 0x36;
                k_opad[i] ^= 0x5c;
        }
        /*
         * perform inner MD5
         */
        MD5Init(&context);                   /* init context for 1st pass */
        MD5Update(&context, k_ipad, 64);     /* start with inner pad */
        MD5Update(&context, text, text_len); /* then text of datagram */
        MD5Final(&context);                  /* finish up 1st pass */
		memcpy(digest, context.digest, 16);
        /*
         * perform outer MD5
         */
        MD5Init(&context);                   /* init context for 2nd pass */
        MD5Update(&context, k_opad, 64);     /* start with outer pad */
        MD5Update(&context, digest, 16);     /* then results of 1st hash */
        MD5Final(&context);                  /* finish up 2nd pass */
		memcpy(digest, context.digest, 16);
}
