/*
 * hmac_sha1.c
 *
 * The code in this module is derived from the HMAC-MD5 sample code in
 * the appendix of RFC2104, "HMAC: Keyed-Hashing for Message
 * Authentication".
 *
 * Original Authors (MD5 version): H. Krawczyk, M. Bellare, and R. Cannetti
 * Modified for SHA-1 and NASD by: Howard Gobioff
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <cryptolib/libcrypt.h>
#include <nasd/nasd_general.h>

#include <nasd/nasd_timer.h>
#include <nasd/nasd_threadstuff.h>

#ifndef KERNEL
#include <stdio.h>
#include <string.h>
#endif /* !KERNEL */

/*
 * Directly derived from the reference implementation
 * hgobioff@cs.cmu.edu
 */

#ifndef TRUE
#define TRUE 1
#endif /* !TRUE */
#ifndef FALSE
#define FALSE 0
#endif /* !FALSE */

#if NASD_SECURITY_KEEP_STATS > 0
nasd_timespec_t nasd_HMAC_SHA1_ts;

NASD_DECLARE_MUTEX(nasd_HMAC_SHA1_timers_mutex);

#define START_HMAC_TIMER {\
  NASD_TM_START(&nasd_HMAC_SHA1_tm); \
}

#define STOP_HMAC_TIMER { \
  NASD_LOCK_MUTEX(nasd_HMAC_SHA1_timers_mutex); \
  NASD_TM_STOP_ACCUM_TS(&nasd_HMAC_SHA1_tm, &nasd_HMAC_SHA1_ts); \
  NASD_UNLOCK_MUTEX(nasd_HMAC_SHA1_timers_mutex); \
}

#else /* NASD_SECURITY_KEEP_STATS > 0 */
#define START_HMAC_TIMER
#define STOP_HMAC_TIMER
#endif /* NASD_SECURITY_KEEP_STATS > 0 */


#if NASD_SECURITY_KEEP_STATS > 0
void
nasd_HMAC_print_stats(void)
{
  nasd_printf("HMAC elapsed time: %d:%09d\n", nasd_HMAC_SHA1_ts.ts_sec, 
	      nasd_HMAC_SHA1_ts.ts_nsec);
}

nasd_status_t
nasd_HMAC_init_stats(void)
{
  nasd_status_t rc;
  
  nasd_HMAC_SHA1_ts.ts_sec=0;
  nasd_HMAC_SHA1_ts.ts_nsec=0;
  rc=nasd_mutex_init(&nasd_HMAC_SHA1_timers_mutex);
  return(rc);
}


nasd_status_t
nasd_HMAC_zero_stats(void)
{
  nasd_status_t rc;
 
  NASD_LOCK_MUTEX(nasd_HMAC_SHA1_timers_mutex); 
  nasd_HMAC_SHA1_ts.ts_sec=0;
  nasd_HMAC_SHA1_ts.ts_nsec=0;
  NASD_UNLOCK_MUTEX(nasd_HMAC_SHA1_timers_mutex); 
  return(NASD_SUCCESS);
}
#endif /* NASD_SECURITY_KEEP_STATS > 0 */

/* 
 * Set up context for SHA1 and the HMAC code
 */
void
HMAC_SHA1_Init(HMAC_SHA1_CTX *context,
	       char *key,
	       int sz)
{ 
#if NASD_SECURITY_KEEP_STATS > 0
  nasd_timer_t nasd_HMAC_SHA1_tm;
#endif /* NASD_SECURITY_KEEP_STATS > 0 */
  char *real_key;
  unsigned char tk[20];
  unsigned char k_ipad[65];    /* inner padding - key XORd with ipad */
  int i;
#ifdef DEBUG
  nasd_uint64 tkey;
#endif /* DEBUG */

  START_HMAC_TIMER;

#ifdef DEBUG
  memcpy(&tkey, key, sizeof(nasd_uint64));
  nasd_printf("HMAC_SHA1Init[0x%p]: sz= %d, key=[%08" NASD_64x_FMT "]\n",
	      context, sz, tkey);
#endif /* DEBUG */

  if (sz > 64) {
    SHA_CTX      tctx;
 
    SHA1_Init(TRUE, &tctx);
    SHA1_Update(TRUE, &tctx, (unsigned char *) key, sz);
    SHA1_Final(TRUE, tk, &tctx);
    real_key = (char *) tk;
    sz = 20;
  } 
  else 
    real_key=key;
 
  /*
   * the HMAC_SHA1 transform looks like:
   *
   * SHA1(K XOR opad, SHA1(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 */
  bzero(k_ipad, sizeof k_ipad);
  bzero(context->k_opad, sizeof context->k_opad);
  bcopy(key, k_ipad, sz);
  bcopy(key, context->k_opad, sz);
 
  /* XOR key with ipad and opad values */
#ifdef __alpha
  for(i=0;i<8;i++) {
    ((long *)k_ipad)[i] ^= 0x3636363636363636;
    ((long *)context->k_opad)[i] ^= 0x5c5c5c5c5c5c5c5c;
  }
#else
  for (i=0; i<64; i++) {
    k_ipad[i] ^= 0x36;
    context->k_opad[i] ^= 0x5c;
  }
#endif /* __alpha */

  /*
   * perform inner SHA1
   */
  SHA1_Init(TRUE, &(context->sha1_stuff)); /* init context for 1st pass */
  SHA1_Update(TRUE, &(context->sha1_stuff), k_ipad, 64); /* start with inner pad */

#ifdef DEBUG
  nasd_printf("HMAC_SHA1_Init[0x%p]: finished initialization with internal state=[%04x%04x%04x%04x]\n",
	      context, context->sha1_stuff.h0, context->sha1_stuff.h1,
	      context->sha1_stuff.h2, context->sha1_stuff.h3);
#endif /* DEBUG */
  STOP_HMAC_TIMER;
}


void
HMAC_SHA1_Update(HMAC_SHA1_CTX *context,
		 unsigned char *input,
		 unsigned int inputLen)
{
#if NASD_SECURITY_KEEP_STATS > 0
  nasd_timer_t nasd_HMAC_SHA1_tm;
#endif /* NASD_SECURITY_KEEP_STATS > 0 */
#if DEBUG > 0
  int i;
  int j;
  nasd_printf("HMAC_SHA1_Update[0x%p]: with %d bytes\n",context,inputLen); 
#endif /* DEBUG > 0 */

  START_HMAC_TIMER;


#if DEBUG > 1
  j = NASD_MIN(inputLen, 64);
  nasd_printf("HMAC_SHA1_Update[0x%p]: first %d bytes  [", context, j);
  for(i=0;i<j;i++)
    nasd_printf("%x ",input[i]);
  nasd_printf("]\n");
    
  nasd_printf("HMAC_SHA1_Update[0x%p]: before internal state=[%04x%04x%04x%04x]\n",
	      context, context->sha1_stuff.h0, context->sha1_stuff.h1,
	      context->sha1_stuff.h2, context->sha1_stuff.h3);
#endif /* DEBUG > 1 */

  SHA1_Update(TRUE, &(context->sha1_stuff), input, inputLen);

#ifdef DEBUG
  nasd_printf("HMAC_SHA1_Update[0x%p]: out current internal state=[%04x%04x%04x%04x]\n",
	      context, context->sha1_stuff.h0, context->sha1_stuff.h1,
	      context->sha1_stuff.h2, context->sha1_stuff.h3);
#endif /* DEBUG */

  STOP_HMAC_TIMER;
}

void
HMAC_SHA1_Final(unsigned char digest[20],
		HMAC_SHA1_CTX *context)
{
#if NASD_SECURITY_KEEP_STATS > 0
  nasd_timer_t nasd_HMAC_SHA1_tm;
#endif /* NASD_SECURITY_KEEP_STATS > 0 */
#ifdef DEBUG
  nasd_uint64 tdigest[2];
#endif /* DEBUG */

#ifdef DEBUG
  nasd_printf("HMAC_SHA1_Final[0x%p]:starting final \n",context);
#endif /* DEBUG */

  START_HMAC_TIMER;

  SHA1_Final(TRUE, digest,&(context->sha1_stuff)); /* finish up 1st pass */

  /*
   * perform outer SHA1
   */
  SHA1_Init(TRUE, &(context->sha1_stuff)); /* init context for 2nd pass */
  SHA1_Update(TRUE, &(context->sha1_stuff),
	      (unsigned char *)context->k_opad, 64); /* start with outer pad */
  SHA1_Update(TRUE, &(context->sha1_stuff), digest, 20); /* then results of 1st hash */
  SHA1_Final(TRUE, digest, &(context->sha1_stuff)); /* finish up 2nd pass */ 

#ifdef DEBUG
  memcpy(tdigest, digest, 2*sizeof(nasd_uint64));
  nasd_printf("HMAC_SHA1_Final[0x%p]: "
	      "HMAC digest=[%08" NASD_64x_FMT "%08" NASD_64x_FMT "]\n\n",
	      context, tdigest[0], tdigest[1]   );
#endif /* DEBUG */

  STOP_HMAC_TIMER;
}
