#include <stdio.h>
#include <malloc.h>
#include "libpgp5.h"

/*-------------------------*/
/* hashes */
#ifndef NO_MD5
#include <md5.h>
#endif

#ifndef NO_SHA1
#include <sha.h>
#endif

#ifndef NO_RIPEMD
#include <ripemd.h>
#endif

#ifndef NO_HAVAL
#include "haval.h"
#endif

#ifndef NO_MD2
#include <md2.h>
#endif

void *hashinit(int ha)
{
  void *ctx;
  switch (ha) {
#ifndef NO_MD5
  case 1:
    ctx = malloc(1 + sizeof(MD5_CTX));
    MD5_Init((void *) ++(unsigned char *) ctx);
    break;
#endif
#ifndef NO_SHA1
  case 2:
    ctx = malloc(1 + sizeof(SHA_CTX));
    SHA1_Init((void *) ++(unsigned char *) ctx);
    break;
#endif
#ifndef NO_RIPEMD
  case 3:
    ctx = malloc(1 + sizeof(RIPEMD160_CTX));
    RIPEMD160_Init((void *) ++(unsigned char *) ctx);
    break;
#endif
#ifndef NO_HAVAL
  case 4:
    ctx = malloc(1 + sizeof(havalContext));
    havalInit((void *) ++(unsigned char *) ctx, 3, 160);
    break;
#endif
#ifndef NO_MD2
  case 5:
    ctx = malloc(1 + sizeof(MD2_CTX));
    MD2_Init((void *) ++(unsigned char *) ctx);
    break;
#endif
  default:
    exit(-1);
  }
  *(unsigned char *) --ctx = ha;
  return ctx;
}

void hashupdate(void *ctx, unsigned char *buf, unsigned int len)
{
  switch (*(unsigned char *) ctx++) {
#ifndef NO_MD5
  case 1:
    MD5_Update(ctx, buf, len);
    break;
#endif
#ifndef NO_SHA1
  case 2:
    SHA1_Update(ctx, buf, len);
    break;
#endif
#ifndef NO_RIPEMD
  case 3:
    RIPEMD160_Update(ctx, buf, len);
    break;
#endif
#ifndef NO_HAVAL
  case 4:
    havalUpdate(ctx, buf, len);
    break;
#endif
#ifndef NO_MD2
  case 5:
    MD2_Update(ctx, buf, len);
    break;
#endif
  default:
    exit(-1);
  }
}

void hashfinal(unsigned char *buf, void *ctx)
{
  switch (*(unsigned char *) ctx++) {
#ifndef NO_MD5
  case 1:
    MD5_Final(buf, ctx);
    break;
#endif
#ifndef NO_SHA1
  case 2:
    SHA1_Final(buf, ctx);
    break;
#endif
#ifndef NO_RIPEMD
  case 3:
    RIPEMD160_Final(buf, ctx);
    break;
#endif
#ifndef NO_HAVAL
  case 4:
    havalFinal(ctx, buf);
    break;
#endif
#ifndef NO_MD2
  case 5:
    MD2_Final(buf, ctx);
    break;
#endif
  default:
    exit(-1);
  }
  free(--ctx);
}

/* this is the last byte of the hash DER header */
unsigned int hashlen[] =
{0, 16, 20, 20, 20, 16};

/* 2nd byte of DER - hashlen + 2 */
unsigned int hashDERlen[] =
{0, 18, 15, 15, 0, 18};

unsigned char hashDER[][64] =
{
  {0},
  {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86,
   0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
  {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02,
   0x1a, 0x05, 0x00, 0x04, 0x14},
  {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02,
   0x01, 0x05, 0x00, 0x04, 0x14},
  {0x30, 0x21, 0x30, 0x09, 0x04, 0x05, 0x48, 0x41, 0x56, 0x41,
   0x4c, 0x05, 0x00, 0x04, 0x14} /***FAKE***/ ,
  {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86,
   0xf7, 0x0d, 0x02, 0x02, 0x05, 0x00, 0x04, 0x10}
};

/*--------------------------------------------------*/
/* string to key */
void keyhash(int ha, int hbytes, int keymat, int keysize,
             unsigned char *hbuf, unsigned char *hout)
{
  unsigned int loops, block;
  void *hctx;

  for (block = 0; block * hashlen[ha] < keysize; block++) {
    hctx = hashinit(ha);
    memset(hout, 0, block);     /* pass prehashes pass-1 zeros */
    hashupdate(hctx, hout, block);
    loops = hbytes / keymat;    /* loops over whole text */
    while (loops--)
      hashupdate(hctx, hbuf, keymat);
    hashupdate(hctx, hbuf, hbytes % keymat);
    hashfinal(hout, hctx);
    hout += hashlen[ha];
  }
}
