/*****************************************************************************
 *
 * HMAC MD5 engine software implementation.
 * Copyright (C) 2001  Bart Trojanowski <bart@jukie.net>.
 *
 * 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.  See <http://www.fsf.org/copyleft/gpl.txt>.
 * 
 * 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.
 *
 *****************************************************************************/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include "engine.h"
#include "engine-debug.h"
#include "digest-hmac_md5.h"

// borow the implementation from freeswan for now
#include "from_freeswan/ipsec_ah.h"
#include "from_freeswan/ipsec_md5c.c"

/* the work is done here... */
static int digest_hmac_md5_sw_con_process( struct generic_job * gj );

/* -------------------------------------------------------------------------
 * STRUCTURE SUBCLASSES
 * ------------------------------------------------------------------------- */

struct md5_engine {
  struct generic_engine ge;
  
  
  /* engine statistics can go here... 
   * ex: # of contexts in use, time in system, etc */
};

struct md5_context {
  struct generic_context gc;
  
  MD5_CTX ictx, octx;
  
  /* context statistics can go here...
   * ex: # of jobs in use, # of jobs total, time since creation, etc */
};

struct md5_job {
  struct generic_job gj;
  
  /* software implementation... no need for any variables */
};


/* -------------------------------------------------------------------------
 * ENGINE METHODS
 * ------------------------------------------------------------------------- */

static int
digest_hmac_md5_sw_eng_context_size( struct generic_engine * ge, void * defn )
{
  int ret;
  FIN();
  ret = sizeof( struct md5_context );
  FEXIT(ret);
  return ret;
}


/* -------------------------------------------------------------------------
 * CONTEXT METHODS
 * ------------------------------------------------------------------------- */

static int
digest_hmac_md5_sw_con_init( struct generic_context * gc, int gfp_flag )
{
  int ret, i;
  unsigned char kb[AHMD596_BLKLEN];
  struct md5_context *md5_con = (void*)gc;
  struct digest_hmac_md5_context_definition *defn;

  FIN();
  
  ret = -EINVAL;		/* XXX need a better return code */
  if( !md5_con->gc.defn )
    goto bail;
  
  defn = (void*)md5_con->gc.defn;

  ret = -EINVAL;		/* XXX return codes */
  if( defn->key_len != AHMD596_ALEN )
    goto bail;
  
  /* initialize the MD5 context structures */
  for (i = 0; i < defn->key_len; i++) {
    kb[i] = defn->key_ptr[i] ^ HMAC_IPAD;
  }
  for (; i < AHMD596_BLKLEN; i++) {
    kb[i] = HMAC_IPAD;
  }
  
  MD5Init(&md5_con->ictx);
  MD5Update(&md5_con->ictx, kb, AHMD596_BLKLEN);
  
  for (i = 0; i < AHMD596_BLKLEN; i++)
    kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
  
  MD5Init(&md5_con->octx);
  MD5Update(&md5_con->octx, kb, AHMD596_BLKLEN);
  
  memset(kb, 0, sizeof(kb));
  //memset((caddr_t)&(ed->ame_key), 0, ed->ame_klen);
  
  /* initialize the processor */
  gc->ops.process = digest_hmac_md5_sw_con_process;
  
  ret = 0;
  
 bail:
  FEXIT(ret==0);
  return ret;
}

static int
digest_hmac_md5_sw_con_cleanup( struct generic_context * gc )
{
  //struct md5_context *md5_con = (void*)gc;
  FIN();
  FOUT();
  return 0;
}

static int
digest_hmac_md5_sw_con_job_size( struct generic_context * gc )
{
  int ret;
  //struct md5_context *md5 = (void*)gc;
  FIN();
  ret =  sizeof( struct md5_job );
  FEXIT(ret!=0);
  return ret; 
}

/* digest processing */
static int
digest_hmac_md5_sw_con_process( struct generic_job * gj )
{
  int ret;
  //struct md5_job *md5_job = (void*)gj;
  struct md5_context *md5_con = (void*)gj->con;
  MD5_CTX md5;
  __u8 hash[AH_AMAX];
  
  FIN();

  ret = -EINVAL;
  if( gj->data.out_len < AH_AMAX )
    goto done;
  
  /* all actuall MD5 processing */
  md5 = md5_con->ictx;
  MD5Update(&md5, gj->data.in_ptr, gj->data.in_len);
  MD5Final(hash, &md5);

  md5 = md5_con->octx;
  MD5Update(&md5, hash, AHMD596_ALEN);
  MD5Final(hash, &md5);
  
  memcpy(gj->data.out_ptr, hash, AH_AMAX);
  gj->data.out_len = AH_AMAX;
  
  memset(&md5, 0, sizeof(md5));
  memset(hash, 0, sizeof(*hash));

  /* issue callback */
  gj->result = ret = 0;
  finalize_job( gj );
  
 done:
  FEXIT(ret==0);
  return ret;
}


/* -------------------------------------------------------------------------
 * JOB METHODS
 * ------------------------------------------------------------------------- */

static void
digest_hmac_md5_sw_job_cancel_async ( struct generic_job * gj)
{
  FIN();
  /* for now it's not possible... but at least we can set the del_flag */
  atomic_set( &gj->del_flag, 1 );
  FOUT();
  return;
}


/* -------------------------------------------------------------------------
 * ENGINE STRUCTURE
 * ------------------------------------------------------------------------- */

DEFINE_GENERIC_ENGINE("digest-hmac_md5-sw",digest_hmac_md5_sw);


/* -------------------------------------------------------------------------
 * MODULE FUNCTIONS
 * ------------------------------------------------------------------------- */

static int __init
engine_init(void)
{
  int ret = 0;
  
  FIN();
  
  printk(KERN_INFO "MD5 engine init\n");
  
  ret = register_generic_engine( &digest_hmac_md5_sw );
  
  FEXIT(ret==0);
  
  return ret;
}

static void __init
engine_cleanup(void)
{
  FIN();
  unregister_generic_engine( &digest_hmac_md5_sw );
  FOUT();
}

module_init(engine_init);
module_exit(engine_cleanup);

/*****************************************************************************
 *
 * $Log: digest-hmac_md5-sw.c,v $
 * Revision 1.1  2001/06/08 23:29:10  bart
 * major refining
 *
 * Revision 1.11  2001/06/07 00:42:08  bart
 * changed name of include path
 *
 * Revision 1.10  2001/06/04 02:14:02  bart
 * merged forward/reverse into one call 'process' which is a function pointer
 * to a specific processor decided in create_context (i.e. con_init)
 *
 * Revision 1.9  2001/06/03 15:23:02  bart
 * fixed ref count bug with context
 *
 * Revision 1.8  2001/06/03 14:29:38  bart
 * fixed a pointer bug passed to memcpy
 *
 * Revision 1.7  2001/06/03 13:17:43  bart
 * moved get_engine from con_init to create_context.
 *
 * Revision 1.6  2001/06/03 03:07:05  bart
 * OOPS! fixed a mistake in the name of digest engine.
 *
 * Revision 1.5  2001/06/03 02:16:33  bart
 * extended the DEFINE_GENERIC_ENGINE macro to take different prefix and name
 *
 * Revision 1.4  2001/06/02 22:24:59  bart
 * added debug messages to generic engine.
 *
 * Revision 1.3  2001/06/01 01:29:11  bart
 * definition passed to context_size function
 *
 * Revision 1.2  2001/05/30 01:05:03  bart
 * md5 implemented - first cut.
 *
 * Revision 1.1  2001/05/29 01:51:56  bart
 * first cut MD5 implementation
 *
 *
 *****************************************************************************/

