#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <bn.h>
#include <rsa.h>

/*------------------------------------*/
/* pgp format to SSLeay bignum */
BIGNUM *
  pgp2BN(unsigned char **buf)
{
  int bytes;
  BIGNUM *N;

  bytes = (((*buf)[0] * 256 + (*buf)[1]) + 7) / 8;  /* num bytes in mpi */
  *buf += 2;                    /* bypass bytecount */
  N = BN_bin2bn(*buf, bytes, NULL);  /* allocates space and convert */
  *buf += bytes;                /* bypass this number */
  return N;
}

/*------------------------------------*/
/* pull pubkey and userid to packet, trim headers to forsig */
FILE *kring;

int pullkeyforsig(FILE * packet, FILE * packet2,
                  unsigned long long *keyid, int nouid)
{
  unsigned char buf[2048], *bp, t;
  unsigned long i, j, ll;
  int gotkey = 0;
  unsigned long long keyfromn;
  RSA *rsakey = RSA_new();

  /* better yet, lookup "0x%8lX" keyid on a keyserver */
  if (!kring && (bp = getenv("PGPPATH"))) {  /* if no kring, try PGPPATH */
    strncpy(buf, bp, 2000);
    strcat(buf, "/pubring.pgp");
    kring = fopen(buf, "rb");
  }
  if (!kring && (bp = getenv("HOME"))) {  /* no, then try default from HOME */
    strncpy(buf, bp, 2000);
    strcat(buf, "/.pgp/pubring.pgp");
    kring = fopen(buf, "rb");
  }
  if (!kring)
    return -3;
  for (;;) {
    t = fgetc(kring);
    buf[0] = t;
    j = (t & 0x7c) >> 2;
    ll = 2;
    if (j == 5 || j == 6 || j == 2) {
      ll = 1 << (t & 3);        /* length of length */
      fread(&buf[1], ll++, 1, kring);  /* decode length of rest */
      for (j = 1, i = 0; j < ll; j++)
        i = (i << 8) + buf[j];
    } else if (j == 12 || j == 13 || j == 14) {
      i = fgetc(kring);
      ll = 2;
      buf[1] = i;
    } else
      break;
    fread(&buf[ll], 1, i, kring);  /* read the rest of whatever it is */
    bp = buf + 8 + ll;
    t = (t & 0x7c) >> 2;
    if (t == 12)
      continue;
    if (gotkey && (t == 13)) {
      if (!nouid) {
        fwrite(buf, 1, i + ll, packet);
        fwrite(&buf[ll], 1, i, packet2);
      } else
        fwrite(buf, 1, i + ll, packet2);
    } else if (t == 2 && gotkey)
      return 0;
    else if (t == 6) {          /* public key */
      if (gotkey)
        return 0;
      rsakey->n = pgp2BN(&bp);
      j = 64;
      keyfromn = 0;
      while (j)
        keyfromn = (keyfromn << 1) + BN_is_bit_set(rsakey->n, --j);
#ifdef TESTMODE
      if (!*keyid)
        *keyid = keyfromn;
#endif
      if ((*keyid & 0xffffffff) != (keyfromn & 0xffffffff))
        continue;
      *keyid = keyfromn;        /* save 64 bit version */
      if (fgetc(kring) == 0xb0 && fgetc(kring) == 1 && fgetc(kring) & 0x20)
        return -2;
      gotkey = 1;
      fwrite(buf, 1, i + ll, packet);
      if (!nouid)
        fwrite(buf, 1, i + ll, packet2);
    } else
      continue;
  }
  return -1;
}

int main(int argc, char *argv[])
{
  unsigned long long keyid = 0LL;
  extern char *optarg;
  int ch, sflag = 0;
  FILE *outf1 = stderr, *outf = stdout;

  kring = NULL;

  while ((ch = getopt(argc, argv, "x:y:k:S")) != -1) {
    if (ch == 'k')
      sscanf(optarg, "%qX", &keyid);
    else if (ch == 'S')         /* pull for signature revocation */
      sflag++;
    else if (ch == 'x') {       /* first piece */
      outf1 = fopen(optarg, "wb");
      if (outf1 == NULL)
        exit(-1);
    } else if (ch == 'y') {     /* second piece */
      outf = fopen(optarg, "wb");
      if (outf == NULL)
        exit(-1);
    }
  }
  pullkeyforsig(outf1, outf, &keyid, sflag);
  return 0;
}
