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

/*****************************************************************************
 * COPYRIGHT AND PERMISSION NOTICE
 * 
 * Copyright (c) 2001-2003 The Queen in Right of Canada
 * 
 * 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, and/or sell
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, provided that the above copyright notice(s) and this
 * permission notice appear in all copies of the Software and that both the
 * above copyright notice(s) and this permission notice appear in supporting
 * documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE 
 * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of a copyright holder shall not
 * be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization of the
 * copyright holder.
 ***************************************************************************/

/*
 * Make master keys, key creation functions
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2016\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: mkkey.c 2912 2016-10-18 19:54:07Z brachman $";
#endif

#include "dacs.h"

#include <openssl/bn.h>
#include <openssl/rsa.h>

static MAYBE_UNUSED const char *log_module_name = "dacskey";

#ifndef PROG

#ifdef NOTDEF
/* The authentication key we are creating. */
static unsigned char auth_key[AUTH_CRYPT_KEY_LENGTH];

/* The HMAC key we are creating. */
static unsigned char hmac_key[CRYPTO_HMAC_KEY_LENGTH];
#endif

static Crypt_keys *
crypt_keys_init(void)
{
  Crypt_keys *ck;

  ck = ALLOC(Crypt_keys);
  ck->fed_id = NULL;

  ck->auth_key = NULL;
  ck->auth_key_len = 0;
  ck->auth_key_str = NULL;

  ck->hmac_key = NULL;
  ck->hmac_key_len = 0;
  ck->hmac_key_str = NULL;

  ck->private_key_pem = NULL;
  ck->private_key_str = NULL;
  ck->private_key = NULL;

  ck->public_key_pem = NULL;
  ck->public_key_str = NULL;
  ck->public_key = NULL;

  return(ck);
}

Ds *
crypt_keys_format(Ds *ods, Crypt_keys *ck)
{
  Ds *ds;

  if (ods == NULL)
	ds = ds_init(NULL);
  else
	ds = ods;

  ds_asprintf(ds, "<crypt_keys");
  ds_asprintf(ds, " fed_id=\"%s\"", ck->fed_id);
  ds_asprintf(ds, " auth_key=\"%s\"", ck->auth_key_str);
  ds_asprintf(ds, " hmac_key=\"%s\"", ck->hmac_key_str);

  ds_asprintf(ds, " public_key=\"%s\"", ck->public_key_str);
  ds_asprintf(ds, " private_key=\"%s\"", ck->private_key_str);

  ds_asprintf(ds, "/>");

  return(ds);
}

static int
mk_rsa(int rsa_key_bits, char **public_key, char **private_key)
{
  int len, st;
  unsigned char *buf, *next;
  unsigned long e_value = RSA_F4;
  RSA *rsa;

#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
  int i;
  BIGNUM *e;

  rsa = RSA_new();
  e = BN_new();
  for (i = 0; i < sizeof(unsigned long) * 8; i++) {
	if (e_value & (1UL << i))
	  BN_set_bit(e, i);
  }

#ifdef OLD
  {
	BN_GENCB cb;
	BN_GENCB_set_old(&cb, NULL, NULL);
	st = RSA_generate_key_ex(rsa, rsa_key_bits, e, &cb);
  }
#else
  st = RSA_generate_key_ex(rsa, rsa_key_bits, e, NULL);
#endif
  BN_free(e);
#else
  rsa = RSA_generate_key(rsa_key_bits, e_value, NULL, NULL);
  if (RSA_check_key(rsa) <= 0)
	st = 0;
  else
	st = 1;
#endif

  if (st == 0) {
	ERR_print_errors_fp(stderr);
	fprintf(stderr,
			"RSA key generation failed -- please rerun this program\n");
	return(-1);
  }

  if ((len = i2d_RSAPrivateKey(rsa, NULL)) == 0) {
	fprintf(stderr, "Error encoding private_key\n");
	ERR_print_errors_fp(stderr);
	return(-1);
  }
  buf = next = (unsigned char *) malloc(len);
  i2d_RSAPrivateKey(rsa, &next);
  strba64(buf, (unsigned int) len, private_key);

  if ((len = i2d_RSAPublicKey(rsa, NULL)) == 0) {
	fprintf(stderr, "Error encoding public_key\n");
	ERR_print_errors_fp(stderr);
	return(-1);
  }
  buf = next = (unsigned char *) malloc(len);
  i2d_RSAPublicKey(rsa, &next);
  strba64(buf, (unsigned int) len, public_key);

  RSA_free(rsa);
  return(0);
}

Crypt_keys *
crypt_keys_create(unsigned char *auth_key, unsigned char *hmac_key,
				  int rsa_bits)
{
  unsigned int len;
  unsigned char *private_key, *public_key;
  struct tm *tm;
  time_t now;
  Crypt_keys *ck;
#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
  const unsigned char *next;
#else
  unsigned char *next;
#endif

  ck = crypt_keys_init();

  /*
   * Construct a (presumably) unique federation id string.
   * Generating new federation keys will also create a new id string.
   * The format is subject to change.
   */
  now = time(NULL);
  tm = localtime(&now);
  ck->fed_id = ds_xprintf("%s,%s", make_short_local_date_string(tm),
						  crypto_make_random_string(NULL, 20));

  ck->auth_key = memdupn(auth_key, AUTH_CRYPT_KEY_LENGTH);
  ck->auth_key_len = AUTH_CRYPT_KEY_LENGTH;

  ck->hmac_key = memdupn(hmac_key, CRYPTO_HMAC_KEY_LENGTH);
  ck->hmac_key_len = CRYPTO_HMAC_KEY_LENGTH;

  /* Convert keys to their external (textual) form. */
  len = strba64(ck->auth_key, AUTH_CRYPT_KEY_LENGTH, &ck->auth_key_str) + 1;
  memzap(auth_key, AUTH_CRYPT_KEY_LENGTH);

  len = strba64(ck->hmac_key, CRYPTO_HMAC_KEY_LENGTH, &ck->hmac_key_str) + 1;
  memzap(hmac_key, CRYPTO_HMAC_KEY_LENGTH);

  if (mk_rsa(rsa_bits, &ck->public_key_str, &ck->private_key_str) == -1)
	return(NULL);

  if (stra64b(ck->private_key_str, &private_key, &len) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Unpacking error for private_key_str"));
	return(NULL);
  }
  next = private_key;
  if ((ck->private_key = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &next,
										(long) len)) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "RSA conversion error for private_key_str"));
	return(NULL);
  }

  if (stra64b(ck->public_key_str, &public_key, &len) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Unpacking error for public_key_str"));
	crypto_log_error();
	return(NULL);
  }

  next = public_key;
  if ((ck->public_key = d2i_PublicKey(EVP_PKEY_RSA, NULL, &next,
									  (long) len)) == NULL) {
	log_msg((LOG_ERROR_LEVEL,
			   "RSA conversion error for public_key_str"));
	crypto_log_error();
	return(NULL);
  }

  ck->private_key_pem = pem_from_evp_priv_pkey(ck->private_key);
  ck->public_key_pem = pem_from_evp_pub_pkey(ck->public_key);

  return(ck);
}

#ifdef NOTDEF
Crypt_keys *
crypt_keys_from_passwd(char *passwd)
{
  int rsa_bits;
  unsigned char *derived_passwd, *auth_key_bytes, *hmac_key_bytes;
  Crypt_keys *ck;

  rsa_bits = CRYPT_KEYS_RSA_KEY_LENGTH_DEFAULT;

  derived_passwd = crypto_pbkdf2((unsigned char *) passwd, -1, NULL, 0,
								 "SHA1", 1024,
								 AUTH_CRYPT_KEY_LENGTH + CRYPTO_HMAC_KEY_LENGTH);
  auth_key_bytes = derived_passwd;
  hmac_key_bytes = auth_key_bytes + AUTH_CRYPT_KEY_LENGTH;

  /* The RSA keys will be randomly generated. */
  ck = crypt_keys_create(auth_key_bytes, hmac_key_bytes, rsa_bits);

  return(ck);
}

int
crypt_encrypt_buf_to_file(FILE *fp, Crypt_keys *ck, unsigned char *plaintext,
						  unsigned int plen, int write_text)
{
  unsigned int enc_len, len;
  char *out_buf;
  unsigned char *encrypted;
  size_t n;

  enc_len = crypto_encrypt_string(ck, plaintext, plen, &encrypted);

  if (write_text == 85)
	len = strba85(encrypted, enc_len, &out_buf);
  else if (write_text == 64)
	len = strba64(encrypted, enc_len, &out_buf);
  else {
	len = enc_len;
	out_buf = (char *) encrypted;
  }

  n = fwrite(out_buf, 1, len, fp);
  if (n != len)
	return(-1);

  return(0);
}

int
crypt_encrypt_stream(FILE *fp_in, FILE *fp_out, Crypt_keys *ck,
					 int write_text)
{

}

int
crypt_decrypt_file_to_buf(FILE *fp, Crypt_keys *ck, unsigned char **plaintext,
						  unsigned int *plen)
{
  int st;
  Ds *ds;

  if ((ds = ds_getf(fp)) == NULL) {
	fprintf(stderr, "Cannot load input file\n");
	return(-1);
  }

  st = crypto_decrypt_string(ck, (unsigned char *) ds_buf(ds),
							 ds_len(ds) - 1, plaintext, plen);

  ds_free(ds);

  return(st);
}

int
crypt_decrypt_stream(FILE *fp_in, FILE *fp_out, Crypt_keys *ck,
					 int write_text)
{

}
#endif

static Parse_attr_tab crypt_keys_attr_tab[] = {
  { "fed_id",      NULL, ATTR_IMPLIED,  NULL, 0 },
  { "auth_key",    NULL, ATTR_REQUIRED, NULL, 0 },
  { "hmac_key",    NULL, ATTR_REQUIRED, NULL, 0 },
  { "private_key", NULL, ATTR_IMPLIED,  NULL, 0 },
  { "public_key",  NULL, ATTR_IMPLIED,  NULL, 0 },
  { NULL,          NULL, ATTR_END,      NULL, 0 }
};

static void
parse_xml_crypt_keys_element_start(void *data, const char *element,
								   const char **attr)
{
  int i;
  char *auth_key, *el, *hmac_key, *errmsg;
  Crypt_keys *ck, **crypt_keys;

  if (parse_xmlns_name(element, NULL, &el) == -1) {
	parse_xml_set_error(ds_xprintf("Invalid namespace: %s", element));
	return;
  }

  crypt_keys = (Crypt_keys **) data;

  ck = crypt_keys_init();
  *crypt_keys = ck;

  if (parse_xml_is_error(NULL))
	return;

  /* Just put something on the parse stack so it's not empty. */
  parse_xml_push(NULL);

  if (!streq(el, "crypt_keys"))
	return;

  crypt_keys_attr_tab[0].value = &ck->fed_id;
  crypt_keys_attr_tab[1].value = &auth_key;
  crypt_keys_attr_tab[2].value = &hmac_key;
  crypt_keys_attr_tab[3].value = &ck->private_key_str;
  crypt_keys_attr_tab[4].value = &ck->public_key_str;
  if (parse_xml_attr(crypt_keys_attr_tab, attr, &errmsg) == -1) {
	parse_xml_set_error(errmsg);
	return;
  }

  if (ck->public_key_str != NULL || ck->private_key_str != NULL) {
	unsigned int nbytes;
	unsigned char *private_key, *public_key;
#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
	const unsigned char *next;
#else
	unsigned char *next;
#endif

	if (ck->public_key_str == NULL || ck->private_key_str == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Format error for crypt_key"));
	  dacs_fatal("parse_xml_crypt_keys_element_start");
	  /*NOTREACHED*/
	}

	if (stra64b(ck->private_key_str, &private_key, &nbytes) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Unpacking error for private_key"));
	  dacs_fatal("parse_xml_crypt_keys_element_start");
	  /*NOTREACHED*/
	}

	next = private_key;
	if ((ck->private_key = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &next,
										  (long) nbytes)) == NULL) {
	  log_msg((LOG_ERROR_LEVEL,
			   "Error decoding private_key, OpenSSL messages follow:"));
	  crypto_log_error();
	  dacs_fatal("parse_xml_crypt_keys_element_start");
	  /*NOTREACHED*/
	}
	memzap(private_key, nbytes);

	if (stra64b(ck->public_key_str, &public_key, &nbytes) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Unpacking error for public_key"));
	  dacs_fatal("parse_xml_crypt_keys_element_start");
	  /*NOTREACHED*/
	}

	next = public_key;
	if ((ck->public_key = d2i_PublicKey(EVP_PKEY_RSA, NULL, &next,
										(long) nbytes)) == NULL) {
	  log_msg((LOG_ERROR_LEVEL,
			   "Error decoding public_key, OpenSSL messages follow:"));
	  crypto_log_error();
	  dacs_fatal("parse_xml_crypt_keys_element_start");
	  /*NOTREACHED*/
	}

	ck->public_key_pem = pem_from_evp_pub_pkey(ck->public_key);
	memzap(public_key, nbytes);
  }

  if (stra64b(auth_key, &ck->auth_key, &ck->auth_key_len) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Format error for auth_key"));
	dacs_fatal("parse_xml_crypt_keys_element_start");
	/*NOTREACHED*/
  }
  if (ck->auth_key_len != AUTH_CRYPT_KEY_LENGTH) {
	log_msg((LOG_ERROR_LEVEL, "Improper auth_key length"));
	dacs_fatal("parse_xml_crypt_keys_element_start");
	/*NOTREACHED*/
  }
  strzap((char *) auth_key);

  if (stra64b(hmac_key, &ck->hmac_key, &ck->hmac_key_len) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Format error for hmac_key"));
	dacs_fatal("parse_xml_crypt_keys_element_start");
	/*NOTREACHED*/
  }
  if (ck->hmac_key_len != CRYPTO_HMAC_KEY_LENGTH) {
	log_msg((LOG_ERROR_LEVEL, "Improper hmac_key length"));
	dacs_fatal("parse_xml_crypt_keys_element_start");
	/*NOTREACHED*/
  }
  strzap((char *) hmac_key);

  for (i = 0; attr[i] != NULL; i += 2)
	strzap((char *) attr[i + 1]);

  parse_xml_pop((void **) NULL);
}

static void
parse_xml_crypt_keys_element_end(void *data, const char *el)
{

}

int
parse_xml_crypt_keys(char *keyfile, Crypt_keys **crypt_keys)
{
  int st;
  Parse_xml_error err;
  XML_Parser p = XML_ParserCreateNS(NULL, XMLNS_SEP_CHAR);

  if (p == NULL)
	return(-1);

  parse_xml_init("Crypt_keys", p);

  XML_SetElementHandler(p, parse_xml_crypt_keys_element_start,
						parse_xml_crypt_keys_element_end);
  XML_SetUserData(p, (void *) crypt_keys);

  st = XML_Parse(p, keyfile, strlen(keyfile), 1);

  if (parse_xml_is_error(&err) || st == 0) {
	if (err.mesg == NULL) {
	  parse_xml_set_error(NULL);
	  parse_xml_is_error(&err);
	}
    log_msg((LOG_ERROR_LEVEL, "parse_xml_crypt_keys: line %d, pos %d",
             err.line, err.pos));
    if (err.mesg != NULL)
      log_msg((LOG_ERROR_LEVEL, "parse_xml_crypt_keys: %s", err.mesg));
    parse_xml_end();
    return(-1);
  }

  parse_xml_end();

  if (parse_xml_is_not_empty())
	return(-1);

  return(0);
}

Crypt_keys *
crypt_keys_from_buf(char *keystr)
{
  Crypt_keys *ck;

  if (parse_xml_crypt_keys(keystr, &ck) == -1)
	return(NULL);

  return(ck);
}

/*
 * It's the caller's responsibility to destroy the key when done with it,
 * preferably by calling crypt_keys_free().
 */
Crypt_keys *
crypt_keys_from_vfs(char *vfs_ref)
{
  char *keystr;
  Crypt_keys *ck;
  Vfs_handle *handle;

  log_msg((LOG_TRACE_LEVEL, "Getting crypt keys"));
  if ((handle = vfs_open_any(vfs_ref)) == NULL) {
  fail:
	log_msg((LOG_ERROR_LEVEL, "crypt_keys_from_vfs: failed for vfs_ref %s",
			 vfs_ref));
	dacs_fatal("crypt_keys_from_vfs");
	/*NOTREACHED*/
  }

  if (vfs_get(handle, NULL, (void **) &keystr, NULL) == -1) {
	if (handle->error_msg != NULL)
	  log_msg((LOG_ERROR_LEVEL, "%s", handle->error_msg));
	log_msg((LOG_ERROR_LEVEL, "crypt_keys_from_vfs: error reading vfs_ref=%s",
			 vfs_ref));
	vfs_close(handle);
	goto fail;
  }

  if (vfs_close(handle) == -1)
	log_msg((LOG_ERROR_LEVEL, "crypt_keys_from_vfs: vfs_close() failed"));

  if ((ck = crypt_keys_from_buf(keystr)) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "XML parse error for key file: %s",
			 handle->sd->naming_context));
	dacs_fatal("crypt_keys_from_vfs");
	/*NOTREACHED*/
  }

  strzap(keystr);
  free(keystr);

  return(ck);
}

void
crypt_keys_free(Crypt_keys *ck)
{

  log_msg((LOG_TRACE_LEVEL, "Freeing crypt keys"));
  if (ck->auth_key != NULL) {
	memzap(ck->auth_key, AUTH_CRYPT_KEY_LENGTH);
	free(ck->auth_key);
  }
  if (ck->hmac_key != NULL) {
	memzap(ck->hmac_key, CRYPTO_HMAC_KEY_LENGTH);
	free(ck->hmac_key);
  }
  if (ck->public_key != NULL)
	EVP_PKEY_free(ck->public_key);
  if (ck->private_key != NULL)
	EVP_PKEY_free(ck->private_key);
  if (ck->private_key_str != NULL)
	strzap(ck->private_key_str);
  if (ck->public_key_pem != NULL)
	strzap(ck->public_key_pem);
  if (ck->public_key_str != NULL)
	strzap(ck->public_key_str);
  if (ck->fed_id != NULL)
	strzap(ck->fed_id);

  memzap(ck, sizeof(Crypt_keys));
  free(ck);
}

static void
show_usage(void)
{

  fprintf(stderr, "Usage:\n");
  fprintf(stderr, "dacskey [dacsoptions] [op] [-p | -pf file] [-pem] [-vfs] [--] keyfile\n");
  fprintf(stderr, "dacsoptions: %s\n", standard_command_line_usage);
  fprintf(stderr, "op is one of:\n");
  fprintf(stderr, "-gen or -check or -priv[ate] or -pub[lic]\n");
  fprintf(stderr, "-p:       prompt for passphrase material from stdin\n");
  fprintf(stderr, "-pem:     print key in PEM format\n");
  fprintf(stderr, "-pf file: reads passphrase material from the file\n");
  fprintf(stderr, "          if the file is \"-\", stdin is read\n");
  fprintf(stderr, "-rsa_key_bits #:  length of the RSA modulus, in bits\n");
  fprintf(stderr, "-vfs:     get keyfile via VFS, except for -gen\n");
  fprintf(stderr, "--:       marks the end of the flag arguments\n");
  fprintf(stderr, "keyfile:  keys are written to or read from this file\n");
  exit(1);
}

int
dacskey_main(int argc, char **argv, int do_init, void *main_out)
{
  int do_gen, do_check, i, rsa_bits, use_passphrase, use_pem, use_vfs;
  int do_show_private, do_show_public;
  unsigned int len;
  char *errmsg, *fed_id, *keyfile, *pfile;
  unsigned char auth_key_bytes[AUTH_CRYPT_KEY_LENGTH];
  unsigned char hmac_key_bytes[CRYPTO_HMAC_KEY_LENGTH];
  char *auth_keystr, *hmac_keystr, *public_key, *private_key;
  struct tm *tm;
  time_t now;
  Crypt_keys *ck;
  Ds *ds;
  FILE *fp;

  errmsg = "Internal error";
  if (dacs_init(DACS_UTILITY, &argc, &argv, NULL, &errmsg) == -1) {
	fprintf(stderr, "Could not initialize: %s\n", errmsg);

  usage:
	show_usage();
	return(-1);
  }

#ifdef NOTDEF
 if (streq(argv[1], "enc")) {
   if ((fp = fopen(argv[3], "w+")) == NULL)
	 return(-1);
   ck = crypt_keys_from_passwd(argv[2]);
   crypt_encrypt_buf_to_file(fp, ck, "Hello, world", 12, 0);
 }
 else if (streq(argv[1], "dec")) {
   unsigned int plen;
   unsigned char *plaintext;

   if ((fp = fopen(argv[3], "r")) == NULL) {
	 fprintf(stderr, "Cannot open \"%s\"\n", argv[3]);
	 return(-1);
   }
   ck = crypt_keys_from_passwd(argv[2]);
   if (ck == NULL)
	 return(-1);
   if (crypt_decrypt_file_to_buf(fp, ck, &plaintext, &plen) == -1) {
	 fprintf(stderr, "Decryption failed\n");
	 return(-1);
   }
   fwrite(plaintext, 1, plen, stdout);
   printf("\n");
 }
 else
   return(-1);
fclose(fp);
return(0);
#endif

  do_check = do_gen = do_show_private = do_show_public = 0;
  rsa_bits = CRYPT_KEYS_RSA_KEY_LENGTH_DEFAULT;
  use_passphrase = 0;
  use_pem = 0;
  use_vfs = 0;
  pfile = NULL;

  for (i = 1; i < argc; i++) {
	if (streq(argv[i], "-gen")) {
	  if (do_check || do_show_private || do_show_public)
		goto usage;
	  do_gen = 1;
	}
	else if (streq(argv[i], "-check")) {
	  if (do_gen | do_show_private || do_show_public)
		goto usage;
	  do_check = 1;
	}
	else if (streq(argv[i], "-priv") || streq(argv[i], "-private")) {
	  if (do_gen | do_show_public || do_check)
		goto usage;
	  do_show_private = 1;
	}
	else if (streq(argv[i], "-pub") || streq(argv[i], "-public")) {
	  if (do_gen | do_show_private || do_check)
		goto usage;
	  do_show_public = 1;
	}
	else if (streq(argv[i], "-p"))
	  use_passphrase = 1;
	else if (streq(argv[i], "-pem"))
	  use_pem = 1;
	else if (streq(argv[i], "-pf")) {
	  use_passphrase = 1;
	  if (++i == argc)
		goto usage;
	  pfile = argv[i];
	}
	else if (streq(argv[i], "-rsa_key_bits")) {
	  if (++i == argc)
		goto usage;
	  rsa_bits = atoi(argv[i]);
	}
	else if (streq(argv[i], "--")) {
	  i++;
	  break;
	}
	else if (argv[i][0] == '-')
	  goto usage;
	else
	  break;
  }

  if (i != (argc - 1))
	goto usage;

  keyfile = argv[i];

  /* The default operation... */
  if ((do_check + do_gen + do_show_private + do_show_public) == 0)
	do_gen = 1;

  if (do_check || do_show_private || do_show_public) {
	char *keystr;
	Crypt_keys *ck;

	if (use_vfs) {
	  if ((ck = crypt_keys_from_vfs(keyfile)) == NULL) {
		fprintf(stderr, "Error getting keys from \"%s\"\n", keyfile);
		exit(1);
	  }
	}
	else {
	  if (load_file(keyfile, &keystr, NULL) == -1) {
		fprintf(stderr, "Error getting keys from \"%s\"\n", keyfile);
		exit(1);
	  }

	  if ((ck = crypt_keys_from_buf(keystr)) == NULL) {
		fprintf(stderr, "Error parsing keys from \"%s\"\n", keyfile);
		exit(1);
	  }
	}

	if (do_check) {
	  if (!dacs_quiet_flag && ck->fed_id != NULL)
		fprintf(stderr, "OK fed_id=\"%s\"\n", ck->fed_id);

	  exit(0);
	}

	if (do_show_public) {
	  if (use_pem)
		printf("%s\n", ck->public_key_pem);
	  else
		printf("%s\n", ck->public_key_str);

	  exit(0);
	}

	if (do_show_private) {
	  if (use_pem) {
		char *str;

		str = pem_from_evp_priv_pkey(ck->private_key);
		printf("%s\n", str);
	  }
	  else
		printf("%s\n", ck->private_key_str);

	  exit(0);
	}

	exit(1);
  }

  if (use_passphrase) {
	char *buf, *ptr;
	size_t flen, need, nread;
	size_t passphrase_bytes_needed, rand_bytes_needed;
	unsigned char *rbuf;

	rand_bytes_needed = AUTH_CRYPT_KEY_LENGTH + CRYPTO_HMAC_KEY_LENGTH;
	/*
	 * This is based on the estimate that about 50 characters of English text
	 * yields about 8 bytes of randomness for a passphrase (Schneier).
	 */
	passphrase_bytes_needed = (rand_bytes_needed * 50) / 8;
	need = passphrase_bytes_needed;

	if (verbose_level) {
	  fprintf(stderr,
			  "Using passphrase of %u bytes to generate %u random bytes\n",
			  (unsigned int) passphrase_bytes_needed,
			  (unsigned int) rand_bytes_needed);
	  fprintf(stderr, "Passphrase bytes are from ");
	  if (pfile == NULL)
		fprintf(stderr, "interactive tty\n");
	  else if (streq(pfile, "-"))
		fprintf(stderr, "stdin\n");
	  else
		fprintf(stderr, "file '%s'\n", pfile);
	}


	if (pfile != NULL && !streq(pfile, "-")) {
	  /* XXX Should we check for reasonable ownership/modes? */
	  /* XXX This may load far more than we need... */
	  if (load_file(pfile, &buf, &flen) == -1) {
		fprintf(stderr, "Can't read passphrase from %s\n", pfile);
		return(-1);
	  }
	  if (flen < need) {
		fprintf(stderr, "Insufficient material in %s\n", pfile);
		fprintf(stderr, "Need %u bytes\n", (unsigned int) need);
		return(-1);
	  }
	  need = 0;
	}
	else
	  buf = ptr = malloc(passphrase_bytes_needed + 1);

	/*
	 * Read stdin until we get the needed amount of text.
	 * XXX The passphrase probably shouldn't be echoed...
	 */
	while (need) {
	  if (pfile == NULL)
		fprintf(stderr, "Enter passphrase text (need %u more characters):\n",
				(unsigned int) need);
	  if ((nread = fread(ptr, 1, need, stdin)) == 0
		  || feof(stdin) || ferror(stdin)) {
		fprintf(stderr, "Read error/EOF\n");
		return(-1);
	  }
	  need -= nread;
	  ptr += nread;
	}

	crypto_make_randomized_from_passphrase((unsigned char *) buf,
										   passphrase_bytes_needed,
										   rand_bytes_needed, &rbuf);

	memcpy(auth_key_bytes, rbuf, AUTH_CRYPT_KEY_LENGTH);
	memcpy(hmac_key_bytes, rbuf + AUTH_CRYPT_KEY_LENGTH,
		   CRYPTO_HMAC_KEY_LENGTH);
  }
  else {
	/* Get the best random bytes available to us. */
	crypto_randomize_buffer(auth_key_bytes, AUTH_CRYPT_KEY_LENGTH);
	crypto_randomize_buffer(hmac_key_bytes, CRYPTO_HMAC_KEY_LENGTH);
  }

  ck = crypt_keys_create(auth_key_bytes, hmac_key_bytes, rsa_bits);

#ifdef NOTDEF
  /* Convert to the external form and print. */
  len = strba64(auth_key, AUTH_CRYPT_KEY_LENGTH, &auth_keystr) + 1;
  memzap(auth_key, AUTH_CRYPT_KEY_LENGTH);
  len = strba64(hmac_key, CRYPTO_HMAC_KEY_LENGTH, &hmac_keystr) + 1;
  memzap(hmac_key, CRYPTO_HMAC_KEY_LENGTH);

  if (mk_rsa(rsa_bits, &public_key, &private_key) == -1)
	return(-1);
#endif

  if ((fp = fopen(keyfile, "w+")) == NULL) {
	perror("fopen");
	fprintf(stderr, "Couldn't create/truncate output file '%s'\n", keyfile);
	return(-1);
  }

  if (fchmod(fileno(fp), 0640) == -1) {
	perror("fchmod");
	fprintf(stderr, "Couldn't set mode of output file '%s'\n", keyfile);
	return(-1);
  }
	
#ifdef NOTDEF
  /*
   * Construct a (presumably) unique federation id string.
   * Generating new federation keys will also create a new id string.
   * The format is subject to change.
   */
  now = time(NULL);
  tm = localtime(&now);
  fed_id = ds_xprintf("%s,%s", make_short_local_date_string(tm),
					  crypto_make_random_string(NULL, 20));

  fprintf(fp, "<crypt_keys");
  fprintf(fp, " fed_id=\"%s\"", fed_id);
  fprintf(fp, " auth_key=\"%s\"", auth_keystr);
  fprintf(fp, " hmac_key=\"%s\"", hmac_keystr);

  fprintf(fp, " public_key=\"%s\"", public_key);
  fprintf(fp, " private_key=\"%s\"", private_key);

  fprintf(fp, "/>\n");
  strzap(auth_keystr);
  strzap(hmac_keystr);
#endif

  ds = crypt_keys_format(NULL, ck);
  fprintf(fp, "%s\n", ds_buf(ds));

  if (fclose(fp) == -1) {
	perror("fopen()");
	fprintf(stderr, "Close failed\n");
	if (unlink(keyfile) == -1) {
	  perror("unlink");
	  fprintf(stderr, "Unlink of '%s' failed\n", keyfile);
	}
	return(-1);
  }

  /*
   * XXX set modes, check ownership, etc. on the file.
   * It should probably be encrypted using a passphrase, but we need to
   * figure out how CGI (non-interactive) programs would also be able to access
   * the key file.
   */
  if (!dacs_quiet_flag)
	fprintf(stderr, "Please check ownership and mode of '%s'\n", keyfile);

  return(0);
}

#else

int
main(int argc, char **argv)
{
  int rc;

  if ((rc = dacskey_main(argc, argv, 1, NULL)) == 0)
	exit(0);

  exit(1);
}
#endif
