/*
 * Big Number compatibility layer and support API
 * This is a fork of OpenSSL's implementation to provide arbitrary integer
 * precision arithmetic functionality without having to deal with the rest
 * of OpenSSL.
 *
 * There are no plans for any significant future development work and
 * no intention to follow future OpenSSL changes and bug fixes, except for what
 * is absolutely necessary.
 * Also, there is no intention to test or develop this BN layer
 * implementation with anything other than the included OpenSSL-derived API.
 */

/*
 * Copyright (c) 2003-2016
 * Distributed Systems Software.  All rights reserved.
 * See the file BN-LICENSE for redistribution information.
 */

/*
 * Copyright (c) 1997-2007  The Stanford SRP Authentication Project
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 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.
 *
 * Redistributions in source or binary form must retain an intact copy
 * of this copyright notice.
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2016\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: bn.c 2885 2016-05-16 23:48:04Z brachman $";
#endif

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

#include "local.h"
#include "bn.h"

#ifdef OPENSSL_ENGINE
#include "openssl/engine.h"
static ENGINE *default_engine = NULL;
# endif

typedef int (*modexp_meth)(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
						   const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *mctx);
static modexp_meth default_modexp = NULL;

BigInteger
BigIntegerFromInt(unsigned int n)
{
#ifdef OPENSSL
  BIGNUM *a = BN_new();
  if (a)
    BN_set_word(a, n);
  return(a);
#elif defined(CRYPTOLIB)
  return(bigInit(n));
#elif defined(GNU_MP)
  BigInteger rv = (BigInteger) malloc(sizeof(MP_INT));
  if (rv)
    mpz_init_set_ui(rv, n);
  return(rv);
#elif defined(GCRYPT)
  BigInteger rv = gcry_mpi_new(32);
  gcry_mpi_set_ui(rv, n);
  return(rv);
#elif defined(MPI) || defined(TOMMATH)
  BigInteger rv = (BigInteger) malloc(sizeof(mp_int));
  if (rv) {
    mp_init(rv);
    mp_set_int(rv, n);
  }
  return(rv);
#endif
}

BigInteger
BigIntegerFromBytes(const unsigned char * bytes, int length)
{
#ifdef OPENSSL
  BIGNUM *a = BN_new();
  BN_bin2bn(bytes, length, a);
  return a;
#elif defined(CRYPTOLIB)
  BigInteger rv, t;
  int i, n;

  rv = bigInit(0);
  if (rv == NULL)
    return rv;
  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 defined(GNU_MP)
  BigInteger rv = (BigInteger) malloc(sizeof(MP_INT));

# ifdef GMP_IMPEXP
  if (rv) {
    mpz_init(rv);
    mpz_import(rv, length, 1, 1, 1, 0, bytes);
  }
# else
  cstr *hexbuf = cstr_new();

  if (hexbuf) {
    if (rv)
      mpz_init_set_str(rv, t_tohexcstr(hexbuf, bytes, length), 16);
    cstr_clear_free(hexbuf);
  }
# endif /* GMP_IMPEXP */

  return rv;
#elif defined(GCRYPT)
  BigInteger rv;
  gcry_mpi_scan(&rv, GCRYMPI_FMT_USG, bytes, length, NULL);
  return rv;
#elif defined(MPI) || defined(TOMMATH)
  BigInteger rv = (BigInteger) malloc(sizeof(mp_int));
  if (rv) {
    mp_init(rv);
    mp_read_unsigned_bin(rv, (unsigned char *)bytes, length);
  }
  return rv;
#endif
}

/*
 * Return the length, in bytes, of the binary representation of SRC.
 * If PTR is non-NULL, set it to point to the first byte of its binary
 * representation.
 * This is a sketchy way to peek at the raw representation of SRC without having
 * to copy it using BigIntegerToBytes().
 */
int
BigIntegerRawData(BigInteger src, unsigned char **ptr)
{
  int n;

#ifdef OPENSSL
  if (ptr != NULL)
	*ptr = (unsigned char *) &src->d[0];

  n = BN_num_bytes(src);

  return(n);
#else
  return(-1);
#endif
}

/*
 * Convert the absolute value of SRC into big-endian form and store it in DEST,
 * which is DESTLEN bytes long (at least BN_num_bytes(SRC) bytes).
 * Return the number of bytes copied into DEST.
 */
int
BigIntegerToBytes(BigInteger src, unsigned char *dest, int destlen)
{
#ifdef OPENSSL
  return BN_bn2bin(src, dest);
#elif defined(CRYPTOLIB)
  int i, j;
  cstr *rawbuf;

  trim(src);
  i = bigBytes(src);
  j = (bigBits(src) + 7) / 8;
  if (i == j)
    RSA_bigToBuf(src, i, dest);
  else {	/* Wouldn't need this if cryptolib behaved better */
    rawbuf = cstr_new();
    cstr_set_length(rawbuf, i);
    RSA_bigToBuf(src, i, rawbuf->data);
    memcpy(dest, rawbuf->data + (i - j), j);
    cstr_clear_free(rawbuf);
  }
  return j;
#elif defined(GNU_MP)
  size_t r = 0;
# ifdef GMP_IMPEXP
  mpz_export(dest, &r, 1, 1, 1, 0, src);
# else
  cstr *hexbuf = cstr_new();

  if (hexbuf) {
    cstr_set_length(hexbuf, mpz_sizeinbase(src, 16) + 1);
    mpz_get_str(hexbuf->data, 16, src);
    r = t_fromhex(dest, hexbuf->data);
    cstr_clear_free(hexbuf);
  }
# endif
  return r;
#elif defined(GCRYPT)
  size_t r = 0;
  gcry_mpi_print(GCRYMPI_FMT_USG, dest, destlen, &r, src);
  return r;
#elif defined(MPI) || defined(TOMMATH)
  mp_to_unsigned_bin(src, dest);
  return mp_unsigned_bin_size(src);
#endif
}

BigIntegerResult
BigIntegerToCstr(BigInteger x, cstr *out)
{
  int n = BigIntegerByteLen(x);

  if (cstr_set_length(out, n) < 0)
    return(BIG_INTEGER_ERROR);
  if (cstr_set_length(out, BigIntegerToBytes(x, (unsigned char *) out->data,
											 n)) < 0)
    return(BIG_INTEGER_ERROR);

  return(BIG_INTEGER_SUCCESS);
}

BigIntegerResult
BigIntegerToCstrEx(BigInteger x, cstr *out, int len)
{
  int n;

  if (cstr_set_length(out, len) < 0)
    return BIG_INTEGER_ERROR;
  n = BigIntegerToBytes(x, (unsigned char *) out->data, len);
  if (n < len) {
    memmove(out->data + (len - n), out->data, n);
    memset(out->data, 0, len - n);
  }
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerToHexBuf(BigInteger src, char *dest, int destlen)
{
#ifdef OPENSSL
  strcpy(dest, BN_bn2hex(src));
#elif defined(CRYPTOLIB)
  trim(src);
  bigsprint(src, dest);
#elif defined(GNU_MP)
  mpz_get_str(dest, 16, src);
#elif defined(GCRYPT)
  gcry_mpi_print(GCRYMPI_FMT_HEX, dest, destlen, NULL, src);
#elif defined(MPI) || defined(TOMMATH)
  mp_toradix(src, dest, 16);
#endif
  return BIG_INTEGER_SUCCESS;
}

char *
BigIntegerToDec(BigInteger src)
{
#ifdef OPENSSL
  return(BN_bn2dec(src));
#else
  return(NULL);
#endif
}

char *
BigIntegerToHex(BigInteger src)
{
#ifdef OPENSSL
  return(BN_bn2hex(src));
#else
  return(NULL);
#endif
}

static char b64table[] =
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";

/*
 * Convert SRC to a base RADIX string representation in DEST (of maximum length
 * DESTLEN bytes).
 *
 * XXX ignores DESTLEN
 */
BigIntegerResult
BigIntegerToString(BigInteger src, char *dest, int destlen, unsigned int radix)
{
  BigInteger t = BigIntegerFromInt(0);
  char *p = dest;
  char c;

  *p++ = b64table[BigIntegerModInt(src, radix, NULL)];
  BigIntegerDivInt(t, src, radix, NULL);
  while (BigIntegerCmpInt(t, 0) > 0) {
    *p++ = b64table[BigIntegerModInt(t, radix, NULL)];
    BigIntegerDivInt(t, t, radix, NULL);
  }
  BigIntegerFree(t);

  *p-- = '\0';
  /* Reverse the string. */
  while (p > dest) {
    c = *p;
    *p-- =  *dest;
    *dest++ = c;
  }
  return(BIG_INTEGER_SUCCESS);
}

/*
 * Convert a base64 string into raw byte array representation.
 */
static int
fromb64(char *dst, const char *src)
{
  int i, j;
  unsigned int size;
  unsigned char *a;
  char *loc;

  while (*src && (*src == ' ' || *src == '\t' || *src == '\n'))
      ++src;
  size = strlen(src);

  a = malloc((size + 1) * sizeof(unsigned char));
  if (a == NULL)
    return(-1);

  i = 0;
  while (i < size) {
    loc = strchr(b64table, src[i]);
    if (loc == NULL)
      break;
    else
      a[i] = loc - b64table;
    ++i;
  }
  size = i;

  i = size - 1;
  j = size;
  while (1) {
    a[j] = a[i];
    if (--i < 0)
      break;
    a[j] |= (a[i] & 3) << 6;
    --j;
    a[j] = (unsigned char) ((a[i] & 0x3c) >> 2);
    if (--i < 0)
      break;
    a[j] |= (a[i] & 0xf) << 4;
    --j;
    a[j] = (unsigned char) ((a[i] & 0x30) >> 4);
    if (--i < 0)
      break;
    a[j] |= (a[i] << 2);

    a[--j] = 0;
    if (--i < 0)
      break;
  }

  while (a[j] == 0 && j <= size)
    ++j;

  memcpy(dst, a + j, size - j + 1);
  free(a);
  return(size - j + 1);
}

int
cstr_fromb64(cstr *dst, const char *src)
{
  int len;

  cstr_set_length(dst, (strlen(src) * 6 + 7) / 8);
  len = fromb64(dst->data, src);
  cstr_set_length(dst, len);

  return(len);
}

BigInteger
BigIntegerFromB64String(char *str)
{
  cstr *cv;
  BigInteger v;

  cv = cstr_new();
  cstr_fromb64(cv, str);
  v = BigIntegerFromBytes((unsigned char *) cv->data, cv->length);
  cstr_free(cv);

  return(v);
}

int
BigIntegerBitLen(BigInteger b)
{
#ifdef OPENSSL
  return BN_num_bits(b);
#elif defined(CRYPTOLIB)
  return bigBits(b);
#elif defined(GNU_MP)
  return mpz_sizeinbase(b, 2);
#elif defined(GCRYPT)
  return gcry_mpi_get_nbits(b);
#elif defined(MPI) || defined(TOMMATH)
  return mp_count_bits(b);
#endif
}

int
BigIntegerCmp(BigInteger c1, BigInteger c2)
{
#ifdef OPENSSL
  return BN_cmp(c1, c2);
#elif defined(CRYPTOLIB)
  return bigCompare(c1, c2);
#elif defined(GNU_MP)
  return mpz_cmp(c1, c2);
#elif defined(GCRYPT)
  return gcry_mpi_cmp(c1, c2);
#elif defined(MPI) || defined(TOMMATH)
  return mp_cmp(c1, c2);
#endif
}

int
BigIntegerCmpInt(BigInteger c1, unsigned int c2)
{
#ifdef OPENSSL
  if (c1->top > 1)
    return 1;
  else if (c1->top < 1)
    return (c2 > 0) ? -1 : 0;
  else {
    if (c1->d[0] > c2)
      return 1;
    else if (c1->d[0] < c2)
      return -1;
    else
      return 0;
  }
#elif defined(CRYPTOLIB)
  BigInteger t;
  int rv;

  t = bigInit(c2);
  rv = bigCompare(c1, t);
  freeBignum(t);
  return rv;
#elif defined(GNU_MP)
  return mpz_cmp_ui(c1, c2);
#elif defined(TOMMATH)
  return mp_cmp_d(c1, c2);
#elif defined(GCRYPT)
  return gcry_mpi_cmp_ui(c1, c2);
#elif defined(MPI)
  return mp_cmp_int(c1, c2);
#endif
}

BigIntegerResult
BigIntegerLShift(BigInteger result, BigInteger x, unsigned int bits)
{
#ifdef OPENSSL
  BN_lshift(result, x, bits);
#elif defined(CRYPTOLIB)
  bigLeftShift(x, bits, result);
#elif defined(GNU_MP)
  mpz_mul_2exp(result, x, bits);
#elif defined(GCRYPT)
  gcry_mpi_mul_2exp(result, x, bits);
#elif defined(MPI) || defined(TOMMATH)
  mp_mul_2d(x, bits, result);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerAdd(BigInteger result, BigInteger a1, BigInteger a2)
{
#ifdef OPENSSL
  BN_add(result, a1, a2);
#elif defined(CRYPTOLIB)
  bigAdd(a1, a2, result);
#elif defined(GNU_MP)
  mpz_add(result, a1, a2);
#elif defined(GCRYPT)
  gcry_mpi_add(result, a1, a2);
#elif defined(MPI) || defined(TOMMATH)
  mp_add(a1, a2, result);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerAddInt(BigInteger result, BigInteger a1, unsigned int a2)
{
#ifdef OPENSSL
  if (result != a1)
    BN_copy(result, a1);
  BN_add_word(result, a2);
#elif defined(CRYPTOLIB)
  BigInteger t;

  t = bigInit(a2);
  bigAdd(a1, t, result);
  freeBignum(t);
#elif defined(GNU_MP)
  mpz_add_ui(result, a1, a2);
#elif defined(GCRYPT)
  gcry_mpi_add_ui(result, a1, a2);
#elif defined(MPI) || defined(TOMMATH)
  mp_add_d(a1, a2, result);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerSub(BigInteger result, BigInteger s1, BigInteger s2)
{
#ifdef OPENSSL
  BN_sub(result, s1, s2);
#elif defined(CRYPTOLIB)
  bigSubtract(s1, s2, result);
#elif defined(GNU_MP)
  mpz_sub(result, s1, s2);
#elif defined(GCRYPT)
  gcry_mpi_sub(result, s1, s2);
#elif defined(MPI) || defined(TOMMATH)
  mp_sub(s1, s2, result);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerSubInt(BigInteger result, BigInteger s1, unsigned int s2)
{
#ifdef OPENSSL
  if (result != s1)
    BN_copy(result, s1);
  BN_sub_word(result, s2);
#elif defined(CRYPTOLIB)
  BigInteger t;

  t = bigInit(s2);
  bigSubtract(s1, t, result);
  freeBignum(t);
#elif defined(GNU_MP)
  mpz_sub_ui(result, s1, s2);
#elif defined(GCRYPT)
  gcry_mpi_sub_ui(result, s1, s2);
#elif defined(MPI) || defined(TOMMATH)
  mp_sub_d(s1, s2, result);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerMul(BigInteger result, BigInteger m1, BigInteger m2, BigIntegerCtx c)
{
#ifdef OPENSSL
  BN_CTX *ctx = NULL;

  if (c == NULL)
    c = ctx = BN_CTX_new();
  BN_mul(result, m1, m2, c);
  if (ctx)
    BN_CTX_free(ctx);
#elif defined(CRYPTOLIB)
  bigMultiply(m1, m2, result);
#elif defined(GNU_MP)
  mpz_mul(result, m1, m2);
#elif defined(GCRYPT)
  gcry_mpi_mul(result, m1, m2);
#elif defined(MPI) || defined(TOMMATH)
  mp_mul(m1, m2, result);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerMulInt(BigInteger result, BigInteger m1, unsigned int m2,
				 BigIntegerCtx c)
{
#ifdef OPENSSL
  if (result != m1)
    BN_copy(result, m1);
  BN_mul_word(result, m2);
#elif defined(CRYPTOLIB)
  BigInteger t;

  t = bigInit(m2);
  bigMultiply(m1, t, result);
  freeBignum(t);
#elif defined(GNU_MP)
  mpz_mul_ui(result, m1, m2);
#elif defined(GCRYPT)
  gcry_mpi_mul_ui(result, m1, m2);
#elif defined(MPI) || defined(TOMMATH)
  mp_mul_d(m1, m2, result);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerDivInt(BigInteger result, BigInteger d, unsigned int m,
				 BigIntegerCtx c)
{
#ifdef OPENSSL
  if (result != d)
    BN_copy(result, d);
  BN_div_word(result, m);
#elif defined(CRYPTOLIB)
  BigInteger t, u, q;

  t = bigInit(m);
  u = bigInit(0);
  /* We use a separate variable q because cryptolib breaks if result == d */
  q = bigInit(0);
  bigDivide(d, t, q, u);
  freeBignum(t);
  freeBignum(u);
  bigCopy(q, result);
  freeBignum(q);
#elif defined(GNU_MP)
# ifdef GMP2
  mpz_fdiv_q_ui(result, d, m);
# else
  mpz_div_ui(result, d, m);
# endif
#elif defined(GCRYPT)
  BigInteger t = BigIntegerFromInt(m);
  gcry_mpi_div(result, NULL, d, t, -1);
  BigIntegerFree(t);
#elif defined(MPI) || defined(TOMMATH)
  mp_div_d(d, m, result, NULL);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerMod(BigInteger result, BigInteger d, BigInteger m, BigIntegerCtx c)
{
#ifdef OPENSSL
  BN_CTX *ctx = NULL;
  if (c == NULL)
    c = ctx = BN_CTX_new();
  BN_mod(result, d, m, c);
  if (ctx)
    BN_CTX_free(ctx);
#elif defined(CRYPTOLIB)
  bigMod(d, m, result);
#elif defined(GNU_MP)
  mpz_mod(result, d, m);
#elif defined(GCRYPT)
  gcry_mpi_mod(result, d, m);
#elif defined(MPI) || defined(TOMMATH)
  mp_mod(d, m, result);
#endif
  return BIG_INTEGER_SUCCESS;
}

unsigned int
BigIntegerModInt(BigInteger d, unsigned int m, BigIntegerCtx c)
{
#ifdef OPENSSL
  return BN_mod_word(d, m);
#elif defined(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 defined(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;
#elif defined(GCRYPT)
  /* TODO: any way to clean this up??? */
  unsigned char r[4];
  size_t len, i;
  unsigned int ret = 0;
  BigInteger t = BigIntegerFromInt(m);
  BigInteger a = BigIntegerFromInt(0);

  gcry_mpi_mod(a, d, t);
  gcry_mpi_print(GCRYMPI_FMT_USG, r, 4, &len, a);
  for (i = 0; i < len; ++i)
    ret = (ret << 8) | r[i];
  BigIntegerFree(t);
  BigIntegerFree(a);
  return ret;
#elif defined(MPI) || defined(TOMMATH)
  mp_digit r;
  mp_mod_d(d, m, &r);
  return r;
#endif
}

BigIntegerResult
BigIntegerModMul(BigInteger r, BigInteger m1, BigInteger m2, BigInteger modulus,
				 BigIntegerCtx c)
{
#ifdef OPENSSL
  BN_CTX *ctx = NULL;
  if (c == NULL)
    c = ctx = BN_CTX_new();
  BN_mod_mul(r, m1, m2, modulus, c);
  if (ctx)
    BN_CTX_free(ctx);
#elif defined(CRYPTOLIB)
  bigMultiply(m1, m2, r);
  bigMod(r, modulus, r);
#elif defined(GNU_MP)
  mpz_mul(r, m1, m2);
  mpz_mod(r, r, modulus);
#elif defined(GCRYPT)
  gcry_mpi_mulm(r, m1, m2, modulus);
#elif defined(MPI) || defined(TOMMATH)
  mp_mulmod(m1, m2, modulus, r);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerModSub(BigInteger result, BigInteger s1, BigInteger s2,
				 BigInteger m, BigIntegerCtx c)
{
#ifdef OPENSSL
  BN_CTX *ctx = NULL;

  if (c == NULL)
    c = ctx = BN_CTX_new();

  BN_mod_sub(result, s1, s2, m, c);
  if (ctx != NULL)
    BN_CTX_free(ctx);

  return(BIG_INTEGER_SUCCESS);
#else
  return(BIG_INTEGER_ERROR);
#endif
}

BigIntegerResult
BigIntegerModExp(BigInteger r, BigInteger b, BigInteger e, BigInteger m,
				 BigIntegerCtx c, BigIntegerModAccel a)
{
#ifdef OPENSSL
  BN_CTX *ctx = NULL;
  if (c == NULL)
    c = ctx = BN_CTX_new();
  if (default_modexp) {
    (*default_modexp)(r, b, e, m, c, a);
  }
  else if (a == NULL) {
    BN_mod_exp(r, b, e, m, c);
  }
#if OPENSSL_VERSION_NUMBER >= 0x00906000
  else if (b->top == 1) {  /* 0.9.6 and above has mont_word optimization */
    BN_ULONG B = b->d[0];

    BN_mod_exp_mont_word(r, B, e, m, c, a);
  }
#endif
  else
    BN_mod_exp_mont(r, b, e, m, c, a);
  if (ctx)
    BN_CTX_free(ctx);
#elif defined(CRYPTOLIB)
  bigPow(b, e, m, r);
#elif defined(GNU_MP)
  mpz_powm(r, b, e, m);
#elif defined(GCRYPT)
  gcry_mpi_powm(r, b, e, m);
#elif defined(MPI) || defined(TOMMATH)
  mp_exptmod(b, e, m, r);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerModAdd(BigInteger result, BigInteger s1, BigInteger s2,
				 BigInteger m, BigIntegerCtx c)
{
  BN_CTX *ctx = NULL;

  if (c == NULL)
    c = ctx = BN_CTX_new();

  BN_mod_add(result, s1, s2, m, c);
  if (ctx != NULL)
    BN_CTX_free(ctx);

  return(BIG_INTEGER_SUCCESS);
}

int
BigIntegerCheckPrime(BigInteger n, BigIntegerCtx c)
{
#ifdef OPENSSL
  int rv;
  BN_CTX * ctx = NULL;

  if (c == NULL)
    c = ctx = BN_CTX_new();
  rv = BN_is_prime(n, 25, NULL, c, NULL);
  if (ctx)
    BN_CTX_free(ctx);
  return rv;
#elif defined(CRYPTOLIB)
#if 0
  /*
   * Ugh.  Not only is cryptolib's bigDivide sensitive to inputs
   * and outputs being the same, but now the primeTest needs random
   * numbers, which it gets by calling cryptolib's broken truerand
   * implementation(!)  We have to fake it out by doing our own
   * seeding explicitly.
   */
  static int seeded = 0;
  static unsigned char seedbuf[64];
  if (!seeded) {
    bn_random(seedbuf, sizeof(seedbuf));
    seedDesRandom(seedbuf, sizeof(seedbuf));
    memset(seedbuf, 0, sizeof(seedbuf));
    seeded = 1;
  }
#endif /* 0 */
  bn_random(NULL, 0);
  return primeTest(n);
#elif defined(GNU_MP)
  return mpz_probab_prime_p(n, 25);
#elif defined(GCRYPT)
  return (gcry_prime_check(n, 0) == GPG_ERR_NO_ERROR);
#elif defined(TOMMATH)
  int rv;
  mp_prime_is_prime(n, 25, &rv);
  return rv;
#elif defined(MPI)
  return (mpp_pprime(n, 25) == MP_YES);
#endif
}

BigIntegerResult
BigIntegerFree(BigInteger b)
{
#ifdef OPENSSL
  BN_free(b);
#elif defined(CRYPTOLIB)
  freeBignum(b);
#elif defined(GNU_MP)
  mpz_clear(b);
  free(b);
#elif defined(GCRYPT)
  gcry_mpi_release(b);
#elif defined(MPI) || defined(TOMMATH)
  mp_clear(b);
  free(b);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerResult
BigIntegerClearFree(BigInteger b)
{
#ifdef OPENSSL
  BN_clear_free(b);
#elif defined(CRYPTOLIB)
  /* TODO */
  freeBignum(b);
#elif defined(GNU_MP)
  /* TODO */
  mpz_clear(b);
  free(b);
#elif defined(GCRYPT)
  /* TODO */
  gcry_mpi_release(b);
#elif defined(MPI) || defined(TOMMATH)
  /* TODO */
  mp_clear(b);
  free(b);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerCtx
BigIntegerCtxNew(void)
{
#ifdef OPENSSL
  return BN_CTX_new();
#else
  return NULL;
#endif
}

BigIntegerResult
BigIntegerCtxFree(BigIntegerCtx ctx)
{
#ifdef OPENSSL
  if (ctx)
    BN_CTX_free(ctx);
#endif
  return BIG_INTEGER_SUCCESS;
}

BigIntegerModAccel
BigIntegerModAccelNew(BigInteger m, BigIntegerCtx c)
{
#ifdef OPENSSL
  BN_CTX *ctx = NULL;
  BN_MONT_CTX *mctx;
  if (default_modexp)
    return NULL;
  if (c == NULL)
    c = ctx = BN_CTX_new();
  mctx = BN_MONT_CTX_new();
  BN_MONT_CTX_set(mctx, m, c);
  if (ctx)
    BN_CTX_free(ctx);
  return mctx;
#else
  return NULL;
#endif
}

BigIntegerResult
BigIntegerModAccelFree(BigIntegerModAccel accel)
{
#ifdef OPENSSL
  if (accel)
    BN_MONT_CTX_free(accel);
#endif
  return(BIG_INTEGER_SUCCESS);
}

BigIntegerResult
BigIntegerInitialize(void)
{
#if OPENSSL_VERSION_NUMBER >= 0x00907000
  ENGINE_load_builtin_engines();
#endif

  return(BIG_INTEGER_SUCCESS);
}

BigIntegerResult
BigIntegerFinalize(void)
{

  return(BigIntegerReleaseEngine());
}

BigIntegerResult
BigIntegerUseEngine(const char * engine)
{
#if defined(OPENSSL) && defined(OPENSSL_ENGINE)
  ENGINE *e = ENGINE_by_id(engine);
  if (e) {
    if (ENGINE_init(e) > 0) {
#if OPENSSL_VERSION_NUMBER >= 0x00907000
      /* 0.9.7 loses the BN_mod_exp method.  Pity. */
      const RSA_METHOD *rsa = ENGINE_get_RSA(e);
      if (rsa)
		default_modexp = (modexp_meth) rsa->bn_mod_exp;
#else
      default_modexp = (modexp_meth) ENGINE_get_BN_mod_exp(e);
#endif
      BigIntegerReleaseEngine();
      default_engine = e;
      return BIG_INTEGER_SUCCESS;
    }
    else
      ENGINE_free(e);
  }
#endif
  return BIG_INTEGER_ERROR;
}

BigIntegerResult
BigIntegerReleaseEngine(void)
{
#if defined(OPENSSL) && defined(OPENSSL_ENGINE)
  if (default_engine) {
    ENGINE_finish(default_engine);
    ENGINE_free(default_engine);
    default_engine = NULL;
    default_modexp = NULL;
  }
#endif
  return BIG_INTEGER_SUCCESS;
}

#define EXPFACTOR	2		/* Minimum expansion factor */
#define MINSIZE		4		/* Absolute minimum - one word */

static char cstr_empty_string[] = { '\0' };
static cstr_allocator *default_alloc = NULL;

/*
 * It is assumed, for efficiency, that it is okay to pass more arguments
 * to a function than are called for, as long as the required arguments
 * are in proper form.  If extra arguments to malloc() and free() cause
 * problems, define PEDANTIC_ARGS below.
 */
#define PEDANTIC_ARGS
#ifdef PEDANTIC_ARGS

static void *
Cmalloc(int n, void *heap)
{
  return(malloc(n));
}

static void
Cfree(void *p, void *heap)
{
  free(p);
}

static cstr_allocator malloc_allocator = { Cmalloc, Cfree, NULL };
#else
static cstr_allocator malloc_allocator = { malloc, free, NULL };
#endif

void
cstr_set_allocator(cstr_allocator *alloc)
{

  default_alloc = alloc;
}

cstr *
cstr_new_alloc(cstr_allocator *alloc)
{
  cstr *str;

  if (alloc == NULL) {
    if (default_alloc == NULL) {
      default_alloc = &malloc_allocator;
    }
    alloc = default_alloc;
  }

  str = (cstr *) (*alloc->alloc)(sizeof(cstr), alloc->heap);
  if (str) {
    str->data = cstr_empty_string;
    str->length = str->cap = 0;
    str->ref = 1;
    str->allocator = alloc;
  }
  return str;
}

cstr *
cstr_new(void)
{

  return(cstr_new_alloc(NULL));
}

cstr *
cstr_dup_alloc(const cstr *str, cstr_allocator *alloc)
{
  cstr *nstr = cstr_new_alloc(alloc);

  if (nstr)
    cstr_setn(nstr, str->data, str->length);

  return(nstr);
}

cstr *
cstr_dup(const cstr *str)
{

  return(cstr_dup_alloc(str, NULL));
}

cstr *
cstr_create(const char *s)
{

  return(cstr_createn(s, strlen(s)));
}

cstr *
cstr_createn(const char *s, int len)
{
  cstr *str = cstr_new();

  if (str) {
    cstr_setn(str, s, len);
  }
  return(str);
}

void
cstr_use(cstr *str)
{

  ++str->ref;
}

void
cstr_clear_free(cstr *str)
{

  if (--str->ref == 0) {
    if (str->cap > 0) {
      memset(str->data, 0, str->cap);
      (*str->allocator->free)(str->data, str->allocator->heap);
    }
    (*str->allocator->free)(str, str->allocator->heap);
  }
}

void
cstr_free(cstr *str)
{

  if (--str->ref == 0) {
    if (str->cap > 0)
      (*str->allocator->free)(str->data, str->allocator->heap);
    (*str->allocator->free)(str, str->allocator->heap);
  }
}

void
cstr_empty(cstr *str)
{

  if (str->cap > 0)
    (*str->allocator->free)(str->data, str->allocator->heap);

  str->data = cstr_empty_string;
  str->length = str->cap = 0;
}

static int
cstr_alloc(cstr *str, int len)
{
  char *t;

  if (len > str->cap) {
    if (len < EXPFACTOR * str->cap)
      len = EXPFACTOR * str->cap;
    if (len < MINSIZE)
      len = MINSIZE;

    t = (char *) (*str->allocator->alloc)(len * sizeof(char),
										  str->allocator->heap);
    if (t) {
      if (str->data) {
		t[str->length] = 0;
		if (str->cap > 0) {
		  if (str->length > 0)
			memcpy(t, str->data, str->length);
		  free(str->data);
		}
      }
      str->data = t;
      str->cap = len;
      return(1);
    }
    else
      return(-1);
  }
  else
    return(0);
}

int
cstr_copy(cstr *dst, const cstr *src)
{

  return(cstr_setn(dst, src->data, src->length));
}

int
cstr_set(cstr *str, const char *s)
{

  return(cstr_setn(str, s, strlen(s)));
}

int
cstr_setn(cstr *str, const char *s, int len)
{

  if (cstr_alloc(str, len + 1) < 0)
    return(-1);
  str->data[len] = 0;
  if (s != NULL && len > 0)
    memmove(str->data, s, len);
  str->length = len;

  return(1);
}

int
cstr_set_length(cstr *str, int len)
{

  if (len < str->length) {
    str->data[len] = 0;
    str->length = len;
    return 1;
  }
  else if (len > str->length) {
    if (cstr_alloc(str, len + 1) < 0)
      return(-1);
    memset(str->data + str->length, 0, len - str->length + 1);
    str->length = len;
    return(1);
  }
  else
    return(0);
}

int
cstr_append(cstr *str, const char *s)
{

  return cstr_appendn(str, s, strlen(s));
}

int
cstr_appendn(cstr *str, const char *s, int len)
{

  if (cstr_alloc(str, str->length + len + 1) < 0)
    return -1;
  memcpy(str->data + str->length, s, len);
  str->length += len;
  str->data[str->length] = 0;
  return 1;
}

int
cstr_append_str(cstr *dst, const cstr *src)
{
  return cstr_appendn(dst, src->data, src->length);
}


/*
 * Copyright (c) 1997-1999  The Stanford SRP Authentication Project
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 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.
 *
 * In addition, the following conditions apply:
 *
 * 1. Any software that incorporates the SRP authentication technology
 *    must display the following acknowlegment:
 *    "This product uses the 'Secure Remote Password' cryptographic
 *     authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)."
 *
 * 2. Any software that incorporates all or part of the SRP distribution
 *    itself must also display the following acknowledgment:
 *    "This product includes software developed by Tom Wu and Eugene
 *     Jhong for the SRP Distribution (http://srp.stanford.edu/srp/)."
 *
 * 3. Redistributions in source or binary form must retain an intact copy
 *    of this copyright notice and list of conditions.
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

#include "sha3.h"

/* An entropy pool. */
static unsigned char randpool[SHA3_MAX_DIGEST_SIZE];
/* Buffer for holding new randomness. */
static unsigned char randout[SHA3_MAX_DIGEST_SIZE];
static unsigned int rand_outpos = 0;
static unsigned int rand_digest_size = 0;

/*
 * envhash - Generate a hash of the environment
 *
 * This routine performs a secure hash of all the "name=value" pairs
 * in the environment concatenated together and dumps them in the
 * output.  While it is true that anyone on the system can see
 * your environment, someone not on the system will have a very
 * difficult time guessing it, especially since some systems play
 * tricks with variable ordering and sometimes define quirky
 * environment variables like $WINDOWID or $_.
 */
static void
envhash(unsigned char *out)
{
  char **ptr;
  char ebuf[256];
  SHA3_ctx ctx;
  extern char **environ;

  if (sha3_init_by_name(BN_RAND_DIGEST_NAME, &ctx, NULL) == NULL)
	abort();

  for (ptr = environ; *ptr; ++ptr) {
    strncpy(ebuf, *ptr, sizeof(ebuf) - 1);
    ebuf[sizeof(ebuf) - 1] = '\0';
    sha3_update(&ctx, (unsigned char *) ebuf, strlen(ebuf));
  }

  sha3_final(&ctx, out);
}

/*
 * fshash - Generate a hash from the file system
 *
 * This routine climbs up the directory tree from the current
 * directory, running stat() on each directory until it hits the
 * root directory.  This information is sensitive to the last
 * access/modification times of all the directories above you,
 * so someone who lists one of those directories injects some
 * entropy into the system.  Obviously, this hash is very sensitive
 * to your current directory when the program is run.
 *
 * For good measure, it also performs an fstat on the standard input,
 * usually your tty, throws that into the buffer, creates a file in
 * /tmp (the inode is unpredictable on a busy system), and runs stat()
 * on that before deleting it.
 *
 * The entire buffer is run once through SHA to obtain the final result.
 */
static void
fshash(unsigned char *out)
{
  int i;
  ino_t pinode;
  char dotpath[128];
  struct stat st;
  SHA3_ctx ctx;
  dev_t pdev;

  sha3_init_by_name(BN_RAND_DIGEST_NAME, &ctx, NULL);

  if (stat(".", &st) >= 0) {
    sha3_update(&ctx, (unsigned char *) &st, sizeof(st));

    pinode = st.st_ino;
    pdev = st.st_dev;
    strcpy(dotpath, "..");
    for (i = 0; i < 40; ++i) {
      if (stat(dotpath, &st) == -1)
		break;

      if (st.st_ino == pinode && st.st_dev == pdev)
		break;

      sha3_update(&ctx, (unsigned char *) &st, sizeof(st));

      pinode = st.st_ino;
      pdev = st.st_dev;
      strcat(dotpath, "/..");
    }
  }

  if (fstat(0, &st) != -1)
    sha3_update(&ctx, (unsigned char *) &st, sizeof(st));

  sprintf(dotpath, "/tmp/rnd.%d", getpid());
  if (creat(dotpath, 0600) != -1 && stat(dotpath, &st) != -1)
    sha3_update(&ctx, (unsigned char *) &st, sizeof(st));
  unlink(dotpath);

  sha3_final(&ctx, out);
}

/*
 * Generate a high-entropy seed for the strong random number generator.
 * This uses a wide variety of quickly gathered and somewhat unpredictable
 * system information.  The 'preseed' structure is assembled from:
 *
 *   The system time in seconds
 *   The system time in microseconds
 *   The current process ID
 *   The parent process ID
 *   A hash of the user's environment
 *   A hash gathered from the file system
 *   Input from a random device, if available
 *   Timings of system interrupts
 *
 * The entire structure (60 bytes on most systems) is fed to the digest function
 * to produce a seed for the strong random number generator.  It is believed
 * that in the worst case (on a quiet system with no random device versus
 * an attacker who has access to the system already), the seed contains at
 * least about digest_bitsize / 2 bits of entropy.  Versus an attacker who
 * does not have access to the system, the entropy should be higher.
 */

static int bn_library_initialized = 0;
static char bn_random_initialized = 0;

static struct {
  unsigned int trand1;
  time_t sec;
  time_t usec;
  short pid;
  short ppid;
  unsigned char envh[SHA3_MAX_DIGEST_SIZE];
  unsigned char fsh[SHA3_MAX_DIGEST_SIZE];
  unsigned char devrand[SHA3_MAX_DIGEST_SIZE];
  unsigned int trand2;
} preseed;

static unsigned long raw_truerand(void);

static void
bn_random_init(void)
{
  int fd;
  ssize_t r;
  SHA3_ctx ctx;
  struct timeval t;

  if (bn_random_initialized)
    return;

  bn_random_initialized = 1;

  r = 0;
  fd = open("/dev/urandom", O_RDONLY);
  if (fd != -1) {
    r += read(fd, preseed.devrand, sizeof(preseed.devrand));
    close(fd);
  }

  /* Resort to raw_truerand() only if desperate for some real entropy. */
  if (r == 0)
    preseed.trand1 = raw_truerand();

  gettimeofday(&t, NULL);

  preseed.sec = t.tv_sec;
  preseed.usec = t.tv_usec;

  preseed.pid = getpid();
  preseed.ppid = getppid();

  envhash(preseed.envh);

  fshash(preseed.fsh);

  if (r == 0)
    preseed.trand2 = raw_truerand();

  sha3_init_by_name(BN_RAND_DIGEST_NAME, &ctx, NULL);
  sha3_update(&ctx, (unsigned char *) &preseed, sizeof(preseed));
  sha3_final(&ctx, randpool);

  rand_digest_size = ctx.digest_bits / 8;

  rand_outpos = 0;
  memset((unsigned char *) &preseed, 0, sizeof(preseed));
  memset((unsigned char *) &ctx, 0, sizeof(ctx));
}

/*
 * For pseudo-compatibility.
 */
static void
bn_random_strong_init(void)
{

  bn_random_init();
}

BigIntegerResult
bn_initialize(void)
{

  if (bn_library_initialized == 0) {
	BigIntegerInitialize();
	bn_random_strong_init();
	bn_library_initialized = 1;
  }

  return(BIG_INTEGER_SUCCESS);
}

BigIntegerResult
bn_finalize(void)
{

  if (bn_library_initialized) {
	bn_library_initialized = 0;
	BigIntegerFinalize();
  }

  return(BIG_INTEGER_SUCCESS);
}

void
BigIntegerRandom(unsigned char *data, size_t size)
{

  return(bn_random(data, size));
}

#define NUM_RANDOMS 12

/*
 * The strong random number generator.  This uses a 256-bit seed
 * with a secure hash function in a feedback configuration to generate
 * successive outputs.  If S[0] is set to the initial seed, then:
 *
 *         S[i+1] = hash(i || S[i])
 *         A[i] = hash(S[i])
 *
 * where the A[i] are the output blocks starting with i=0.
 * Each cycle generates 256/8 bytes of new output.
 */
void
bn_random(unsigned char *data, size_t size)
{
  static SHA3_ctx randctx;
  static unsigned long randcnt = 0;

  if (!bn_random_initialized)
    bn_random_init();

  if (size <= 0) {
	/* bn_random(NULL, 0) forces seed (re)initialization. */
	bn_random_initialized = 0;
	bn_random_init();
    return;
  }

  while (size > rand_outpos) {
    if (rand_outpos > 0) {
      memcpy(data, randout + (rand_digest_size - rand_outpos), rand_outpos);
      data += rand_outpos;
      size -= rand_outpos;
    }

    /* Grab output bytes by mashing the entropy pool. */
    sha3_init_by_name(BN_RAND_DIGEST_NAME, &randctx, NULL);
    sha3_update(&randctx, randpool, sizeof(randpool));
    sha3_final(&randctx, randout);

    /* Stir (recycle) contents of the entropy pool. */
    sha3_init_by_name(BN_RAND_DIGEST_NAME, &randctx, NULL);
    sha3_update(&randctx, (unsigned char *) &randcnt, sizeof(randcnt));
    randcnt++;
    sha3_update(&randctx, randpool, sizeof(randpool));
    sha3_final(&randctx, randpool);

    rand_outpos = rand_digest_size;
  }

  if (size > 0) {
    memcpy(data, randout + (rand_digest_size - rand_outpos), size);
    rand_outpos -= size;
  }
}

/*
 *      Physically random numbers (very nearly uniform)
 *      D. P. Mitchell
 *      Modified by Matt Blaze 7/95
 */
/*
 * The authors of this software are Don Mitchell and Matt Blaze.
 *              Copyright (c) 1995 by AT&T.
 * Permission to use, copy, and modify this software without fee
 * is hereby granted, provided that this entire notice is included in
 * all copies of any software which is or includes a copy or
 * modification of this software and in all copies of the supporting
 * documentation for such software.
 *
 * This software may be subject to United States export controls.
 *
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */

/*
 * WARNING: depending on the particular platform, raw_truerand()
 * output may be biased or correlated.  In general, you can expect
 * about 16 bits of "pseudo-entropy" out of each 32 bit word returned
 * by truerand(), but it may not be uniformly diffused.  You should
 * raw_therefore run the output through some post-whitening function
 * (like MD5 or DES or whatever) before using it to generate key
 * material.  (RSAREF's random package does this for you when you feed
 * raw_truerand() bits to the seed input function.)
 *
 * The application interface, for 8, 16, and 32 bit properly "whitened"
 * random numbers, can be found in trand8(), trand16(), and trand32().
 * Use those instead of calling raw_truerand() directly.
 *
 * The basic idea here is that between clock "skew" and various
 * hard-to-predict OS event arrivals, counting a tight loop will yield
 * a little (maybe a third of a bit or so) of "good" randomness per
 * interval clock tick.  This seems to work well even on unloaded
 * machines.  If there is a human operator at the machine, you should
 * augment truerand with other measure, like keyboard event timing.
 * On server machines (e.g., when you need to generate a
 * Diffie-Hellman secret) truerand alone may be good enough.
 *
 * Test these assumptions on your own platform before fielding a
 * system based on this software or these techniques.
 *
 * This software seems to work well (at 10 or so bits per
 * raw_truerand() call) on a Sun Sparc-20 under SunOS 4.1.3 and on a
 * P100 under BSDI 2.0.  You're on your own elsewhere.
 *
 */

#include <signal.h>
#include <setjmp.h>
#include <sys/time.h>
#include <math.h>
#include <stdio.h>

#ifdef OLD_TRUERAND
static jmp_buf env;
#endif
static unsigned volatile count
#ifndef OLD_TRUERAND
  , done = 0
#endif
;

static unsigned ocount;
static unsigned buffer;

static void
tick(void)
{
  struct itimerval it, oit;

  it.it_interval.tv_sec = 0;
  it.it_interval.tv_usec = 0;
  it.it_value.tv_sec = 0;
  it.it_value.tv_usec = 16665;
  if (setitimer(ITIMER_REAL, &it, &oit) < 0)
	perror("tick");
}

static void
interrupt(int s)
{

  if (count) {
#ifdef OLD_TRUERAND
	longjmp(env, 1);
#else
	++done;
	return;
#endif
  }

  (void) signal(SIGALRM, interrupt);
  tick();
}

static unsigned long
roulette(void)
{
#ifdef OLD_TRUERAND
  if (setjmp(env)) {
	count ^= (count>>3) ^ (count>>6) ^ ocount;
	count &= 0x7;
	ocount=count;
	buffer = (buffer<<3) ^ count;
	return buffer;
  }
#else
  done = 0;
#endif
  (void) signal(SIGALRM, interrupt);
  count = 0;
  tick();
#ifdef OLD_TRUERAND
  for (;;)
#else
	while (done == 0)
#endif
	  count++;        /* about 1 MHz on VAX 11/780 */
#ifndef OLD_TRUERAND
  count ^= (count>>3) ^ (count>>6) ^ ocount;
  count &= 0x7;
  ocount=count;
  buffer = (buffer<<3) ^ count;
  return buffer;
#endif
}

static unsigned long
raw_truerand(void)
{

  count = 0;
  (void) roulette();
  (void) roulette();
  (void) roulette();
  (void) roulette();
  (void) roulette();
  (void) roulette();
  (void) roulette();
  (void) roulette();
  (void) roulette();
  (void) roulette();
  return roulette();
}

BIGNUM *
BN_value_one(void)
{
  static BN_ULONG data_one = 1L;
  static BIGNUM const_one = { &data_one, 1, 1, 0};

  return(&const_one);
}

/* solves ax == 1 (mod n) */
BIGNUM *
BN_mod_inverse(BIGNUM *in, BIGNUM *a, const BIGNUM *n, BN_CTX *ctx)
{
  BIGNUM *A, *B, *X, *Y, *M, *D, *R= NULL;
  BIGNUM *T, *ret = NULL;
  int sign;

  bn_check_top(a);
  bn_check_top(n);

  BN_CTX_start(ctx);
  A = BN_CTX_get(ctx);
  B = BN_CTX_get(ctx);
  X = BN_CTX_get(ctx);
  D = BN_CTX_get(ctx);
  M = BN_CTX_get(ctx);
  Y = BN_CTX_get(ctx);
  if (Y == NULL)
	goto err;

  if (in == NULL)
	R = BN_new();
  else
	R = in;
  if (R == NULL)
	goto err;

  BN_zero(X);
  BN_one(Y);
  if (BN_copy(A,a) == NULL)
	goto err;
  if (BN_copy(B,n) == NULL)
	goto err;
  sign = 1;

  while (!BN_is_zero(B)) {
	if (!BN_div(D, M, A, B, ctx))
	  goto err;
	T = A;
	A = B;
	B = M;
	/* T has a struct, M does not */

	if (!BN_mul(T, D, X, ctx))
	  goto err;
	if (!BN_add(T, T, Y))
	  goto err;
	M = Y;
	Y = X;
	X = T;
	sign = -sign;
  }
  if (sign < 0) {
	if (!BN_sub(Y, n, Y))
	  goto err;
  }

  if (BN_is_one(A)) {
	if (!BN_mod(R, Y, n, ctx))
	  goto err;
  }
  else {
	goto err;
  }
  ret = R;

err:
  if ((ret == NULL) && (in == NULL))
	BN_free(R);
  BN_CTX_end(ctx);
  return(ret);
}

int
BN_set_bit(BIGNUM *a, int n)
{
  int i, j, k;

  i = n / BN_BITS2;
  j = n % BN_BITS2;
  if (a->top <= i) {
	if (bn_wexpand(a, i + 1) == NULL)
	  return(0);
	for (k = a->top; k < i + 1; k++)
	  a->d[k] = 0;
	a->top = i + 1;
  }

  a->d[i] |= (((BN_ULONG) 1) << j);
  return(1);
}

static int
bnrand(int pseudorand, BIGNUM *rnd, int bits, int top, int bottom)
{
  unsigned char *buf = NULL;
  int ret = 0, bit, bytes, mask;

  if (bits == 0) {
	BN_zero(rnd);
	return 1;
  }

  bytes = (bits + 7) / 8;
  bit = (bits - 1) % 8;
  mask = 0xff << bit;

  buf = (unsigned char *) malloc(bytes);
  if (buf == NULL)
	goto err;

  /* make a random number and set the top and bottom bits */
  /* this ignores the pseudorand flag */

  bn_random(buf, bytes);

  if (top) {
	if (bit == 0) {
	  buf[0] = 1;
	  buf[1] |= 0x80;
	}
	else {
	  buf[0] |= (3 << (bit - 1));
	  buf[0] &= ~(mask << 1);
	}
  }
  else {
	buf[0] |= (1 << bit);
	buf[0] &= ~(mask << 1);
  }
  if (bottom) /* set bottom bits to whatever odd is */
	buf[bytes - 1] |= 1;
  if (!BN_bin2bn(buf, bytes, rnd))
	goto err;
  ret = 1;

err:
  if (buf != NULL) {
	memset(buf, 0, bytes);
	free(buf);
  }
  return(ret);
}

/* BN_pseudo_rand is the same as BN_rand, now. */

int
BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom)
{
  return bnrand(1, rnd, bits, top, bottom);
}

#define MONT_WORD /* use the faster word-based algorithm */

int BN_mod_mul_montgomery(BIGNUM *r, BIGNUM *a, BIGNUM *b,
						  BN_MONT_CTX *mont, BN_CTX *ctx)
{
  BIGNUM *tmp,*tmp2;
  int ret = 0;

  BN_CTX_start(ctx);
  tmp = BN_CTX_get(ctx);
  tmp2 = BN_CTX_get(ctx);
  if (tmp == NULL || tmp2 == NULL)
	goto err;

  bn_check_top(tmp);
  bn_check_top(tmp2);

  if (a == b) {
	if (!BN_sqr(tmp, a, ctx))
	  goto err;
  }
  else {
	if (!BN_mul(tmp, a, b, ctx))
	  goto err;
  }
  /* reduce from aRR to aR */
  if (!BN_from_montgomery(r, tmp, mont, ctx))
	goto err;
  ret = 1;

err:
  BN_CTX_end(ctx);
  return(ret);
}

int
BN_from_montgomery(BIGNUM *ret, BIGNUM *a, BN_MONT_CTX *mont, BN_CTX *ctx)
{
  int retn = 0;

#ifdef MONT_WORD
  BIGNUM *n, *r;
  BN_ULONG *ap, *np, *rp, n0, v, *nrp;
  int al, nl, max, i, x, ri;

  BN_CTX_start(ctx);
  if ((r = BN_CTX_get(ctx)) == NULL)
	goto err;

  if (!BN_copy(r, a))
	goto err;
  n = &(mont->N);

  ap = a->d;
  /* mont->ri is the size of mont->N in bits (rounded up
	 to the word size) */
  al = ri = mont->ri / BN_BITS2;

  nl = n->top;
  if ((al == 0) || (nl == 0)) {
	r->top = 0;
	return(1);
  }

  max = (nl + al + 1); /* allow for overflow (no?) XXX */
  if (bn_wexpand(r, max) == NULL)
	goto err;
  if (bn_wexpand(ret, max) == NULL)
	goto err;

  r->neg = a->neg ^ n->neg;
  np = n->d;
  rp = r->d;
  nrp = &(r->d[nl]);

  /* clear the top words of T */
#if 1
  for (i = r->top; i < max; i++) /* memset? XXX */
	r->d[i] = 0;
#else
	memset(&(r->d[r->top]), 0, (max-r->top) * sizeof(BN_ULONG));
#endif

	r->top = max;
	n0 = mont->n0;

#ifdef BN_COUNT
	printf("word BN_from_montgomery %d * %d\n", nl, nl);
#endif
	for (i = 0; i < nl; i++) {
#ifdef __TANDEM
	  {
		long long t1;
		long long t2;
		long long t3;

		t1 = rp[0] * (n0 & 0177777);
		t2 = 037777600000l;
		t2 = n0 & t2;
		t3 = rp[0] & 0177777;
		t2 = (t3 * t2) & BN_MASK2;
		t1 = t1 + t2;
		v = bn_mul_add_words(rp, np, nl, (BN_ULONG) t1);
	  }
#else
	  v = bn_mul_add_words(rp, np, nl, (rp[0] * n0) & BN_MASK2);
#endif
	  nrp++;
	  rp++;
	  if (((nrp[-1] += v) & BN_MASK2) >= v)
		continue;
	  else {
		if (((++nrp[0]) & BN_MASK2) != 0)
		  continue;
		if (((++nrp[1]) & BN_MASK2) != 0)
		  continue;
		for (x = 2; (((++nrp[x]) & BN_MASK2) == 0); x++)
		  ;
	  }
	}
	bn_fix_top(r);

	/* mont->ri will be a multiple of the word size */
#if 0
	BN_rshift(ret, r, mont->ri);
#else
	ret->neg = r->neg;
	x = ri;
	rp = ret->d;
	ap = &(r->d[x]);
	if (r->top < x)
		al = 0;
	else
		al = r->top - x;
	ret->top=al;
	al -= 4;
	for (i = 0; i < al; i += 4) {
	  BN_ULONG t1, t2, t3, t4;

	  t1 = ap[i + 0];
	  t2 = ap[i + 1];
	  t3 = ap[i + 2];
	  t4 = ap[i + 3];
	  rp[i + 0] = t1;
	  rp[i + 1] = t2;
	  rp[i + 2] = t3;
	  rp[i + 3] = t4;
	}
	al += 4;
	for (; i < al; i++)
	  rp[i] = ap[i];
#endif
#else /* !MONT_WORD */
	BIGNUM *t1, *t2;

	BN_CTX_start(ctx);
	t1 = BN_CTX_get(ctx);
	t2 = BN_CTX_get(ctx);
	if (t1 == NULL || t2 == NULL)
	  goto err;

	if (!BN_copy(t1, a))
	  goto err;
	BN_mask_bits(t1, mont->ri);

	if (!BN_mul(t2, t1, &mont->Ni, ctx))
	  goto err;
	BN_mask_bits(t2, mont->ri);

	if (!BN_mul(t1, t2, &mont->N, ctx))
	  goto err;
	if (!BN_add(t2, a, t1))
	  goto err;
	BN_rshift(ret, t2, mont->ri);
#endif /* MONT_WORD */

	if (BN_ucmp(ret, &(mont->N)) >= 0)
	  BN_usub(ret, ret, &mont->N);
	retn = 1;

 err:
	BN_CTX_end(ctx);
	return(retn);
}

void
BN_MONT_CTX_init(BN_MONT_CTX *ctx)
{

  ctx->ri = 0;
  BN_init(&(ctx->RR));
  BN_init(&(ctx->N));
  BN_init(&(ctx->Ni));
  ctx->flags = 0;
}

BN_MONT_CTX *
BN_MONT_CTX_new(void)
{
  BN_MONT_CTX *ret;

  if ((ret = (BN_MONT_CTX *) malloc(sizeof(BN_MONT_CTX))) == NULL)
	return(NULL);

  BN_MONT_CTX_init(ret);
  ret->flags = BN_FLG_MALLOCED;
  return(ret);
}

void
BN_MONT_CTX_free(BN_MONT_CTX *mont)
{

  if (mont == NULL)
	return;

  BN_free(&(mont->RR));
  BN_free(&(mont->N));
  BN_free(&(mont->Ni));
  if (mont->flags & BN_FLG_MALLOCED)
	free(mont);
}

int
BN_MONT_CTX_set(BN_MONT_CTX *mont, const BIGNUM *mod, BN_CTX *ctx)
{
  BIGNUM Ri, *R;

  BN_init(&Ri);
  R= &(mont->RR);                                 /* grab RR as a temp */
  BN_copy(&(mont->N), mod);                        /* Set N */

#ifdef MONT_WORD
  {
	BIGNUM tmod;
	BN_ULONG buf[2];

	mont->ri = (BN_num_bits(mod) + (BN_BITS2 - 1)) / BN_BITS2 * BN_BITS2;
	BN_zero(R);
	BN_set_bit(R, BN_BITS2);                 /* R */

	buf[0] = mod->d[0]; /* tmod = N mod word size */
	buf[1] = 0;
	tmod.d = buf;
	tmod.top = 1;
	tmod.dmax = 2;
	tmod.neg = mod->neg;
	/* Ri = R^-1 mod N*/
	if ((BN_mod_inverse(&Ri, R, &tmod, ctx)) == NULL)
	  goto err;
	BN_lshift(&Ri, &Ri, BN_BITS2);            /* R*Ri */
	if (!BN_is_zero(&Ri))
	  BN_sub_word(&Ri, 1);
	else /* if N mod word size == 1 */
	  BN_set_word(&Ri, BN_MASK2);  /* Ri-- (mod word size) */
	BN_div(&Ri, NULL, &Ri, &tmod, ctx); /* Ni = (R*Ri-1)/N,
										 * keep only least significant word: */
	mont->n0 = Ri.d[0];
	BN_free(&Ri);
  }
#else /* !MONT_WORD */
  { /* bignum version */
	mont->ri = BN_num_bits(mod);
	BN_zero(R);
	BN_set_bit(R, mont->ri);                 /* R = 2^ri */
	/* Ri = R^-1 mod N*/
	if ((BN_mod_inverse(&Ri, R, mod, ctx)) == NULL)
	  goto err;
	BN_lshift(&Ri, &Ri, mont->ri);            /* R*Ri */
	BN_sub_word(&Ri, 1);
	/* Ni = (R*Ri-1) / N */
	BN_div(&(mont->Ni), NULL, &Ri, mod, ctx);
	BN_free(&Ri);
  }
#endif

  /* setup RR for conversions */
  BN_zero(&(mont->RR));
  BN_set_bit(&(mont->RR), mont->ri*2);
  BN_mod(&(mont->RR), &(mont->RR), &(mont->N), ctx);

  return(1);

 err:
  return(0);
}

static int
witness(BIGNUM *w, const BIGNUM *a, const BIGNUM *a1,
		const BIGNUM *a1_odd, int k, BN_CTX *ctx, BN_MONT_CTX *mont)
{

  if (!BN_mod_exp_mont(w, w, a1_odd, a, ctx, mont)) /* w := w^a1_odd mod a */
	return -1;
  if (BN_is_one(w))
	return 0; /* probably prime */
  if (BN_cmp(w, a1) == 0)
	return 0; /* w == -1 (mod a),  'a' is probably prime */
  while (--k) {
	if (!BN_mod_mul(w, w, w, a, ctx)) /* w := w^2 mod a */
	  return -1;
	if (BN_is_one(w))
	  return 1; /* 'a' is composite, otherwise a previous 'w' would
				 * have been == -1 (mod 'a') */
	if (BN_cmp(w, a1) == 0)
	  return 0; /* w == -1 (mod a), 'a' is probably prime */
  }
  /* If we get here, 'w' is the (a-1)/2-th power of the original 'w',
   * and it is neither -1 nor +1 -- so 'a' cannot be prime */
  return 1;
}

int
BN_is_prime(const BIGNUM *a, int checks, void (*callback)(int,int,void *),
			BN_CTX *ctx_passed, void *cb_arg)
{
  return BN_is_prime_fasttest(a, checks, callback, ctx_passed, cb_arg, 0);
}

int
BN_is_prime_fasttest(const BIGNUM *a, int checks,
					 void (*callback)(int,int,void *),
					 BN_CTX *ctx_passed, void *cb_arg,
					 int do_trial_division)
{
  int i, j, ret = -1;
  int k;
  BN_CTX *ctx = NULL;
  BIGNUM *A1, *A1_odd, *check; /* taken from ctx */
  BN_MONT_CTX *mont = NULL;
  const BIGNUM *A = NULL;

  if (checks == BN_prime_checks)
	checks = BN_prime_checks_for_size(BN_num_bits(a));

  /* first look for small factors */
  if (!BN_is_odd(a))
	return(0);
  if (do_trial_division) {
	for (i = 1; i < NUMPRIMES; i++)
	  if (BN_mod_word(a, primes[i]) == 0)
		return 0;
	if (callback != NULL) callback(1, -1, cb_arg);
  }

  if (ctx_passed != NULL)
	ctx = ctx_passed;
  else {
	if ((ctx=BN_CTX_new()) == NULL)
	  goto err;
  }
  BN_CTX_start(ctx);

  /* A := abs(a) */
  if (a->neg) {
	BIGNUM *t;

	if ((t = BN_CTX_get(ctx)) == NULL)
	  goto err;
	BN_copy(t, a);
	t->neg = 0;
	A = t;
  }
  else
	A = a;

  A1 = BN_CTX_get(ctx);
  A1_odd = BN_CTX_get(ctx);
  check = BN_CTX_get(ctx);
  if (check == NULL)
	goto err;

  /* compute A1 := A - 1 */
  if (!BN_copy(A1, A))
	goto err;
  if (!BN_sub_word(A1, 1))
	goto err;
  if (BN_is_zero(A1)) {
	ret = 0;
	goto err;
  }

  /* write  A1  as  A1_odd * 2^k */
  k = 1;
  while (!BN_is_bit_set(A1, k))
	k++;
  if (!BN_rshift(A1_odd, A1, k))
	goto err;

  /* Montgomery setup for computations mod A */
  mont = BN_MONT_CTX_new();
  if (mont == NULL)
	goto err;
  if (!BN_MONT_CTX_set(mont, A, ctx))
	goto err;

  for (i = 0; i < checks; i++) {
	if (!BN_pseudo_rand(check, BN_num_bits(A1), 0, 0))
	  goto err;
	if (BN_cmp(check, A1) >= 0)
	  if (!BN_sub(check, check, A1))
		goto err;
	if (!BN_add_word(check, 1))
	  goto err;
	/* now 1 <= check < A */

	j = witness(check, A, A1, A1_odd, k, ctx, mont);
	if (j == -1)
	  goto err;
	if (j) {
	  ret=0;
	  goto err;
	}
	if (callback != NULL)
	  callback(1,i,cb_arg);
  }
	ret=1;
err:
	if (ctx != NULL) {
	  BN_CTX_end(ctx);
	  if (ctx_passed == NULL)
		BN_CTX_free(ctx);
	}
	if (mont != NULL)
	  BN_MONT_CTX_free(mont);

	return(ret);
}


#define TABLE_SIZE      32

int
BN_mod_exp_mont(BIGNUM *rr, BIGNUM *a, const BIGNUM *p,
				const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont)
{
  int i, j, bits, ret = 0, wstart, wend, window, wvalue;
  int start = 1, ts = 0;
  BIGNUM *d, *r;
  BIGNUM *aa;
  BIGNUM val[TABLE_SIZE];
  BN_MONT_CTX *mont = NULL;

  bn_check_top(a);
  bn_check_top(p);
  bn_check_top(m);

  if (!(m->d[0] & 1))
	return(0);

  bits = BN_num_bits(p);
  if (bits == 0) {
	BN_one(rr);
	return(1);
  }
  BN_CTX_start(ctx);
  d = BN_CTX_get(ctx);
  r = BN_CTX_get(ctx);
  if (d == NULL || r == NULL)
	goto err;

  /* If this is not done, things will break in the montgomery
   * part */

  if (in_mont != NULL)
	mont=in_mont;
  else {
	if ((mont=BN_MONT_CTX_new()) == NULL)
	  goto err;
	if (!BN_MONT_CTX_set(mont,m,ctx))
	  goto err;
  }

  BN_init(&val[0]);
  ts = 1;
  if (BN_ucmp(a, m) >= 0) {
	if (!BN_mod(&(val[0]), a, m, ctx))
	  goto err;
	aa = &(val[0]);
  }
  else
	aa = a;
  if (!BN_to_montgomery(&(val[0]), aa, mont, ctx)) goto err; /* 1 */

  window = BN_window_bits_for_exponent_size(bits);
  if (window > 1) {
	if (!BN_mod_mul_montgomery(d, &(val[0]), &(val[0]), mont, ctx))
	  goto err; /* 2 */
	j = 1 << (window - 1);
	for (i = 1; i < j; i++) {
	  BN_init(&(val[i]));
	  if (!BN_mod_mul_montgomery(&(val[i]), &(val[i-1]), d, mont, ctx))
		goto err;
	}
	ts = i;
  }

  start = 1;        /* This is used to avoid multiplication etc
					 * when there is only the value '1' in the
					 * buffer. */
  wvalue = 0;       /* The 'value' of the window */
  wstart = bits - 1;  /* The top bit of the window */
  wend = 0;         /* The bottom bit of the window */

  if (!BN_to_montgomery(r, BN_value_one(), mont, ctx))
	goto err;

  for (;;) {
	if (BN_is_bit_set(p, wstart) == 0) {
	  if (!start) {
		if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
		  goto err;
	  }
	  if (wstart == 0)
		break;
	  wstart--;
	  continue;
	}
	/* We now have wstart on a 'set' bit, we now need to work out
	 * how bit a window to do.  To do this we need to scan
	 * forward until the last set bit before the end of the
	 * window */
	j = wstart;
	wvalue = 1;
	wend = 0;
	for (i = 1; i < window; i++) {
	  if (wstart - i < 0)
		break;
	  if (BN_is_bit_set(p, wstart - i)) {
		wvalue <<= (i - wend);
		wvalue |= 1;
		wend = i;
	  }
	}

	/* wend is the size of the current window */
	j = wend + 1;
	/* add the 'bytes above' */
	if (!start) {
	  for (i = 0; i < j; i++)
		{
		  if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
			goto err;
		}
	}

	/* wvalue will be an odd number < 2^window */
	if (!BN_mod_mul_montgomery(r, r, &(val[wvalue>>1]), mont, ctx))
	  goto err;

	/* move the 'window' down further */
	wstart -= wend + 1;
	wvalue = 0;
	start = 0;
	if (wstart < 0)
	  break;
  }
  if (!BN_from_montgomery(rr, r, mont, ctx))
	goto err;
  ret = 1;

err:
	if ((in_mont == NULL) && (mont != NULL))
	  BN_MONT_CTX_free(mont);
	BN_CTX_end(ctx);
	for (i = 0; i < ts; i++)
	  BN_clear_free(&(val[i]));
	return(ret);
}

/* rem != m */
int
BN_mod(BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx)
{
#if 0 /* The old slow way */
  int i, nm, nd;
  BIGNUM *dv;

  if (BN_ucmp(m, d) < 0)
	return((BN_copy(rem, m) == NULL) ? 0 : 1);

  BN_CTX_start(ctx);
  dv=BN_CTX_get(ctx);

  if (!BN_copy(rem, m))
	goto err;

  nm = BN_num_bits(rem);
  nd = BN_num_bits(d);
  if (!BN_lshift(dv, d, nm - nd))
	goto err;
  for (i = nm - nd; i >= 0; i--) {
	if (BN_cmp(rem, dv) >= 0) {
	  if (!BN_sub(rem, rem, dv))
		goto err;
	}
	if (!BN_rshift1(dv, dv))
	  goto err;
  }
  BN_CTX_end(ctx);
  return(1);

 err:
  BN_CTX_end(ctx);
  return(0);
#else
  return(BN_div(NULL, rem, m, d, ctx));
#endif
}
