#include "libopgp.h"
#include <md5.h>
#include <sha.h>
#include <ripemd.h>
#include <md2.h>

typedef void (*hi) (u8_t *);
typedef void (*hu) (u8_t *, u8_t *, int);
typedef void (*hf) (u8_t *, void *);
#define MAXHASH 6
static const hi GHI[MAXHASH] =
{
  NULL, (hi) MD5_Init, (hi) SHA1_Init, (hi) RIPEMD160_Init, NULL,
  (hi) MD2_Init,
};
static const hu GHU[MAXHASH] =
{
  NULL, (hu) MD5_Update, (hu) SHA1_Update, (hu) RIPEMD160_Update, NULL,
  (hu) MD2_Update,
};
static const hf GHF[MAXHASH] =
{
  NULL, (hf) MD5_Final, (hf) SHA1_Final, (hf) RIPEMD160_Final, NULL,
  (hf) MD2_Final,
};

const u32_t PGP_hctxsiz[MAXHASH] =
{-1, sizeof(MD5_CTX), sizeof(SHA_CTX), sizeof(RIPEMD160_CTX), -1,
 sizeof(MD2_CTX), };

void *PGP_hini(int ha)
{
  u8_t *ctx;
  int siz;

  if (ha >= MAXHASH || GHI[ha] == NULL)
    return NULL;
  if (0 >= (siz = PGP_hctxsiz[ha]))
    return NULL;
  ctx = malloc(++siz);
  *ctx++ = ha;
  (*GHI[ha]) (ctx);
  return --ctx;
}

void PGP_hblk(u8_t * ctx, u8_t * buf, u32_t len)
{
  int ha = *ctx++;
  if (ha >= MAXHASH || GHI[ha] == NULL)
    exit(-1);
  (*GHU[ha]) (ctx, buf, len);
}

void PGP_hfin(u8_t * buf, u8_t * ctx)
{
  int ha = *ctx++;
  if (ha >= MAXHASH || GHI[ha] == NULL)
    exit(-1);
  (*GHF[ha]) (buf, ctx);
  free(--ctx);
}

u8_t PGP_hlen[MAXHASH] =
{0, 16, 20, 20, 0, 16};

const char PGP_hbnr[MAXHASH][24] =
{"SHA1", "RIPEMD160", "*,not,used,*", "MD2", };

static const u8_t PGP_hpfx[MAXHASH][24] =
{
  {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,},
  {0},
  {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
   0x02, 0x02, 0x05, 0x00, 0x04, 0x10,},
};

static const u8_t PGP_hpfxlen[MAXHASH] =
{0, 18, 15, 15, 0, 18,};

int PGP_hder(u8_t halg, u8_t * hbuf)
{
  int k = PGP_hpfxlen[halg];
  if (k) {
    memmove(&hbuf[k], hbuf, PGP_hlen[halg]);
    memcpy(hbuf, PGP_hpfx[halg], k);
  }
  return k + PGP_hlen[halg];
}

/*--------------------------------------------------*/
/* string to key hashing */
void PGP_khash(u8_t ha, u32_t hsiz, u32_t kmat, u32_t ksiz,
               u8_t * hbuf, u8_t * hout)
{
  u32_t loops, block;
  u8_t *hctx;

  for (block = 0; block * PGP_hlen[ha] < ksiz; block++) {
    hctx = PGP_hini(ha);
    memset(hout, 0, block);     /* pass prehashes pass-1 zeros */
    PGP_hblk(hctx, hout, block);
    loops = hsiz;               /* loops over whole text */
    while (loops > kmat)
      PGP_hblk(hctx, hbuf, kmat), loops -= kmat;
    PGP_hblk(hctx, hbuf, loops);
    PGP_hfin(hout, hctx);
    hout += PGP_hlen[ha];
  }
}
