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

#include <sha.h>

/*------------------------------------*/
/* pgp format to SSLeay bignum */

void mpiBN(unsigned char **buf)
{
  int len;

  len = *(*buf)++ * 256, len += *(*buf)++, len = (len + 7) / 8;
  *buf += len;                  /* bypass this number */
}

/*------------------------------------*/
/* hash for new keyid - SHA hardcoded */

unsigned long long gkid(unsigned char *buf, int len)
{
  int i;
  SHA_CTX hctx;
  unsigned long long keyid = 0;
  unsigned char newkeyid[20];
  unsigned char keypfx[3];

  SHA1_Init(&hctx);
  keypfx[0] = 0x99, keypfx[1] = len >> 8, keypfx[2] = len & 0xff;
  SHA1_Update(&hctx, keypfx, 3);
  SHA1_Update(&hctx, buf, len);
  SHA1_Final(newkeyid, &hctx);

  for (i = 0; i < 8; i++) {
    keyid <<= 8;
    keyid |= newkeyid[12 + i];
  }
  return keyid;
}

/*------------------------------------*/
#define MAXLEN 32768

void *gtpkt(FILE * fp, unsigned char ctb, int *gaz)
{
  unsigned long len;
  int neof = 0, i, k;
  unsigned char *bufptr, *bp;
  unsigned long gazouta;

  gazouta = 0;
  if (ctb < 0xc0) {
    i = 1 << (ctb & 3);         /* length of length */
    for (k = 0, len = 0; k < i; k++)
      len = (len << 8) + fgetc(fp);
  } else {
    if ((len = fgetc(fp)) >= 0xc0) {
      if (len >= 0xe0)
        if (len == 0xff)
          for (k = 0, len = 0; k < 4; k++)
            len = (len << 8) + fgetc(fp);
        else
          len = 1 << (len & 31), neof = 1;  /* noteof */
      else
        len = ((len & 31) << 8) + 192 + fgetc(fp);
    }
  }
  if (len > MAXLEN || !len)
    return NULL;
  bufptr = malloc(neof ? MAXLEN + 1 : len + 1);  /* +1 to add final null */

  bp = bufptr;
  for (;;) {
    gazouta += len;
    if (gazouta > MAXLEN) {
      if (0 >= (i = fread(bp, 1, MAXLEN - gazouta, fp)))
        exit(-2);
      gazouta += i - len;
      len = 0, neof = 0;
    }
    while (len) {
      if (0 >= (i = fread(bp, 1, len, fp)))
        exit(-2);
      len -= i, bp += i;
    }
    if (!neof) {
      bufptr[gazouta] = 0;
      if (gaz)
        *gaz = gazouta;
      return bufptr;
    }
    neof = 0;
    if ((len = fgetc(fp)) >= 0xc0) {
      if (len >= 0xe0)
        len = 1 << (len & 31), neof += len;  /* noteof */
      else
        len = ((len & 31) << 8) + 192 + fgetc(fp);
    }
  }
}

/*------------------------------------*/
void ringout(FILE * ringf, FILE * outf, FILE * listf)
{

  unsigned char *bp0, *bp, t, *sp = NULL, *xp = NULL;
  unsigned long ll;
  int j, ctb, k;
  unsigned long long keyid = 0, altkid = 0;

  while (!feof(ringf)) {
    ctb = fgetc(ringf);
    if (!(bp = gtpkt(ringf, ctb, &j)))
      break;
    bp0 = bp;

    ctb = ctb & 0x7c;
    ctb |= 1;

    t = (ctb & 0x7c) >> 2;
    switch (t) {
    case 5:
    case 6:
    case 7:
    case 14:

      switch (t) {
      case 5:
        sp = "====================\nKEY";
        break;
      case 6:
        sp = "====================\nkey";
        break;
      case 7:
        sp = "SUB";
        break;
      case 14:
        sp = "sub";
        break;
      }
      k = *bp++;                /* vers */
      /* timestamp */
      ll = *bp++ << 24, ll += *bp++ << 16, ll += *bp++ << 8, ll += *bp++;
      if (k != 4)
        bp += 2;                /* old valid days */
      switch (*bp++) {
      case 17:
        xp = "DSA Sign";
        mpiBN(&bp), mpiBN(&bp), mpiBN(&bp), mpiBN(&bp);
        keyid = gkid(bp0, bp - bp0);
        break;
      case 16:
      case 20:
        xp = "DH Crypt";
        mpiBN(&bp), mpiBN(&bp), mpiBN(&bp);
        keyid = gkid(bp0, bp - bp0);
        break;
      case 1:
      case 2:
      case 3:
        xp = "RSA Both";
        mpiBN(&bp);
        k = 8;
        keyid = 0;
        bp -= 8;
        while (k--)
          keyid = (keyid << 8) + *bp++;
        mpiBN(&bp);
        altkid = gkid(bp0, bp - bp0);
        break;
      }
#if 0
      fputc(0xa8, outf);        /* I know, this violates the spec */
      fputc(8, outf);
      for (k = 7; k >= 0; k--)
        fputc(0xff & (keyid >> (k << 3)), outf);
#endif
      fputc(ctb, outf);
      fputc(j >> 8, outf);
      fputc(j & 0xff, outf);
      fwrite(bp0, 1, j, outf);

      fprintf(listf, "%s %016qX %s\n", sp, keyid, xp);
      break;
    case 13:                   /* uid - should match */
#if 0
      fputc(ctb, outf);
      fputc(j >> 8, outf);
      fputc(j & 0xff, outf);
      fwrite(bp0, 1, j, outf);
#endif
      fprintf(listf, "    %s\n", bp);
      continue;
#if 0
    case 2:                    /* sig */
      fprintf(listf, "sig\n");
      continue;
    case 12:                   /* validity */
      fprintf(listf, "val %02x\n", *bp);
      continue;
    default:
      fprintf(listf, "??? %d\n", t);
#endif
    }
    if (bp0)
      free(bp0);

  }
}

/*------------------------------------*/
const unsigned long crc24t[256] =
{
0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC, 0x9F7F17,
0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23, 0xB8B2D5, 0x3EFE2E,
0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868, 0xD0E493, 0xDC7D65, 0x5A319E,
0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646, 0xF72951, 0x7165AA, 0x7DFC5C, 0xFBB0A7,
0x0CD1E9, 0x8A9D12, 0x8604E4, 0x00481F, 0x9F3708, 0x197BF3, 0x15E205, 0x93AEFE,
0xAD50D0, 0x2B1C2B, 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C, 0x322FC7,
0xC99F60, 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C, 0x56E077,
0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5, 0xF7614E,
0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8, 0x00903E, 0x86DCC5,
0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A, 0xAD88F1, 0xA11107, 0x275DFC,
0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD, 0x4F0BBA, 0xC94741, 0xC5DEB7, 0x43924C,
0x7D6C62, 0xFB2099, 0xF7B96F, 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E, 0xE21375,
0x15723B, 0x933EC0, 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7, 0x8A0D2C,
0xB4F302, 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE, 0x2B8C15,
0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E, 0x4F43A5,
0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791, 0x688E67, 0xEEC29C,
0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145, 0x26EDBE, 0x2A7448, 0xAC38B3,
0x92C69D, 0x148A66, 0x181390, 0x9E5F6B, 0x01207C, 0x876C87, 0x8BF571, 0x0DB98A,
0xF6092D, 0x7045D6, 0x7CDC20, 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1, 0x69763A,
0x578814, 0xD1C4EF, 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8, 0xC8F703,
0x3F964D, 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1, 0xA0E95A,
0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498, 0x016863,
0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE, 0xE3EB28, 0x65A7D3,
0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C, 0x4EF3E7, 0x426A11, 0xC426EA,
0x2AE476, 0xACA88D, 0xA0317B, 0x267D80, 0xB90297, 0x3F4E6C, 0x33D79A, 0xB59B61,
0x8B654F, 0x0D29B4, 0x01B042, 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3, 0x141A58,
0xEFAAFF, 0x69E604, 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913, 0x70D5E8,
0x4E2BC6, 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A, 0xD154D1,
0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673, 0xB94A88,
0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC, 0x9E874A, 0x18CBB1,
0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7, 0xF6D10C, 0xFA48FA, 0x7C0401,
0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9, 0xD11CCE, 0x575035, 0x5BC9C3, 0xDD8538,
};

const unsigned char mtab[66] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=-";

void arm3x4(unsigned char *in, unsigned char *out)
{
  out[0] = mtab[in[0] >> 2];
  out[1] = mtab[((in[0] << 4) & 0x30) | ((in[1] >> 4) & 0x0f)];
  out[2] = mtab[((in[1] << 2) & 0x3c) | ((in[2] >> 6) & 0x03)];
  out[3] = mtab[in[2] & 0x3f];
}

/*-------------------------*/
int armor(FILE * ifd, FILE * ofd, char *cert)
{
  unsigned char inb[48], outb[65], *in = inb, *out = outb;
  int i = 0, j, q = 0;
  unsigned long ccrc = 0xB704CE;

  fprintf(ofd, "-----BEGIN PGP %s KEY BLOCK-----\n\n", cert);
  for (;;) {
    if (!q) {
      out = outb, in = inb;
      memset(in, 0, 48);
      if (0 >= (i = fread(in, 1, 48, ifd)))
        break;
      for (j = 0; j < i; j++)
        ccrc = ccrc << 8 ^ crc24t[0xff & ((ccrc >> 16) ^ in[j])];
    }
    arm3x4(in, out);
    if (i < 3)
      break;
    q += 4, i -= 3;
    if (q == 64) {
      out[4] = '\n', out[5] = 0;
      fputs(outb, ofd);
      q = 0;
    }
    if (q && !i)
      break;
    out += 4, in += 3;
  }
  if (i == 1)
    out[2] = '=';
  if (i)
    out[3] = '=', q = 4;
  if (q) {
    out[4] = '\n', out[5] = 0;
    fputs(outb, ofd);
  }
  fputc('=', ofd);
  in[0] = ccrc >> 16, in[1] = ccrc >> 8, in[2] = ccrc;
  arm3x4(in, out);
  out[4] = '\n', out[5] = 0;
  fputs(out, ofd);
  fprintf(ofd, "-----END PGP %s-----\n", cert);
  return 0;
}

/*------------------------------------*/
int main(int argc, char *argv[])
{
  FILE *outf = stdout, *ringf, *ascf, *listf;

  outf = tmpfile();

  listf = fopen("keylist.asc", "w");

  ringf = fopen("pubring.pkr", "rb");
  if (!ringf)
    return -3;                  /* no keyring */
  ringout(ringf, outf, listf);
  fclose(ringf);

  rewind(outf);
  ascf = fopen("pubring.asc", "w");
  armor(outf, ascf, "PUBLIC");
  fclose(ascf);
  fclose(outf);

  outf = tmpfile();
  ringf = fopen("secring.skr", "rb");
  if (!ringf)
    return -3;                  /* no keyring */

  fprintf(listf, "====================\n");
  fprintf(listf, "*** SECRET RING ***\n");

  ringout(ringf, outf, listf);
  fclose(ringf);
  rewind(outf);
  ascf = fopen("secring.asc", "w");
  armor(outf, ascf, "PRIVATE");
  fclose(ascf);
  fclose(outf);

}
