
/*
 * mkkey.c -- create the public, private, and modulo keys for
 * netrek RSA verification and write out the info a server needs
 * and generate the rsa-client.c client source file.
 *
 * Compile with something like:
 * cc -O -o mkkey mkkey.c -lmp
 * (if you're using GNU MP then:
 * cc -O -o mkkey mkkey.c -lmp -lgmp
 * mkkey needs stuff in gmp as well.)
 *
 * You can tell mkkey to read in an old-style keys.h file with the
 * -h option, or read in a secret keycap file with the -k option.
 * By default, mkkey will generate a new key.
 *
 * mkkey will normally generate 3 files: rsa-client.c, key_name, and
 * key_name.secret.  key_name is the keycap for the key, key_name.secret
 * is the keycap with the additional field sk, which holds the secret
 * (private) key.  If you used the -k option, only rsa-client.c is
 * generated.
 *
 * This is a merger of genkey.c (author: Ray Jones) and mkrsaclient.c
 * plus too many features.
 *
 * NOTE: the generated rsa-client.c cannot be used with the old
 * rsa_util{mp}.c, you must use rsa_clientutil.c.
 *
 * Author: Sam Shen (sls@aero.org)
 */

/* if you hack mkkey and release it change the following line in
 * some reasonable way, perhaps like this:
 * static char version[] = "[atm: July 4, 1993] based on [sls: June 7, 1993]";
 */
static char version[] = "[jmn,jaw,nbt: June 22, 1993] based on [sls: June 12, 1993]";

#include "config.h"
#include <mp.h>

#ifdef __GNU_MP__
#include <gmp.h>
#endif

#include <fcntl.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>

#include <sys/param.h>
#include <sys/stat.h>
#include <time.h>
#include INC_SYS_TIME
#include <ctype.h>
#include "defs.h"

/*extern long random();
   extern void srandom();
 */
extern char *malloc ();
extern void free ();
extern int getpid ();

#define SIZE 32
#define HALF 16

#ifndef mfree
#define mfree
#endif

static MINT *zero;

#define check_positive(n) assert(mcmp(n, zero) >= 0)

void
rand_raw (str, num)
     unsigned char *str;
     int num;
{
  int i;

  for (i = 0; i < num - 1; i++)
    str[i] = RANDOM () & 0xFF;

  /* force it to be num digits long */
  str[i] = 0;
  while (!str[i])
    str[i] = RANDOM () & 0xFF;
  i++;

  for (; i < SIZE; i++)
    str[i] = 0;
}

void
raw_to_num (out, in)
     MINT *out;
     unsigned char *in;
{
  int i;
  MINT *temp, *twofiftysix, *thisval;

  temp = itom (0);
  mult (temp, temp, out);	/* IckyIcky how do you zero in libmp? */
  twofiftysix = itom (256);

  for (i = 0; i < SIZE; i++)
    {
      mult (temp, twofiftysix, temp);
      thisval = itom (in[SIZE - i - 1]);
      madd (temp, thisval, temp);
      mfree (thisval);
    }
  madd (temp, out, out);	/* There is no copy function! */
  mfree (temp);
  mfree (twofiftysix);
}

void
num_to_raw (out, in)		/* Destroys in */
     unsigned char *out;
     MINT *in;
{
  int i;
  short temp;

  for (i = 0; i < SIZE; i++)
    {
      if (!mcmp (in, zero))
	temp = 0;
      else
	sdiv (in, 256, in, &temp);
      out[i] = temp & 0xFF;
    }
}

#ifdef __GNU_MP__
#define is_prime(n) mpz_probab_prime_p(n, 50)
#else
int
is_prime (n)
     MINT *n;
{
  int i;
  short j;
  MINT *temp1, *temp2;

  temp2 = itom (0);

  /* 25 is quite a lot, actually */
  for (i = 0; i < 25; i++)
    {
      /*
         * We should choose a number that is between 0 and n, but this
         * works well enough.
       */
      j = (short) RANDOM ();
      if (j < 0)
	j = (-j);
      temp1 = itom (j);
      pow (temp1, n, n, temp2);
      if (mcmp (temp1, temp2))
	{
	  mfree (temp1);
	  mfree (temp2);
	  return (0);
	}
      mfree (temp1);
    }
  mfree (temp2);
  return (1);
}

#endif

static void
mmod (a, b, c)
     MINT *a, *b, *c;
{
  MINT *quotient = itom (0);
  mdiv (a, b, quotient, c);
  mfree (quotient);
}

#ifdef __GNU_MP__
/*
 * GNU MP (and possibly other implementations of mp) does not have
 * invert so this was transliterated from the equivalent routine in
 * PGP 2.2
 *
 * rjones had an my_invert here but it didn't seem to work
 */

#define move(src, dest) madd(src, zero, dest)

#define iplus1  ( i==2 ? 0 : i+1 )	/* used by Euclid algorithms */
#define iminus1 ( i==0 ? 2 : i-1 )	/* used by Euclid algorithms */

static void
invert (a, n, x)
     MINT *x, *a, *n;
{
  MINT *g[3];
  MINT *v[3];
  MINT *y;
  MINT *temp;
  MINT *temp2;
  int i;
  y = itom (0);
  temp = itom (0);
  temp2 = itom (0);
  for (i = 0; i < 3; i++)
    g[i] = itom (0);
  move (n, g[0]);
  move (a, g[1]);
  v[0] = itom (0);
  v[1] = itom (1);
  v[2] = itom (0);
  i = 1;
  while (mcmp (g[i], zero))
    {
      check_positive (g[iminus1]);
      check_positive (g[i]);
      mdiv (g[iminus1], g[i], y, g[iplus1]);
      mult (y, v[i], temp);
      move (v[iminus1], v[iplus1]);
      msub (v[iplus1], temp, temp2);
      move (temp2, v[iplus1]);
      i = iplus1;
    }
  move (v[iminus1], x);
  while (mcmp (x, zero) < 0)
    {
      madd (x, n, temp2);
      move (temp2, x);
    }
  for (i = 0; i < 3; i++)
    {
      mfree (v[i]);
      mfree (g[i]);
    }
  mfree (y);
  mfree (temp);
  mfree (temp2);
}

#endif

/* verify_key checks to make sure a RSA key actually works
 */
void
verify_key (raw_public, raw_private, raw_global)
     unsigned char *raw_public, *raw_private, *raw_global;
{
  MINT *orig_message = itom (0);
  MINT *message = itom (0);
  MINT *crypt_text = itom (0);
  MINT *public = itom (0);
  MINT *private = itom (0);
  MINT *global = itom (0);
  int failures = 0;
  int i;
  unsigned char temp[SIZE];

  printf ("Testing key");
  fflush (stdout);
  raw_to_num (public, raw_public);
  raw_to_num (private, raw_private);
  raw_to_num (global, raw_global);
  for (i = 0; i < 50; i++)
    {
      rand_raw (temp, SIZE - 1);
      raw_to_num (orig_message, temp);
      pow (orig_message, private, global, crypt_text);
      pow (crypt_text, public, global, message);
      if (mcmp (message, orig_message) != 0)
	{
	  putchar ('!');
	  failures++;
	}
      else
	{
	  putchar ('.');
	}
      fflush (stdout);
    }
  if (failures == 0)
    printf ("key seems o.k.\n");
  else
    {
      printf ("\nSorry, key is bogus.\n");
      exit (1);
    }
  mfree (orig_message);
  mfree (message);
  mfree (crypt_text);
  mfree (public);
  mfree (private);
  mfree (global);
}

/* kgetkeyname, kgetstr and kgetkey are stolen from Ted Hadley's
 * rsa_keycomp.c
 */

#define SEP                     ':'
#define GLOBAL_KEY_FIELD        "gk="
#define PUBLIC_KEY_FIELD        "pk="
#define SECRET_KEY_FIELD        "sk="
#define CLIENT_TYPE_FIELD       "ct="
#define CREATOR_FIELD           "cr="
#define CREATED_FIELD           "cd="
#define ARCH_TYPE_FIELD         "ar="
#define COMMENTS_FIELD          "cm="
#define KEY_SIZE                SIZE

/*
 * Extract the key name descriptor from an entry buffer
 * "Key Name:"
 */

int
kgetkeyname (src, dest)
     char *src;
     char *dest;
{
  int l = strchr (src, SEP) - src;
  STRNCPY (dest, src, l);
  dest[l] = 0;
  return 1;
}

/*
 * Place contents of specified field entry into destination string.
 * "pk=Text Text Text:"
 */

int
kgetstr (src, field, dest)
     char *src, *field, *dest;
{
  char *s = (char *) strstr (src, field);
  int l;
  if (!s)
    return 0;

  s += strlen (field);
  l = strchr (s, SEP) - s;
  STRNCPY (dest, s, l);
  dest[l] = 0;
  return 1;
}

/*
 * Place contents of specified binary field entry into destination string.
 * "pk=Text Text Text:"
 */

int
kgetkey (src, field, dest)

     char *src, *field;
     unsigned char *dest;
{
  char unencoded_dest[KEY_SIZE * 2 + 1], uc[3];
  register char *s;
  unsigned int c;

  if (!kgetstr (src, field, unencoded_dest))
    return 0;

  uc[2] = '\0';
  /* convert encoded binary to binary */
  s = unencoded_dest;
  while (*s)
    {
      uc[0] = *s++;
      uc[1] = *s++;
      sscanf (uc, "%x", &c);
      *dest++ = (unsigned char) c;
    }
  return 1;
}

int
scan_array_element (buffer, xp, np)
     char *buffer;
     int *xp;
     int *np;
{
  char *bp = buffer;
  while (*bp && isspace (*bp))
    bp++;
  if (*bp == '0' && *(bp + 1) == 'x')
    {
      if (sscanf (bp + 2, "%x", xp) != 1)
	return 0;
      bp += 2;
    }
  else if (sscanf (bp, "%d", xp) != 1)
    return 0;
  while (*bp && isxdigit (*bp))
    bp++;
  while (*bp && *bp != ',')
    bp++;
  bp++;
  *np = bp - buffer;
  return 1;
}

/* look through the text in buffer for name, and read in len comma
 * separated ints into data
 */
void
get_array (buffer, name, data, len)
     char *buffer;
     char *name;
     unsigned char *data;
     int len;
{
  int i, n, x;
  char *start;
  start = (char *) strstr (buffer, name);
  if (start == NULL)
    goto err;
  while (*start && *start != '{')
    start++;
  if (!*start)
    goto err;
  start++;
  for (i = 0; i < len; i++)
    {
      if (scan_array_element (start, &x, &n) != 1)
	goto err;
      data[i] = x;
      start += n;
    }
  return;
err:
  fprintf (stderr, "Ooops! Couldn't get array %s in keys.h file.\n",
	   name);
  exit (1);
}

static char *header = "\
/* rsa_key.c\n\
 * \n\
 * Ray Jones \n\
 * DO NOT EDIT THIS FILE!!! GENERATED AUTOMATICALLY!!! \n\
 */\n\
#include \"config.h\"\n\
#include \"copyright2.h\"\n\
#include <stdio.h>\n\
#include <sys/types.h>\n\
#include <sys/socket.h>\n\
#include <netinet/in.h>\n\
#include <netdb.h>\n\
#include \"defs.h\"\n\
#include \"packets.h\"\n\
#include \"Wlib.h\"\n\
#include \"struct.h\"\n\
#include \"data.h\"\n\
\n\
#ifdef RSA_DEBUG\n\
void rsa_dump(msg, stuff, len)\n\
     char* msg;\n\
     unsigned char* stuff;\n\
     int len;\n\
{\n\
    int i;\n\
    printf(\"%s\", msg);\n\
    for (i = 0; i < len; i++)\n\
	printf(\"%02x\", stuff[i]);\n\
    printf(\"\\n\");\n\
}\n\
#endif\n\
\n\
#ifdef GATEWAY\n\
extern unsigned long netaddr;\n\
extern int serv_port;\n\
#endif\n\
\n\
#ifdef RSA_DEBUG\n\
unsigned char straight_private[KEY_SIZE];\n\
#endif\n\
\n\
\n\
encryptRSAPacket(spacket, cpacket)\n\
struct rsa_key_spacket *spacket;\n\
struct rsa_key_cpacket *cpacket;\n\
{\n\
    struct sockaddr_in saddr;\n\
    int len, i;\n\
    u_short port;\n\
    unsigned char *data;\n\
    unsigned char key_private[2*KEY_SIZE];\n\
    unsigned char key_public[2*KEY_SIZE];\n\
    unsigned char key_global[2*KEY_SIZE];\n\
    int key_private_ndx[2*KEY_SIZE];\n\
    int key_global_ndx[2*KEY_SIZE];\n\
    int key_public_ndx[2*KEY_SIZE];\n\
\n\
#ifndef RSA_TEST\n\
#ifdef GATEWAY\n\
    /* if we didn't get it from -H, go ahead and query the socket */\n\
    if (netaddr == 0) {\n\
        len = sizeof(saddr);\n\
        if (getpeername(sock,(struct sockaddr*)&saddr, &len) < 0) {\n\
            perror(\"getpeername(sock)\");\n\
            exit(1);\n\
        }\n\
    } else {\n\
        saddr.sin_addr.s_addr = htonl(netaddr);\n\
        saddr.sin_port = htons(serv_port);\n\
    }\n\
#else\n\
        /* query the socket to determine the remote host (ATM) */\n\
    len = sizeof(saddr);\n\
    if (getpeername(sock,(struct sockaddr*) &saddr, &len) < 0) {\n\
        perror(\"getpeername(sock)\");\n\
        exit(1);\n\
    }\n\
#endif\n\
\n\
    /* replace the first few bytes of the message */\n\
    /* will be the low order bytes of the number */\n\
    data = spacket->data;\n\
    MCOPY (&saddr.sin_addr.s_addr, data, sizeof(saddr.sin_addr.s_addr));\n\
    data += sizeof(saddr.sin_addr.s_addr);\n\
    MCOPY (&saddr.sin_port, data, sizeof(saddr.sin_port));\n\
#endif\n\
\n\
";

static char *trailer = "\
    for (i = 0; i < KEY_SIZE; i++) {\n\
        cpacket->global[i] = key_global[key_global_ndx[i]];\n\
        cpacket->public[i] = key_public[key_public_ndx[i]];\n\
    }\n\
#ifdef RSA_DEBUG\n\
    {\n\
      for (i = 0; i < KEY_SIZE; i++)\n\
          straight_private[i] = key_private[key_private_ndx[i]];\n\
      rsa_dump(\"global key: \", cpacket->global, KEY_SIZE);\n\
      rsa_dump(\"public key: \", cpacket->public, KEY_SIZE);\n\
      rsa_dump(\"private key: \", straight_private, KEY_SIZE);\n\
   }\n\
#endif\n\
    rsa_client_encode(cpacket->resp, spacket->data, key_private,\n\
                      key_private_ndx, cpacket->global, KEY_SIZE);\n\
}\n\
";

#define swap(a,b) \
    do { int tmp; tmp = a; a = b; b = tmp; } while (0)

/* the idea here is to write a bunch of statements that assign to
 * name[0..len-1] to key[0..len-1].  Three things are done to make the
 * sequence difficult to follow: 1, the assignements are done in
 * RANDOM order.  2, key[n] is written as the sum of 1 to 4
 * of (RANDOM n in 1..31) * key[0..n-1] and a constant.  And 3,
 * the indexes of key are permuted and written in the array ndx_name.
 *
 * Because of the way rsa_client_encode uses the index, the indexes are
 * 2*len long.
 */
void
gen_shuffled_key (fp, name, key, len, ndx_name)
     FILE *fp;
     char *name;
     unsigned char *key;
     int len;
     char *ndx_name;
{
  int i, j, k, n, sum, f;
  int *ndx, *ndx2, *ndx3;
  ndx = (int *) malloc (len * sizeof (int));
  assert (ndx != NULL);
  ndx2 = (int *) malloc (2 * len * sizeof (int));
  assert (ndx2 != NULL);
  ndx3 = (int *) malloc (2 * len * sizeof (int));
  assert (ndx3 != NULL);
  /* create a permuted index in ndx, this is used to randomize
     * the order in which we do the assignements to name
   */
  for (i = 0; i < len; i++)
    ndx[i] = i;
  for (i = 0; i < len; i++)
    {
      j = RANDOM () % len;
      swap (ndx[i], ndx[j]);
    }
  /* do the same for ndx2, ndx2 will be written out to ndx_name.
   */
  for (i = 0; i < 2 * len; i++)
    ndx2[i] = i;
  for (i = 0; i < 2 * len; i++)
    {
      j = RANDOM () % len;
      swap (ndx2[i], ndx2[j]);
    }
  /* now write out assignments to name */
  for (i = 0; i < len; i++)
    {
      fprintf (fp, "    %s[%d] = ", name, ndx2[ndx[i]]);
      sum = 0;
      if (i > 0)
	{
	  n = (RANDOM () % 4) + 1;
	  for (j = 0; j < n; j++)
	    {
	      k = RANDOM () % i;
	      f = (RANDOM () % 30) + 1;
	      fprintf (fp, "%d * %s[%d] + ", f, name, ndx2[ndx[k]]);
	      sum += f * key[ndx[k]];
	    }
	}
      fprintf (fp, "%d;\n", key[ndx[i]] - sum);
    }
  /* create a permuted index for ndx3, this is used to randomize
     * the order in which we do the assignments to ndx_name
   */
  for (i = 0; i < 2 * len; i++)
    ndx3[i] = i;
  for (i = 0; i < 2 * len; i++)
    {
      j = RANDOM () % len;
      swap (ndx3[i], ndx3[j]);
    }
  /* now write out the index assignments */
  for (i = 0; i < 2 * len; i++)
    {
      fprintf (fp, "    %s[%d] = ", ndx_name, ndx3[i]);
      sum = 0;
      if (i > 0)
	{
	  n = (RANDOM () % 4) + 1;
	  for (j = 0; j < n; j++)
	    {
	      k = RANDOM () % i;
	      f = (RANDOM () % 30) + 1;
	      fprintf (fp, "%d * %s[%d] + ", f, ndx_name, ndx3[k]);
	      sum += f * ndx2[ndx3[k]];
	    }
	}
      fprintf (fp, "%d;\n", ndx2[ndx3[i]] - sum);
    }
  free (ndx3);
  free (ndx2);
  free (ndx);
}

char *
allocbuf ()
{
  char *buf = (char *) malloc (BUFSIZ);
  assert (buf != NULL);
  return buf;
}

int
main (argc, argv)
     int argc;
     char *argv[];
{
  MINT *x, *y, *global, *xyminus1, *private, *public;
  MINT *one, *two;
  unsigned char temp[SIZE];
  unsigned char raw_global[SIZE], raw_private[SIZE], raw_public[SIZE];
  FILE *clientsfp;		/* clientsfp is the rsa-client.c file */
  FILE *keycapfp;		/* keycapfp is the keycap file */
  FILE *skeycapfp;		/* skeycapfp is the keycap file with
				 * the secret key included
				 */
  int i, n, verify_only;
  char buf[200];
  time_t today;
  char *key_name, *client_type, *architecture, *class, *creator, *comments;
  char *created;
  char *keydoth, *keycap;

  SRANDOM (time (0) ^ (getpid () << 16));

  printf ("mkkey version %s\n", version);
  created = NULL;
  keydoth = NULL;
  keycap = NULL;
  verify_only = 0;
  zero = itom (0);
  n = 1;
  while (argc > n)
    {
      if (!strcmp (argv[n], "-h"))
	{
	  keydoth = argv[n + 1];
	  n += 2;
	}
      else if (!strcmp (argv[n], "-k"))
	{
	  keycap = argv[n + 1];
	  n += 2;
	}
      else if (!strcmp (argv[n], "-v"))
	{
	  verify_only = 1;
	  n++;
	}
      else
	break;
    }
  if (keycap != NULL && keydoth != NULL)
    {
      fprintf (stderr, "%s: can't use both a keys.h file and keycap file\n",
	       argv[0]);
      exit (1);
    }
  if (keycap == NULL && verify_only == 0 && (argc - n) != 6)
    {
      fprintf (stderr,
	       "usage: %s [-h keys.h-file] key-name client-type architecture/OS creator class comments | -k secret-keycap-file\n",
	       argv[0]);
      exit (1);
    }

  if (keycap == NULL)
    {
      key_name = argv[n];
      client_type = argv[n + 1];
      architecture = argv[n + 2];
      creator = argv[n + 3];
      class = argv[n + 4];
      comments = argv[n + 5];
    }

  if (keydoth == NULL && keycap == NULL)
    {
      printf ("Making new key, hold on....\n");

#ifdef __GNU_MP__
      printf ("(using GNU MP)\n");
#else
      printf ("(using the system's mp)\n");
#endif

      one = itom (1);
      two = itom (2);
      x = itom (0);
      y = itom (0);
      global = itom (0);
      private = itom (0);
      public = itom (0);
      xyminus1 = itom (0);

      /*
         * here we find x and y, two large primes
       */
      rand_raw (temp, HALF);
      temp[0] |= 1;		/* force odd */
      raw_to_num (x, temp);
      while (!is_prime (x))
	madd (x, two, x);
      check_positive (x);
      assert (is_prime (x));

      rand_raw (temp, HALF);
      temp[0] |= 1;		/* force odd */
      raw_to_num (y, temp);
      while (!is_prime (y))
	madd (y, two, y);
      check_positive (y);
      assert (is_prime (y));

      /*
         * the private key is a large prime (it should be the larger
         * than x & y)
       */
      rand_raw (temp, HALF + 1);
      temp[0] |= 1;		/* force odd */
      raw_to_num (private, temp);
      while (!is_prime (private))
	madd (private, two, private);
      check_positive (private);
      assert (is_prime (private));
      if (mcmp (x, private) > 0)
	{
	  MINT *tmp;
	  tmp = x;
	  x = private;
	  private = tmp;
	}
      if (mcmp (y, private) > 0)
	{
	  MINT *tmp;
	  tmp = y;
	  y = private;
	  private = tmp;
	}
      assert (mcmp (private, x) > 0);
      assert (mcmp (private, y) > 0);

      /*
         * the modulus global is x * y
       */
      mult (x, y, global);
      check_positive (global);

      /*
         * the public key is such that
         * (public * private) mod ((x - 1) * (y - 1)) == 1
       */
      msub (x, one, x);
      msub (y, one, y);
      mult (x, y, xyminus1);
      assert (!is_prime (xyminus1));
      invert (private, xyminus1, public);
      check_positive (public);

      /* check to make sure the invert worked */
      {
	MINT *ps;
	MINT *m;
	MINT *one;
	ps = itom (0);
	one = itom (1);
	m = itom (0);
	mult (private, public, ps);
	mmod (ps, xyminus1, m);
	if (mcmp (m, one) != 0)
	  {
	    printf ("Ooops! invert failed!\n");
	    exit (1);
	  }
	mfree (ps);
	mfree (m);
	mfree (one);
      }

      /*
         * convert to raw format
       */
      num_to_raw (raw_global, global);
      num_to_raw (raw_public, public);
      num_to_raw (raw_private, private);

      /* cleanup */
      mfree (one);
      mfree (two);
      mfree (x);
      mfree (y);
      mfree (global);
      mfree (public);
      mfree (private);
      mfree (xyminus1);
    }
  else if (keydoth != NULL)
    {
      FILE *fp = fopen (keydoth, "r");
      char *buffer;
      struct stat statbuf;
      printf ("Reading old key from keys.h file \"%s\"...\n", keydoth);
      if (fp == NULL)
	{
	  printf ("%s: can't open keys.h file \"%s\"\n", argv[0],
		  keydoth);
	  perror ("");
	  exit (1);
	}
      if (fstat (fileno (fp), &statbuf) < 0)
	{
	  perror ("fstat");
	  exit (1);
	}
      buffer = (char *) malloc (statbuf.st_size);
      assert (buffer != NULL);
      fread (buffer, 1, statbuf.st_size, fp);
      fclose (fp);
      get_array (buffer, "key_global", raw_global, SIZE);
      get_array (buffer, "key_public", raw_public, SIZE);
      get_array (buffer, "key_private", raw_private, SIZE);
      free (buffer);
    }
  else
    {
      char *buffer;
      struct stat statbuf;
      FILE *fp = fopen (keycap, "r");
      printf ("Reading key from keycap file \"%s\"...\n", keycap);
      if (fp == NULL)
	{
	  printf ("%s: can't open keycap file \"%s\"\n", argv[0], keycap);
	  perror ("");
	  exit (1);
	}
      if (fstat (fileno (fp), &statbuf) < 0)
	{
	  perror ("fstat");
	  exit (1);
	}
      buffer = (char *) malloc (statbuf.st_size);
      assert (buffer != NULL);
      fread (buffer, 1, statbuf.st_size, fp);
      fclose (fp);
      key_name = allocbuf ();
      client_type = allocbuf ();
      architecture = allocbuf ();
      creator = allocbuf ();
      created = allocbuf ();
      comments = allocbuf ();
      if (!kgetkeyname (buffer, key_name) ||
	  !kgetstr (buffer, CLIENT_TYPE_FIELD, client_type) ||
	  !kgetstr (buffer, CREATOR_FIELD, creator) ||
	  !kgetstr (buffer, CREATED_FIELD, created) ||
	  !kgetstr (buffer, ARCH_TYPE_FIELD, architecture) ||
	  !kgetstr (buffer, COMMENTS_FIELD, comments) ||
	  !kgetkey (buffer, GLOBAL_KEY_FIELD, raw_global) ||
	  !kgetkey (buffer, PUBLIC_KEY_FIELD, raw_public) ||
	  !kgetkey (buffer, SECRET_KEY_FIELD, raw_private))
	{
	  fprintf (stderr, "%s: can't parse keycap file \"%s\"\n", argv[0],
		   keycap);
	  exit (1);
	}
      free (buffer);
    }

  verify_key (raw_public, raw_private, raw_global);
  if (verify_only)
    exit (0);

  if (keycap == NULL)
    {
      keycapfp = fopen (key_name, "w");
      assert (keycapfp != NULL);
      strcpy (buf, key_name);
      strcat (buf, ".secret");
      skeycapfp = fopen (buf, "w");
      assert (skeycapfp != NULL);
    }
  else
    {
      keycapfp = NULL;
    }
  clientsfp = fopen ("rsa-client.c", "w");
  assert (clientsfp != NULL);

  /*
     * first write out some generic info
   */
  fprintf (clientsfp, "char key_name[] = \"%s\";\n", key_name);
  fprintf (clientsfp, "char client_type[] = \"%s\";\n", client_type);
  fprintf (clientsfp, "char client_arch[] = \"%s\";\n", architecture);
  fprintf (clientsfp, "char client_creator[] = \"%s\";\n", creator);
  fprintf (clientsfp, "char client_comments[] = \"%s\";\n", comments);
  if (created == NULL)
    {
      time (&today);
      strftime (buf, sizeof (buf), "%B %Y", gmtime (&today));
      created = buf;
    }
  fprintf (clientsfp, "char client_key_date[] = \"%s\";\n", created);
  if (keycapfp != NULL)
    {
      fprintf (keycapfp,
	   "%s:ct=%s:cr=%s:\\\n   :cd=%s:ar=%s:class=%s:\\\n   :cm=%s:\\\n",
	key_name, client_type, creator, buf, architecture, class, comments);
      fprintf (skeycapfp,
	   "%s:ct=%s:cr=%s:\\\n   :cd=%s:ar=%s:class=%s:\\\n   :cm=%s:\\\n",
	key_name, client_type, creator, buf, architecture, class, comments);
    }

  /*
     * write the rsa-client.c header
   */
  fprintf (clientsfp, "%s", header);

  /*
     * write the global modulos
   */
  if (keycapfp != NULL)
    {
      fprintf (keycapfp, "   :gk=");
      fprintf (skeycapfp, "   :gk=");
      for (i = 0; i < SIZE; i++)
	{
	  fprintf (keycapfp, "%02x", (int) raw_global[i]);
	  fprintf (skeycapfp, "%02x", (int) raw_global[i]);
	}
      fprintf (keycapfp, ":\\\n");
      fprintf (skeycapfp, ":\\\n");
    }
  gen_shuffled_key (clientsfp, "key_global", raw_global, SIZE,
		    "key_global_ndx");

  /*
     * write the public key
   */
  if (keycapfp != NULL)
    {
      fprintf (keycapfp, "   :pk=");
      fprintf (skeycapfp, "   :pk=");
      for (i = 0; i < SIZE; i++)
	{
	  fprintf (keycapfp, "%02x", (int) raw_public[i]);
	  fprintf (skeycapfp, "%02x", (int) raw_public[i]);
	}
      fprintf (keycapfp, ":\n");
      fprintf (skeycapfp, ":\\\n");
    }
  gen_shuffled_key (clientsfp, "key_public", raw_public, SIZE,
		    "key_public_ndx");

  /*
     * write the private key
   */
  if (keycapfp != NULL)
    {
      fprintf (skeycapfp, "   :sk=");
      for (i = 0; i < SIZE; i++)
	fprintf (skeycapfp, "%02x", (int) raw_private[i]);
      fprintf (skeycapfp, ":\n");
    }
  gen_shuffled_key (clientsfp, "key_private", raw_private, SIZE,
		    "key_private_ndx");

  /*
     * write the rsa-client.c trailer
   */
  fprintf (clientsfp, "%s", trailer);

  /* cleanup */
  fclose (clientsfp);
  if (keycapfp != NULL)
    {
      fclose (keycapfp);
      fclose (skeycapfp);
    }

  exit (0);
}
