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

#include <sha.h>
#include <rsa.h>

#include "libpgp5.h"

#define RBSIZ 10000

int main(int argc, char *argv[])
{
  DSA *dsakey;
  DH *dhkey;
#ifdef RSAKEY
  RSA *rsakey;
#endif
  unsigned char hctx[1024];

  unsigned char dbuf[RBSIZ];
  unsigned char passwdhash[256], *pp = NULL;
  unsigned char xch = 0, hash[20], mdck[2];
  unsigned long long keyid = 0LL;
  int i, j, k, ll = 0, neof = 0, stype = 0, opflg = 0;
  int halg = 2, sigalg = 0x11, dosig = 0;
  extern char *optarg;
  FILE *inf = stdin, *outf = stdout;
  FILE *sigfile = NULL;

  memset(passwdhash, 0, 16);
  while ((i = getopt(argc, argv, "i:o:t:k:p:r:s:a:A:v1")) != -1) {
    if (i == 'p') {             /* password */
      pp = passwdhash;
      strcpy(passwdhash, optarg);
    } else if (i == 'k')
      sscanf(optarg, "%qx", &keyid);
    else if (i == 'v')
      dosig = 1;
    else if (i == 'r')
      setkeyring5(optarg);
    else if (i == 't')
      stype = atoi(optarg);
    else if (i == 'a')
      halg = atoi(optarg);
    else if (i == 'A')
      sigalg = atoi(optarg);
    else if (i == 'i') {
      inf = fopen(optarg, "rb");
      if (inf == NULL)
        exit(-1);
    } else if (i == 'o') {
      outf = fopen(optarg, "wb");
      if (outf == NULL)
        exit(-1);
    } else if (i == '1')
      opflg++;
    else if (i == 's') {
      if (NULL == (sigfile = fopen(optarg, "rb")))
        exit(-1);
    } else {
      fprintf(stderr, "usage: pgp5sign -k DSSkeyid [-r ringfile] [-p pp] "
              "[-t type] [-1] [-Aa alg]\n"
              "\n types are: 0 binary, 1 text, 17-19 pk+uid, 24 subkey, etc."
              "or pgp5sstr -d [-s detatched.sig] [-r ring] <file\n");
      exit(-1);
    }
  }
/* check signatures */
  if (sigfile) {
    i = sigchk5(inf, sigfile);
    fprintf(stderr, "%s\n", i ? "BAD SIGNATURE" : "Good signature");
    fclose(sigfile);
    return i;
  } else if (dosig) {
    /* bypass sig prefix if any - info in the 1pass sig header is
       duplicated later except for the nesting bit */
    while (!feof(inf) && (xch != 0xcb)) {
      xch = fgetc(inf);
      neof = 0;
      ll = 0xff & fgetc(inf);
      if (ll >= 0xc0) {
        if (ll >= 0xe0)
          neof = 1, ll = 1 << (ll & 31);  /* noteof */
        else {
          ll = ((ll & 31) << 8) + 192;
          ll += 0xff & fgetc(inf);
        }
      }
      if (xch != 0xcb && xch != 0xc8) {
        while (ll) {
          i = ll > RBSIZ ? RBSIZ : ll;
          ll -= i;
          fread(dbuf, 1, i, inf);
        }
        if (xch == 0xc4)        /* one pass sig header */
          halg = dbuf[2];
      } else
        break;
    }
    /* do only literals or compressed */
    if (xch != 0xcb && xch != 0xc8)
      exit(-1);
    /* literal - bypass stuff */
    if (xch == 0xcb) {
      fread(dbuf, 1, 2, inf);
      ll -= dbuf[1] + 6;
      fread(dbuf, 1, dbuf[1] + 4, inf);
    } else
      fputc(0xa3, outf);        /* old style packet ID */
    /* ADD: check halg is valid */
    /* pass and hash */
    hashinit(halg, hctx);
    while (ll) {
      i = ll > RBSIZ ? RBSIZ : ll;
      ll -= i;
      fread(dbuf, 1, i, inf);
      fwrite(dbuf, 1, i, outf);
      hashupdate(halg, hctx, dbuf, i);
      if (!ll && neof) {
        neof = 0;
        ll = 0xff & fgetc(inf);
        if (ll >= 0xc0) {
          if (ll >= 0xe0)
            neof = 1, ll = 1 << (ll & 31);  /* noteof */
          else {
            ll = ((ll & 31) << 8) + 192;
            ll += 0xff & fgetc(inf);
          }
        }
      }
    }
    ll = fgetc(inf);
    if (ll != 0xc2 && ll != 0x89)  /* 89 should be ll & 0xfc == 0x88  */
      return 0;                 /* 0xc2/0x89 - attached 1 pass sig */
    if (ll == 0xc2)
      ll = fgetc(inf);          /* len - 1 byte length (hopefully) */
    else
      ll = fgetc(inf) * 256, ll += fgetc(inf);
    fgetc(inf);                 /* 3 - alg */
    i = fgetc(inf);             /* 5 - x mat len */
    fread(dbuf, 1, i, inf);
    hashupdate(halg, hctx, dbuf, i);
    hashfinal(halg, hash, hctx);
    fread(dbuf, 1, 8, inf);
    for (j = 0; j < 8; j++)
      keyid = (keyid << 8) + dbuf[j];
    sigalg = fgetc(inf);

    if (fgetc(inf) != halg)
      return -2;

    fread(mdck, 1, 2, inf);
    if (mdck[0] != hash[0] || mdck[1] != hash[1]) {
      fprintf(stderr, "BAD SIGNATURE HASH\n");
      return -1;
    }

    i = ll - 14 - i;            /* remainder length */
    fread(dbuf, 1, i, inf);

    k = sigchk5x(dbuf, sigalg, hash, halg, keyid);
    fprintf(stderr, k ? "BAD SIGNATURE\n" : "Good Signature\n");
    return k;
  }
/* make signature */
  if (sigalg == 0x11)
    dsakey = DSA_new(),
      k = getkey5(NULL, &dsakey, pp, &keyid);
#ifndef NOELGSIG
  else if (sigalg == 0x10)
    dhkey = DH_new(),
      k = getkey5(&dhkey, NULL, pp, &keyid);
#endif
#ifdef RSAKEY
  else if (sigalg == 1 || sigalg == 3)
    rsakey = RSA_new(),
      k = getkey2(&rsakey, NULL, pp, &keyid);
#endif
  else
    return -3;
  if (k) {
    fprintf(stderr, "No Secret key found\n");
    return -5;
  }
  if (opflg) {
    fputc(0xc4, outf);
    fputc(0x0d, outf);
    fputc(0x03, outf);          /* ver */
    fputc(stype, outf);
    fputc(halg, outf);          /* hash alg */
    fputc(sigalg, outf);        /* sigalg */
    for (i = 0; i < 8; i++)     /* keyid */
      fputc(0xff & (keyid >> (56 - 8 * i)), outf);
    fputc(0x01, outf);          /* nested flag */
    return 0;
  }
  if (sigalg == 0x11)
    i = sigmak5(dsakey, inf, outf, keyid, stype, halg, sigalg);
#ifndef NOELGSIG
  else if (sigalg == 0x10)
    i = sigmak5(dhkey, inf, outf, keyid, stype, halg, sigalg);
#endif
#ifdef RSAKEY
  else if (sigalg == 1 || sigalg == 3)
    i = sigmak5(rsakey, inf, outf, keyid, stype, halg, sigalg);
#endif
  return 0;
}
