#include <time.h>
#include "libopgp.h"
#include <rsa.h>
#include <dh.h>
#include <dsa.h>

#define MAXSIGM 512
/*--------------------------------------------------*/
int PGP_sigmk(void *seckey, void *hctx, char *sigbuf,
              keyid_t keyid, u8_t stype, u8_t halg, u8_t salg, u8_t * sv4)
{
  u8_t dbuf[MAXSIGM], xmat[10], hash[100], mdck[2];
  char *bp;
  u32_t i, j = 0, k, s4l = 0;
  BN_CTX *ctx = BN_CTX_new();
  BIGNUM *r = BN_new(), *u1 = BN_new();
  BIGNUM *t1 = BN_new(), *t2 = BN_new(), *u2 = BN_new();

  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;
    PGP_hblk(hctx, &xmat[2], xmat[1]);
  } 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++ = 16;
      *bp++ = 5, *bp++ = 0x82;  /* timestamp */
      *bp++ = k >> 24, *bp++ = k >> 16, *bp++ = k >> 8, *bp++ = k;
      *bp++ = 9, *bp++ = 0x90;  /* keyid */
      for (k = 0; k < 8; k++)
        *bp++ = keyid >> (56 - 8 * k);
      *bp++ = 0, *bp++ = 0;     /* unhashed size */
    }
    /* 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;
    k = s4l + 4;
    *bp++ = k >> 24, *bp++ = k >> 16, *bp++ = k >> 8, *bp++ = k;

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

  if (salg == 17) {
    BN_bin2bn(hash, PGP_hlen[halg], t2);
    BN_rand(u2, 160, 1, 0);     /* random */
    /* Compute r = (g^u2 mod p) mod q */
    BN_mod_exp(u1, ((DSA *) seckey)->g, u2, ((DSA *) seckey)->p, ctx);
    BN_mod(r, u1, ((DSA *) seckey)->q, ctx);
    /* Compute  u1 = inv(u2) (t2 + xr) mod q */
    BN_mul(u1, ((DSA *) seckey)->priv_key, r);
    BN_add(u1, u1, t2);
    t1 = BN_mod_inverse(u2, ((DSA *) seckey)->q, ctx);
    BN_mod_mul(u1, u1, t1, ((DSA *) seckey)->q, ctx);
    DSA_free(((DSA *) seckey));
  } else
    j = PGP_hder(halg, hash);
  if (salg == 16 || salg == 20) {
    memset(dbuf, 0xff, MAXSIGM);
    dbuf[0] = 0, dbuf[1] = 1;
    k = BN_num_bytes(((DH *) seckey)->p);
    dbuf[k - j - 1] = 0;
    memcpy(&dbuf[k - j], hash, j);
    BN_bin2bn(dbuf, k, t2);

    BN_sub(t1, ((DH *) seckey)->p, BN_value_one());
    j = BN_num_bits(((DH *) seckey)->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, ((DH *) seckey)->g, u2, ((DH *) seckey)->p, ctx);
    BN_mod_mul(u1, ((DH *) seckey)->priv_key, r, t1, ctx);
    DH_free(((DH *) seckey));
    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);
  } else if (salg == 1 || salg == 3) {
    j = RSA_private_encrypt(j, hash, dbuf, ((RSA *) seckey), RSA_PKCS1_PADDING);
    RSA_free(((RSA *) seckey));
    while (dbuf[0] == 0)
      memmove(dbuf, &dbuf[1], j--);
    BN_bin2bn(dbuf, j, r);
    BN_set_word(u1, 0);
  } else if (salg != 17)
    return -1;
  BN_CTX_free(ctx), BN_clear_free(u2), BN_clear_free(t1), BN_clear_free(t2);

  bp = sigbuf;
  j = BN_num_bits(r), i = BN_num_bits(u1);  /* calc length */
  k = 6 + (j + 7) / 8 + (i + 7) / 8 + ((salg == 1 || salg == 3) ? 2 : 4);
  if (!sv4) {
    k += 13;
    *sigbuf++ = 0x89, *sigbuf++ = k >> 8, *sigbuf++ = k;
    memcpy(sigbuf, xmat, 7);
    sigbuf += 7;
    for (k = 0; k < 8; k++)     /* keyid */
      *sigbuf++ = keyid >> (56 - 8 * k);
    *sigbuf++ = salg, *sigbuf++ = halg;
  } else {
    k += s4l;
    *sigbuf++ = 0x89, *sigbuf++ = k >> 8, *sigbuf++ = k;
    memcpy(sigbuf, xmat, 4);
    sigbuf += 4;
    memcpy(sigbuf, sv4, s4l);
    sigbuf += s4l;
  }
  *sigbuf++ = mdck[0], *sigbuf++ = mdck[1];
  *sigbuf++ = j >> 8, *sigbuf++ = j;
  sigbuf += BN_bn2bin(r, sigbuf);
  if (salg != 1 && salg != 3) {
    *sigbuf++ = i >> 8, *sigbuf++ = i;
    sigbuf += BN_bn2bin(u1, sigbuf);
  }
  BN_clear_free(r), BN_clear_free(u1);
  k = sigbuf - bp;
  return k;
}
