/*
 * 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 "t_defines.h"
#include "t_pwd.h"
#include "t_read.h"

_TYPE( struct t_conf * )
t_openconf(fp)
     FILE * fp;
{
  struct t_conf * tc;
  char close_flag = 0;

  if(fp == NULL) {	/* NULL means to open the system default file */
    if((fp = fopen(DEFAULT_CONF, "r")) == NULL)
      return NULL;
    close_flag = 1;
  }
  else
    close_flag = 0;	/* If it's a real fd, don't close it automatically */

  if((tc = malloc(sizeof(struct t_conf))) == NULL)
    return NULL;
  tc->instream = fp;
  tc->close_on_exit = close_flag;

  return tc;
}

_TYPE( struct t_conf * )
t_openconfbyname(confname)
     const char * confname;
{
  FILE * fp;
  struct t_conf * t;

  if(confname == NULL)
    return t_openconf(NULL);

  if((fp = fopen(confname, "r")) == NULL)
    return NULL;

  t = t_openconf(fp);
  t->close_on_exit = 1;		/* Since we opened it, we should close it */
  return t;
}

_TYPE( void )
t_closeconf(tc)
     struct t_conf * tc;
{
  if(tc->close_on_exit)
    fclose(tc->instream);
  free(tc);
}

_TYPE( void )
t_rewindconf(tc)
     struct t_conf * tc;
{
  rewind(tc->instream);
}

_TYPE( struct t_confent * )
t_getconfent(tc)
     struct t_conf * tc;
{
  char indexbuf[16];
  char b64buf[MAXB64PARAMLEN];

  while(1) {
    if(t_nextfield(tc->instream, indexbuf, 16) > 0 &&
       (tc->tcbuf.index = atoi(indexbuf)) > 0 &&
       t_nextfield(tc->instream, b64buf, MAXB64PARAMLEN) > 0 &&
       (tc->tcbuf.modulus.len = t_fromb64(tc->modbuf, b64buf)) > 0 &&
       t_nextfield(tc->instream, b64buf, MAXB64PARAMLEN) > 0 &&
       (tc->tcbuf.generator.len = t_fromb64(tc->genbuf, b64buf)) > 0) {
      tc->tcbuf.modulus.data = tc->modbuf;
      tc->tcbuf.generator.data = tc->genbuf;
      t_nextline(tc->instream);
      return &tc->tcbuf;
    }
    else if(t_nextline(tc->instream) < 0)
      return NULL;
  }
}

_TYPE( struct t_confent * )
t_getconflast(tc)
     struct t_conf * tc;
{
  int valid = 0;

  t_rewindconf(tc);
  while(t_getconfent(tc) != NULL)
    valid = 1;
  if(valid)
    return &tc->tcbuf;
  else
    return NULL;
}

_TYPE( struct t_confent * )
t_getconfbyindex(tc, index)
     struct t_conf * tc;
     int index;
{
  char indexbuf[16];
  char b64buf[MAXB64PARAMLEN];
  int tindex;

  t_rewindconf(tc);

  while(t_nextfield(tc->instream, indexbuf, 16) > 0) {
    if((tindex = atoi(indexbuf)) == index)
      if(t_nextfield(tc->instream, b64buf, MAXB64PARAMLEN) > 0 &&
	 (tc->tcbuf.modulus.len = t_fromb64(tc->modbuf, b64buf)) > 0 &&
	 t_nextfield(tc->instream, b64buf, MAXB64PARAMLEN) > 0 &&
	 (tc->tcbuf.generator.len = t_fromb64(tc->genbuf, b64buf)) > 0) {
	tc->tcbuf.index = tindex;
	tc->tcbuf.modulus.data = tc->modbuf;
	tc->tcbuf.generator.data = tc->genbuf;
	t_nextline(tc->instream);
	return &tc->tcbuf;
      }
    if(t_nextline(tc->instream) < 0)
      return NULL;
  }
  return NULL;
}

/*
 * This is the safe prime generation logic.
 * To generate a safe prime p (where p = 2q+1 and q is prime), we start
 * with a random odd q that is one bit shorter than the desired length
 * of p.  We use a simple 30-element sieve to filter the values of q
 * and consider only those that are 11, 23, or 29 (mod 30).  (If q were
 * anything else, either q or p would be divisible by 2, 3, or 5).
 * For the values of q that are left, we apply the following tests in
 * this order:
 *
 *   trial divide q
 *   let p = 2q + 1
 *   trial divide p
 *   apply Fermat test to q (2^q == 2 (mod q))
 *   apply Fermat test to p (2^p == 2 (mod p))
 *   apply real probablistic primality test to q
 *   apply real probablistic primality test to p
 *
 * A number that passes all these tests is considered a safe prime for
 * our purposes.  The tests are ordered this way for efficiency; the
 * slower tests are run rarely if ever at all.
 */

static int
trialdiv(x)
     const BigInteger x;
{
  static int primes[] = {		/* All odd primes < 256 */
      3,   5,   7,  11,  13,  17,  19,  23,  29,
     31,  37,  41,  43,  47,  53,  59,  61,  67,
     71,  73,  79,  83,  89,  93,  97, 101, 103,
    107, 109, 113, 127, 131, 137, 139, 149, 151,
    157, 163, 167, 173, 179, 181, 191, 193, 197,
    199, 211, 223, 227, 229, 233, 239, 241, 251
  };
  static int nprimes = sizeof(primes) / sizeof(int);
  int i;

  for(i = 0; i < nprimes; ++i) {
    if(BigIntegerModInt(x, primes[i]) == 0)
      return primes[i];
  }
  return 1;
}

/* x + sieve30[x%30] == 11, 23, or 29 (mod 30) */

static int sieve30[] =
{  11, 10,  9,  8,  7,  6,  5,  4,  3,  2,
    1, 12, 11, 10,  9,  8,  7,  6,  5,  4,
    3,  2,  1,  6,  5,  4,  3,  2,  1, 12
};

/* Find a Sophie-Germain prime between "lo" and "hi".  NOTE: this is not
   a "safe prime", but the smaller prime.  Take 2q+1 to get the safe prime. */

static void
sophie_germain(q, lo, hi)
     BigInteger q;		/* assumed initialized */
     const BigInteger lo;
     const BigInteger hi;
{
  BigInteger m, p, r;
  char parambuf[MAXPARAMLEN];
  int foundprime = 0;
  int i, mod30;

  m = BigIntegerFromInt(0);
  BigIntegerSub(m, hi, lo);
  i = (BigIntegerBitLen(m) + 7) / 8;
  t_random(parambuf, i);
  r = BigIntegerFromBytes(parambuf, i);
  BigIntegerMod(r, r, m);

  BigIntegerAdd(q, r, lo);
  if(BigIntegerModInt(q, 2) == 0)
    BigIntegerAddInt(q, q, 1);		/* make q odd */

  mod30 = BigIntegerModInt(q, 30);	/* mod30 = q % 30 */

  BigIntegerFree(m);
  m = BigIntegerFromInt(2);			/* m = 2 */
  p = BigIntegerFromInt(0);

  while(BigIntegerCmp(q, hi) < 0) {
    if(trialdiv(q) < 2) {
      BigIntegerMulInt(p, q, 2);			/* p = 2 * q */
      BigIntegerAddInt(p, p, 1);		/* p += 1 */
      if(trialdiv(p) < 2) {
	BigIntegerModExp(r, m, q, q);		/* r = 2^q % q */
	if(BigIntegerCmpInt(r, 2) == 0) {	/* if(r == 2) */
	  BigIntegerModExp(r, m, p, p);		/* r = 2^p % p */
	  if(BigIntegerCmpInt(r, 2) == 0) {	/* if(r == 2) */
	    if(BigIntegerCheckPrime(q) && BigIntegerCheckPrime(p)) {
	      ++foundprime;
	      break;
	    }
	  }
	}
      }
    }

    i = sieve30[mod30];
    BigIntegerAddInt(q, q, i);		/* q += i */
    mod30 = (mod30 + i) % 30;
  }

  /* should wrap around on failure */
  if(!foundprime) {
    fprintf(stderr, "Prime generation failed!\n");
    exit(1);
  }

  BigIntegerFree(r);
  BigIntegerFree(m);
  BigIntegerFree(p);
}

_TYPE( struct t_confent * )
t_makeconfent(tc, nsize)
     struct t_conf * tc;
     int nsize;
{
  BigInteger n, g, q, t, u;

  t = BigIntegerFromInt(0);
  u = BigIntegerFromInt(1);		/* u = 1 */
  BigIntegerLShift(t, u, nsize - 2);	/* t = 2^(nsize-2) */
  BigIntegerMulInt(u, t, 2);		/* u = 2^(nsize-1) */

  q = BigIntegerFromInt(0);
  sophie_germain(q, t, u);

  n = BigIntegerFromInt(0);
  BigIntegerMulInt(n, q, 2);
  BigIntegerAddInt(n, n, 1);

  /* Look for a generator mod n */
  g = BigIntegerFromInt(2);
  while(1) {
    BigIntegerModExp(t, g, q, n);		/* t = g^q % n */
    if(BigIntegerCmpInt(t, 1) == 0)		/* if(t == 1) */
      BigIntegerAddInt(g, g, 1);		/* ++g */
    else
      break;
  }
  BigIntegerFree(t);
  BigIntegerFree(u);
  BigIntegerFree(q);

  tc->tcbuf.modulus.data = tc->modbuf;
  tc->tcbuf.modulus.len = BigIntegerToBytes(n, tc->tcbuf.modulus.data);
  BigIntegerFree(n);

  tc->tcbuf.generator.data = tc->genbuf;
  tc->tcbuf.generator.len = BigIntegerToBytes(g, tc->tcbuf.generator.data);
  BigIntegerFree(g);
  
  tc->tcbuf.index = 1;
  return &tc->tcbuf;
}

_TYPE( struct t_confent * )
t_makeconfent_c(tc, nsize)
     struct t_conf * tc;
     int nsize;
{
  BigInteger g, n, p, q, j, k, t, u;
  int psize, qsize;

  psize = nsize / 2;
  qsize = nsize - psize;

  t = BigIntegerFromInt(1);		/* t = 1 */
  u = BigIntegerFromInt(0);
  BigIntegerLShift(u, t, psize - 3);	/* u = t*2^(psize-3) = 2^(psize-3) */
  BigIntegerMulInt(t, u, 3);			/* t = 3*u = 1.5*2^(psize-2) */
  BigIntegerAdd(u, u, t);			/* u += t [u = 2^(psize-1)] */
  j = BigIntegerFromInt(0);
  sophie_germain(j, t, u);

  k = BigIntegerFromInt(0);
  if(qsize != psize) {
    BigIntegerFree(t);
    t = BigIntegerFromInt(1);		/* t = 1 */
    BigIntegerLShift(u, t, qsize - 3);	/* u = t*2^(qsize-3) = 2^(qsize-3) */
    BigIntegerMulInt(t, u, 3);		/* t = 3*u = 1.5*2^(qsize-2) */
    BigIntegerAdd(u, u, t);		/* u += t [u = 2^(qsize-1)] */
  }
  sophie_germain(k, t, u);

  p = BigIntegerFromInt(0);
  BigIntegerMulInt(p, j, 2);		/* p = 2 * j */
  BigIntegerAddInt(p, p, 1);		/* p += 1 */

  q = BigIntegerFromInt(0);
  BigIntegerMulInt(q, k, 2);		/* q = 2 * k */
  BigIntegerAddInt(q, q, 1);		/* q += 1 */

  n = BigIntegerFromInt(0);
  BigIntegerMul(n, p, q);		/* n = p * q */
  BigIntegerMul(u, j, k);		/* u = j * k */

  BigIntegerFree(p);
  BigIntegerFree(q);
  BigIntegerFree(j);
  BigIntegerFree(k);

  g = BigIntegerFromInt(2);		/* g = 2 */

  /* Look for a generator mod n */
  while(1) {
    BigIntegerModExp(t, g, u, n);	/* t = g^u % n */
    if(BigIntegerCmpInt(t, 1) == 0)
      BigIntegerAddInt(g, g, 1);	/* ++g */
    else
      break;
  }

  BigIntegerFree(u);
  BigIntegerFree(t);

  tc->tcbuf.modulus.data = tc->modbuf;
  tc->tcbuf.modulus.len = BigIntegerToBytes(n, tc->tcbuf.modulus.data);
  BigIntegerFree(n);

  tc->tcbuf.generator.data = tc->genbuf;
  tc->tcbuf.generator.len = BigIntegerToBytes(g, tc->tcbuf.generator.data);
  BigIntegerFree(g);
  
  tc->tcbuf.index = 1;
  return &tc->tcbuf;
}

_TYPE( struct t_confent * )
t_newconfent(tc)
    struct t_conf * tc;
{
  tc->tcbuf.index = 0;
  tc->tcbuf.modulus.data = tc->modbuf;
  tc->tcbuf.modulus.len = 0;
  tc->tcbuf.generator.data = tc->genbuf;
  tc->tcbuf.generator.len = 0;
  return &tc->tcbuf;
}

_TYPE( int )
t_cmpconfent(cf1, cf2)
     const struct t_confent * cf1;
     const struct t_confent * cf2;
{
  int diff;

  diff = cf1->modulus.len - cf2->modulus.len;
  if(diff != 0)
    return diff;
  diff = cf1->generator.len - cf2->generator.len;
  if(diff != 0)
    return diff;
  diff = memcmp(cf1->modulus.data, cf2->modulus.data, cf1->modulus.len);
  if(diff != 0)
    return diff;
  return memcmp(cf1->generator.data, cf2->generator.data, cf1->generator.len);
}

_TYPE( void )
t_putconfent(ent, fp)
     const struct t_confent * ent;
     FILE * fp;
{
  char strbuf[MAXB64PARAMLEN];

  fprintf(fp, "%d:%s:", ent->index,
	  t_tob64(strbuf, ent->modulus.data, ent->modulus.len));
  fprintf(fp, "%s\n",
	  t_tob64(strbuf, ent->generator.data, ent->generator.len));
}

static int
t_isprime(x)
     BigInteger x;
{
  BigInteger t;
  int iscomp;

  if(trialdiv(x) > 1)
    return 0;

  t = BigIntegerFromInt(2);
  BigIntegerModExp(t, t, x, x);
  iscomp = (BigIntegerCmpInt(t, 2) != 0);
  BigIntegerFree(t);
  if(iscomp)
    return 0;
  else
    return 1;
}

_TYPE( int )
t_checkprime(num)
     const struct t_num * num;
{
  BigInteger x, halfx;
  int retval;

  retval = NUM_SAFE;
  x = BigIntegerFromBytes(num->data, num->len);
  if(!t_isprime(x))
    retval = NUM_NOTPRIME;
  else {
    halfx = BigIntegerFromInt(0);
    BigIntegerSubInt(x, x, 1);
    BigIntegerDivInt(halfx, x, 2);
    if(!t_isprime(halfx))
      retval = NUM_NOTSAFE;
    BigIntegerFree(halfx);
  }
  BigIntegerFree(x);
  return retval;
}

/* System conf file accessors */

#include "nys_config.h"

static struct t_conf * sysconf = NULL;

static int
confinit()
{
  if(sysconf == NULL) {
    if((sysconf = t_openconfbyname(DEFAULT_CONF)) == NULL)
      return -1;
  }
  return 0;
}

#ifdef ENABLE_NSW
struct t_confent *
_gettcent
#else
_TYPE( struct t_confent * )
gettcent
#endif
()
{
  if(confinit() < 0)
    return NULL;
  return t_getconfent(sysconf);
}

#ifdef ENABLE_NSW
struct t_confent *
_gettcid
#else
_TYPE( struct t_confent * )
gettcid
#endif
(id)
     int id;
{
  if(confinit() < 0)
    return NULL;
  return t_getconfbyindex(sysconf, id);
}

#ifdef ENABLE_NSW
void
_settcent
#else
_TYPE( void )
settcent
#endif
()
{
  if(confinit() < 0)
    return;
  t_rewindconf(sysconf);
}

#ifdef ENABLE_NSW
void
_endtcent
#else
_TYPE( void )
endtcent
#endif
()
{
  if(sysconf != NULL) {
    t_closeconf(sysconf);
    sysconf = NULL;
  }
}
