/*
 * Copyright (c) 1997 Stanford University
 *
 * The use of this software for revenue-generating purposes may require a
 * license from the owners of the underlying intellectual property.
 *
 * Within that constraint, permission to use, copy, modify, and distribute
 * this software and its documentation for any purpose is hereby granted
 * without fee, provided that the above copyright notices and this permission
 * notice appear in all copies of the software and related documentation.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
 * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdio.h>
#include <sys/types.h>

#include "config.h"

#ifdef CRYPTOLIB
# include "libcrypt.h"
typedef BigInt BigInteger;
#elif GNU_MP
# include "gmp.h"
typedef MP_INT * BigInteger;
#else
no math library specified!
#endif
#define MATH_PRIV

#include "t_defines.h"
#include "t_pwd.h"

/* Math library interface stubs */

BigInteger
BigIntegerFromInt(n)
     unsigned int n;
{
#ifdef CRYPTOLIB
  return bigInit(n);
#elif GNU_MP
  BigInteger rv;

  rv = (BigInteger) malloc(sizeof(MP_INT));
  mpz_init_set_ui(rv, n);
  return rv;
#endif
}

BigInteger
BigIntegerFromBytes(bytes, length)
     unsigned char * bytes;
     int length;
{
#ifdef CRYPTOLIB
  BigInteger rv, t;
  int i, n;

  rv = bigInit(0);
  if(length % 4 == 0)
    RSA_bufToBig(bytes, length, rv);
  else {	/* Wouldn't need this if cryptolib behaved better */
    i = length & 0x3;
    if(length > i)
      RSA_bufToBig(bytes + i, length - i, rv);
    for(n = 0; i > 0; --i)
      n = (n << 8) | *bytes++;
    t = bigInit(n);
    bigLeftShift(t, (length & ~0x3) << 3, t);
    bigAdd(rv, t, rv);
    freeBignum(t);
  }
  return rv;
#elif GNU_MP
  BigInteger rv;
  char hexbuf[MAXHEXPARAMLEN];
  char *p;

  rv = (BigInteger) malloc(sizeof(MP_INT));
  mpz_init_set_str(rv, t_tohex(hexbuf, bytes, length), 16);
  for(p = hexbuf; *p; ++p)
    *p = '\0';
  return rv;
#endif
}

int
BigIntegerToBytes(src, dest)
     BigInteger src;
     unsigned char * dest;
{
#ifdef CRYPTOLIB
  int i;

  trim(src);
  i = bigBytes(src);
  RSA_bigToBuf(src, i, dest);
  return i;
#elif GNU_MP
  char hexbuf[MAXHEXPARAMLEN];
  char * p;
  int r;

  mpz_get_str(hexbuf, 16, src);
  r = t_fromhex(dest, hexbuf);
  for(p = hexbuf; *p; ++p)
    *p = '\0';
  return r;
#endif
}

void
BigIntegerToHex(src, dest)
     BigInteger src;
     char * dest;
{
#ifdef CRYPTOLIB
  trim(src);
  bigsprint(src, dest);
#elif GNU_MP
  mpz_get_str(dest, 16, src);
#endif
}

int
BigIntegerBitLen(b)
     BigInteger b;
{
#ifdef CRYPTOLIB
  return bigBits(b);
#elif GNU_MP
  return mpz_sizeinbase(b, 2);
#endif
}

int
BigIntegerCmp(c1, c2)
     BigInteger c1, c2;
{
#ifdef CRYPTOLIB
  return bigCompare(c1, c2);
#elif GNU_MP
  return mpz_cmp(c1, c2);
#endif
}

int
BigIntegerCmpInt(c1, c2)
     BigInteger c1;
     unsigned int c2;
{
#ifdef CRYPTOLIB
  BigInteger t;
  int rv;

  t = bigInit(c2);
  rv = bigCompare(c1, t);
  freeBignum(t);
  return rv;
#elif GNU_MP
  return mpz_cmp_ui(c1, c2);
#endif
}

void
BigIntegerLShift(result, x, bits)
     BigInteger result, x;
     unsigned int bits;
{
#ifdef CRYPTOLIB
  bigLeftShift(x, bits, result);
#elif GNU_MP
  mpz_mul_2exp(result, x, bits);
#endif
}

void
BigIntegerAdd(result, a1, a2)
     BigInteger result, a1, a2;
{
#ifdef CRYPTOLIB
  bigAdd(a1, a2, result);
#elif GNU_MP
  mpz_add(result, a1, a2);
#endif
}

void
BigIntegerAddInt(result, a1, a2)
     BigInteger result, a1;
     unsigned int a2;
{
#ifdef CRYPTOLIB
  BigInteger t;

  t = bigInit(a2);
  bigAdd(a1, t, result);
  freeBignum(t);
#elif GNU_MP
  mpz_add_ui(result, a1, a2);
#endif
}

void
BigIntegerSub(result, s1, s2)
     BigInteger result, s1, s2;
{
#ifdef CRYPTOLIB
  bigSubtract(s1, s2, result);
#elif GNU_MP
  mpz_sub(result, s1, s2);
#endif
}

void
BigIntegerSubInt(result, s1, s2)
     BigInteger result, s1;
     unsigned int s2;
{
#ifdef CRYPTOLIB
  BigInteger t;

  t = bigInit(s2);
  bigSubtract(s1, t, result);
  freeBignum(t);
#elif GNU_MP
  mpz_sub_ui(result, s1, s2);
#endif
}

void
BigIntegerMul(result, m1, m2)
     BigInteger result, m1, m2;
{
#ifdef CRYPTOLIB
  bigMultiply(m1, m2, result);
#elif GNU_MP
  mpz_mul(result, m1, m2);
#endif
}

void
BigIntegerMulInt(result, m1, m2)
     BigInteger result, m1;
     unsigned int m2;
{
#ifdef CRYPTOLIB
  BigInteger t;

  t = bigInit(m2);
  bigMultiply(m1, t, result);
  freeBignum(t);
#elif GNU_MP
  mpz_mul_ui(result, m1, m2);
#endif
}

void
BigIntegerDivInt(result, d, m)
     BigInteger result, d;
     unsigned int m;
{
#ifdef CRYPTOLIB
  BigInteger t, u;

  t = bigInit(m);
  u = bigInit(0);
  bigDivide(d, t, result, u);
  freeBignum(t);
  freeBignum(u);
#elif GNU_MP
# ifdef GMP2
  mpz_fdiv_q_ui(result, d, m);
# else
  mpz_div_ui(result, d, m);
# endif
#endif
}

void
BigIntegerMod(result, d, m)
     BigInteger result, d, m;
{
#ifdef CRYPTOLIB
  bigMod(d, m, result);
#elif GNU_MP
  mpz_mod(result, d, m);
#endif
}

unsigned int
BigIntegerModInt(d, m)
     BigInteger d;
     unsigned int m;
{
#ifdef CRYPTOLIB
  BigInteger t, u;
  unsigned char r[4];

  t = bigInit(m);
  u = bigInit(0);
  bigMod(d, t, u);
  bigToBuf(u, sizeof(r), r);
  freeBignum(t);
  freeBignum(u);
  return r[0] | (r[1] << 8) | (r[2] << 16) | (r[3] << 24);
#elif GNU_MP
  MP_INT result;
  unsigned int i;

  mpz_init(&result);

/* Define GMP2 if you're using an old gmp.h but want to link against a
 * newer libgmp.a (e.g. 2.0 or later). */

# ifdef GMP2
  mpz_fdiv_r_ui(&result, d, m);
# else
  mpz_mod_ui(&result, d, m);
# endif
  i = mpz_get_ui(&result);
  mpz_clear(&result);
  return i;
#endif
}

void
BigIntegerModMul(r, m1, m2, modulus)
     BigInteger r, m1, m2, modulus;
{
#ifdef CRYPTOLIB
  bigMultiply(m1, m2, r);
  bigMod(r, modulus, r);
#elif GNU_MP
  mpz_mul(r, m1, m2);
  mpz_mod(r, r, modulus);
#endif
}

void
BigIntegerModExp(r, b, e, m)
     BigInteger r, b, e, m;
{
#ifdef CRYPTOLIB
  bigPow(b, e, m, r);
#elif GNU_MP
  mpz_powm(r, b, e, m);
#endif
}

void
BigIntegerModExpInt(r, b, e, m)
     BigInteger r, b;
     unsigned int e;
     BigInteger m;
{
#ifdef CRYPTOLIB
  BigInteger t;

  t = bigInit(e);
  bigPow(b, t, m, r);
  freeBignum(t);
#elif GNU_MP
  mpz_powm_ui(r, b, e, m);
#endif
}

int
BigIntegerCheckPrime(n)
     BigInteger n;
{
#ifdef CRYPTOLIB
  return primeTest(n);
#elif GNU_MP
  return mpz_probab_prime_p(n, 25);
#endif
}

void
BigIntegerFree(b)
     BigInteger b;
{
#ifdef CRYPTOLIB
  freeBignum(b);
#elif GNU_MP
  mpz_clear(b);
  free(b);
#endif
}
