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

#include "libpgp5.h"

/*--------------------------------------------------*/
/* do DSA verify function */

int checkdsa(BIGNUM * r, BIGNUM * u1, DSA * dsakey,
             unsigned char *hash, unsigned int hashlen)
{
  BN_CTX *ctx = BN_CTX_new();
  BIGNUM *t1 = BN_new(), *t2 = BN_new(), *u2 = NULL;
  int ret;

  u2 = BN_mod_inverse(u1, dsakey->q, ctx);
  BN_bin2bn(hash, hashlen, u1);
  BN_mod_mul(u1, u1, u2, dsakey->q, ctx);
  BN_mod_mul(u2, r, u2, dsakey->q, ctx);
  BN_mod_exp(t1, dsakey->g, u1, dsakey->p, ctx);
  BN_mod_exp(t2, dsakey->pub_key, u2, dsakey->p, ctx);
  BN_mod_mul(u1, t1, t2, dsakey->p, ctx);
  BN_mod(u1, u1, dsakey->q, ctx);
  DSA_free(dsakey);

  ret = BN_ucmp(u1, r);
  BN_CTX_free(ctx), BN_clear_free(u2), BN_clear_free(t1);
  BN_clear_free(t2), BN_clear_free(u1), BN_clear_free(r);
  return ret;
}

#ifndef NOELGSIG
/*--------------------------------------------------*/
/* do ElG verify function */

int checkelg(BIGNUM * a, BIGNUM * b, DH * dhkey,
             unsigned char *hash, unsigned int hashlen)
{
  BN_CTX *ctx = BN_CTX_new();
  BIGNUM *t = BN_new(), *u = BN_new();
  unsigned char hbuf[1024];
  unsigned int k;

  BN_sub(t, dhkey->p, BN_value_one());
  BN_gcd(u, a, t, ctx);
  BN_set_word(t, 2);
  if (BN_ucmp(u, t) > 0) {
    BN_print_fp(stderr, u);
    fprintf(stderr, " = gcd(p-1,r)\n");
    fprintf(stderr, "WARNING: Weak El Gamal Signature\n");
    exit(-1);
  }
  memset(hbuf, 0xff, 1024);
  hbuf[0] = 0;
  hbuf[1] = 1;
  k = BN_num_bytes(dhkey->p);
  hbuf[k - hashlen - 1] = 0;
  memcpy(&hbuf[k - hashlen], hash, hashlen);
  BN_bin2bn(hbuf, k, u);

  BN_mod_exp(t, dhkey->g, u, dhkey->p, ctx);

  BN_mod_exp(u, a, b, dhkey->p, ctx);
  BN_mod_exp(b, dhkey->pub_key, a, dhkey->p, ctx);
  BN_mod_mul(a, b, u, dhkey->p, ctx);
  DH_free(dhkey);

  k = BN_ucmp(t, a);
  BN_CTX_free(ctx), BN_clear_free(u), BN_clear_free(t);
  BN_clear_free(a), BN_clear_free(b);
  return k;
}
#endif

#ifndef NO_RSA
/*--------------------------------------------------*/
/* do RSA verify function */

int checkrsa(BIGNUM * a, RSA * key, unsigned char *hash, unsigned int hashlen)
{
  unsigned char dbuf[1024];
  int j, ll;

  j = BN_bn2bin(a, dbuf);
  ll = BN_num_bytes(key->n);
  while (j < ll)
    memmove(&dbuf[1], dbuf, j++), dbuf[0] = 0;
  j = RSA_public_decrypt(ll, dbuf, dbuf, key, RSA_PKCS1_PADDING);
  RSA_free(key);
  BN_clear_free(a);
  return (j != hashlen || memcmp(dbuf, hash, j));
}
#endif

/*--------------------------------------------------*/
int sigchk5(unsigned char *sigp, void *hctx)
{
  unsigned char xmat[1024], *bp;
  unsigned long long keyid = 0LL;
  unsigned int j, xmln = 0, sigalg = 0x11, halg = 0;
  int k;
  BIGNUM *r, *u1 = NULL;
  DSA *dsakey;
  DH *dhkey;
#ifndef NO_RSA
  RSA *rsakey;

#endif

  j = *sigp++;
  if (j == 3) {
    xmln = *sigp++;             /* extramd length - fixed on old ver */
    memcpy(xmat, sigp, xmln);
    sigp += xmln;
    for (j = 0; j < 8; j++)
      keyid = (keyid << 8) + *sigp++;
    sigalg = *sigp++;
    halg = *sigp++;             /* hash algorithm */
  } else if (j == 4) {
    xmat[0] = 4;
    memcpy(&xmat[1], sigp, 5);
    sigp += 5;
    sigalg = xmat[2];
    halg = xmat[3];
    j = xmat[4] * 256 + xmat[5];
    memcpy(&xmat[6], sigp, j);
    sigp += j;
    /* { chunklen / subpacket type 2-sigtime, 9-keyexptime, 11-prefs } */
    xmln = 6 + j;
    k = *sigp++ * 256, k += *sigp++;
    memcpy(&xmat[xmln], sigp, k);  /* append unhashed subpackets */
    sigp += k;
    j += k;
    bp = &xmat[6];
    while (j > 0) {
      if ((bp[1] & 0x7f) == 0x10)
        for (k = 2; k < 10; k++)
          keyid = (keyid << 8) + bp[k];
      j -= *bp + 1;
      bp += *bp + 1;
    }
    if (j)
      return -2;
    bp = &xmat[xmln];           /* reset to end of original xmat */
    *bp++ = 4, *bp++ = 0xff;
    *bp++ = xmln >> 24, *bp++ = xmln >> 16, *bp++ = xmln >> 8, *bp++ = xmln;
    xmln += 6;
  }
  hashupdate(hctx, xmat, xmln);
  hashfinal(xmat, hctx);

  if (*sigp++ != xmat[0] || *sigp++ != xmat[1])
    return -1;
  if (sigalg != 0x11 && sigalg != 0x10 && sigalg != 1 && sigalg != 3)
    return -3;
  if (sigalg == 0x11)
    dsakey = DSA_new(),
      k = getkey5(NULL, &dsakey, NULL, &keyid);
#ifndef NOELGSIG
  else if (sigalg == 0x10)
    dhkey = DH_new(),
      k = getkey5(&dhkey, NULL, NULL, &keyid);
#endif
#ifndef NO_RSA
  else if (sigalg == 1 || sigalg == 3)
    rsakey = RSA_new(),
      k = getkey5((DH **) & rsakey, (DSA **) & rsakey, NULL, &keyid);
#endif
  else
    return -4;
  if (k)
    return -5;

  r = pgp2BN5(&sigp);
  if (sigalg != 1 && sigalg != 3)
    u1 = pgp2BN5(&sigp);
  if (sigalg == 0x11)
    k = checkdsa(r, u1, dsakey, xmat, hashlen[halg]);
  else {
    /* prefix the DER stuff */
    memmove(&xmat[hashDERlen[halg]], xmat, hashlen[halg]);
    memcpy(xmat, hashDER[halg], hashDERlen[halg]);
#ifndef NOELGSIG
    if (sigalg == 0x10)
      k = checkelg(r, u1, dhkey, xmat, hashlen[halg] + hashDERlen[halg]);
#endif
#ifndef NO_RSA
    if (sigalg == 1 || sigalg == 3)
      k = checkrsa(r, rsakey, xmat, hashlen[halg] + hashDERlen[halg]);
#endif
  }
  return k;
}
