#include "libopgp.h"

#include <idea.h>
#include <des.h>
#include <cast.h>
#include <blowfish.h>

union cipkey {
  IDEA_KEY_SCHEDULE iks;
  des_key_schedule dk[3];
  CAST_KEY castkey;
  BF_KEY bfkey;
};

#define MAXBSZ 8
struct cipctx {
  u8_t cipnum;                  /* algorithm number */
  u8_t ed;                      /* encrypt if nonzero else decrypt */
  u32_t bsiz;                   /* block size, all are 8 */
  unsigned int irm;             /* remaining bytes in feedback */
  u8_t civ[MAXBSZ];             /* feedback register */
  union cipkey ck;              /* formatted key material */
};

static const int glbcipsiz[] =
{0, 16, 24, 16, 16, 0};
int PGP_cksz(int cipher)
{
  if (cipher > 0 && cipher < 5)
    return glbcipsiz[cipher];
  return -1;
}

void *PGP_cini(u8_t * key, u8_t * iv0, u8_t cipno, u8_t enc)
{
  struct cipctx *ctx;

  ctx = malloc(sizeof(struct cipctx));
  ctx->cipnum = cipno;
  ctx->irm = 0;
  ctx->bsiz = 8;
  memcpy(ctx->civ, iv0, ctx->bsiz);
  ctx->ed = enc;
  switch (cipno) {
  case 1:
    idea_set_encrypt_key(key, &ctx->ck.iks);
    ctx->ed = enc ? IDEA_ENCRYPT : IDEA_DECRYPT;
    break;
  case 2:
    des_set_key((des_cblock *) key, ctx->ck.dk[0]);
    des_set_key((des_cblock *) & key[8], ctx->ck.dk[1]);
    des_set_key((des_cblock *) & key[16], ctx->ck.dk[2]);
    ctx->ed = enc ? DES_ENCRYPT : DES_DECRYPT;
    break;
  case 3:
    CAST_set_key(&ctx->ck.castkey, CAST_KEY_LENGTH, key);
    ctx->ed = enc ? CAST_ENCRYPT : CAST_DECRYPT;
    break;
  case 4:
    BF_set_key(&ctx->ck.bfkey, 16, key);
    ctx->ed = enc ? BF_ENCRYPT : BF_DECRYPT;
    break;
  default:
    return NULL;
  }
  return ctx;
}

void PGP_cblk(u8_t * buf, u32_t ln, void *cctx)
{
  struct cipctx *C = cctx;

  if (C->cipnum == 2)
    des_ede3_cfb64_encrypt(buf, buf, ln, C->ck.dk[0], C->ck.dk[1], C->ck.dk[2],
                           (des_cblock *) C->civ, &C->irm, C->ed);
  else if (C->cipnum == 3)
    CAST_cfb64_encrypt(buf, buf, ln, &C->ck.castkey, C->civ, &C->irm, C->ed);
  else if (C->cipnum == 1)
    idea_cfb64_encrypt(buf, buf, ln, &C->ck.iks, C->civ, &C->irm, C->ed);
  else if (C->cipnum == 4)
    BF_cfb64_encrypt(buf, buf, ln, &C->ck.bfkey, C->civ, &C->irm, C->ed);
}

void PGP_cres(u8_t * save, void *cctx)
{
  struct cipctx *CCTX = cctx;

  if (CCTX->bsiz > 9)
    return;
  memcpy((struct cipctx *) CCTX->civ, save, CCTX->bsiz);
  CCTX->irm = 0;
}
