
/*
 * Copyright (c) 2003-2016
 * Distributed Systems Software.  All rights reserved.
 * See the file BN-LICENSE for redistribution information.
 *
 * $Id: main.c 2896 2016-06-08 19:28:07Z brachman $
 */

#include "bn.h"

#include <stdlib.h>
#include <stdio.h>

static void
crypto_randomize_buffer(unsigned char *rbuf, size_t len)
{

  BigIntegerRandom(rbuf, len);
}

/*
 * Return 0 if GENERATOR can be used with MODULUS, -1 otherwise.
 */
int
dh_check_generator(BigInteger generator, BigInteger modulus)
{
  BigInteger mm1, p, pp, v;
  BigIntegerCtx bctx;

  bctx = BigIntegerCtxNew();

  pp = BigIntegerFromInt(0);
  BigIntegerSubInt(pp, modulus, 1);

  p = BigIntegerFromInt(0);
  BigIntegerDivInt(p, pp, 2, NULL);

  v = BigIntegerFromInt(0);
  BigIntegerModExp(v, generator, p, modulus, bctx, NULL);

  mm1 = BigIntegerFromInt(0);
  BigIntegerSubInt(mm1, modulus, 1);

  if (BigIntegerCmp(v, mm1) != 0)
	return(-1);

  return(0);
}

/*
 * Return 0 if MODULUS is suitable, -1 otherwise.
 */
int
dh_check_modulus(BigInteger modulus)
{
  BigInteger rem, twelve;
  BigIntegerCtx bctx;

  bctx = BigIntegerCtxNew();

  rem = BigIntegerFromInt(0);
  twelve = BigIntegerFromInt(12);

  BigIntegerMod(rem, modulus, twelve, bctx);

  if (BigIntegerCmpInt(rem, 11) != 0)
	return(-1);

  return(0);
}

/*
 * Compute the Diffie-Hellman public key for the given private key
 * and parameters.  This is the key that one party sends to the other.
 */
BigInteger
dh_pub(BigInteger modulus, BigInteger generator, BigInteger priv)
{
  BigInteger pub;
  BigIntegerCtx bctx;

  bctx = BigIntegerCtxNew();

  pub = BigIntegerFromInt(0);
  BigIntegerModExp(pub, generator, priv, modulus, bctx, NULL);

  return(pub);
}

/*
 * Compute the Diffie-Hellman shared key to go with our private key
 * and our partner's public key.
 */
BigInteger
dh_shared_key(BigInteger modulus, BigInteger pub, BigInteger priv)
{
  BigInteger k;
  BigIntegerCtx bctx;

  bctx = BigIntegerCtxNew();

  k = BigIntegerFromInt(0);
  BigIntegerModExp(k, pub, priv, modulus, bctx, NULL);

  return(k);
}

/*
 * Cryptography and Network Security: Principles and Practice
 * Second Edition
 * William Stallings
 * p 192
 */
int
dh_example(int do_rand, int mod_arg)
{
  int m, nbytes;
  BigInteger A, B, a, b, generator, AK, BK, modulus;
  BigIntegerCtx bctx;
  static unsigned char rbuf[512];

  bctx = BigIntegerCtxNew();

  /*
   * The modulus is an agreed upon large (safe) prime number, at least 2048 bits.
   * For instance:
   *   openssl dhparam -5 -C 2048
   * A safe prime q is a prime number of the form 2p + 1, where p is a prime.
   * A safe prime q > 7 will be of the form q == 11 (mod 12).
   * g is a generator if g^p == -1 (mod q)
   *
   * Also see RFC 3526:
   *   http://tools.ietf.org/html/rfc3526
   * Also see OpenSSL: BN_generate_prime_ex()
   *
   * Examples: 11, 23, 47, 59, 83, 107, 983, 1019, 1187, 2903
   * modulus = BigIntegerFromInt(97);
   * modulus = BigIntegerFromInt(107);
   */
  if (mod_arg != 0)
	m = mod_arg;
  else
	m = 23;

  modulus = BigIntegerFromInt(m);

  if (dh_check_modulus(modulus) == -1) {
	fprintf(stderr, "An invalid modulus has been configured: %s\n",
			BigIntegerToDec(modulus));
	exit(1);
  }
  nbytes = (m + 255) / 256;

  /* The base, which must be a primitive root wrt the modulus. */
  generator = BigIntegerFromInt(5);

  if (dh_check_generator(generator, modulus) == -1) {
	fprintf(stderr, "An invalid generator has been configured: %s\n",
			BigIntegerToDec(generator));
	exit(1);
  }

  fprintf(stderr, "Using Modulus=%s, Generator=%s\n",
		  BigIntegerToDec(modulus), BigIntegerToDec(generator));
  fprintf(stderr, "\n");

  if (do_rand) {
	crypto_randomize_buffer(rbuf, sizeof(rbuf));
	a = BigIntegerFromBytes(rbuf, nbytes);
  }
  else
	a = BigIntegerFromInt(36);
  BigIntegerMod(a, a, modulus, bctx);
  fprintf(stderr, "Alice's secret number is %s\n", BigIntegerToDec(a));

  if (do_rand) {
	crypto_randomize_buffer(rbuf, sizeof(rbuf));
	b = BigIntegerFromBytes(rbuf, nbytes);
  }
  else
	b = BigIntegerFromInt(58);
  BigIntegerMod(b, b, modulus, bctx);
  fprintf(stderr, "Bob's secret number is %s\n", BigIntegerToDec(b));

  A = dh_pub(modulus, generator, a);
  fprintf(stderr, "Alice gives Bob %s\n", BigIntegerToDec(A));

  B = dh_pub(modulus, generator, b);
  fprintf(stderr, "Bob gives Alice %s\n", BigIntegerToDec(B));

  AK = dh_shared_key(modulus, B, a);
  fprintf(stderr, "Alice uses the secret key %s\n", BigIntegerToDec(AK));

  BK = dh_shared_key(modulus, A, b);
  fprintf(stderr, "Bob uses the secret key %s\n", BigIntegerToDec(BK));

  if (BigIntegerCmp(AK, BK) != 0)
	fprintf(stderr, "Ooops, something is wrong!\n");
  else
	fprintf(stderr, "They both know the same secret key!\n");

  return(0);
}

#ifdef PROG
int
main(int argc, char **argv)
{
  int do_rand;

  if (argv[1] != NULL && strcmp(argv[1], "norand") == 0) {
	do_rand = 0;
	argv++;
  }
  else
	do_rand = 1;

  if (argv[1] != NULL)
	dh_example(do_rand, atoi(argv[1]));
  else
	dh_example(do_rand, 107);

  exit(0);
}
#endif
