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

/*--------------------------------------------------*/
int sigmak5(void *privkey, FILE * chkfile, FILE * sigfile,
            unsigned long long keyid, unsigned int stype,
            unsigned int halg, unsigned int salg, unsigned char *sv4)
{
  unsigned char dbuf[4096], xmat[256], hash[1024], mdck[2], *bp;
  unsigned long i, j, k;
  unsigned int s4l = 0;
  void *hctx;
  BN_CTX *ctx = BN_CTX_new();
  BIGNUM *r = BN_new();
  BIGNUM *t1 = BN_new(), *t2 = BN_new();
  BIGNUM *u1 = BN_new(), *u2 = BN_new();

  /* ADD: check halg is valid */
  hctx = hashinit(halg);
  while (!feof(chkfile))        /* do message digest */
    if ((k = fread(dbuf, 1, 4096, chkfile)))
      hashupdate(hctx, dbuf, k);

  k = time(NULL);               /* timestamp */

  if (!sv4) {
    /* version, xmatlen, file type */
    bp = xmat;
    *bp++ = 3, *bp++ = 5, *bp++ = stype;
    *bp++ = k >> 24, *bp++ = k >> 16, *bp++ = k >> 8, *bp++ = k;
    hashupdate(hctx, &xmat[2], 5);
  } else {
    if (sv4[0] == 0xff && sv4[1] == 0xff && sv4[2] == 0xff && sv4[3] == 0xff) {
      /* if null, add in time and keyid */
      bp = sv4;
      *bp++ = 0, *bp++ = 6, *bp++ = 5, *bp++ = 0x82;
      *bp++ = k >> 24, *bp++ = k >> 16, *bp++ = k >> 8, *bp++ = k;
      *bp++ = 0, *bp++ = 10, *bp++ = 9, *bp++ = 0x90;
      for (k = 0; k < 8; k++)   /* keyid */
        *bp++ = keyid >> (56 - 8 * k);
    }
    /* version, ftype, sig, hash */
    bp = xmat;
    *bp++ = 4, *bp++ = stype, *bp++ = salg, *bp++ = halg;
    s4l = sv4[0] * 256 + 2 + sv4[1];  /* hashed sig data */
    i = sv4[s4l] * 256 + 2 + sv4[s4l + 1];  /* unhashed sig data */

    *bp++ = 4, *bp++ = 0xff;
    s4l += 4;
    *bp++ = s4l >> 24, *bp++ = s4l >> 16, *bp++ = s4l >> 8, *bp++ = s4l;
    s4l -= 4;

    hashupdate(hctx, xmat, 4);
    hashupdate(hctx, sv4, s4l);
    hashupdate(hctx, &xmat[4], 6);
    s4l += i;
  }
  hashfinal(hash, hctx);
  memcpy(mdck, hash, 2);

  if (salg == 0x11) {
    DSA *dsakey = (DSA *) privkey;

    BN_bin2bn(hash, hashlen[halg], t2);

    BN_rand(u2, 160, 1, 0);     /* random */
    /* Compute r = (g^u2 mod p) mod q */
    BN_mod_exp(u1, dsakey->g, u2, dsakey->p, ctx);
    BN_mod(r, u1, dsakey->q, ctx);

    /* Compute  u1 = inv(u2) (t2 + xr) mod q */
    BN_mul(u1, dsakey->priv_key, r);
    BN_add(u1, u1, t2);
    t1 = BN_mod_inverse(u2, dsakey->q, ctx);
    BN_mod_mul(u1, u1, t1, dsakey->q, ctx);

    DSA_free(dsakey);
#ifndef NOELGSIG
  } else if (salg == 0x10) {
    DH *dhkey = (DH *) privkey;

    /* prefix the DER stuff */
    memmove(&hash[hashDERlen[halg]], hash, hashlen[halg]);
    memcpy(hash, hashDER[halg], hashDERlen[halg]);

    memset(dbuf, 0xff, 1024);
    dbuf[0] = 0;
    dbuf[1] = 1;
    k = BN_num_bytes(dhkey->p);
    j = hashlen[halg] + hashDERlen[halg];
    dbuf[k - j - 1] = 0;
    memcpy(&dbuf[k - j], hash, j);
    BN_bin2bn(dbuf, k, t2);

    BN_sub(t1, dhkey->p, BN_value_one());
    j = BN_num_bits(dhkey->p) - 1;
    do {                        /* random u2 */
      BN_rand(u2, j, 1, 1);
      BN_gcd(u1, u2, t1, ctx);
    } while (BN_ucmp(u1, BN_value_one()));
    BN_mod_exp(r, dhkey->g, u2, dhkey->p, ctx);
    BN_mod_mul(u1, dhkey->priv_key, r, t1, ctx);
    DH_free(dhkey);
    if (BN_cmp(t2, u1) < 0)
      BN_add(t2, t2, t1);
    BN_sub(t2, t2, u1);
    BN_mod_mul(u1, t2, BN_mod_inverse(u2, t1, ctx), t1, ctx);
#endif
#ifndef NO_RSA
  } else if (salg == 1 || salg == 3) {
    RSA *rsakey = (RSA *) privkey;
    /* prefix the DER stuff */
    memmove(&hash[hashDERlen[halg]], hash, hashlen[halg]);
    memcpy(hash, hashDER[halg], hashDERlen[halg]);
    j = RSA_private_encrypt(hashDERlen[halg] + hashlen[halg], hash,
                            dbuf, rsakey, RSA_PKCS1_PADDING);
    RSA_free(rsakey);
    while (dbuf[0] == 0)
      memmove(dbuf, &dbuf[1], j--);
    BN_bin2bn(dbuf, j, r);
    BN_set_word(u1, 0);
#endif
  } else
    return -1;

/* output r, u1 as PGP mpi numbers */
  j = BN_num_bits(r);
  i = BN_num_bits(u1);
  k = 6 + (j + 7) / 8 + (i + 7) / 8;
  if (salg == 1 || salg == 3)
    k -= 2;
  if (!sv4)
    k += 17;
  else
    k += 4 + s4l;
  if (0 && sv4) {
    fputc(0xc2, sigfile);
    fputc(k, sigfile);
  } else {
    fputc(0x89, sigfile);
    fputc(k >> 8, sigfile), fputc(k, sigfile);  /* packet length */
  }

  if (!sv4) {
    fwrite(xmat, 1, 7, sigfile);  /* bin/text and timestamp */
    for (k = 0; k < 8; k++)     /* keyid */
      fputc(0xff & (keyid >> (56 - 8 * k)), sigfile);
    fputc(salg, sigfile);       /* sig alg */
    fputc(halg, sigfile);       /* hash alg */
  } else {
    fwrite(xmat, 1, 4, sigfile);  /* v4 header */
    fwrite(sv4, 1, s4l, sigfile);
  }

  fwrite(mdck, 1, 2, sigfile);  /* hash quick check */
  fputc(j >> 8, sigfile), fputc(j, sigfile);
  BN_bn2bin(r, dbuf);
  fwrite(dbuf, 1, (j + 7) / 8, sigfile);
  if (salg != 1) {
    fputc(i >> 8, sigfile), fputc(i, sigfile);
    BN_bn2bin(u1, dbuf);
    fwrite(dbuf, 1, (i + 7) / 8, sigfile);
  }
  BN_CTX_free(ctx), BN_clear_free(r);
  BN_clear_free(u1), BN_clear_free(u2);
  BN_clear_free(t1), BN_clear_free(t2);

  return 0;
}
