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

#define MAXHASH 6

const u32_t PGP_hctxsiz[MAXHASH] =
{-1, sizeof(MD5_CTX), sizeof(SHA_CTX), sizeof(RIPEMD160_CTX), -1,
 sizeof(MD2_CTX)};
const char PGP_hbnr[MAXHASH][24] =
{"SHA1", "RIPEMD160", "*not,used*", "MD2"};

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

  if (ha >= MAXHASH)
    return NULL;
  if (0 >= (siz = PGP_hctxsiz[ha]))
    return NULL;

  ctx = malloc(siz + 2);

  *ctx++ = ha;
  *ctx++ = 0;                   /* word align */

  if (ha == 2)
    SHA1_Init(ctx);
  else if (ha == 1)
    MD5_Init(ctx);
  else if (ha == 3)
    RIPEMD160_Init(ctx);
  else if (ha == 5)
    MD2_Init(ctx);
  else
    ErrDisplay("BadHashAlg");

  ctx--;
  return --ctx;
}

void PGP_hblk(u8_t * ctx, u8_t * buf, u32_t len)
{
  int ha = *ctx++;
  if (ha >= MAXHASH)
    ErrDisplay("BadHashAlg");

  if (ha == 2)
    SHA1_Update(++ctx, buf, len);
  else if (ha == 1)
    MD5_Update(++ctx, buf, len);
  else if (ha == 3)
    RIPEMD160_Update(++ctx, buf, len);
  else if (ha == 5)
    MD2_Update(++ctx, buf, len);

}

void PGP_hfin(u8_t * buf, u8_t * ctx)
{
  int ha = *ctx++;
  if (ha >= MAXHASH)
    ErrDisplay("BadHashAlg");

  if (ha == 2)
    SHA1_Final(buf, ++ctx);
  else if (ha == 1)
    MD5_Final(buf, ++ctx);
  else if (ha == 3)
    RIPEMD160_Final(buf, ++ctx);
  else if (ha == 5)
    MD2_Final(buf, ++ctx);
  ctx--;

  free(--ctx);

}

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

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];
  }
}
