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

/*-------------------------*/
static int write_BN(unsigned char **sp, unsigned char **pp,
                    BIGNUM * bnptr)
{
  int i, j;
  unsigned char icp[4096];

  i = BN_num_bits(bnptr);
  icp[0] = i >> 8, icp[1] = i;
  BN_bn2bin(bnptr, &icp[2]);
  j = 2 + (i + 7) / 8;

  if (pp)
    memcpy(*pp, icp, j), *pp += j;
  if (sp)
    memcpy(*sp, icp, j), *sp += j;
  return j;
}

static void adduid(unsigned char **pp, unsigned char *userid)
{
  if (userid) {
    *(*pp)++ = 0xb4;
    *(*pp)++ = strlen(userid);
    memcpy(*pp, userid, strlen(userid));
    *pp += strlen(userid);
  }
}

/*------------------------------------------------------------------------*/
void keyout5(FILE * secout, FILE * pubout, void *key, int primary, int alg,
             unsigned char *passph, char *userid)
{
  unsigned char pubbuf[8192], secbuf[10240], *pp, *sp;
  unsigned char *cp, *icp;
  unsigned long i, len;
  DH *newkey = key;
  DSA *newsig = key;
#ifndef NO_RSA
  RSA *rsakey = key;
#endif

  pp = pubbuf, sp = secbuf;

  if (primary)
    *pp++ = 0x99, *sp++ = 0x95;
  else
    *pp++ = 0xb9, *sp++ = 0x9d;

  cp = pp;
  pp += 2;
  *pp++ = 4;
  i = time(NULL);               /* timestamp */
  *pp++ = i >> 24, *pp++ = i >> 16, *pp++ = i >> 8, *pp++ = i;
  *pp++ = alg;
  memcpy(sp, cp, 8);
  sp += 8;
  len = 6;
  switch (alg) {
#ifndef NO_RSA
  case 1:
    len += write_BN(&sp, &pp, rsakey->n);
    len += write_BN(&sp, &pp, rsakey->e);
    if (!secout)
      RSA_free(rsakey);
    break;
#endif
  case 17:
    len += write_BN(&sp, &pp, newsig->p);
    len += write_BN(&sp, &pp, newsig->q);
    len += write_BN(&sp, &pp, newsig->g);
    len += write_BN(&sp, &pp, newsig->pub_key);
    if (!secout)
      DSA_free(newsig);
    break;
  case 16:
    len += write_BN(&sp, &pp, newkey->p);
    len += write_BN(&sp, &pp, newkey->g);
    len += write_BN(&sp, &pp, newkey->pub_key);
    if (!secout)
      DH_free(newkey);
    break;
  }
  pubbuf[1] = len >> 8, pubbuf[2] = len;

  *pp++ = 0xb0, *pp++ = 0x01, *pp++ = 0x87;
  adduid(&pp, userid);
  if (userid)
    *pp++ = 0xb0, *pp++ = 0x01, *pp++ = 0x03;

  if (secout) {
    void *cfbc;
    i = putcfbkey(sp, passph, 0xff, &cfbc);
    sp += i, len += i;
    cp = sp;                    /* mark start of crypt/checksum */
    switch (alg) {
#ifndef NO_RSA
    case 1:
      len += write_BN(&sp, NULL, rsakey->d);
      len += write_BN(&sp, NULL, rsakey->q);
      len += write_BN(&sp, NULL, rsakey->p);
      len += write_BN(&sp, NULL, rsakey->iqmp);
      RSA_free(rsakey);
      break;
#endif
    case 17:
      len += write_BN(&sp, NULL, newsig->priv_key);
      DSA_free(newsig);
      break;
    case 16:
      len += write_BN(&sp, NULL, newkey->priv_key);
      DH_free(newkey);
      break;
    }
    /* compute checksum */
    icp = cp;
    i = 0;
    while (icp != sp)
      i += *icp++;
    *sp++ = i >> 8, *sp++ = i;
    len += 2;
    if (cfbc) {
      docfb(cp, sp - cp, cfbc);
      free(cfbc);
    }
    secbuf[1] = len >> 8, secbuf[2] = len;
    adduid(&sp, userid);
    fwrite(secbuf, 1, sp - secbuf, secout);
  }
  fwrite(pubbuf, 1, pp - pubbuf, pubout);
}
