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

/*
 * Assorted cryptographic support, including a convenience layer on top
 * of OpenSSL, secure hashing/digests, keyed hashes, symmetric encryption,
 * public key encryption, public/private keys and X.509 certificates, passwords,
 * key deriviation, digital signatures, and random numbers and strings.
 * Includes some self-testing using published test vectors.
 */

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

#include "auth.h"
#include "dacs_crypto.h"

#define DEBUG_ABORT(VAR, ERR)		if (crypto_debug_flag) \
	abort(); else VAR = ERR;

static int crypto_debug_flag = 1;

static char *log_module_name = "crypto";

/*
 * For functions provided by OpenSSL, the canonical name must be the exact
 * name used by OpenSSL.
 * A hyphen (not a synonym) must be used as the separator in a canonical name;
 * see crypto_digest_name_match().
 *
 * XXX could extend the name syntax to include optional parameters; e.g.,
 * sha512t[32], scrypt[1024,8,16]
 */
static Digest_tab digest_tab[] = {
  { "md5",         DIGEST_MD5,              64, 16, PASSWD_ALG_MD5,
    NULL, NULL,
	DIGEST_OPENSSL | DIGEST_USE_PASSWD | DIGEST_USE_HMAC | DIGEST_USE_SIGN },
#if (OPENSSL_VERSION_NUMBER < 0x1010000fL)
  /* Apparently removed in OpenSSL 1.1.0. */
  { "sha",         DIGEST_SHA,              64, 20, PASSWD_ALG_NONE,
	NULL, NULL,
	DIGEST_OPENSSL },
#endif
  { "sha1",        DIGEST_SHA1,             64, 20, PASSWD_ALG_SHA1,
	NULL, NULL,
	DIGEST_OPENSSL | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_HMAC | DIGEST_USE_SIGN },
  { "sha224",      DIGEST_SHA224,           64, 28, PASSWD_ALG_SHA224,
	NULL, NULL,
	DIGEST_OPENSSL | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_HMAC | DIGEST_USE_SIGN },
  { "sha256",      DIGEST_SHA256,           64, 32, PASSWD_ALG_SHA256,
	NULL, NULL,
	DIGEST_OPENSSL | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_HMAC | DIGEST_USE_SIGN },
  { "sha384",      DIGEST_SHA384,          128, 48, PASSWD_ALG_SHA384,
	NULL, NULL,
	DIGEST_OPENSSL | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_HMAC | DIGEST_USE_SIGN },
  { "sha512",      DIGEST_SHA512,          128, 64, PASSWD_ALG_SHA512,
	NULL, NULL,
	DIGEST_OPENSSL | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_HMAC | DIGEST_USE_SIGN },
  { "sha-512-224", DIGEST_SHA512_224,      128, 28, PASSWD_ALG_SHA512_224,
	NULL, NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD },
  { "sha-512-256", DIGEST_SHA512_256,      128, 32, PASSWD_ALG_SHA512_256,
	NULL, NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD },
  { "sha-512-t",   DIGEST_SHA512t,         128,  0, PASSWD_ALG_SHA512t,
	"t", NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PARAMS | DIGEST_USE_VARLEN },
  { "sha3-224",    DIGEST_SHA3_224,        144, 28, PASSWD_ALG_SHA3_224,
	NULL, NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2 | DIGEST_USE_HMAC },
  { "sha3-256",    DIGEST_SHA3_256,        136, 32, PASSWD_ALG_SHA3_256,
	NULL, NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2 | DIGEST_USE_HMAC },
  { "sha3-384",    DIGEST_SHA3_384,        104, 48, PASSWD_ALG_SHA3_384,
	NULL, NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2 | DIGEST_USE_HMAC },
  { "sha3-512",    DIGEST_SHA3_512,         72, 64, PASSWD_ALG_SHA3_512,
	NULL, NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2 | DIGEST_USE_HMAC },
  /*
   * XXX Check if:
   * hmac("message", 0, "secret key", 0, BLAKE2) gives
   *  "e3c8102868d28b5ff85fc35dda07329970d1a01e273c37481326fe0c861c8142"
   */
  { "blake2",      DIGEST_BLAKE2,          128,  BLAKE2B_OUTBYTES, PASSWD_ALG_BLAKE2,
	"hlen", NULL,
	DIGEST_DACS | DIGEST_USE_HMAC | DIGEST_USE_VARLEN },
  { "pbkdf2",      DIGEST_PBKDF2,           64, 0, PASSWD_ALG_PBKDF2,
	"a,count,dklen", NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_PARAMS | DIGEST_USE_VARLEN },
  { "pbkdf2-sha1", DIGEST_PBKDF2_SHA1,      64, 0, PASSWD_ALG_PBKDF2_SHA1,
	"count,dklen", NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_PARAMS | DIGEST_USE_VARLEN },
  { "pbkdf2-sha256", DIGEST_PBKDF2_SHA256, 64, 0, PASSWD_ALG_PBKDF2_SHA256,
	"count,dklen", NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_PARAMS | DIGEST_USE_VARLEN },
  { "pbkdf2-sha512", DIGEST_PBKDF2_SHA512,128, 0, PASSWD_ALG_PBKDF2_SHA512,
	"count,dklen", NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PBKDF2
	| DIGEST_USE_PARAMS | DIGEST_USE_VARLEN },
  { "scrypt", DIGEST_SCRYPT,                64, 0, PASSWD_ALG_SCRYPT,
	"N,r,p,dklen", NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_SCRYPT
	| DIGEST_USE_PARAMS | DIGEST_USE_VARLEN },
  { "argon2", DIGEST_ARGON2,               128, 32, PASSWD_ALG_ARGON2,
	"y,v,m,t,p,T,X,S", NULL,
	DIGEST_DACS | DIGEST_USE_PASSWD | DIGEST_USE_PARAMS | DIGEST_USE_VARLEN },
  { "crypt", DIGEST_CRYPT,                   0, 0, PASSWD_ALG_CRYPT,
	NULL, NULL,
	DIGEST_SYSTEM | DIGEST_USE_PASSWD | DIGEST_USE_VARLEN },

  { NULL,          DIGEST_INVALID,           0, 0, PASSWD_ALG_NONE,
	NULL, NULL,
	DIGEST_NONE }
};

static Digest_ctx *digest_make_ctx(Digest_tab *dt);

static inline int
is_name_match_sep(int ch)
{

  return(strchr("-_/", ch) != NULL);
}

/*
 * The DIGEST_NAME is case-insensitive and may use an underscore or slash
 * instead of a hyphen as a separator character.  When matching DIGEST_NAME
 * against a canonical name, hyphens are ignored, except that more than one
 * consecutive separator is disallowed and a hyphen may not be elided if doing
 * so would conflate two digits.  A leading or trailing separator is disallowed.
 * (e.g., "Sha224" matches "sha-224", "sha512t" matches "sha-512-t",
 * and "SHA_512_224" matches "sha-512-224", but "sha3224" is invalid for
 * "sha3-224", as is "sha3--224".
 *
 * Return 1 if DD matches DIGEST_NAME, 0 otherwise.
 */
static int
crypto_digest_name_match(Digest_tab *dt, const char *digest_name)
{
  int desc_dn_prev, dn_prev;
  const char *dn, *desc_dn;

  if (strcaseeq(digest_name, dt->name))
	return(1);

  if (is_digit_string(digest_name)) {
	/*
	 * The older format identified each password digest algorithm only by
	 * a unique non-negative integer (Passwd_digest_alg).
	 */
	if (atoi(digest_name) == dt->passwd_alg)
	  return(1);
	/* Proceed, but a match is unlikely. */
  }

  desc_dn = dt->name;
  desc_dn_prev = 0;

  dn = digest_name;
  dn_prev = 0;

  while (1) {
	if ((*dn == *desc_dn) && (*dn == '\0'))
	  return(1);

	if (tolower((int) *dn) == tolower((int) *desc_dn)) {
	  desc_dn_prev = *desc_dn;
	  dn_prev = *dn;
	  desc_dn++;
	}
	else if (is_name_match_sep((int) *dn) && *desc_dn == '-') {
	  desc_dn_prev = 0;
	  dn_prev = 0;
	  desc_dn++;
	}
	else if (*desc_dn == '-' && desc_dn_prev != 0
			 && tolower((int) *(desc_dn + 1)) == tolower((int) *dn)
			 && (!isdigit((int) desc_dn_prev) || !isdigit((int) *dn))) {
	  desc_dn_prev = 0;
	  dn_prev = 0;
	  desc_dn += 2;
	}
	else if (is_name_match_sep((int) *dn) && dn_prev != 0
			 && !is_name_match_sep((int) dn_prev)
			 && (tolower((int) *(dn + 1)) == tolower((int) *desc_dn))
			 && (!isdigit((int) dn_prev) || !isdigit((int) *desc_dn))) {
	  desc_dn_prev = 0;
	  dn_prev = 0;
	  desc_dn++;
	  dn++;
	}
	else
	  break;

	dn++;
  }

  return(0);
}

char *
crypto_digest_make_desc(Digest_desc *dd)
{
  Digest_tab *dt;
  Ds ds;

  ds_init(&ds);
  dt = dd->dt;
  ds_asprintf(&ds, "%s", dt->name);
  switch (dt->alg) {
  case DIGEST_PBKDF2:
	ds_asprintf(&ds, "[a=%s,count=%u,dklen=%u]",
				crypto_lookup_digest_name_by_algorithm(dd->u.pbkdf2.alg),
				dd->u.pbkdf2.count, dd->u.pbkdf2.dklen);
	break;

  case DIGEST_PBKDF2_SHA1:
  case DIGEST_PBKDF2_SHA256:
  case DIGEST_PBKDF2_SHA512:
	ds_asprintf(&ds, "[count=%u,dklen=%u]",
				dd->u.pbkdf2.count, dd->u.pbkdf2.dklen);
	break;

  case DIGEST_SHA512t:
	ds_asprintf(&ds, "[t=%u]", dd->u.sha512t.t);
	break;

  case DIGEST_SCRYPT:
	ds_asprintf(&ds, "[N=%llu,r=%u,p=%u,dklen=%u]",
				(uint64_t) dd->u.scrypt.N, dd->u.scrypt.r, dd->u.scrypt.p,
				dd->u.scrypt.dklen);
	break;

  case DIGEST_ARGON2:
	{
	  char *enc_ad, *enc_salt;

	  if (dd->u.argon2.ad == NULL || dd->u.argon2.adlen == 0)
		enc_ad = "";
	  else
		enc_ad = strbtohex(dd->u.argon2.ad, dd->u.argon2.adlen,
						   STRBTOHEX_DEFAULT);

	  if (dd->u.argon2.salt == NULL || dd->u.argon2.saltlen == 0)
		enc_salt = "";
	  else
		enc_salt = strbtohex(dd->u.argon2.salt, dd->u.argon2.saltlen,
							 STRBTOHEX_DEFAULT);

	  ds_asprintf(&ds, "[y=%s,v=%x,m=%u,t=%u,p=%u,T=%u,X=%s,S=%s]",
				  (dd->u.argon2.type == Argon2_d) ? "d" : "i",
				  dd->u.argon2.version, dd->u.argon2.m_cost, dd->u.argon2.t_cost,
				  dd->u.argon2.lanes, dd->u.argon2.outlen, enc_ad, enc_salt);

	}
	break;

  default:
	break;
  }

  return(ds_buf(&ds));
}

static int
parse_digest_desc_args(const char *startp, Kwv *kwv, Dsvec *dsv_args)
{
  int i;
  char *argstr, *p, *s;
  Dsvec *dsv;

  p = strdup(startp);

  if (*p != '[') {
	log_msg((LOG_ERROR_LEVEL, "Invalid digest argument list"));
	return(-1);
  }

  s = p + 1;
  if ((p = strchr(s, (int) ']')) == NULL || *(p + 1) != '\0') {
	log_msg((LOG_ERROR_LEVEL, "Invalid digest argument list"));
	return(-1);
  }
  *p = '\0';

  argstr = s;
  if ((dsv = strsplit_nocopy(argstr, ",", 0)) == NULL || dsvec_len(dsv) == 0) {
	log_msg((LOG_ERROR_LEVEL, "Digest argument list syntax error"));
	return(-1);
  }

  for (i = 0; i < dsvec_len(dsv); i++) {
	char *arg, *endp;
	Kwv_pair pair;
	Kwv_parse_conf conf = { KWV_PARSE_NEED_VALUE, '=', 0, NULL };

	arg = (char *) dsvec_ptr_index(dsv, i);
	kwv_init_pair(&pair);
	if (kwv_parse_qstr(arg, &pair.name, &pair.val, &endp, &conf) == -1
		|| *endp != '\0') {
	  log_msg((LOG_ERROR_LEVEL, "Digest argument parse error: \"%s\", arg"));
	  return(-1);
	}

	if (dsvec_find(dsv_args, pair.name, NULL) == 0) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid digest argument: \"%s\"", arg));
	  return(-1);
	}

	if (kwv_add_pair_nocopy(kwv, &pair) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Digest argument list processing error"));
	  return(-1);
	}
  }

  return(0);
}

/*
 * Parse a digest name descriptor and return the digest name and, if present,
 * any arguments that are attached.
 * Syntax:
 *   <digest-spec> ::= <dn> <ws>* [ '[' <argstr> ']' ]
 * (a digest name, optionally followed by whitespace, optionally followed by
 * an argument string within square brackets.  Nothing can follow a closing
 * square bracket.
 *   <argstr> ::= <name> '=' <value> [, <argstr> ]
 * Whitespace may precede <name>, but everything following the '=' is part of
 * the value.  The <value> ends at a comma or the closing bracket, so
 * embedded whitespace is possible.  Whitespace following a comma is ignored.
 * Quoting and escaping is not allowed.
 *
 * The purpose of this function is to simplify the specification of
 * parameterized digest functions and key derivation functions in configurations.
 * Examples:
 *   PASSWORD_DIGEST "scrypt[N=1024, r=8, p=16, dklen=32]"
 *   "sha512/t[t=72]"
 *   "pbkdf2[a=sha256,c=4098, dklen=20]"
 *
 * Return the simplified digest name, and if KWV_ARGS is non-NULL, the
 * attached argument list.  If non-NULL, KWV_ARGS is set to NULL if there are
 * no arguments.
 *
 * XXX the syntax may be a little too ad hoc, but is probably adequate since
 * allowing embedded quotes and escaping may be even worse.  The kwv parsing
 * function is capable of handling quotes.
 */
Digest_desc *
crypto_parse_digest_desc(const char *digest_desc)
{
  char *argstr, *dn, *s;
  const char *endp, *p;
  Digest_alg alg;
  Digest_desc *dd;
  Digest_tab *dt;
  Dsvec *dsv_args;
  Kwv *kwv_args;

  for (p = digest_desc; *p != '\0' && *p != ' ' && *p != '['; p++)
	;
  endp = p;
  dn = strndup(digest_desc, endp - digest_desc);

  if ((dt = crypto_lookup_digest_by_name(dn)) == NULL)
	return(NULL);

  if (dt->arg_spec != NULL) {
	if ((dsv_args = dsvec_strlist(NULL, dt->arg_spec)) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Digest argument configuration error: \"%s\"",
			   dt->arg_spec));
	  return(NULL);
	}
  }
  else
	dsv_args = NULL;

  /* Skip any whitespace that follows the digest name. */
  while (*p == ' ' || *p == '\t')
	p++;

  /* Now we're either at the end of string or start of parameters. */
  if (*p == '\0')
	kwv_args = NULL;
  else {
	kwv_args = kwv_init(4);
	kwv_args->dup_mode = KWV_NO_DUPS;

	if (parse_digest_desc_args(p, kwv_args, dsv_args) == -1)
	  return(NULL);
  }

  /*
   * Check that the expected number of arguments is present.
   */
  if (digest_usage(dt->flags) & DIGEST_USE_PARAMS) {
	/* One or more arguments are required. */
	if (dsvec_len(dsv_args) != kwv_count(kwv_args, NULL)) {
	  log_msg((LOG_ERROR_LEVEL, "Digest requires %d argument%s, got %d",
			   dsvec_len(dsv_args), (dsvec_len(dsv_args) == 1) ? "" : "s",
			   kwv_count(kwv_args, NULL)));
	  return(NULL);
	}
  }
  else {
	/* No arguments should be present. */
	if (kwv_count(kwv_args, NULL) != 0) {
	  log_msg((LOG_ERROR_LEVEL, "Digest takes no arguments: \"%s\"",
			   digest_desc));
	  return(NULL);
	}
  }

  dd = ALLOC(Digest_desc);
  dd->desc = strdup(digest_desc);
  dd->dt = dt;

  alg = dt->alg;
  if (alg == DIGEST_PBKDF2) {
	unsigned int count, dklen;
	char *alg_str;
	Digest_tab *dt;

	/* Hash algorithm for DIGEST_PBKDF2 must be a parameter. */
	if (kwv_count(kwv_args, NULL) == 0) {
	  log_msg((LOG_ERROR_LEVEL, "Digest algorithm requires arguments"));
	  return(NULL);
	}

	if ((alg_str = kwv_lookup_value(kwv_args, "a")) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Digest algorithm requires \"a\" argument"));
	  return(NULL);
	}
	if ((dt = crypto_lookup_digest_by_name(alg_str)) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Unrecognized digest algorithm: \"%s\"",
			   alg_str));
	  return(NULL);
	}
	if ((digest_usage(dt->flags) & DIGEST_USE_PBKDF2) == 0) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid PBKDF2 digest algorithm"));
	  return(NULL);
	}
	dd->u.pbkdf2.alg = dt->alg;

	/* Change this to identify the actual algorithm. */
	alg = dt->alg;

	if (kwv_lookup_strnum(kwv_args, "count", STRNUM_UI, &count) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"count\" argument"));
	  return(NULL);
	}
	dd->u.pbkdf2.count = count;

	if (kwv_lookup_strnum(kwv_args, "dklen", STRNUM_UI, &dklen) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"dklen\" argument"));
	  return(NULL);
	}
	dd->u.pbkdf2.dklen = dklen;
  }
  else if (alg == DIGEST_PBKDF2_SHA1
		   || alg == DIGEST_PBKDF2_SHA256
		   || alg == DIGEST_PBKDF2_SHA512) {
	unsigned int count, dklen;

	/* Hash algorithm is implied by the digest name. */

	dd->u.pbkdf2.alg = alg;

	if (kwv_count(kwv_args, NULL) == 0) {
	  log_msg((LOG_ERROR_LEVEL, "Digest algorithm requires arguments"));
	  return(NULL);
	}

	if (kwv_lookup_strnum(kwv_args, "count", STRNUM_UI, &count) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"count\" argument"));
	  return(NULL);
	}
	dd->u.pbkdf2.count = count;

	if (kwv_lookup_strnum(kwv_args, "dklen", STRNUM_UI, &dklen) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"dklen\" argument"));
	  return(NULL);
	}
	dd->u.pbkdf2.dklen = dklen;
  }
  else if (alg == DIGEST_SHA512t) {
	unsigned int t;

	if (kwv_count(kwv_args, NULL) == 0) {
	  log_msg((LOG_ERROR_LEVEL, "Digest algorithm requires arguments"));
	  return(NULL);
	}

	if (kwv_lookup_strnum(kwv_args, "t", STRNUM_UI, &t) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"t\" argument"));
	  return(NULL);
	}
	dd->u.sha512t.t = t;
  }
  else if (dt->alg == DIGEST_SCRYPT) {
	uint64_t N;
	uint32_t p, r;
	unsigned int dklen;

	if (kwv_count(kwv_args, NULL) == 0)
	  return(NULL);

	if (kwv_lookup_strnum(kwv_args, "N", STRNUM_UI64_T, &N) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"N\" argument"));
	  return(NULL);
	}
	dd->u.scrypt.N = N;

	if (kwv_lookup_strnum(kwv_args, "r", STRNUM_UI32_T, &r) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"r\" argument"));
	  return(NULL);
	}
	dd->u.scrypt.r = r;

	if (kwv_lookup_strnum(kwv_args, "p", STRNUM_UI32_T, &p) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"p\" argument"));
	  return(NULL);
	}
	dd->u.scrypt.p = p;

	if (kwv_lookup_strnum(kwv_args, "dklen", STRNUM_UI, &dklen) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"dklen\" argument"));
	  return(NULL);
	}
	dd->u.scrypt.dklen = dklen;
  }
  else if (dt->alg == DIGEST_ARGON2) {
	char *enc_ad, *enc_salt, *alg_type;
	uint32_t lanes, m_cost, outlen, t_cost, version;
	Kwv_pair *pair;

	if (kwv_count(kwv_args, NULL) != 8)
	  return(NULL);

	/* y, v, m, t, p, T, X, S */

	if ((alg_type = kwv_lookup_value(kwv_args, "y")) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Missing \"y\" (type) argument"));
	  return(NULL);
	}
	if (strcaseeq(alg_type, "i"))
	  dd->u.argon2.type = Argon2_i;
	else if (strcaseeq(alg_type, "d"))
	  dd->u.argon2.type = Argon2_d;
	else {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"y\" (type) argument"));
	  return(NULL);
	}

	if (kwv_lookup_strnum(kwv_args, "v", STRNUM_UI32_T, &version) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"v\" (version) argument"));
	  return(NULL);
	}
	if (version != ARGON2_VERSION_NUMBER) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"v\" (Argon2 version) argument"));
	  return(NULL);
	}
	dd->u.argon2.version = version;

	if (kwv_lookup_strnum(kwv_args, "m", STRNUM_UI32_T, &m_cost) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"m_cost\" argument"));
	  return(NULL);
	}
	if (m_cost < ARGON2_MIN_MEMORY || m_cost > ARGON2_MAX_MEMORY) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"m_cost\" argument range"));
	  return(NULL);
	}
	dd->u.argon2.m_cost = m_cost;

	if (kwv_lookup_strnum(kwv_args, "t", STRNUM_UI32_T, &t_cost) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"t_cost\" argument"));
	  return(NULL);
	}
	if (t_cost < ARGON2_MIN_TIME || t_cost > ARGON2_MAX_TIME) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"t_cost\" argument range"));
	  return(NULL);
	}
	dd->u.argon2.t_cost = t_cost;

	if (kwv_lookup_strnum(kwv_args, "p", STRNUM_UI32_T, &lanes) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"p\" (lanes) argument"));
	  return(NULL);
	}
	if (lanes < ARGON2_MIN_LANES || lanes > ARGON2_MAX_LANES) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"p\" (lanes) argument range"));
	  return(NULL);
	}
	dd->u.argon2.lanes = lanes;

	if (kwv_lookup_strnum(kwv_args, "T", STRNUM_UI32_T, &outlen) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"T\" (outlen) argument"));
	  return(NULL);
	}
	if (outlen < ARGON2_MIN_OUTLEN || outlen > ARGON2_MAX_OUTLEN) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid \"T\" (outlen) argument range"));
	  return(NULL);
	}
	dd->u.argon2.outlen = outlen;

	if ((pair = kwv_lookup(kwv_args, "X")) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Missing \"X\" argument"));
	  return(NULL);
	}
	if (pair->val != NULL && pair->val[0] != '\0') {
	  enc_ad = pair->val;
	  if ((dd->u.argon2.ad = strhextob(enc_ad, &dd->u.argon2.adlen)) == NULL) {
		log_msg((LOG_ERROR_LEVEL, "Invalid hex-encoded \"X\" (ad) argument"));
		return(NULL);
	  }
	  if (dd->u.argon2.adlen > ARGON2_MAX_AD_LENGTH) {
		log_msg((LOG_ERROR_LEVEL, "Invalid \"X\" (ad) argument length"));
		return(NULL);
	  }
	}
	else {
	  dd->u.argon2.ad = NULL;
	  dd->u.argon2.adlen = 0;
	}

	if ((pair = kwv_lookup(kwv_args, "S")) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Missing \"S\" argument"));
	  return(NULL);
	}
	if (pair->val != NULL && pair->val[0] != '\0') {
	  enc_salt = pair->val;
	  if ((dd->u.argon2.salt = strhextob(enc_salt,
										 &dd->u.argon2.saltlen)) == NULL) {
		log_msg((LOG_ERROR_LEVEL, "Invalid hex-encoded \"S\" (salt) argument"));
		return(NULL);
	  }
	  if (dd->u.argon2.saltlen < ARGON2_MIN_SALT_LENGTH
		  || dd->u.argon2.saltlen > ARGON2_MAX_SALT_LENGTH) {
		log_msg((LOG_ERROR_LEVEL, "Invalid \"S\" (salt) argument length"));
		return(NULL);
	  }
	}
	else {
	  dd->u.argon2.salt = NULL;
	  dd->u.argon2.saltlen = 0;
	}
  }
  else {
	/* These algorithms have no special parameters. */
  }

  dd->formatted_desc = crypto_digest_make_desc(dd);

  return(dd);
}

/*
 * Return the digest descriptor for DIGEST_NAME, or NULL if it is not found.
 */
Digest_tab *
crypto_lookup_digest_by_name(const char *digest_name)
{
  Digest_tab *dt;

  for (dt = &digest_tab[0]; dt->name != NULL; dt++) {
	if (crypto_digest_name_match(dt, digest_name))
	  return(dt);
  }

  return(NULL);
}

/*
 * Return the digest descriptor for ALG, or NULL if it is not found.
 */
Digest_tab *
crypto_lookup_digest_by_algorithm(Digest_alg alg)
{
  Digest_tab *dt;

  for (dt = &digest_tab[0]; dt->name != NULL; dt++) {
	if (dt->alg == alg)
	  return(dt);
  }

  return(NULL);
}

const char *
crypto_lookup_digest_name_by_algorithm(Digest_alg alg)
{
  Digest_tab *dt;

  if ((dt = crypto_lookup_digest_by_algorithm(alg)) == NULL)
	return(NULL);

  return(dt->name);
}

/*
 * Return the digest size (in bytes) for DIGEST_NAME, or 0 if it is not found.
 */
unsigned int
crypto_lookup_digest_size(const char *digest_name)
{
  Digest_tab *dt;

  if ((dt = crypto_lookup_digest_by_name(digest_name)) == NULL)
	return(0);

  return(dt->digest_size);
}

/*
 * RFC 2104
 */
Digest_tab *
crypto_lookup_hmac_digest_by_size(unsigned int size)
{
  Digest_tab *dt;

  for (dt = &digest_tab[0]; dt->name != NULL; dt++) {
	if ((digest_usage(dt->flags) & DIGEST_USE_HMAC) && dt->digest_size == size)
	  return(dt);
  }

  return(NULL);
}

Digest_tab *
crypto_lookup_hmac_digest_by_name(const char *digest_name)
{
  Digest_tab *dt;

  for (dt = &digest_tab[0]; dt->name != NULL; dt++) {
	if ((digest_usage(dt->flags) & DIGEST_USE_HMAC)
		&& crypto_digest_name_match(dt, digest_name))
	  return(dt);
  }

  return(NULL);
}

Digest_desc *
crypto_lookup_passwd_digest_desc_by_name(const char *digest_desc)
{
  Digest_desc *dd;

  if ((dd = crypto_parse_digest_desc(digest_desc)) == NULL
	  || !digest_is_passwd_alg(dd->dt))
	return(NULL);

  return(dd);
}  

Passwd_digest_alg
crypto_lookup_passwd_digest_algorithm(const char *digest_name)
{
  Digest_tab *dt;

  for (dt = &digest_tab[0]; dt->name != NULL; dt++) {
	if (digest_is_passwd_alg(dt) && crypto_digest_name_match(dt, digest_name))
      return(dt->passwd_alg);
  }

  return(PASSWD_ALG_NONE);
}

Digest_tab *
crypto_lookup_passwd_digest_by_algorithm(Passwd_digest_alg alg)
{
  Digest_tab *dt;

  for (dt = &digest_tab[0]; dt->name != NULL; dt++) {
	if (digest_is_passwd_alg(dt) && dt->passwd_alg == alg)
      return(dt);
  }

  return(NULL);
}

/*
 * Initialize and validate cryptographic digest algorithms.
 * This must be called at startup time, before any of the digest functions
 * are used.
 */
static int
crypto_digest_init(void)
{
  Digest_tab *dt;
  static int did_init = 0;

  if (did_init)
	return(0);

  OpenSSL_add_all_digests();

  for (dt = &digest_tab[0]; dt->name != NULL; dt++) {
	if (digest_implementation(dt->flags) == DIGEST_OPENSSL)
	  dt->evp = EVP_get_digestbyname(dt->name);
	else if (digest_implementation(dt->flags) == DIGEST_DACS
			 && digest_is_sha512_ext(dt))
	  dt->evp = EVP_get_digestbyname("SHA512");
	else
	  continue;

	if (dt->evp == NULL) {
	  crypto_log_error();
	  if (dt->name != NULL)
		log_msg((LOG_ERROR_LEVEL, "Unrecognized digest name: \"%s\"", dt->name));
	  return(-1);
	}
  }

  did_init = 1;

  return(0);
}

static int
digest_format_info(Ds *ds, const char *digest_name)
{
  Digest_tab *dt;

  if ((dt = crypto_lookup_digest_by_name(digest_name)) == NULL)
	return(-1);

  ds_asprintf(ds, "%s: bsize=%d, dsize=%d",
			  dt->name, dt->block_size, dt->digest_size);
  if (dt->passwd_alg != PASSWD_ALG_NONE)
	ds_asprintf(ds, ", alg=%u", (unsigned int) dt->passwd_alg);

  ds_asprintf(ds, " [");
  if (digest_implementation(dt->flags) == DIGEST_DACS)
	ds_asprintf(ds, "DACS");
  else if (digest_implementation(dt->flags) == DIGEST_OPENSSL)
	ds_asprintf(ds, "OpenSSL");
  else if (digest_implementation(dt->flags) == DIGEST_SYSTEM)
	ds_asprintf(ds, "System");

  if (digest_usage(dt->flags) & DIGEST_USE_PASSWD)
	ds_asprintf(ds, ", Password");
  if (digest_usage(dt->flags) & DIGEST_USE_HMAC)
	ds_asprintf(ds, ", HMAC");
  if (digest_usage(dt->flags) & DIGEST_USE_SIGN)
	ds_asprintf(ds, ", Sign");
  if (digest_usage(dt->flags) & DIGEST_USE_PBKDF2)
	ds_asprintf(ds, ", PBKDF2");
  if (digest_usage(dt->flags) & DIGEST_USE_SCRYPT)
	ds_asprintf(ds, ", scrypt");
  if (digest_usage(dt->flags) & DIGEST_USE_PARAMS)
	ds_asprintf(ds, ", Parameters");
  if (digest_usage(dt->flags) & DIGEST_USE_VARLEN)
	ds_asprintf(ds, ", Varlen");

  ds_asprintf(ds, "]");

  return(0);
}

char *
crypto_digest_list(void)
{
  Digest_tab *dt;
  Ds ds;

  ds_init(&ds);
  for (dt = &digest_tab[0]; dt->name != NULL; dt++)
	ds_asprintf(&ds, "%s%s", (ds_len(&ds) == 0) ? "" : ", ", dt->name);

  return(ds_buf(&ds));
}

void
crypto_show_digest_info(FILE *ofp, const char *digest_name)
{
  Digest_tab *dt;
  Ds ds;
  FILE *fp;

  if (ofp == NULL)
	fp = stderr;
  else
	fp = ofp;

  if (digest_name != NULL) {
	if ((dt = crypto_lookup_digest_by_name(digest_name)) == NULL)
	  return;
	ds_init(&ds);
	digest_format_info(&ds, dt->name);
	fprintf(fp, "%s\n", ds_buf(&ds));

	return;
  }

  for (dt = &digest_tab[0]; dt->name != NULL; dt++) {
	ds_init(&ds);
	digest_format_info(&ds, dt->name);
	fprintf(fp, "%s\n", ds_buf(&ds));
  }
}

#ifdef NOTDEF
/*
 * A string equality test that is resistant to timing attacks.
 * MAXLEN must be at least as long as the longest possible P or Q.
 * The goal is for the test to always take the same amount of time to perform,
 * as nearly as possible, regardless of P and Q.
 * In particular, an attacker should not be able to get information about where
 * P and Q begin to differ so as to be able to deduce their matching
 * prefixes.
 * Obviously, this is purposely inefficient.
 */
int
crypto_streq(const char *p, const char *q, size_t maxlen)
{
  int neq, end_i, end_j, i, j, k;

  neq = 0;
  i = j = k = 0;
  end_i = end_j = 0;
  while (k < maxlen) {
	neq |= (p[i] - q[j]);
	if (end_i)
	  i = (i + 1) % end_i;
	else if (p[i] == '\0') {
	  end_i = i;
	  i = 0;
	}
	else
	  i++;
	
	if (end_j)
	  j = (j + 1) % end_j;
	else if (q[j] == '\0') {
	  end_j = j;
	  j = 0;
	}
	else
	  j++;

	k++;
  }
  if (!end_i || !end_j) {
	log_msg((LOG_ERROR_LEVEL, "Configuration error"));
	dacs_abort("Bad crypto_streq() call");
	/*NOTREACHED*/
  }

  return(!neq);
}
#endif

void
crypto_log_error(void)
{
  long err;
  char msg[1024];

  while ((err = ERR_get_error()) != 0) {
	ERR_error_string_n(err, msg, sizeof(msg));
	log_msg((LOG_ERROR_LEVEL, "%s", msg));
  }
}

int
crypto_init(void)
{

  if (crypto_digest_init() == -1) {
	/* Configuration error! */
	log_msg((LOG_ERROR_LEVEL, "Could not initialize digests"));
	return(-1);
  }

  if (AUTH_MAX_RANDOM_PADDING_LENGTH > 255
	  || AUTH_MIN_RANDOM_PADDING_LENGTH > AUTH_MAX_RANDOM_PADDING_LENGTH) {
	/* Configuration error! */
	log_msg((LOG_ERROR_LEVEL, "Invalid auth padding configuration"));
	return(-1);
  }

  ERR_load_crypto_strings();

#ifdef CRYPT_ENGINE_NAME
  {
	ENGINE *e;

	if ((e = ENGINE_by_id(CRYPT_ENGINE_NAME)) == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "ENGINE_by_id lookup error for %s",
			   CRYPT_ENGINE_NAME));
	  return(-1);
	}

	if (ENGINE_set_default(e, ENGINE_METHOD_ALL) == 0) {
	  log_msg((LOG_ERROR_LEVEL, "ENGINE_set_default error for %s",
			   CRYPT_ENGINE_NAME));
	  return(-1);
	}
  }
#endif

  return(0);
}

/*
 * STR is a PEM base64 encoded DER X.509 certificate.
 * Create a copy of STR with everything up to and including the start of
 * certificate header stripped and everything following and including the end
 * of certificate header stripped.
 * Return NULL if the expected header and trailer are absent.
 */
Ds *
pem_cert_strip_str(char *str)
{
  char *e, *p, *s;
  Ds *buf;

  if (str == NULL)
	return(NULL);

  if ((s = strstr(str, "CERTIFICATE-----")) == NULL)
	return(NULL);
  s += strlen("CERTIFICATE-----");

  if ((e = strstr(s, "-----END")) == NULL)
	return(NULL);

  buf = ds_init(NULL);
  for (p = s; p < e; p++) {
	if (*p != '\n' && *p != '\r' && *p != ' ')
	  ds_appendc(buf, (int) *p);
  }
  ds_appendc(buf, (int) '\0');

  return(buf);
}

Ds *
pem_cert_strip(Ds *ds)
{
  Ds *buf;

  if (ds == NULL)
	return(NULL);

  buf = pem_cert_strip_str(ds_buf(ds));

  return(buf);
}

/*
 * Convert BUFLEN bytes of BUF to hex and format them as a colon-separated,
 * null-terminated hex byte string (e.g., "00:0A:02:99")
 */
static Ds *
thumb_fmt(unsigned char *buf, size_t buflen)
{
  char *p, *s;
  Ds *ds;

  s = strbtohex(buf, buflen, STRBTOHEX_UPPER);

  ds = ds_init(NULL);
  for (p = s; *p != '\0'; p++) {
	if (p != s)
	  ds_appendc(ds, (int) ':');
	ds_appendc(ds, (int) *p++);
	ds_appendc(ds, (int) *p);
  }
  ds_appendc(ds, (int) '\0');

  return(ds);
}

/*
 * Compute the standard certificate thumbprint (hash) of STRIPPED_CERT, a PEM
 * base64 encoded DER X.509 cert with everything before the header and
 * following the trailer already removed.
 */
Ds *
crypto_cert_thumbprint(char *stripped_cert, char **fmt)
{
  unsigned char *dec;
  long len;
  Ds *digest;

  len = mime_decode_base64(stripped_cert, &dec);
  digest = sha1(dec, len);

  if (fmt != NULL) {
	Ds *fmt_digest;

	fmt_digest = thumb_fmt(ds_ucbuf(digest), ds_len(digest));
	*fmt = ds_buf(fmt_digest);
  }

  return(digest);
}

char *
pem_from_evp_pub_pkey(EVP_PKEY *public_key)
{
  char *ptr, *public_key_pem;
  BIO *mem;
  Ds ds;

  mem = BIO_new(BIO_s_mem());
  PEM_write_bio_PUBKEY(mem, public_key);
  /* WTF? Can't we get this null-terminated? */
  BIO_get_mem_data(mem, &ptr);
  ds_init(&ds);
  ds_copyb(&ds, ptr, BIO_number_written(mem), 0);
  ds_appendc(&ds, (int) '\0');
  public_key_pem = ds_buf(&ds);
  BIO_free(mem);

  return(public_key_pem);
}

char *
pem_from_evp_priv_pkey(EVP_PKEY *private_key)
{
  char *ptr, *private_key_pem;
  BIO *mem;
  Ds ds;

  mem = BIO_new(BIO_s_mem());
  PEM_write_bio_PrivateKey(mem, private_key, NULL, NULL, 0, NULL, NULL);
  /* WTF? Can't we get this null-terminated? */
  BIO_get_mem_data(mem, &ptr);
  ds_init(&ds);
  ds_copyb(&ds, ptr, BIO_number_written(mem), 0);
  ds_appendc(&ds, (int) '\0');
  private_key_pem = ds_buf(&ds);
  BIO_free(mem);

  return(private_key_pem);
}

/*
 * Return a base64 encoded thumbprint of CERT, a PEM base64 encoded DER X.509
 * cert.
 */
Ds *
pem_cert_thumbprint(char *cert)
{
  char *enc;
  Ds *digest, *ds, *tp;

  if ((ds = pem_cert_strip_str(cert)) == NULL)
	return(NULL);

  digest = crypto_cert_thumbprint(ds_buf(ds), NULL);

  mime_encode_base64(ds_ucbuf(digest), ds_len(digest), &enc);
  tp = ds_setn(NULL, (unsigned char *) enc, strlen(enc) + 1);

  return(tp);
}

/*
 * Return a hex-formatted string representation of the PEM certificate CERT.
 */
char *
pem_cert_thumbprint_fmt_str(char *cert)
{
  Ds *digest, *ds, *fmt_digest;

  if ((ds = pem_cert_strip_str(cert)) == NULL)
	return(NULL);

  digest = crypto_cert_thumbprint(ds_buf(ds), NULL);

  fmt_digest = thumb_fmt(ds_ucbuf(digest), ds_len(digest));

  return(ds_buf(fmt_digest));
}

char *
pem_cert_thumbprint_fmt(Ds *tp)
{

  return(pem_cert_thumbprint_fmt_str(ds_buf(tp)));
}

Ds *
pem_cert_load_stripped(char *certfile)
{
  Ds *cert, *ds;

  if ((ds = ds_load_file(NULL, certfile)) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Could not load server cert from %s", certfile));
	return(NULL);
  }

  cert = pem_cert_strip(ds);
  ds_free(ds);

  return(cert);
}

RSA *
pem_load_private_key_from_buf(char *buf, int buflen, char *passphrase)
{
  BIO *bio;
  RSA *priv_key;

  if ((bio = BIO_new_mem_buf(buf, buflen)) == NULL)
	return(NULL);

  priv_key = (RSA *) PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, passphrase);
  if (priv_key == NULL) {
	crypto_log_error();
	return(NULL);
  }

  return(priv_key);
}

RSA *
pem_load_private_key(char *keyfile, char *passphrase)
{
  BIO *bio;
  Ds *pkey;
  RSA *priv_key;

  if ((pkey = ds_load_file(NULL, keyfile)) == NULL)
	return(NULL);

  priv_key = pem_load_private_key_from_buf(ds_buf(pkey), ds_len(pkey) - 1,
										   passphrase);

  return(priv_key);
}

Ds *
pem_make_cert(char *enc_cert_str)
{
  int i;
  char *p;
  Ds *ds;

  ds = ds_init(NULL);
  ds_asprintf(ds, "-----BEGIN CERTIFICATE-----\n");
  i = 0;
  for (p = enc_cert_str; *p != '\0'; p++) {
	if (i == 78) {
	  ds_asprintf(ds, "\n");
	  i = 0;
	}
	ds_asprintf(ds, "%c", *p);
	i++;
  }
  ds_asprintf(ds, "\n");
  ds_asprintf(ds, "-----END CERTIFICATE-----");

  return(ds);
}

static int debug_sha512_t = 0;

static char *
to_hex(unsigned char *value, size_t nbytes)
{
  int i;
  char *p;
  unsigned char *v;
  static char buf[512];

  v = value;
  p = buf;
  for (i = 0; i < nbytes; i++)
	p += sprintf(p, "%.2x", *v++ & 0xff);

  return(buf);
}

static void
dump_h(uint64_t *h, int n)
{
  int i;

  for (i = 0; i < n; i++)
	printf("H0[%d]=%s\n", i, to_hex((unsigned char *) &h[i], 8));
  printf("\n");
}

/*
 * SHA512_CTX is from OpenSSL.
 */
static Digest_ctx *
sha512_t_init(unsigned int t)
{
  int i, namelen, ret;
  unsigned int len, outlen;
  char name[14];
  unsigned char result[64];
  uint64_t *ui64p;
  const EVP_MD *evp;
  Digest_ctx *ctx;
  Digest_tab *dt;
  SHA512_CTX *c512;

  if (t == 0 || t == 384 || t >= 512 || (t % 8) != 0) {
	log_msg((LOG_ERROR_LEVEL, "Invalid t value"));
	return(NULL);
  }

  if ((dt = crypto_lookup_digest_by_name("SHA512")) == NULL) {
	crypto_log_error();
	log_msg((LOG_ERROR_LEVEL, "Unrecognized digest name: \"%s\"", "SHA512"));
	return(NULL);
  }

  if ((ctx = digest_make_ctx(dt)) == NULL)
	return(NULL);
  ctx->sha512_t = t;

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  c512 = (SHA512_CTX *) EVP_MD_CTX_md_data(ctx->u.evp_ctx);
  memset(c512, 0, sizeof(SHA512_CTX));
#else
  c512 = (SHA512_CTX *) ctx->u.evp_ctx->md_data;
  memset(c512, 0, sizeof(EVP_MD_CTX));
#endif

  c512->h[0] = U64(0x6a09e667f3bcc908) ^ U64(0xa5a5a5a5a5a5a5a5);
  c512->h[1] = U64(0xbb67ae8584caa73b) ^ U64(0xa5a5a5a5a5a5a5a5);
  c512->h[2] = U64(0x3c6ef372fe94f82b) ^ U64(0xa5a5a5a5a5a5a5a5);
  c512->h[3] = U64(0xa54ff53a5f1d36f1) ^ U64(0xa5a5a5a5a5a5a5a5);
  c512->h[4] = U64(0x510e527fade682d1) ^ U64(0xa5a5a5a5a5a5a5a5);
  c512->h[5] = U64(0x9b05688c2b3e6c1f) ^ U64(0xa5a5a5a5a5a5a5a5);
  c512->h[6] = U64(0x1f83d9abfb41bd6b) ^ U64(0xa5a5a5a5a5a5a5a5);
  c512->h[7] = U64(0x5be0cd19137e2179) ^ U64(0xa5a5a5a5a5a5a5a5);

  c512->Nl = 0;
  c512->Nh = 0;
  c512->num = 0;
  c512->md_len = SHA512_DIGEST_LENGTH;

  namelen = sprintf(name, "SHA-512/%u", t);
  EVP_DigestUpdate(ctx->u.evp_ctx, name, (unsigned int) namelen);

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  {
	EVP_MD const *md;
	EVP_MD_CTX *evp;
	evp = ctx->u.evp_ctx;
	md = EVP_MD_CTX_md(evp);
	ret = (EVP_MD_meth_get_final(md))(evp, result);
  }
#else
  ret = ctx->u.evp_ctx->digest->final(ctx->u.evp_ctx, result);
#endif

  ui64p = (uint64_t *) result;
  for (i = 0; i < 8; i++)
	c512->h[i] = bendian_dec64(&ui64p[i]);

  c512->Nl = 0;
  c512->Nh = 0;
  c512->num = 0;
  c512->md_len = SHA512_DIGEST_LENGTH;
  memset(c512->u.d, 0, sizeof(c512->u.d));
  memset(c512->u.p, 0, sizeof(c512->u.p));

  if (debug_sha512_t) {
	printf("sha512_t_init(%d):\n", t);
	dump_h((uint64_t *) c512->h, 8);
  }

  return(ctx);
}

/*
 * Compute SHA-512/t (t > 0 && t < 512 && t != 384)
 * INPUT_LEN is in bytes and T is in bits.
 * T must be a multiple of 8, if only because no test vectors have been
 * found for the general case
 * XXX Should be able to handle input in bits
 */
char *
sha512_t(unsigned int t, const void *input, int input_len)
{
  unsigned int len, outlen;
  unsigned char result[64];
  Digest_ctx *ctx;
  SHA512_CTX *c512;

  if ((ctx = sha512_t_init(t)) == NULL)
	return(NULL);

  if (input_len < 0)
	len = strlen((char *) input);
  else
	len = (unsigned int) input_len;

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  c512 = (SHA512_CTX *) EVP_MD_CTX_md_data(ctx->u.evp_ctx);
#else
  c512 = (SHA512_CTX *) ctx->u.evp_ctx->md_data;
#endif

  EVP_DigestUpdate(ctx->u.evp_ctx, input, len);
  EVP_DigestFinal(ctx->u.evp_ctx, result, &outlen);

  return(to_hex(result, (t + 7) / 8));
}

static Digest_ctx *
sha512_224_init(Digest_ctx *octx)
{
  unsigned int len;
  unsigned char result[64];
  const EVP_MD *evp;
  Digest_ctx *ctx;
  SHA512_CTX *c512;

  if (octx == NULL) {
	if ((ctx = crypto_digest_open("SHA512")) == NULL)
	  return(NULL);
  }
  else
	ctx = octx;

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  c512 = (SHA512_CTX *) EVP_MD_CTX_md_data(ctx->u.evp_ctx);
  memset(c512, 0, sizeof(SHA512_CTX));
#else
  c512 = (SHA512_CTX *) ctx->u.evp_ctx->md_data;
  memset(c512, 0, sizeof(EVP_MD_CTX));
#endif


  c512->h[0] = U64(0x8c3d37c819544da2);
  c512->h[1] = U64(0x73e1996689dcd4d6);
  c512->h[2] = U64(0x1dfab7ae32ff9c82);
  c512->h[3] = U64(0x679dd514582f9fcf);
  c512->h[4] = U64(0x0f6d2b697bd44da8);
  c512->h[5] = U64(0x77e36f7304c48942);
  c512->h[6] = U64(0x3f9d85a86a1d36c8);
  c512->h[7] = U64(0x1112e6ad91d692a1);

  c512->Nl = 0;
  c512->Nh = 0;
  c512->num = 0;
  c512->md_len = SHA512_DIGEST_LENGTH;

  return(ctx);
}

/*
 * This doesn't add anything to sha512_t(224, INPUT, INPUT_LEN) but the
 * alternate implementation is semi-useful for validation purposes.
 */
char *
sha512_224(const void *input, int input_len)
{
int i;
  unsigned int len, outlen;
  unsigned char result[64];
  Digest_ctx *ctx;
  SHA512_CTX *c512;

  ctx = sha512_224_init(NULL);

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  c512 = (SHA512_CTX *) EVP_MD_CTX_md_data(ctx->u.evp_ctx);
#else
  c512 = (SHA512_CTX *) ctx->u.evp_ctx->md_data;
#endif

  if (debug_sha512_t) {
	printf("sha512_224 after init:\n");
	dump_h((uint64_t *) c512->h, 8);
  }

  if (input_len < 0)
	len = strlen((char *) input);
  else
	len = (unsigned int) input_len;

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  c512 = (SHA512_CTX *) EVP_MD_CTX_md_data(ctx->u.evp_ctx);
#else
  c512 = (SHA512_CTX *) ctx->u.evp_ctx->md_data;
#endif

  if (debug_sha512_t)
	dump_h((uint64_t *) c512->h, 8);

  EVP_DigestUpdate(ctx->u.evp_ctx, input, len);
  EVP_DigestFinal(ctx->u.evp_ctx, result, &outlen);

  return(to_hex(result, 28));
}

static Digest_ctx *
sha512_256_init(Digest_ctx *octx)
{
  unsigned int len;
  unsigned char result[64];
  const EVP_MD *evp;
  Digest_ctx *ctx;
  SHA512_CTX *c512;

  if (octx == NULL) {
	if ((ctx = crypto_digest_open("SHA512")) == NULL)
	  return(NULL);
  }
  else
	ctx = octx;

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  c512 = (SHA512_CTX *) EVP_MD_CTX_md_data(ctx->u.evp_ctx);
  memset(c512, 0, sizeof(SHA512_CTX));
#else
  c512 = (SHA512_CTX *) ctx->u.evp_ctx->md_data;
  memset(c512, 0, sizeof(EVP_MD_CTX));
#endif

  c512->h[0] = U64(0x22312194fc2bf72c);
  c512->h[1] = U64(0x9f555fa3c84c64c2);
  c512->h[2] = U64(0x2393b86b6f53b151);
  c512->h[3] = U64(0x963877195940eabd);
  c512->h[4] = U64(0x96283ee2a88effe3);
  c512->h[5] = U64(0xbe5e1e2553863992);
  c512->h[6] = U64(0x2b0199fc2c85b8aa);
  c512->h[7] = U64(0x0eb72ddc81c52ca2);

  c512->Nl = 0;
  c512->Nh = 0;
  c512->num = 0;
  c512->md_len = SHA512_DIGEST_LENGTH;

  return(ctx);
}

/*
 * This doesn't add anything to sha512_t(256, INPUT, INPUT_LEN) but this
 * alternate implementation is semi-useful for validation purposes.
 */
char *
sha512_256(const void *input, int input_len)
{
  int i;
  unsigned int len, outlen;
  unsigned char result[64];
  Digest_ctx *ctx;
  SHA512_CTX *c512;

  ctx = sha512_256_init(NULL);
#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  c512 = (SHA512_CTX *) EVP_MD_CTX_md_data(ctx->u.evp_ctx);
#else
  c512 = (SHA512_CTX *) ctx->u.evp_ctx->md_data;
#endif

  if (debug_sha512_t)
	dump_h((uint64_t *) c512->h, 8);

  if (input_len < 0)
	len = strlen((char *) input);
  else
	len = (unsigned int) input_len;

  EVP_DigestUpdate(ctx->u.evp_ctx, input, len);
  EVP_DigestFinal(ctx->u.evp_ctx, result, &outlen);

  return(to_hex(result, 32));
}

/*
 * SHA-3 (based on FIPS PUB 202 of August, 2015)
 * http://csrc.nist.gov/groups/ST/hash/sha-3/fips202_standard_2015.html
 * http://keccak.noekeon.org/
 *
 * These functions are approved for use with HMAC:
 * Hash Function:    SHA3-224 SHA3-256 SHA3-384 SHA3-512
 * Input Block Size
 *            (bytes)     144      136      104       72
 *
 * "The SHA-3 functions are defined on messages of any bit length, including the
 * empty string. A conforming implementation of a SHA-3 function may restrict
 * the set of supported bit lengths for messages."
 *
 * Adapted from Aleksey Kravchenko's implementation for RHash
 * Copyright: 2013 Aleksey Kravchenko <rhash.admin@gmail.com>
 * https://github.com/rhash/RHash
 * http://cpansearch.perl.org/src/RHASH/Crypt-RHash-0.91/librhash/
 * See NOTICES. 
 */

/*
 * Validated against test vectors found in the following and elsewhere:
 * http://csrc.nist.gov/groups/ST/toolkit/examples.html
 * http://www.di-mgt.com.au/sha_testvectors.html
 * https://github.com/coruus/nist-testvectors/tree/master/fips202
 */

#define LE_DEC64(X)			lendian_dec64(&X)
#define ROTL64(QWORD, N)	((QWORD) << (N) ^ ((QWORD) >> (64 - (N))))
#define IS_ALIGNED_64(P)	(0 == (7 & ((const char *) (P) - (const char *) 0)))

#define SHA3_MUST_FINALIZE	0x40000000
#define SHA3_FINALIZED		0x80000000

/* Constants for 24 rounds */
static uint64_t keccak_round_constants[SHA3_NUMBER_OF_ROUNDS] = {
  INT64_C(0x0000000000000001), INT64_C(0x0000000000008082),
  INT64_C(0x800000000000808A), INT64_C(0x8000000080008000),
  INT64_C(0x000000000000808B), INT64_C(0x0000000080000001),
  INT64_C(0x8000000080008081), INT64_C(0x8000000000008009),
  INT64_C(0x000000000000008A), INT64_C(0x0000000000000088),
  INT64_C(0x0000000080008009), INT64_C(0x000000008000000A),
  INT64_C(0x000000008000808B), INT64_C(0x800000000000008B),
  INT64_C(0x8000000000008089), INT64_C(0x8000000000008003),
  INT64_C(0x8000000000008002), INT64_C(0x8000000000000080),
  INT64_C(0x000000000000800A), INT64_C(0x800000008000000A),
  INT64_C(0x8000000080008081), INT64_C(0x8000000000008080),
  INT64_C(0x0000000080000001), INT64_C(0x8000000080008008)
};

/*
 * Initialize a context for digest size of BITS bits.
 */
void
crypto_keccak_init(SHA3_ctx *ctx, unsigned int nbits)
{
  unsigned int rate;

  rate = 1600 - nbits * 2;
  if (rate > 1600 || (rate % 64) != 0) {
	log_msg((LOG_ERROR_LEVEL, "Bug: invalid rate"));
	abort();
  }

  ctx->rate = rate;
  ctx->block_size = rate / 8;
  ctx->digest_bits = nbits;
  ctx->rest = 0;
  ctx->bit_padding = 0;

  memset(ctx->hash, 0, sizeof(ctx->hash));
  memset(ctx->message, 0, sizeof(ctx->message));
}

static SHA3_ctx *
do_sha3_init(Digest_tab *dt, SHA3_ctx *octx, unsigned int *digest_size)
{
  SHA3_ctx *ctx;

  ctx = (octx == NULL) ? ALLOC(SHA3_ctx) : octx;
  ctx->dt = dt;

  switch (dt->alg) {
  case DIGEST_SHA3_224:
	crypto_keccak_init(ctx, 224);
	break;

  case DIGEST_SHA3_256:
	crypto_keccak_init(ctx, 256);
	break;

  case DIGEST_SHA3_384:
	crypto_keccak_init(ctx, 384);
	break;

  case DIGEST_SHA3_512:
	crypto_keccak_init(ctx, 512);
	break;

  default:
	free(ctx);
	return(NULL);
  }

  if (digest_size != NULL)
	*digest_size = dt->digest_size;

  return(ctx);
}

/*
 * Initialize a SHA-3 context for SHA-3 algorithm ALG.
 * If OCTX is non-NULL, initialize it, otherwise allocate a new context.
 *
 * Return the initialized context, or NULL.
 * If DIGEST_SIZE is non-NULL, set it to the size of the digest for ALG,
 * in bytes.
 */
SHA3_ctx *
sha3_init(Digest_alg alg, SHA3_ctx *octx, unsigned int *digest_size)
{
  Digest_tab *dt;

  if ((dt = crypto_lookup_digest_by_algorithm(alg)) == NULL)
	return(NULL);

  return(do_sha3_init(dt, octx, digest_size));
}

SHA3_ctx *
sha3_init_by_name(const char *digest_name, SHA3_ctx *octx,
				  unsigned int *digest_size)
{
  Digest_tab *dt;

  if ((dt = crypto_lookup_digest_by_name(digest_name)) == NULL)
	return(NULL);

  return(do_sha3_init(dt, octx, digest_size));
}

/* Keccak theta() transformation */
static void
keccak_theta(uint64_t *A)
{
  unsigned int x;
  uint64_t C[5], D[5];

  for (x = 0; x < 5; x++)
	C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20];

  D[0] = ROTL64(C[1], 1) ^ C[4];
  D[1] = ROTL64(C[2], 1) ^ C[0];
  D[2] = ROTL64(C[3], 1) ^ C[1];
  D[3] = ROTL64(C[4], 1) ^ C[2];
  D[4] = ROTL64(C[0], 1) ^ C[3];

  for (x = 0; x < 5; x++) {
	A[x]      ^= D[x];
	A[x + 5]  ^= D[x];
	A[x + 10] ^= D[x];
	A[x + 15] ^= D[x];
	A[x + 20] ^= D[x];
  }
}

/* Keccak pi() transformation */
static void
keccak_pi(uint64_t *A)
{
  uint64_t A1;

  A1 = A[1];
  A[ 1] = A[ 6];
  A[ 6] = A[ 9];
  A[ 9] = A[22];
  A[22] = A[14];
  A[14] = A[20];
  A[20] = A[ 2];
  A[ 2] = A[12];
  A[12] = A[13];
  A[13] = A[19];
  A[19] = A[23];
  A[23] = A[15];
  A[15] = A[ 4];
  A[ 4] = A[24];
  A[24] = A[21];
  A[21] = A[ 8];
  A[ 8] = A[16];
  A[16] = A[ 5];
  A[ 5] = A[ 3];
  A[ 3] = A[18];
  A[18] = A[17];
  A[17] = A[11];
  A[11] = A[ 7];
  A[ 7] = A[10];
  A[10] = A1;
  /* A[ 0] is left as is. */
}

/* Keccak chi() transformation */
static void
keccak_chi(uint64_t *A)
{
  int i;

  for (i = 0; i < 25; i += 5) {
	uint64_t A0 = A[0 + i], A1 = A[1 + i];

	A[0 + i] ^= ~A1 & A[2 + i];
	A[1 + i] ^= ~A[2 + i] & A[3 + i];
	A[2 + i] ^= ~A[3 + i] & A[4 + i];
	A[3 + i] ^= ~A[4 + i] & A0;
	A[4 + i] ^= ~A0 & A1;
  }
}

static Digest_ctx *
digest_make_ctx(Digest_tab *dt)
{
  Digest_ctx *ctx;

  ctx = ALLOC(Digest_ctx);
  ctx->dt = dt;
  ctx->sha512_t = 0;

  if (digest_implementation(dt->flags) == DIGEST_OPENSSL) {
	ctx->u.evp_ctx = EVP_MD_CTX_create();
	if (!EVP_DigestInit_ex(ctx->u.evp_ctx, dt->evp, NULL)) {
	  crypto_log_error();
	  dacs_fatal(ds_xprintf("Unable to initialize digest name: \"%s\"",
							dt->name));
	  /*NOTREACHED*/
	}
  }
  else if (digest_implementation(dt->flags) == DIGEST_DACS) {
	if (digest_is_sha3(dt))
	  crypto_keccak_init(&ctx->u.sha3_ctx, dt->digest_size * 8);
	else if (dt->alg == DIGEST_SHA512_224) {
	  ctx->u.evp_ctx = EVP_MD_CTX_create();
	  if (!EVP_DigestInit_ex(ctx->u.evp_ctx, dt->evp, NULL)) {
		crypto_log_error();
		dacs_fatal(ds_xprintf("Unable to initialize digest name: \"%s\"",
							  dt->name));
		/*NOTREACHED*/
	  }
	  sha512_224_init(ctx);
	}
	else if (dt->alg == DIGEST_SHA512_256) {
	  ctx->u.evp_ctx = EVP_MD_CTX_create();
	  if (!EVP_DigestInit_ex(ctx->u.evp_ctx, dt->evp, NULL)) {
		crypto_log_error();
		dacs_fatal(ds_xprintf("Unable to initialize digest name: \"%s\"",
							  dt->name));
		/*NOTREACHED*/
	  }
	  sha512_256_init(ctx);
	}
	else {
	  dacs_fatal(ds_xprintf("Unimplemented digest name: \"%s\"", dt->name));
	  /*NOTREACHED*/
	}
  }
  else {
	free(ctx);
	ctx = NULL;
  }

  return(ctx);
}

static void
sha3_permutation(uint64_t *state)
{
  int round;

  for (round = 0; round < SHA3_NUMBER_OF_ROUNDS; round++) {
	keccak_theta(state);

	/* Apply Keccak rho() transformation. */
	state[ 1] = ROTL64(state[ 1],  1);
	state[ 2] = ROTL64(state[ 2], 62);
	state[ 3] = ROTL64(state[ 3], 28);
	state[ 4] = ROTL64(state[ 4], 27);
	state[ 5] = ROTL64(state[ 5], 36);
	state[ 6] = ROTL64(state[ 6], 44);
	state[ 7] = ROTL64(state[ 7],  6);
	state[ 8] = ROTL64(state[ 8], 55);
	state[ 9] = ROTL64(state[ 9], 20);
	state[10] = ROTL64(state[10],  3);
	state[11] = ROTL64(state[11], 10);
	state[12] = ROTL64(state[12], 43);
	state[13] = ROTL64(state[13], 25);
	state[14] = ROTL64(state[14], 39);
	state[15] = ROTL64(state[15], 41);
	state[16] = ROTL64(state[16], 45);
	state[17] = ROTL64(state[17], 15);
	state[18] = ROTL64(state[18], 21);
	state[19] = ROTL64(state[19],  8);
	state[20] = ROTL64(state[20], 18);
	state[21] = ROTL64(state[21],  2);
	state[22] = ROTL64(state[22], 61);
	state[23] = ROTL64(state[23], 56);
	state[24] = ROTL64(state[24], 14);
		
	keccak_pi(state);
	keccak_chi(state);

	/* Apply iota(state, round). */
	*state ^= keccak_round_constants[round];
  }
}

/*
 * The core transformation: process the specified block of data using state HASH,
 * on message block BLOCK of BLOCK_SIZE bytes.
 */
static void
sha3_process_block(uint64_t hash[25], const uint64_t *block, size_t block_size)
{

  hash[ 0] ^= LE_DEC64(block[ 0]);
  hash[ 1] ^= LE_DEC64(block[ 1]);
  hash[ 2] ^= LE_DEC64(block[ 2]);
  hash[ 3] ^= LE_DEC64(block[ 3]);
  hash[ 4] ^= LE_DEC64(block[ 4]);
  hash[ 5] ^= LE_DEC64(block[ 5]);
  hash[ 6] ^= LE_DEC64(block[ 6]);
  hash[ 7] ^= LE_DEC64(block[ 7]);
  hash[ 8] ^= LE_DEC64(block[ 8]);

  /* If not SHA3-512. */
  if (block_size > 72) {
	hash[ 9] ^= LE_DEC64(block[ 9]);
	hash[10] ^= LE_DEC64(block[10]);
	hash[11] ^= LE_DEC64(block[11]);
	hash[12] ^= LE_DEC64(block[12]);

	/* If not SHA3-384. */
	if (block_size > 104) {
	  hash[13] ^= LE_DEC64(block[13]);
	  hash[14] ^= LE_DEC64(block[14]);
	  hash[15] ^= LE_DEC64(block[15]);
	  hash[16] ^= LE_DEC64(block[16]);

	  /* If not SHA3-256. */
	  if (block_size > 136) {
		hash[17] ^= LE_DEC64(block[17]);

#ifdef FULL_SHA3_FAMILY_SUPPORT
		/* If not SHA3-224. */
		if (block_size > 144) {
		  hash[18] ^= LE_DEC64(block[18]);
		  hash[19] ^= LE_DEC64(block[19]);
		  hash[20] ^= LE_DEC64(block[20]);
		  hash[21] ^= LE_DEC64(block[21]);
		  hash[22] ^= LE_DEC64(block[22]);
		  hash[23] ^= LE_DEC64(block[23]);
		  hash[24] ^= LE_DEC64(block[24]);
		}
#endif
	  }
	}
  }

  sha3_permutation(hash);
}

/*
 * Feed MSG (SIZE bytes long) to the hash function with context CTX.
 */
void
sha3_update(SHA3_ctx *ctx, const unsigned char *msg, size_t size)
{
  size_t block_size, index;

  if (ctx->rest & (SHA3_FINALIZED | SHA3_MUST_FINALIZE))
	return;

  index = (size_t) ctx->rest;
  block_size = (size_t) ctx->block_size;

  ctx->rest = (unsigned int) ((ctx->rest + size) % block_size);

  if (index) {
	size_t left = block_size - index;

	/* Add MSG to the partial block from the previous call. */
	memcpy((char *) ctx->message + index, msg, (size < left) ? size : left);

	/* If the block is still incomplete, we're done for now. */
	if (size < left)
	  return;

	/* Process the completed block. */
	sha3_process_block(ctx->hash, ctx->message, block_size);
	msg  += left;
	size -= left;
  }

  /* Process as many complete blocks as possible. */
  while (size >= block_size) {
	uint64_t *aligned_message_block;

	if (IS_ALIGNED_64(msg)) {
	  /* The most common case: processing an aligned message without copying. */
	  aligned_message_block = (uint64_t *) msg;
	} else {
	  memcpy(ctx->message, msg, block_size);
	  aligned_message_block = ctx->message;
	}

	sha3_process_block(ctx->hash, aligned_message_block, block_size);
	msg  += block_size;
	size -= block_size;
  }

  if (size) {
	/* Save the remaining partial block. */
	memcpy(ctx->message, msg, size);
  }
}

void
sha3_update_bits(SHA3_ctx *ctx, const unsigned char *msg, size_t nbits)
{
  size_t nbytes;

  if (ctx->rest & (SHA3_FINALIZED | SHA3_MUST_FINALIZE))
	return;

  nbytes = (nbits + 7) / 8;
  sha3_update(ctx, msg, nbytes);

  if ((nbits % 8) != 0) {
	/* Only the final update may not be a multiple of 8 bits. */
	ctx->bit_padding = 8 - ((nbytes * 8) - nbits);
	ctx->rest |= SHA3_MUST_FINALIZE;
  }
}

/*
 * NIST FIPS 202 SHA-3 bit-length message tests
 * http://csrc.nist.gov/groups/ST/toolkit/examples.html
 * The zero-length tests are included in the other set.
 *
 * Originally, none of the 1605-bit test vectors matched the published values.
 * Others reported this.  At some point the published values were corrected
 * and now all agree with these tests.
 */
static const char *sha3_bit_tests[] = {
  /* SHA3-{224,256,384,512}_Msg5 */
  "ffbad5da96bad71789330206dc6768ecaeb1b32dca6b3301489674ab",
  "7b0047cf5a456882363cbf0fb05322cf65f4b7059a46365e830132e3b5d957af",
  "737c9b491885e9bf7428e792741a7bf8dca9653471c3e148473f2c236b6a0a6455eb1dce9f779b4b6b237fef171b1c64",
  "a13e01494114c09800622a70288c432121ce70039d753cadd2e006e4d961cb27544c1481e5814bdceb53be6733d5e099795e5e81918addb058e22a9f24883f37",

  /* SHA3-{224,256,384,512}_Msg30 */
  "d666a514cc9dba25ac1ba69ed3930460deaac9851b5f0baab007df3b",
  "c8242fef409e5ae9d1f1c857ae4dc624b92b19809f62aa8c07411c54a078b1d0",
  "955b4dd1be03261bd76f807a7efd432435c417362811b8a50c564e7ee9585e1ac7626dde2fdc030f876196ea267f08c3",
  "9834c05a11e1c5d3da9c740e1c106d9e590a0e530b6f6aaa7830525d075ca5db1bd8a6aa981a28613ac334934a01823cd45f45e49b6d7e6917f2f16778067bab",

  /* SHA3-{224,256,384,512}_Msg1600 */
  "9376816aba503f72f96ce7eb65ac095deee3be4bf9bbc2a1cb7e11e0",
  "79f38adec5c20307a98ef76e8324afbfd46cfd81b22e3973c65fa1bd9de31787",
  "1881de2ca7e41ef95dc4732b8f5f002b189cc1e42b74168ed1732649ce1dbcdd76197a31fd55ee989f2d7050dd473e8f",
  "e76dfad22084a8b1467fcf2ffa58361bec7628edf5f3fdc0e4805dc48caeeca81b7c13c30adf52a3659584739a2df46be589c51ca1a4a8416df6545a1ce8ba00",

  /*
   * SHA3-{224,256,384,512}_Msg1605
   * Note: In the draft, the published vectors used E3 as the final padded byte
   * but they should have used C3.
   * This was corrected in the standard and the updated FIPS 202 example vectors.
   * https://groups.google.com/forum/#!topic/sci.crypt/kU5YMPdnB1s
   */
  "22d2f7bb0b173fd8c19686f9173166e3ee62738047d7eadd69efb228",
  "81ee769bed0950862b1ddded2e84aaa6ab7bfdd3ceaa471be31163d40336363c",
  "a31fdbd8d576551c21fb1191b54bda65b6c5fe97f0f4a69103424b43f7fdb835979fdbeae8b3fe16cb82e587381eb624",
  /* Published val=01541542D1B06EBF5A01BFA35955C5FFB87EC81E64BF1BB8BFEBDF84A6406CFDF9303319D64A2308B6AFF61702E539310F98BF77246E7F7D51CF0D0C01F46023 */
  "fc4a167ccb31a937d698fde82b04348c9539b28f0c9d3b4505709c03812350e4990e9622974f6e575c47861c0d2e638ccfc2023c365bb60a93f528550698786b",

  /* SHA3-{224,256,384,512}_Msg1630 */
  "4e907bb1057861f200a599e9d4f85b02d88453bf5b8ace9ac589134c",
  "52860aa301214c610d922a6b6cab981ccd06012e54ef689d744021e738b9ed20",
  "3485d3b280bd384cf4a777844e94678173055d1cbc40c7c2c3833d9ef12345172d6fcd31923bb8795ac81847d3d8855c",
  "cf9a30ac1f1f6ac0916f9fef1919c595debe2ee80c85421210fdf05f1c6af73aa9cac881d0f91db6d034a2bbadc1cf7fbcb2ecfa9d191d3a5016fb3fad8709c9",

  NULL
}; 

static int
do_sha3_bit_tests(void)
{
  int i, t, testnum;
  char *v;
  unsigned char msg[512];
  unsigned char result[512], result2[512];
  Digest_alg alg;
  SHA3_ctx ctx;

  fprintf(stderr, "\nSHA-3 bit vector tests\n");
  testnum = 0;
  for (t = 0; t < 5; t++) {
	int tbits;

	fprintf(stderr, "SHA-3 bit test vector %d:\n", t);
	switch (t) {
	case 0:
	  {
		/* Msg = 1100 1 */
		const unsigned char test_5bits[1] = { 0x13 };

		memcpy(msg, test_5bits, sizeof(test_5bits));
		tbits = 5;
		break;
	  }

	case 1:
	  {
		const unsigned char test_30bits[4] = { 0x53, 0x58, 0x7b, 0x19 };

		memcpy(msg, test_30bits, sizeof(test_30bits));
		tbits = 30;
		break;
	  }

	case 2:
	  memset(msg, 0xa3, 200);
	  tbits = 1600;
	  break;

	case 3:
	  memset(msg, 0xa3, 200);
	  msg[200] = 0x03;
	  tbits = 1605;
	  break;

	case 4:
	  memset(msg, 0xa3, 203);
	  msg[203] = 0x23;
	  tbits = 1630;
	  break;
	}

	for (i = 0; i < 4; i++) {
	  int bits;

	  switch (i % 4) {
	  case 0:
		sha3_init(DIGEST_SHA3_224, &ctx, NULL);
		bits = 224;
		alg = DIGEST_SHA3_224;
		break;

	  case 1:
		sha3_init(DIGEST_SHA3_256, &ctx, NULL);
		bits = 256;
		alg = DIGEST_SHA3_256;
		break;

	  case 2:
		sha3_init(DIGEST_SHA3_384, &ctx, NULL);
		bits = 384;
		alg = DIGEST_SHA3_384;
		break;

	  case 3:
		sha3_init(DIGEST_SHA3_512, &ctx, NULL);
		bits = 512;
		alg = DIGEST_SHA3_512;
		break;
	  }

	  sha3_update_bits(&ctx, msg, tbits);
	  sha3_final(&ctx, result);

	  v = to_hex(result, bits / 8);
	  fprintf(stderr, "%2d. %s ", testnum, v);
	  if (strcmp(v, sha3_bit_tests[testnum])) {
		fprintf(stderr, "  Failed!\n");
		return(-1);
	  }

	  /* Test an alternate API. */
	  sha3_bits(alg, msg, tbits, result2, NULL);
	  if (memcmp(result, result2, bits / 8)) {
		fprintf(stderr, "  Failed!\n");
		return(-1);
	  }
	  fprintf(stderr, " ok\n");
	  testnum++;
	}
  }

  return(0);
}

/*
 * Store the final hash value into RESULT, which is assumed to be large enough.
 */
void
sha3_final(SHA3_ctx *ctx, unsigned char *result)
{
  size_t block_size, digest_length;

  digest_length = 100 - ctx->block_size / 2;		/* in bytes */
  block_size = ctx->block_size;						/* in bytes */

  if (! (ctx->rest & SHA3_FINALIZED)) {
	ctx->rest &= (~SHA3_MUST_FINALIZE);

	/* Zero anything that follows the partial block in the buffer. */
	memset((char *) ctx->message + ctx->rest, 0, block_size - ctx->rest);

	/* Domain separation bits added in FIPS 202, May, 2014 draft document. */
#ifdef ORIG
	((char *) ctx->message)[ctx->rest] |= 0x06;
#else
	/* Padding is needed if the message length is not a multiple of 8. */
	if (ctx->bit_padding == 6) {
	  ((char *) ctx->message)[ctx->rest - 1] |= 0x80;
	  ((char *) ctx->message)[ctx->rest] |= 0x01;
	}
	else if (ctx->bit_padding == 7)
	  ((char *) ctx->message)[ctx->rest] |= 0x03;
	else if (ctx->bit_padding)
	  ((char *) ctx->message)[ctx->rest - 1] |= (0x06 << ctx->bit_padding);
	else
	  ((char *) ctx->message)[ctx->rest] |= 0x06;
#endif

	((char *) ctx->message)[block_size - 1] |= 0x80;

	/* Process the final block. */
	sha3_process_block(ctx->hash, ctx->message, block_size);
	ctx->rest = SHA3_FINALIZED;
  }

  if (block_size <= digest_length) {
	log_msg((LOG_ERROR_LEVEL, "Bug: invalid block_size"));
	abort();
  }

  if (result)
	lendian_enc64_buf(result, ctx->hash, digest_length);
}

int
sha3(Digest_alg alg, const unsigned char *msg, int msglen,
	 unsigned char *result, unsigned int *digest_size)
{
  size_t size;
  SHA3_ctx ctx;

  if (sha3_init(alg, &ctx, digest_size) == NULL)
	return(-1);

  if (msglen < 0)
    size = strlen((const char *) msg);
  else
    size = (size_t) msglen;

  sha3_update(&ctx, msg, size);
  sha3_final(&ctx, result);

  return(0);
}

int
sha3_by_name(const char *digest_name, const unsigned char *msg, int msglen,
			 unsigned char *result, unsigned int *digest_size)
{
  size_t size;
  SHA3_ctx ctx;

  if (sha3_init_by_name(digest_name, &ctx, digest_size) == NULL)
	return(-1);

  if (msglen < 0)
    size = strlen((const char *) msg);
  else
    size = (size_t) msglen;

  sha3_update(&ctx, msg, size);

  sha3_final(&ctx, result);

  return(0);
}

int
sha3_bits(Digest_alg alg, const unsigned char *msg, size_t nbits,
		  unsigned char *result, unsigned int *digest_size)
{
  SHA3_ctx ctx;

  if (sha3_init(alg, &ctx, digest_size) == NULL)
	return(-1);

  sha3_update_bits(&ctx, msg, nbits);
  sha3_final(&ctx, result);

  return(0);
}

void
sha3_224(const unsigned char *msg, int msglen, unsigned char *result)
{

  sha3(DIGEST_SHA3_224, msg, msglen, result, NULL);
}

void
sha3_256(const unsigned char *msg, int msglen, unsigned char *result)
{

  sha3(DIGEST_SHA3_256, msg, msglen, result, NULL);
}

void
sha3_384(const unsigned char *msg, int msglen, unsigned char *result)
{

  sha3(DIGEST_SHA3_384, msg, msglen, result, NULL);
}

void
sha3_512(const unsigned char *msg, int msglen, unsigned char *result)
{

  sha3(DIGEST_SHA3_512, msg, msglen, result, NULL);
}

typedef struct SHA3_test {
  Digest_alg alg;
  char *msg;
  char *digest_value;
} SHA3_test;

/*
 * Test vectors - see:
 * http://cpansearch.perl.org/src/RHASH/Crypt-RHash-0.91/librhash/test_hashes.c
 * http://www.di-mgt.com.au/sha_testvectors.html
 */

static SHA3_test sha3_tests[] = {
  { DIGEST_SHA3_224, "",
	"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7" },
  { DIGEST_SHA3_256, "",
	"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a" },
  { DIGEST_SHA3_384, "",
	"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004" },
  { DIGEST_SHA3_512, "",
	"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26" },

  { DIGEST_SHA3_224, "abc",
	"e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf" },
  { DIGEST_SHA3_256, "abc",
	"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532" },
  { DIGEST_SHA3_384, "abc",
	"ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25" },
  { DIGEST_SHA3_512, "abc", 
	"b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0" },

  { DIGEST_SHA3_224, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
	"8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33" },
  { DIGEST_SHA3_256, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
	"41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376" },
  { DIGEST_SHA3_384, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
	"991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22" },
  { DIGEST_SHA3_512, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
	"04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e" },

  { DIGEST_SHA3_224, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
	"543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc" },
  { DIGEST_SHA3_256, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
	"916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18" },
  { DIGEST_SHA3_384, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
	"79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7" },
  { DIGEST_SHA3_512, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
	"afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185" },

  { DIGEST_NONE, NULL, NULL }
};

static int
test_1M_a(void)
{
  unsigned int digest_size;
  unsigned char a[1000000];
  char *v;
  unsigned char result[512];

  memset(a, (int) 'a', sizeof(a));

  sha3(DIGEST_SHA3_224, (unsigned char *) a, sizeof(a), result, &digest_size);
  fprintf(stderr, "SHA3-224(<1Ma>):\n  ");
  v = to_hex(result, digest_size);
  fprintf(stderr, "%s", v);
#define DIGEST_VALUE_224	"d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c"
  if (strcmp(v, DIGEST_VALUE_224)) {
	fprintf(stderr, "FAILED\n");
	return(-1);
  }
  fprintf(stderr, "\n");

  sha3(DIGEST_SHA3_256, (unsigned char *) a, sizeof(a), result, &digest_size);
  fprintf(stderr, "SHA3-256(<1Ma>):\n  ");
  v = to_hex(result, digest_size);
  fprintf(stderr, "%s", v);
#define DIGEST_VALUE_256	"5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1"
  if (strcmp(v, DIGEST_VALUE_256)) {
	fprintf(stderr, "FAILED\n");
	return(-1);
  }
  fprintf(stderr, "\n");

  sha3(DIGEST_SHA3_384, (unsigned char *) a, sizeof(a), result, &digest_size);
  fprintf(stderr, "SHA3-384(<1Ma>):\n  ");
  v = to_hex(result, digest_size);
  fprintf(stderr, "%s", v);
#define DIGEST_VALUE_384	"eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340"
  if (strcmp(v, DIGEST_VALUE_384)) {
	fprintf(stderr, "FAILED\n");
	return(-1);
  }
  fprintf(stderr, "\n");

  sha3(DIGEST_SHA3_512, (unsigned char *) a, sizeof(a), result, &digest_size);
  fprintf(stderr, "SHA3-512(<1Ma>):\n  ");
  v = to_hex(result, digest_size);
  fprintf(stderr, "%s", v);
#define DIGEST_VALUE_512	"3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87"
  if (strcmp(v, DIGEST_VALUE_512)) {
	fprintf(stderr, "FAILED\n");
	return(-1);
  }
  fprintf(stderr, "\n");

  return(0);

#undef DIGEST_VALUE_224
#undef DIGEST_VALUE_256
#undef DIGEST_VALUE_384
#undef DIGEST_VALUE_512
}

static void
do_big_repeat(SHA3_ctx *ctx, char *msg, size_t msglen, unsigned char *result)
{
  int i;

  for (i = 1; i <= 16777216; i++) {
	sha3_update(ctx, (unsigned char *) msg, msglen);
	if ((i % 1000000) == 0)
	  fprintf(stderr, ".");
  }
  fprintf(stderr, "\n");

  sha3_final(ctx, result);
}

static int
test_big_repeat(void)
{
  char *v;
  size_t msglen;
  unsigned char result[512];
  SHA3_ctx ctx;

#define MSG	"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"
  msglen = strlen(MSG);

  sha3_init(DIGEST_SHA3_224, &ctx, NULL);
  fprintf(stderr, "SHA3-224(<BigRepeat>): ");
  do_big_repeat(&ctx, MSG, msglen, result);
  v = to_hex(result, crypto_lookup_digest_size("SHA3_224"));
  fprintf(stderr, "%s ", v);
#define DIGEST_VALUE_224	"c6d66e77ae289566afb2ce39277752d6da2a3c46010f1e0a0970ff60"
  if (strcmp(v, DIGEST_VALUE_224)) {
	fprintf(stderr, "FAILED\n");
	return(-1);
  }
  fprintf(stderr, "ok\n");

  sha3_init(DIGEST_SHA3_256, &ctx, NULL);
  fprintf(stderr, "SHA3-256(<BigRepeat>): ");
  do_big_repeat(&ctx, MSG, msglen, result);
  v = to_hex(result, crypto_lookup_digest_size("SHA3_256"));
  fprintf(stderr, "%s ", v);
#define DIGEST_VALUE_256	"ecbbc42cbf296603acb2c6bc0410ef4378bafb24b710357f12df607758b33e2b"
  if (strcmp(v, DIGEST_VALUE_256)) {
	fprintf(stderr, "FAILED\n");
	return(-1);
  }
  fprintf(stderr, "ok\n");

  sha3_init(DIGEST_SHA3_384, &ctx, NULL);
  fprintf(stderr, "SHA3-384(<BigRepeat>): ");
  do_big_repeat(&ctx, MSG, msglen, result);
  v = to_hex(result, crypto_lookup_digest_size("SHA3_384"));
  fprintf(stderr, "%s ", v);
#define DIGEST_VALUE_384	"a04296f4fcaae14871bb5ad33e28dcf69238b04204d9941b8782e816d014bcb7540e4af54f30d578f1a1ca2930847a12"
  if (strcmp(v, DIGEST_VALUE_384)) {
	fprintf(stderr, "FAILED\n");
	return(-1);
  }
  fprintf(stderr, "ok\n");

  sha3_init(DIGEST_SHA3_512, &ctx, NULL);
  fprintf(stderr, "SHA3-512(<BigRepeat>): ");
  do_big_repeat(&ctx, MSG, msglen, result);
  v = to_hex(result, crypto_lookup_digest_size("SHA3_512"));
  fprintf(stderr, "%s ", v);
#define DIGEST_VALUE_512	"235ffd53504ef836a1342b488f483b396eabbfe642cf78ee0d31feec788b23d0d18d5c339550dd5958a500d4b95363da1b5fa18affc1bab2292dc63b7d85097c"
  if (strcmp(v, DIGEST_VALUE_512)) {
	fprintf(stderr, "FAILED\n");
	return(-1);
  }
  fprintf(stderr, "ok\n");

  return(0);

#undef MSG
#undef DIGEST_VALUE_224
#undef DIGEST_VALUE_256
#undef DIGEST_VALUE_384
#undef DIGEST_VALUE_512
}

static int
do_sha3_tests(void)
{
  int i;
  char *alg_name, *val;
  unsigned int digest_size;
  unsigned char result[512];
  SHA3_test *t;

  digest_size = 0;
  for (i = 0, t = &sha3_tests[0]; t->msg != NULL; i++, t++) {
	sha3(t->alg, (unsigned char *) t->msg, -1, result, &digest_size);

	if (t->alg == DIGEST_SHA3_224)
	  alg_name = "SHA3-224";
	else if (t->alg == DIGEST_SHA3_256)
	  alg_name = "SHA3-256";
	else if (t->alg == DIGEST_SHA3_384)
	  alg_name = "SHA3-384";
	else if (t->alg == DIGEST_SHA3_512)
	  alg_name = "SHA3-512";
	else
	  return(-1);

	fprintf(stderr, "%2d. %s(\"%s\"):\n  ", i + 1, alg_name, t->msg);
	val = to_hex(result, digest_size);
	fprintf(stderr, "%s ", val);
	if (t->digest_value != NULL
		&& strcmp(t->digest_value, val)) {
	  fprintf(stderr, "  Failed!\n");
	  return(-1);
	}
	fprintf(stderr, " ok\n");
  }

  test_1M_a();

  test_big_repeat();

  return(0);
}

typedef struct SHA512t_test {
  Digest_alg alg;
  unsigned int t;
  char *msg;
  char *digest_value;
} SHA512t_test;

/*
 * Test vectors:
 * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_224.pdf
 * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_256.pdf
 * https://github.com/ottodietz/sha512t/blob/master/sha512t.py
 * Also by using "Digest::SHA" for t==224 and t==256.
 */
static SHA512t_test sha512t_test[] = {
  { DIGEST_SHA512t, 224, "",
    "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4" },
  { DIGEST_SHA512t, 224, "abc",
	"4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa" },
  { DIGEST_SHA512t, 224, "Hi there",
	"9aab5d13e3177bbb44e5aa0b20ec551e3a9633baae1f38e2df55ae4d" },
  { DIGEST_SHA512t, 224, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
	"23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9" },

  { DIGEST_SHA512t, 256, "",
    "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a" },
  { DIGEST_SHA512t, 256, "abc",
	"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23" },
  { DIGEST_SHA512t, 256, "Hi there",
	"407682efd23550957fc62f31087b1c4938bcbc1de36b88d063ad645a323343d2" },
  { DIGEST_SHA512t, 256, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
	"3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a" },

  { DIGEST_SHA512_224, 0, "",
    "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4" },
  { DIGEST_SHA512_224, 0, "abc",
	"4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa" },
  { DIGEST_SHA512_224, 0, "Hi there",
	"9aab5d13e3177bbb44e5aa0b20ec551e3a9633baae1f38e2df55ae4d" },
  { DIGEST_SHA512_224, 0, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
	"23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9" },

  { DIGEST_SHA512_256, 0, "",
    "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a" },
  { DIGEST_SHA512_256, 0, "abc",
	"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23" },
  { DIGEST_SHA512_256, 0, "Hi there",
	"407682efd23550957fc62f31087b1c4938bcbc1de36b88d063ad645a323343d2" },
  { DIGEST_SHA512_256, 0, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
	"3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a" },

  { DIGEST_SHA512t, 8, "000000", "40" },
  { DIGEST_SHA512t, 8, "123456", "e6" },
  { DIGEST_SHA512t, 8, "uvwxyz", "1c" },

  { DIGEST_SHA512t, 16, "000000", "1893" },
  { DIGEST_SHA512t, 16, "123456", "ed51" },
  { DIGEST_SHA512t, 16, "uvwxyz", "d4f3" },

  { DIGEST_SHA512t, 24, "000000", "c36145" },
  { DIGEST_SHA512t, 24, "123456", "322e02" },
  { DIGEST_SHA512t, 24, "uvwxyz", "b82f96" },

  { DIGEST_SHA512t, 32, "000000", "0fb87900" },
  { DIGEST_SHA512t, 32, "123456", "b01b3f58" },
  { DIGEST_SHA512t, 32, "uvwxyz", "a7802f1c" },

  { DIGEST_SHA512t, 40, "000000", "41561e6db1" },
  { DIGEST_SHA512t, 40, "123456", "6d92e23336" },
  { DIGEST_SHA512t, 40, "uvwxyz", "12807bfe4b" },

  { DIGEST_SHA512t, 48, "000000", "e71453add8ee" },
  { DIGEST_SHA512t, 48, "123456", "38ebd7973918" },
  { DIGEST_SHA512t, 48, "uvwxyz", "d444c346a738" },

  { DIGEST_SHA512t, 56, "000000", "34acd645778a15" },
  { DIGEST_SHA512t, 56, "123456", "b49acfda08675b" },
  { DIGEST_SHA512t, 56, "uvwxyz", "350690fa30f57b" },

  { DIGEST_SHA512t, 64, "000000", "0c8beffa2e95addd" },
  { DIGEST_SHA512t, 64, "123456", "52261eacb45fe456" },
  { DIGEST_SHA512t, 64, "uvwxyz", "ecfacd8d22e401c6" },

  { DIGEST_SHA512t, 72, "000000", "857a80e7286803cf57" },
  { DIGEST_SHA512t, 72, "123456", "2dc46cba92f277e7ee" },
  { DIGEST_SHA512t, 72, "uvwxyz", "804886d65bb10ba6b9" },

  { DIGEST_SHA512t, 80, "000000", "21fb1ba1eb71f834d109" },
  { DIGEST_SHA512t, 80, "123456", "eeb1e73ed321fb324774" },
  { DIGEST_SHA512t, 80, "uvwxyz", "008184950f9c830e7fdc" },

  { DIGEST_SHA512t, 88, "000000", "e382856f8019363544df4e" },
  { DIGEST_SHA512t, 88, "123456", "0689adfdb5dc251ebee870" },
  { DIGEST_SHA512t, 88, "uvwxyz", "298eaa31c4ecf5f5963d09" },

  { DIGEST_SHA512t, 96, "000000", "189135861b522f62756400b2" },
  { DIGEST_SHA512t, 96, "123456", "7faeba0512326c125ddc89f7" },
  { DIGEST_SHA512t, 96, "uvwxyz", "555e97a5903df06d2f818a19" },

  { DIGEST_SHA512t, 104, "000000", "b7a5c6dbfad0d3c9d77560b9ea" },
  { DIGEST_SHA512t, 104, "123456", "7fa9d643798cb0605eb8b07311" },
  { DIGEST_SHA512t, 104, "uvwxyz", "a3d0a1dce670fe231c80a2fbfb" },

  { DIGEST_SHA512t, 112, "000000", "b61089a3983050f4fbf8687d6fbf" },
  { DIGEST_SHA512t, 112, "123456", "c415857e3a4d15cac1439003f386" },
  { DIGEST_SHA512t, 112, "uvwxyz", "36f690af14514051741107661876" },

  { DIGEST_SHA512t, 120, "000000", "44e707b93fd9187f8d9a80feb80e23" },

  { DIGEST_NONE, 0, NULL, NULL }
};

static int
do_sha512t_test(void)
{
  int i;
  char *val;
  SHA512t_test *t2;

  /* FIPS-180-4 5.3.6, 6.6, 6.7 */
  for (i = 0, t2 = &sha512t_test[0]; t2->alg != DIGEST_NONE; t2++, i++) {
	fprintf(stderr, "%2d. ", i + 1);
	if (t2->alg == DIGEST_SHA512_224) {
	  fprintf(stderr, "SHA512/224(\"%s\"):", t2->msg);
	  val = sha512_224(t2->msg, -1);
	}
	else if (t2->alg == DIGEST_SHA512_256) {
	  fprintf(stderr, "SHA512/256(\"%s\"):", t2->msg);
	  val = sha512_256(t2->msg, -1);
	}
	else if (t2->alg == DIGEST_SHA512t) {
	  fprintf(stderr, "SHA-512/%u(\"%s\"):", t2->t, t2->msg);
	  val = sha512_t(t2->t, t2->msg, -1);
	}
	else {
	  fprintf(stderr, "Unrecognized SHA512t test\n");
	  return(-1);
	}

	if (val == NULL) {
	  fprintf(stderr, " FAILED\n");
	  return(-1);
	}

	fprintf(stderr, "\n%s ", val);
	if (strcmp(val, t2->digest_value)) {
	  fprintf(stderr, "FAILED\n");
	  return(-1);
	}
	fprintf(stderr, "ok\n");
  }
  fprintf(stderr, "\n");

  return(0);
}

/*
 * Digest names, as recognized by crypto_digest_name_match():
 *    "MD5", "SHA1", "SHA-224", "sha224", "SHA256", etc.
 */
Digest_ctx *
crypto_digest_open(const char *digest_name, ...)
{
  Digest_ctx *ctx;
  Digest_tab *dt;

  if ((dt = crypto_lookup_digest_by_name(digest_name)) == NULL) {
	crypto_log_error();
	if (digest_name != NULL)
	  log_msg((LOG_ERROR_LEVEL, "Unrecognized digest name: \"%s\"",
			   digest_name));
	return(NULL);
  }

  if (digest_is_sha512_ext(dt)) {
	int sha512_t;
	va_list ap;

	va_start(ap, digest_name);
	sha512_t = va_arg(ap, unsigned int);
	va_end(ap);

	ctx = sha512_t_init(sha512_t);
  }
  else if (dt->alg == DIGEST_BLAKE2) {
	int hlen;
	unsigned int key_len;
	char *key_str;
	va_list ap;

	ctx = ALLOC(Digest_ctx);
	ctx->dt = dt;
	ctx->sha512_t = 0;

	va_start(ap, digest_name);
	hlen = va_arg(ap, int);
	key_str = va_arg(ap, char *);
	key_len = va_arg(ap, unsigned int);
	va_end(ap);

	if (key_str == NULL) {
	  if (blake2b_init(&ctx->u.blake2_ctx.blake2_state, (size_t) hlen) == -1) {
		log_msg((LOG_ERROR_LEVEL, "Cannot initialize Blake2"));
		return(NULL);
	  }
	}
	else {
	  if (blake2b_init_key(&ctx->u.blake2_ctx.blake2_state, (size_t) hlen,
						   key_str, (size_t) key_len) == -1) {
		log_msg((LOG_ERROR_LEVEL, "Cannot initialize Blake2"));
		return(NULL);
	  }
	}
  }
  else
	ctx = digest_make_ctx(dt);

  return(ctx);
}

Digest_ctx *
crypto_digest_open_dd(Digest_desc *dd)
{
  Digest_ctx *ctx;

  if (dd == NULL || dd->dt == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Unspecified digest"));
	return(NULL);
  }

  if (digest_is_sha512_ext(dd->dt))
	ctx = sha512_t_init(dd->u.sha512t.t);
  else if (dd->dt->alg == DIGEST_BLAKE2) {
	log_msg((LOG_ERROR_LEVEL, "Cannot initialize Blake2"));
	return(NULL);
  }
  else
	ctx = digest_make_ctx(dd->dt);

  return(ctx);
}

Digest_ctx *
crypto_digest_open_x(const char *digest_name, va_list ap)
{
  Digest_ctx *ctx;
  Digest_tab *dt;

  if ((dt = crypto_lookup_digest_by_name(digest_name)) == NULL) {
	crypto_log_error();
	if (digest_name != NULL)
	  log_msg((LOG_ERROR_LEVEL, "Unrecognized digest name: \"%s\"",
			   digest_name));
	return(NULL);
  }

  if (dt->alg == DIGEST_SHA512t) {
	int sha512_t;

	sha512_t = va_arg(ap, int);
	ctx = sha512_t_init((unsigned int) sha512_t);
  }
  else if (dt->alg == DIGEST_BLAKE2) {
	int hlen;
	unsigned int key_len;
	char *key_str;

	ctx = ALLOC(Digest_ctx);
	ctx->dt = dt;
	ctx->sha512_t = 0;

	hlen = va_arg(ap, int);
	key_str = va_arg(ap, char *);
	key_len = va_arg(ap, unsigned int);
	if (key_str == NULL) {
	  if (blake2b_init(&ctx->u.blake2_ctx.blake2_state, (size_t) hlen) == -1) {
		log_msg((LOG_ERROR_LEVEL, "Cannot initialize Blake2"));
		return(NULL);
	  }
	}
	else {
	  if (blake2b_init_key(&ctx->u.blake2_ctx.blake2_state, (size_t) hlen,
						   key_str, (size_t) key_len) == -1) {
		log_msg((LOG_ERROR_LEVEL, "Cannot initialize Blake2"));
		return(NULL);
	  }
	}
  }
  else
	ctx = digest_make_ctx(dt);

  return(ctx);
}

void
crypto_digest_hash(Digest_ctx *ctx, const void *input, size_t input_len)
{

  if (digest_is_sha3(ctx->dt))
	sha3_update(&ctx->u.sha3_ctx, input, input_len);
  else if (ctx->dt->alg == DIGEST_BLAKE2) {
	/* XXX this can return an error */
	if (blake2b_update(&ctx->u.blake2_ctx.blake2_state,
					   input, input_len) == -1) {
	  dacs_fatal("blake2b_update() failed");
	  /*NOTREACHED*/
	}
  }
  else {
	if (!EVP_DigestUpdate(ctx->u.evp_ctx, input, input_len)) {
	  crypto_log_error();
	  dacs_fatal("EVP_DigestUpdate() failed");
	  /*NOTREACHED*/
	}
  }
}

/*
 * Complete the digest for CTX.
 * If OUTPUT is non-NULL, it is assumed to be a buffer large enough to
 * hold the digest value, otherwise a buffer is allocated.
 * If non-NULL, OUTPUT_LEN is set to the length of the digest in bytes.
 */
unsigned char *
crypto_digest_close(Digest_ctx *ctx, unsigned char *output,
					unsigned int *output_len)
{
  size_t need;
  unsigned char *outp;

  if (ctx->dt->alg == DIGEST_SHA512 && ctx->sha512_t != 0)
	need = (ctx->sha512_t + 7) / 8;
  else if (ctx->dt->alg == DIGEST_BLAKE2)
	need = ctx->u.blake2_ctx.blake2_state.outlen;
  else
	need = ctx->dt->digest_size;

  if (need == 0) {
	dacs_fatal("Bug: no digest output buffer has been allocated!");
	/*NOTREACHED*/
  }

  if (output == NULL)
	outp = (unsigned char *) malloc(need);
  else
	outp = output;

  if (digest_implementation(ctx->dt->flags) == DIGEST_OPENSSL
	  || (digest_implementation(ctx->dt->flags) == DIGEST_DACS
		  && digest_is_sha512_ext(ctx->dt))) {
	if (!EVP_DigestFinal(ctx->u.evp_ctx, outp, output_len)) {
	  crypto_log_error();
	  dacs_fatal("EVP_DigestFinal() failed");
	  /*NOTREACHED*/
	}

	EVP_MD_CTX_destroy(ctx->u.evp_ctx);
	ctx->u.evp_ctx = NULL;
  }
  else if (digest_is_sha3(ctx->dt))
	sha3_final(&ctx->u.sha3_ctx, outp);
  else if (ctx->dt->alg == DIGEST_BLAKE2) {
	if (blake2b_final(&ctx->u.blake2_ctx.blake2_state, outp,
					  ctx->u.blake2_ctx.blake2_state.outlen) == -1) {
	  dacs_fatal("blake2b_final() failed");
	  /*NOTREACHED*/
	}
  }
  else {
	dacs_fatal("Bug!  Unrecognized digest type");
	/*NOTREACHED*/
  }

  if (output_len != NULL)
	*output_len = need;

  return(outp);
}

unsigned char *
crypto_digest(const char *digest_name, const void *input,
			  size_t input_len, unsigned char *output,
			  unsigned int *output_len, ...)
{
  va_list ap;
  Digest_ctx *ctx;

  va_start(ap, output_len);
  ctx = crypto_digest_open_x(digest_name, ap);
  va_end(ap);
  if (ctx == NULL)
	return(NULL);

  crypto_digest_hash(ctx, input, input_len);
  return(crypto_digest_close(ctx, output, output_len));
}

/*
 * A convenience function to compute a SHA1 hash on BUF of length BUFLEN bytes.
 */
Ds *
sha1(unsigned char *buf, size_t buflen)
{
  unsigned char *digest;
  unsigned int digest_len;
  Ds *ds;

  digest = crypto_digest("SHA1", buf, buflen, NULL, &digest_len);
  ds = ds_setn(NULL, digest, digest_len);

  return(ds);
}

/*
 * A convenience function to compute a SHA-256 hash on BUF of length BUFLEN
 * bytes.
 */
Ds *
sha256(unsigned char *buf, size_t buflen)
{
  unsigned char *digest;
  unsigned int digest_len;
  Ds *ds;

  digest = crypto_digest("SHA256", buf, buflen, NULL, &digest_len);
  ds = ds_setn(NULL, digest, digest_len);

  return(ds);
}

/* FIPS 198 S3 */
int
crypto_hmac_key_length(char *digest_name)
{
  char *dn;
  Digest_tab *dt;

  if ((dn = digest_name) == NULL)
	dn = CRYPTO_HMAC_DIGEST_ALG_NAME;

  if ((dt = crypto_lookup_hmac_digest_by_name(dn)) == NULL)
	return(-1);

  return(dt->digest_size);
}

int
crypto_hmac_hmac_length(char *digest_name)
{
  char *dn;
  Digest_tab *dt;

  if ((dn = digest_name) == NULL)
	dn = CRYPTO_HMAC_DIGEST_ALG_NAME;

  if ((dt = crypto_lookup_hmac_digest_by_name(dn)) == NULL)
	return(-1);

  return(dt->digest_size);
}

static Hmac_handle *
hmac_init(const char *digest_name)
{
  Digest_tab *dt;
  Hmac_handle *hmac;

  if ((dt = crypto_lookup_hmac_digest_by_name(digest_name)) == NULL)
	return(NULL);

  hmac = ALLOC(Hmac_handle);
  hmac->digest_name = strdup(dt->name);
  hmac->alg = dt->alg;
  hmac->block_size = dt->block_size;

  /* XXX possibly problematic for variable-length output digest functions. */
  hmac->hmac_size = dt->digest_size;

  hmac->hmac_key_ipad = (unsigned char *) malloc(hmac->block_size);
  hmac->hmac_key_opad = (unsigned char *) malloc(hmac->block_size);

  return(hmac);
}

Hmac_handle *
crypto_hmac_open(const char *digest_name, unsigned char *key,
				 unsigned int keylen)
{
  int i;
  const char *dn;
  unsigned char *k0;
  Hmac_handle *hmac;

  if ((dn = digest_name) == NULL)
	dn = CRYPTO_HMAC_DIGEST_ALG_NAME;

  if ((hmac = hmac_init(dn)) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Unrecognized digest algorithm for HMAC"));
	return(NULL);
  }

  /* Step 1 */
  if (keylen == hmac->block_size)
	k0 = key;
  else if (keylen > hmac->block_size) {
	unsigned char *k;
	Digest_ctx *ctx;

	/* Step 2 */
	if (hmac->alg == DIGEST_BLAKE2)
	  ctx = crypto_digest_open(dn, BLAKE2_HMAC_DIGEST_SIZE, NULL, 0);
	else
	  ctx = crypto_digest_open(dn);
	if (ctx == NULL) {
	  log_msg((LOG_ERROR_LEVEL, "Could not initialize digest function"));
	  return(NULL);
	}

	crypto_digest_hash(ctx, key, keylen);
	k = crypto_digest_close(ctx, NULL, &hmac->hmac_size);
	k0 = (unsigned char *) malloc(hmac->block_size);
	memcpy(k0, k, hmac->hmac_size);
	for (i = hmac->hmac_size; i < hmac->block_size; i++)
	  k0[i] = 0;
  }
  else {
	/* Step 3 */
	k0 = malloc(hmac->block_size);
	memcpy(k0, key, keylen);
	for (i = keylen; i < hmac->block_size; i++)
	  k0[i] = 0;
  }

  /* Steps 4 & 7 */
  for (i = 0; i < hmac->block_size; i++) {
	hmac->hmac_key_ipad[i] = k0[i] ^ 0x36;
	hmac->hmac_key_opad[i] = k0[i] ^ 0x5c;
  }

  if (keylen != hmac->block_size) {
	memzap(k0, hmac->block_size);
	free(k0);
  }

  /* Steps 5 & 6 */
  if (hmac->alg == DIGEST_BLAKE2)
	hmac->ctx = crypto_digest_open(dn, BLAKE2_HMAC_DIGEST_SIZE, NULL, 0);
  else
	hmac->ctx = crypto_digest_open(dn);
  if (hmac->ctx == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Could not initialize HMAC digest function"));
	return(NULL);
  }
  crypto_digest_hash(hmac->ctx, hmac->hmac_key_ipad, hmac->block_size);

  return(hmac);
}

/*
 * May be called repeatedly.
 */
void
crypto_hmac_hash(Hmac_handle *hmac, unsigned char *str, unsigned int len)
{

  /* Steps 5 & 6 */
  crypto_digest_hash(hmac->ctx, str, len);
}

/*
 * XXX could add optional argument to return "inner" hash value (tmp, below)
 * to the caller and an API that can use the value to complete the HMAC.
 */
unsigned char *
crypto_hmac_close(Hmac_handle *hmac, unsigned char *hmac_buf,
				  unsigned int *hmac_len)
{
  unsigned char *outp, *tmp;
  unsigned int tmp_len;
  Digest_ctx *ctx;

  tmp = crypto_digest_close(hmac->ctx, NULL, &tmp_len);

  /* Steps 8 & 9 */
  if (hmac->alg == DIGEST_BLAKE2)
	ctx = crypto_digest_open(hmac->digest_name, BLAKE2_HMAC_DIGEST_SIZE,
							 NULL, 0);
  else
	ctx = crypto_digest_open(hmac->digest_name);
  if (ctx == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Could not initialize HMAC digest function"));
	return(NULL);
  }

  crypto_digest_hash(ctx, hmac->hmac_key_opad, hmac->block_size);
  crypto_digest_hash(ctx, tmp, tmp_len);
  memzap(tmp, tmp_len);
  free(tmp);

  /* Step 10 */
  outp = crypto_digest_close(ctx, hmac_buf, hmac_len);

  memzap(hmac->hmac_key_ipad, hmac->block_size);
  memzap(hmac->hmac_key_opad, hmac->block_size);
  free(hmac->hmac_key_ipad);
  free(hmac->hmac_key_opad);  
  memzap(hmac, sizeof(Hmac_handle));
  free(hmac);

  return(outp);
}

static MAYBE_UNUSED void
hmac_show(FILE *fp, unsigned char *outbuf)
{
  int i;
  FILE *f;

  if ((f = fp) == NULL)
	f = stderr;

  for (i = 0; i < CRYPTO_HMAC_BYTE_LENGTH; i++) {
	fprintf(f, "%02x", outbuf[i]);
	if ((i + 1) % 32 == 0)
	  fprintf(f, "\n");
	else if ((i + 1) % 4 == 0)
	  fprintf(f, " ");
  }
}

unsigned char *
crypto_hmac(const char *digest_name, unsigned char *key, unsigned int klen,
			unsigned char *value, unsigned int vlen,
			unsigned char **hmac, unsigned int *hlen)
{
  unsigned int kklen, vvlen;
  unsigned char *outp;
  Hmac_handle *h;

  if ((kklen = klen) == 0)
	kklen = strlen((char *) key);

  if ((vvlen = vlen) == 0)
	vvlen = strlen((char *) value);

  if ((h = crypto_hmac_open(digest_name, key, kklen)) == NULL)
	return(NULL);

  crypto_hmac_hash(h, value, vvlen);

  outp = crypto_hmac_close(h, NULL, hlen);
  if (hmac != NULL)
	*hmac = outp;

  return(outp);
}

typedef struct Hmac_vec {
  char *name;
  char *hashalg;
  unsigned char *key;
  unsigned int keylen;
  unsigned char *mesg;
  unsigned int mesglen;
  unsigned int taglen;
  char *expected_hmac;
} Hmac_vec;

/*
 * Refer to FIPS PUB 198
 * See also:
 *   http://csrc.nist.gov/groups/STM/cavp/documents/mac/hmactestvectors.zip
 */
static MAYBE_UNUSED void
hmac_test(void)
{
  char *p;
  unsigned char *outp;
  Hmac_vec *hv;
  static unsigned char key_A1[] = {
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
  };
  static unsigned char key_A2[] = {
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
	0x40, 0x41, 0x42, 0x43
  };
  static unsigned char key_A3[] = {
	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
	0xb0, 0xb1, 0xb2, 0xb3
  };
  static unsigned char key_A4[] = {
	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
	0xa0
  };
  static unsigned char key_MD5_1[] = {
	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
  };
  static unsigned char key_MD5_2[] = {
	"Jefe"
  };
  static unsigned char key_MD5_3[] = {
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
  };
  static unsigned char msg_MD5_3[] = {
	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
	0xdd, 0xdd
  };

  static Hmac_vec hmac_vecA[] = {
	{ "A.1", "SHA1", key_A1, sizeof(key_A1),
	  (unsigned char *) "Sample #1", 0,
	  20, "4f4ca3d5d68ba7cc0a1208c9c61e9c5da0403c0a" },

	{ "A.2", "SHA1", key_A2, sizeof(key_A2),
	  (unsigned char *) "Sample #2", 0,
	  20, "0922d3405faa3d194f82a45830737d5cc6c75d24" },

	{ "A.3", "SHA1", key_A3, sizeof(key_A3),
	  (unsigned char *) "Sample #3", 0,
	  20, "bcf41eab8bb2d802f3d05caf7cb092ecf8d1a3aa" },

	{ "A.4", "SHA1", key_A4, sizeof(key_A4),
	  (unsigned char *) "Sample #4", 0,
	  12, "9ea886efe268dbecce420c75" },

	{ "MD5.1", "MD5", key_MD5_1, sizeof(key_MD5_1),
	  (unsigned char *) "Hi There", 0,
	  16, "9294727a3638bb1c13f48ef8158bfc9d" },

	{ "MD5.2", "MD5", key_MD5_2, sizeof(key_MD5_2),
	  (unsigned char *) "what do ya want for nothing?", 0,
	  16, "750c783e6ab0b503eaa86e310a5db738" },

	{ "MD5.3", "MD5", key_MD5_3, sizeof(key_MD5_3),
	  msg_MD5_3, sizeof(msg_MD5_3),
	  16, "56be34521d144c88dbb8c733f0e8b3f6" },

	{ NULL, NULL, NULL, 0, NULL, 0, 0, NULL }
  };
 
  static Hmac_vec hmac_vecB[] = {
	{ "HMAC_All.1", "SHA1", NULL, 64,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  20, "5fd596ee78d5553c8ff4e72d266dfd192366da29" },

	{ "HMAC_All.2", "SHA1", NULL, 20,
	  (unsigned char *) "Sample message for keylen<blocklen", 0,
	  20, "4c99ff0cb1b31bd33f8431dbaf4d17fcd356a807" },

	{ "HMAC_All.3", "SHA1", NULL, 100,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  20, "2d51b2f7750e410584662e38f133435f4c4fd42a" },

	{ "HMAC_All.4", "SHA1", NULL, 49,
	  (unsigned char *) "Sample message for keylen<blocklen, with truncated tag", 0,
	  12, "fe3529565cd8e28c5fa79eac" },

	{ "HMAC_All.5", "SHA224", NULL, 64,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  28, "c7405e3ae058e8cd30b08b4140248581ed174cb34e1224bcc1efc81b" },

	{ "HMAC_All.6", "SHA224", NULL, 28,
	  (unsigned char *) "Sample message for keylen<blocklen", 0,
	  28, "e3d249a8cfb67ef8b7a169e9a0a599714a2cecba65999a51beb8fbbe" },

	{ "HMAC_All.7", "SHA224", NULL, 100,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  28, "91c52509e5af8531601ae6230099d90bef88aaefb961f4080abc014d" },

	{ "HMAC_All.8", "SHA224", NULL, 49,
	  (unsigned char *) "Sample message for keylen<blocklen, with truncated tag", 0,
	  16, "d522f1df596ca4b4b1c23d27bde067d6" },
	  
	{ "HMAC_All.9", "SHA256", NULL, 64,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  32, "8bb9a1db9806f20df7f77b82138c7914d174d59e13dc4d0169c9057b133e1d62" },

	{ "HMAC_All.10", "SHA256", NULL, 32,
	  (unsigned char *) "Sample message for keylen<blocklen", 0,
	  32, "a28cf43130ee696a98f14a37678b56bcfcbdd9e5cf69717fecf5480f0ebdf790" },

	{ "HMAC_All.11", "SHA256", NULL, 100,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  32, "bdccb6c72ddeadb500ae768386cb38cc41c63dbb0878ddb9c7a38a431b78378d" },

	{ "HMAC_All.12", "SHA256", NULL, 49,
	  (unsigned char *) "Sample message for keylen<blocklen, with truncated tag", 0,
	  16, "27a8b157839efeac98df070b331d5936" },

	{ "HMAC_All.13", "SHA384", NULL, 128,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  48, "63c5daa5e651847ca897c95814ab830bededc7d25e83eef9195cd45857a37f448947858f5af50cc2b1b730ddf29671a9" },

	{ "HMAC_All.14", "SHA384", NULL, 48,
	  (unsigned char *) "Sample message for keylen<blocklen", 0,
	  48, "6eb242bdbb582ca17bebfa481b1e23211464d2b7f8c20b9ff2201637b93646af5ae9ac316e98db45d9cae773675eeed0" },

	{ "HMAC_All.15", "SHA384", NULL, 200,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  48, "5b664436df69b0ca22551231a3f0a3d5b4f97991713cfa84bff4d0792eff96c27dccbbb6f79b65d548b40e8564cef594" },

	{ "HMAC_All.16", "SHA384", NULL, 49,
	  (unsigned char *) "Sample message for keylen<blocklen, with truncated tag", 0,
	  24, "c48130d3df703dd7cdaa56800dfbd2ba2458320e6e1f98fe" },

	{ "HMAC_All.17", "SHA512", NULL, 128,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  64, "fc25e240658ca785b7a811a8d3f7b4ca48cfa26a8a366bf2cd1f836b05fcb024bd36853081811d6cea4216ebad79da1cfcb95ea4586b8a0ce356596a55fb1347" },

	{ "HMAC_All.18", "SHA512", NULL, 64,
	  (unsigned char *) "Sample message for keylen<blocklen", 0,
	  64, "fd44c18bda0bb0a6ce0e82b031bf2818f6539bd56ec00bdc10a8a2d730b3634de2545d639b0f2cf710d0692c72a1896f1f211c2b922d1a96c392e07e7ea9fedc" },

	{ "HMAC_All.19", "SHA512", NULL, 200,
	  (unsigned char *) "Sample message for keylen=blocklen", 0,
	  64, "d93ec8d2de1ad2a9957cb9b83f14e76ad6b5e0cce285079a127d3b14bccb7aa7286d4ac0d4ce64215f2bc9e6870b33d97438be4aaa20cda5c5a912b48b8e27f3" },

	{ "HMAC_All.20", "SHA512", NULL, 49,
	  (unsigned char *) "Sample message for keylen<blocklen, with truncated tag", 0,
	  32, "00f3e9a77bb0f06de15f160603e42b5028758808596664c03e1ab8fb2b076778" },

	{ NULL, NULL, NULL, 0, NULL, 0, 0, NULL }
  };

  /*
   * Tested against pyblake2.
   * https://github.com/dchest/pyblake2
   */
  static Hmac_vec hmac_vecC[] = {
	{ "BLAKE2.1", "blake2", "secret key", 0,
	  (unsigned char *) "message", 0,
	  64, "0bf25f8602c439556d793a29b8c63b30c6235cc1f8a99e0e08e00a8afc325aa998e19669498fde08a235b4c7c5881127aa14ab82d0a105b5997cb5ec7df0f53d" },

	{ "BLAKE2.2", "blake2", "Hello, world.", 0,
	  (unsigned char *) "abc", 0,
	  64, "12ba0ae73102718ad8f69b2a2cc4ad83f0d81083ceabf90166c34ffa8cbd6732d063e1d74397bcedb9bd9152795dd349ea7ed02d65f1bec75cc0076f4f6d9637" },

	{ NULL, NULL, NULL, 0, NULL, 0, 0, NULL }
  };


  fprintf(stderr, "Doing FIPS PUB 198a Appendix A\n");
  fprintf(stderr, "and RFC 2104 Appendix HMAC Examples\n");
  fprintf(stderr, "and extra HMAC compositions:\n");
  fprintf(stderr,
		  "(http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf)\n");

  for (hv = &hmac_vecA[0]; hv->name != NULL; hv++) {
	fprintf(stderr, "%s: ", hv->name);
	outp = crypto_hmac(hv->hashalg, hv->key, hv->keylen,
					   hv->mesg, hv->mesglen, NULL, NULL);
	p = strbtohex(outp, hv->taglen, 0);
	fprintf(stderr, "%s... ", p);
	if (!streq(p, hv->expected_hmac)) {
	  fprintf(stderr, "Failed!\n");
	  exit(1);
	}
	fprintf(stderr, "ok\n");
  }

  fprintf(stderr, "\nDoing FIPS PUB 198-1 HMAC_All.pdf Examples:\n");
  fprintf(stderr,
		  "(http://csrc.nist.gov/groups/ST/toolkit/examples.html#aMsgAuth)\n");

  for (hv = &hmac_vecB[0]; hv->name != NULL; hv++) {
	int i;
	unsigned char k[1024];	/* XXX */

	for (i = 0; i < hv->keylen; i++)
	  k[i] = i;

	fprintf(stderr, "%s: ", hv->name);
	outp = crypto_hmac(hv->hashalg, k, hv->keylen,
					   hv->mesg, hv->mesglen, NULL, NULL);
	p = strbtohex(outp, hv->taglen, 0);
	fprintf(stderr, "%s... ", p);
	if (!streq(p, hv->expected_hmac)) {
	  fprintf(stderr, "Failed!\n");
	  exit(1);
	}
	fprintf(stderr, "ok\n");
  }

  for (hv = &hmac_vecC[0]; hv->name != NULL; hv++) {
	fprintf(stderr, "%s: ", hv->name);
	outp = crypto_hmac(hv->hashalg, hv->key, hv->keylen,
					   hv->mesg, hv->mesglen, NULL, NULL);
	p = strbtohex(outp, hv->taglen, 0);
	fprintf(stderr, "%s... ", p);
	if (!streq(p, hv->expected_hmac)) {
	  fprintf(stderr, "Failed!\n");
	  exit(1);
	}
	fprintf(stderr, "ok\n");
  }

  fprintf(stderr, "All HMAC example tests succeeded\n");
}

/***************************************************************************/

static int enable_padding = 0;

int
crypto_cipher_set_padding(int enable)
{
  int prev;

  prev = enable_padding;
  enable_padding = enable;

  return(prev);
}

/*
 * Cipher names, as recognized by OpenSSL 0.9.8j (case-insensitive):
 * aes-[128|192|256]-cbc  (equiv: aes-[128|192|256])
 * aes-[128|192|256]-cfb
 * aes-[128|192|256]-cfb1
 * aes-[128|192|256]-cfb8
 * aes-[128|192|256]-ecb
 * aes-[128|192|256]-ofb
 * plus others...
 */
const EVP_CIPHER *
crypto_cipher_methodbyname(char *name)
{
  const EVP_CIPHER *evp;
  static int done_init = 0;

  if (!done_init) {
	OpenSSL_add_all_ciphers();
	done_init = 1;
  }

  if ((evp = EVP_get_cipherbyname(name)) == NULL) {
	crypto_log_error();
	log_msg((LOG_ERROR_LEVEL, "Unrecognized encryption algorithm: \"%s\"",
			 name));
	return(NULL);
  }

  return(evp);
}

/*
 * Find the key length, in bytes, for CIPHER_NAME and set LEN to it.
 * Return -1 if an error occurs (such as if CIPHER_NAME is not recognized),
 * 0 otherwise.
 */
int
crypto_cipher_key_length(char *cipher_name, unsigned int *len)
{
  const EVP_CIPHER *evp;

  if (len == NULL || (evp = crypto_cipher_methodbyname(cipher_name)) == NULL)
	return(-1);

  *len = EVP_CIPHER_key_length(evp);

  return(0);
}

/*
 * Find the IV length, in bytes, for CIPHER_NAME and set LEN to it.
 * Return -1 if an error occurs (such as if CIPHER_NAME is not recognized),
 * 0 otherwise.
 */
int
crypto_cipher_iv_length(char *cipher_name, unsigned int *len)
{
  const EVP_CIPHER *evp;

  if (len == NULL || (evp = crypto_cipher_methodbyname(cipher_name)) == NULL)
	return(-1);

  *len = EVP_CIPHER_iv_length(evp);

  return(0);
}

static Cipher_handle *
crypto_cipher_open(Crypto_function func, char *cipher_name,
				   unsigned char *key, unsigned char **ivp,
				   PKI_session_keys *sk)
{
  unsigned char *iv;
  unsigned int len;
  Cipher_handle *cipher;
  EVP_CIPHER_CTX *ctx;
  const EVP_CIPHER *evp;

  if ((evp = crypto_cipher_methodbyname(cipher_name)) == NULL)
	return(NULL);

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  ctx = EVP_CIPHER_CTX_new();
#else
  ctx = ALLOC(EVP_CIPHER_CTX);
#endif
  EVP_CIPHER_CTX_init(ctx);

  if (ivp == NULL)
	iv = NULL;
  else {
	if (*ivp == NULL) {
	  len = EVP_CIPHER_iv_length(evp);
	  *ivp = iv = (unsigned char *) malloc(len);
	  memset(iv, 0, len);
	}
	else
	  iv = *ivp;
  }

  if (func == CRYPTO_SYMMETRIC_ENCRYPT || func == CRYPTO_SYMMETRIC_DECRYPT) {
	if (!EVP_CipherInit_ex(ctx, evp, NULL, key, iv,
						   func == CRYPTO_SYMMETRIC_ENCRYPT)) {
	  crypto_log_error();
	  dacs_fatal("EVP_CipherInit_ex failed");
	  /*NOTREACHED*/
	}

	if (!EVP_CIPHER_CTX_set_padding(ctx, enable_padding)) {
	  crypto_log_error();
	  dacs_fatal("EVP_CIPHER_CTX_set_padding failed");
	  /*NOTREACHED*/
	}
	log_msg((LOG_TRACE_LEVEL, "Set padding for %s: %s",
			 cipher_name, enable_padding ? "enabled" : "disabled"));
	log_msg((LOG_TRACE_LEVEL, "Cipher block size is %d",
			 EVP_CIPHER_block_size(evp)));
  }
  else if (func == CRYPTO_PKI_ENCRYPT) {
	sk->nkeys = 1;
	sk->ek = (unsigned char **) malloc(sizeof(unsigned char *) * 1);
	sk->ekl = (int *) malloc(sizeof(int) * 1);
	sk->pubk = (EVP_PKEY **) malloc(sizeof(EVP_PKEY *) * 1);

	sk->pubk[0] = (EVP_PKEY *) key;
	sk->ek[0] = (unsigned char *) malloc(EVP_PKEY_size((EVP_PKEY *) key));

	if (!EVP_SealInit(ctx, evp, sk->ek, sk->ekl, iv, sk->pubk, sk->nkeys)) {
	  crypto_log_error();
	  dacs_fatal("EVP_SealInit failed");
	  /*NOTREACHED*/
	}
  }
  else if (func == CRYPTO_PKI_DECRYPT) {
	sk->pubk = (EVP_PKEY **) malloc(sizeof(EVP_PKEY *) * 1);
	sk->pubk[0] = (EVP_PKEY *) key;

	if (!EVP_OpenInit(ctx, evp, sk->ek[0], sk->ekl[0], iv, sk->pubk[0])) {
	  crypto_log_error();
	  dacs_fatal("EVP_OpenInit failed");
	  /*NOTREACHED*/
	}
  }
  else {
	/* ??? */
	dacs_fatal("Unrecognized Crypto_function argument?!");
	/*NOTREACHED*/
  }

  cipher = ALLOC(Cipher_handle);
  cipher->func = func;
  cipher->cipher_name = strdup(cipher_name);
  cipher->ctx = ctx;

  return(cipher);
}

/*
 * Note that EVP_CipherInit_ex() set the mode of operation (encrypt or
 * decrypt), so that this one function can be called by both the encrypt
 * and the decrypt API.
 */
static unsigned char *
internal_crypto_cipher(Cipher_handle *cipher, unsigned char *input,
					   int input_len, unsigned char *output,
					   int *output_len)
{
  int outp_len, st;
  unsigned char *outp;

  if (output == NULL)
	outp = (unsigned char *)
	  malloc(input_len + EVP_CIPHER_CTX_block_size(cipher->ctx));
  else
	outp = output;

  if (cipher->func == CRYPTO_PKI_ENCRYPT)
	st = EVP_SealUpdate(cipher->ctx, outp, &outp_len, input, input_len);
  else if (cipher->func == CRYPTO_PKI_DECRYPT)
	st = EVP_OpenUpdate(cipher->ctx, outp, &outp_len, input, input_len);
  else
	st = EVP_CipherUpdate(cipher->ctx, outp, &outp_len, input, input_len);

  if (st == 0) {
	crypto_log_error();
	dacs_fatal("Encryption failed");
	/*NOTREACHED*/
  }

  if (output_len != NULL)
	*output_len += outp_len;

  return(outp);
}

static unsigned char *
crypto_cipher_encrypt(Cipher_handle *cipher, unsigned char *input,
					  int input_len, unsigned char *output,
					  int *output_len)
{

  if (output_len != NULL)
	*output_len = 0;

  return(internal_crypto_cipher(cipher, input, input_len, output, output_len));
}

static unsigned char *
crypto_cipher_decrypt(Cipher_handle *cipher, unsigned char *input,
					  int input_len, unsigned char *output,
					  int *output_len)
{

  if (output_len != NULL)
	*output_len = 0;

  return(internal_crypto_cipher(cipher, input, input_len, output, output_len));
}

static unsigned char *
crypto_cipher_close(Cipher_handle *cipher, unsigned char *output,
					int *output_len)
{
  int outp_len, st;
  unsigned char *outp;

  if (output == NULL)
	outp = (unsigned char *) malloc(EVP_CIPHER_CTX_block_size(cipher->ctx));
  else
	outp = output;

  if (cipher->func == CRYPTO_PKI_ENCRYPT)
	st = EVP_SealFinal(cipher->ctx, outp, &outp_len);
  else if (cipher->func == CRYPTO_PKI_DECRYPT)
	st = EVP_OpenFinal(cipher->ctx, outp, &outp_len);
  else
	st = EVP_CipherFinal_ex(cipher->ctx, outp, &outp_len);

  if (st == 0) {
	crypto_log_error();
	dacs_fatal("Encryption finalization failed");
	/*NOTREACHED*/
  }

  if (!EVP_CIPHER_CTX_cleanup(cipher->ctx)) {
	crypto_log_error();
	dacs_fatal(NULL);
	/*NOTREACHED*/
  }
  free(cipher->ctx);
  free(cipher);

  if (output_len != NULL)
	*output_len += outp_len;

  return(outp);
}

static unsigned char *
internal_crypto_crypt(Crypto_function func, char *cipher_name,
					  unsigned char *key, unsigned char **iv,
					  PKI_session_keys *sk,
					  unsigned char *input, int input_len,
					  unsigned char *output, int *output_len)
{
  unsigned int len;
  unsigned char *outp;
  Cipher_handle *cipher;

  cipher = crypto_cipher_open(func, cipher_name, key, iv, sk);
  
  if (output == NULL) {
	len = input_len + EVP_CIPHER_CTX_block_size(cipher->ctx);
	outp = (unsigned char *) malloc(len);
  }
  else
	outp = output;

  if (sk != NULL)
	sk->cipher_name = strdup(cipher_name);

  if (func == CRYPTO_SYMMETRIC_ENCRYPT || func == CRYPTO_PKI_ENCRYPT)
	crypto_cipher_encrypt(cipher, input, input_len, outp, output_len);
  else
	crypto_cipher_decrypt(cipher, input, input_len, outp, output_len);

  crypto_cipher_close(cipher, outp, output_len);

  return(outp);
}

unsigned char *
crypto_encipher(Crypto_function func, char *cipher_name,
				unsigned char *key, unsigned char **ivp,
				PKI_session_keys *sk,
				unsigned char *input, int input_len,
				unsigned char *output, int *output_len)
{
  unsigned char *outp;

  outp = internal_crypto_crypt(func, cipher_name, key, ivp, sk,
							   input, input_len, output, output_len);
  return(outp);
}

unsigned char *
crypto_decipher(Crypto_function func, char *cipher_name,
				unsigned char *key, unsigned char **ivp,
				PKI_session_keys *sk,
				unsigned char *input, int input_len,
				unsigned char *output, int *output_len)
{
  unsigned char *outp;

  outp = internal_crypto_crypt(func, cipher_name, key, ivp, sk,
							   input, input_len, output, output_len);
  return(outp);
}

/*
 * Encrypt INPUT/INPUT_LEN using public key KEY and symmetric cipher
 * CIPHER_NAME and convert the result to text.
 * If INPUT_LEN is 0, then use the string length of INPUT.
 * The output includes the cipher name, encrypted session key, IV, and
 * ciphertext.
 * Additionally, if DIGEST_NAME is non-NULL, use it and SIGN_KEY to sign the
 * ciphertext (and DIGEST_NAME) and append the signature to the output.
 * Output format:
 *    <cipher-name>,<ek>,[<iv>],<cipher-text>[,<digest-name>,<signature>]
 */
char *
crypto_pki_encrypt(char *cipher_name, EVP_PKEY *key, unsigned char *input,
				   int input_len, char *digest_name, EVP_PKEY *sign_key)
{
  int enclen, len;
  char *enc_cipher, *enc_ek, *enc_iv, *enc_str, *enc_text;
  unsigned char *encp, *iv;
  Ds ds;
  PKI_session_keys sk;

  if (input_len == 0)
	len = strlen((char *) input) + 1;
  else
	len = input_len;

  iv = NULL;
  encp = crypto_encipher(CRYPTO_PKI_ENCRYPT, cipher_name,
						 (unsigned char *) key, &iv, &sk, input, len, NULL,
						 &enclen);
  strba64((unsigned char *) cipher_name, strlen(cipher_name) + 1, &enc_cipher);
  strba64(sk.ek[0], (unsigned int) sk.ekl[0], &enc_ek);
  if (iv != NULL) {
	int ivlen;
	const EVP_CIPHER *evp;

	if ((evp = crypto_cipher_methodbyname(cipher_name)) == NULL)
	  return(NULL);
	ivlen = EVP_CIPHER_iv_length(evp);
	strba64(iv, ivlen, &enc_iv);
  }
  else
	enc_iv = "";
  strba64(encp, enclen, &enc_text);

  ds_init(&ds);
  ds_asprintf(&ds, "%s,%s,%s,%s", enc_cipher, enc_ek, enc_iv, enc_text);

  if (digest_name != NULL) {
	char *enc_digest, *enc_sign;
	unsigned int sign_len;
	unsigned char *sign;

	strba64((unsigned char *) digest_name, strlen(digest_name) + 1,
			&enc_digest);
	ds_asprintf(&ds, ",%s", enc_digest);
	if (crypto_sign(digest_name, ds_ucbuf(&ds), ds_len(&ds),
					&sign, &sign_len, sign_key) == NULL)
	  return(NULL);
	strba64(sign, sign_len, &enc_sign);
	ds_asprintf(&ds, ",%s", enc_sign);
  }
  enc_str = ds_buf(&ds);

  return(enc_str);
}

/*
 * Decrypt INPUT, produced by crypto_pki_encrypt(), using public key KEY.
 * If a signature is included, verify it.
 * Return the plaintext and set OUTPUT_LEN, otherwise return NULL if an
 * error occurs.
 */
unsigned char *
crypto_pki_decrypt(EVP_PKEY *key, char *input, int *output_len)
{
  unsigned int enclen, ivlen, len;
  char *cipher_name, *enc_str_no_sign;
  char *enc_cipher, *enc_ek, *enc_iv, *enc_text;
  unsigned char *decp, *encp, *iv;
  Dsvec *dsv;
  PKI_session_keys sk;

  if ((dsv = strsplit(input, ",", 0)) == NULL
	  || (dsvec_len(dsv) != 4 && dsvec_len(dsv) != 6)) {
	log_msg((LOG_ERROR_LEVEL, "Invalid exported PKI string"));
	return(NULL);
  }

  enc_cipher = (char *) dsvec_ptr_index(dsv, 0);
  enc_ek = (char *) dsvec_ptr_index(dsv, 1);
  enc_iv = (char *) dsvec_ptr_index(dsv, 2);
  enc_text = (char *) dsvec_ptr_index(dsv, 3);

  if (stra64b(enc_cipher, (unsigned char **) &cipher_name, &len) == NULL)
	return(NULL);
  if (cipher_name[len - 1] != '\0' || strlen(cipher_name) != len - 1)
	return(NULL);
  sk.ek = (unsigned char **) malloc(sizeof(unsigned char *) * 1);
  sk.ekl = (int *) malloc(sizeof(int *) * 1);
  if (stra64b(enc_ek, &sk.ek[0], (unsigned int *) &sk.ekl[0]) == NULL)
	return(NULL);
  if (*enc_iv == '\0')
	iv = NULL;
  else
	stra64b(enc_iv, &iv, &ivlen);
  if (stra64b(enc_text, &encp, &enclen) == NULL)
	return(NULL);

  if (dsvec_len(dsv) == 6) {
	int st;
	unsigned int sign_len;
	char *digest_name, *enc_digest, *enc_sign;
	unsigned char *sign;

	enc_digest = (char *) dsvec_ptr_index(dsv, 4);
	/*
	 * Rather than modify INPUT, recreate the string.  This might be
	 * expensive though.
	 */
	enc_str_no_sign = ds_xprintf("%s,%s,%s,%s,%s",
								 enc_cipher, enc_ek, enc_iv, enc_text,
								 enc_digest);

	if (stra64b(enc_digest, (unsigned char **) &digest_name, &len) == NULL)
	  return(NULL);
	if (digest_name[len - 1] != '\0' || strlen(digest_name) != len - 1)
	  return(NULL);

	enc_sign = (char *) dsvec_ptr_index(dsv, 5);
	if (stra64b(enc_sign, &sign, &sign_len) == NULL)
	  return(NULL);

	st = crypto_signv(digest_name, (unsigned char *) enc_str_no_sign, 0,
					  sign, sign_len, key);
	if (st != 1)
	  return(NULL);
  }
  else
	enc_str_no_sign = input;

  decp = crypto_decipher(CRYPTO_PKI_DECRYPT, cipher_name,
						 (unsigned char *) key, &iv, &sk, encp, enclen, NULL,
						 output_len);

  return(decp);
}

#include <openssl/rsa.h>

static MAYBE_UNUSED int
crypto_pki_test(char *cipher_name, EVP_PKEY *public_key, EVP_PKEY *private_key,
				char *filename)
{
  int data_len, in_len;
  char *data, *out;
  unsigned char *in;
  EVP_PKEY *sign_key;
  RSA *rrr;

  if (load_file(filename, &data, NULL) == -1)
	return(-1);

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  rrr = EVP_PKEY_get1_RSA(private_key);
#else
  rrr = RSAPrivateKey_dup(private_key->pkey.rsa);
#endif
  sign_key = EVP_PKEY_new();
  EVP_PKEY_set1_RSA(sign_key, rrr);

  data_len = strlen(data) + 1;
  if ((out = crypto_pki_encrypt(cipher_name, public_key,
								(unsigned char *) data, data_len,
								"sha256", sign_key)) == NULL)
	return(-1);

  if ((in = crypto_pki_decrypt(private_key, out, &in_len)) == NULL)
	return(-1);

  if (data_len == in_len && memcmp(data, in, data_len) == 0)
	return(1);

  return(0);
}

/*
 * See NIST Special Publication 800-38A, 2001 Edition
 * "Recommendation for Block Cipher Modes of Operation: Methods and Techniques"
 * Appendix C: Generation of Initialization Vectors
 *
 * N.B. This function must never generate the same IVBUF value more than once
 * for a particular encryption key.
 */
static void
make_iv(unsigned char *key, int len, unsigned char **ivbuf)
{
  int count_len, r;
  unsigned char *nonce;
  static time_t base_time = 0;
  static unsigned int count = 0;

  count_len = sizeof(base_time) + sizeof(count);
  if (CRYPTO_USE_RANDOMIZED_IV || count_len >= len) {
	unsigned char *outp;

	outp = (unsigned char *) malloc(len);
	crypto_randomize_buffer(outp, len);
	*ivbuf = outp;
	return;
  }

  if (base_time == 0 || ++count == 0)
	base_time = time(NULL);

  nonce = malloc(len);
  r = len - count_len;
  crypto_randomize_buffer(nonce, r);
  memcpy(nonce + r, &base_time, sizeof(base_time));
  memcpy(nonce + r + sizeof(base_time), &count, sizeof(count));

  *ivbuf = crypto_encipher(CRYPTO_SYMMETRIC_ENCRYPT, CRYPTO_IV_CIPHER_NAME,
						   key, NULL, NULL, nonce, len, NULL, NULL);

  memzap(nonce, len);
  free(nonce);
}

/*
 * Encrypt PLAINTEXT (LEN bytes) using KEYS as the encryption and signing keys,
 * setting ENCRYPTED to point to the buffer allocated for the result.
 * The output is a buffer consisting of an IV followed by the enciphered:
 *   1. randomly-generated byte count (within a range),
 *   2. that number of random bytes,
 *   3. PLAINTEXT, and
 *   4. plaintext length (in bytes, as an unsigned long in network byte order).
 * Following that is the (unencrypted) MAC of the IV and ciphertext.
 * Note that PLAINTEXT need not literally be ASCII text.
 * Return the total number of bytes copied into *ENCRYPTED.
 */
unsigned int
crypto_encrypt_string(Crypt_keys *keys, unsigned char *plaintext,
					  unsigned int len, unsigned char **encrypted)
{
  int n, m, nwritten, rlen;
  unsigned char *ep;
  unsigned int encrypted_length, xlen;
  unsigned char rvbuf[AUTH_MAX_RANDOM_PADDING_LENGTH], rv1;
  unsigned char *iv;
  Cipher_handle *cipher;
  Hmac_handle *hmac;

  log_msg((LOG_TRACE_LEVEL, "encrypting %u bytes", len));

  /*
   * The IV need not be secret, but it should be random and must be unique.
   * Its integrity is protected by the MAC.
   * See HAC, p. 231.
   * See AC, p. 201
   */
  make_iv(keys->auth_key, AUTH_CRYPT_KEY_LENGTH, &iv);

  cipher = crypto_cipher_open(CRYPTO_SYMMETRIC_ENCRYPT, CRYPTO_CRYPT_ALG_NAME,
							  keys->auth_key, &iv, NULL);

  /* Compute the maximum possible encrypted length. */
  encrypted_length = AUTH_CRYPT_KEY_LENGTH
	+ 1 + AUTH_MAX_RANDOM_PADDING_LENGTH
	+ len + sizeof(xlen)
	+ crypto_hmac_hmac_length(NULL);
  ep = *encrypted = malloc(encrypted_length);

  memcpy(ep, iv, AUTH_CRYPT_KEY_LENGTH);
  log_msg((LOG_TRACE_LEVEL, "IV:\n%s", hexdump(ep, AUTH_CRYPT_KEY_LENGTH)));
  ep += AUTH_CRYPT_KEY_LENGTH;
  nwritten = AUTH_CRYPT_KEY_LENGTH;

  /*
   * Insert some random padding.  This serves four important purposes:
   * 1) It boosts security somewhat because it's a little harder for an
   * attacker to tell where the payload starts.
   *
   * 2) It means that the same (and almost the same) plaintext will likely be
   * encrypted to different lengths on different calls, which might make it
   * more difficult for an attacker to draw some conclusions about the
   * contents merely from the length.
   *
   * 3) This and the block swap below are intended to make known-plaintext
   * attacks more difficult.  In particular, this is important wrt preventing
   * recovery of the key stream (just XOR ciphertext and known plaintext),
   * since then undetectable changes to the plaintext can be made.
   * Because the MAC's computation includes the random padding, an attacker who
   * knows the plaintext but not the random padding cannot recover all of the
   * key stream - if the ciphertext is changed a correct MAC cannot be
   * computed.
   * See HAC, Remark 9.86, p. 366.
   *
   * 4) It makes some kinds of brute force attack more difficult simply
   * because credentials have more bits.
   *
   * These basic ideas can be extended in various ways if necessary
   * (e.g., other permutations of the plaintext, maybe pre-whitening)
   */
  crypto_randomize_buffer(rvbuf, 1);
  rv1 = rvbuf[0]
	% (AUTH_MAX_RANDOM_PADDING_LENGTH - AUTH_MIN_RANDOM_PADDING_LENGTH + 1)
	+ AUTH_MIN_RANDOM_PADDING_LENGTH;
  log_msg((LOG_TRACE_LEVEL, "rvbuf[0]=%u, rv1=%u",
		   (unsigned int) rvbuf[0], (unsigned int) rv1));
  crypto_cipher_encrypt(cipher, &rv1, 1, ep, &rlen);
  if (rlen != 1)
	log_msg((LOG_ALERT_LEVEL, "???1 rv1: rlen=%d, not 1", rlen));
  ep++;
  nwritten += rlen;

  if (rv1) {
	int rlen;

	crypto_randomize_buffer(rvbuf, (int) rv1);
	crypto_cipher_encrypt(cipher, rvbuf, (int) rv1, ep, &rlen);
	if (rlen != (int) rv1)
	  log_msg((LOG_ALERT_LEVEL, "???2 rv1: rlen=%d, not %d",
			   rlen, (int) rv1));
	nwritten += rlen;
	log_msg((LOG_TRACE_LEVEL, "rlen=%d", rlen));
	log_msg((LOG_TRACE_LEVEL, "rvbuf plain:\n%s", hexdump(rvbuf, rv1)));
	log_msg((LOG_TRACE_LEVEL, "rvbuf encrypted:\n%s", hexdump(ep, rv1)));
	ep += rv1;
  }

  /*
   * Splitting the plaintext into two blocks and swapping them before
   * encryption can help to hide stereotypical formatting.
   * XXX Consider using an all-or-nothing transform
   */
  n = len / 2;
  m = len - n;
  crypto_cipher_encrypt(cipher, plaintext + n, m, ep, &rlen);
  nwritten += rlen;
  crypto_cipher_encrypt(cipher, plaintext, n, ep + m, &rlen);
  nwritten += rlen;
  log_msg((LOG_TRACE_LEVEL, "encrypted plaintext:\n%s", hexdump(ep, len)));
  ep += len;

  /*
   * Write the length of the plaintext, in network byte order.
   * Note that htonl() doesn't actually return a long.
   */
  xlen = htonl((uint32_t) len);
  crypto_cipher_encrypt(cipher, (unsigned char *) &xlen, sizeof(xlen),
						ep, &rlen);
  nwritten += rlen;

  log_msg((LOG_TRACE_LEVEL, "len = %u (xlen=%lu):\n%s",
		   len, xlen, hexdump((unsigned char *) &xlen, sizeof(xlen))));
  log_msg((LOG_TRACE_LEVEL, "encrypted xlen:\n%s", hexdump(ep, sizeof(xlen))));
  ep += sizeof(xlen);

  rlen = 0;
  crypto_cipher_close(cipher, ep, &rlen);
  nwritten += rlen;

  /* Double-check that we've written what we think we've written. */
  if (nwritten != (ep - *encrypted))
	log_msg((LOG_ALERT_LEVEL, "??? size mismatch %d != %d",
			 nwritten, ep - *encrypted));

  /*
   * Compute a MAC (HMAC, specifically) over the IV and all of the enciphered
   * message (i.e., the encrypt-then-authenticate method).
   * See "The order of encryption and authentication for protecting
   * communications (Or: how secure is SSL?)", Hugo Krawczyk, 2001.
   * See HAC, 9.6.5, p. 364-368
   */
  hmac = crypto_hmac_open(CRYPTO_HMAC_DIGEST_ALG_NAME, keys->hmac_key,
						  CRYPTO_HMAC_KEY_LENGTH);
  if (hmac == NULL) {
	log_msg((LOG_ALERT_LEVEL, "HMAC failed!"));
	return(0);
  }
  crypto_hmac_hash(hmac, *encrypted, ep - *encrypted);
  crypto_hmac_close(hmac, ep, NULL);

  log_msg((LOG_TRACE_LEVEL, "hmac:\n%s",
		   hexdump(ep, CRYPTO_HMAC_BYTE_LENGTH)));
  ep += CRYPTO_HMAC_BYTE_LENGTH;

  log_msg((LOG_TRACE_LEVEL, "Total encrypted length = %u", ep - *encrypted));

  return(ep - *encrypted);
}

/*
 * The inverse of crypto_encrypt_string().
 * Decrypt ENCRYPTED (the ciphertext), which is LEN bytes long.
 * The resulting plaintext is copied into an allocated buffer, which we set
 * PLAINTEXT to point to and set PLAINTEXT_LENGTH to its length in bytes.
 * Return 0 if all is well, -1 if decryption failed.
 */
int
crypto_decrypt_string(Crypt_keys *keys, unsigned char *encrypted,
					  unsigned int len, unsigned char **plaintext,
					  unsigned int *plaintext_length)
{
  int dlen, n, m;
  unsigned int min_len, olen, plen, xlen;
  unsigned char *ep, *iv, *pt, rv1;
  unsigned char rvbuf[AUTH_MAX_RANDOM_PADDING_LENGTH];
  unsigned char expected_hmac[CRYPTO_HMAC_BYTE_LENGTH];
  Cipher_handle *cipher;
  Hmac_handle *hmac;

  log_msg((LOG_TRACE_LEVEL, "decrypting %u bytes", len));

  /*
   * It must be at least as long as the IV + MAC + random count and
   * length fields.
   */
  min_len = AUTH_CRYPT_KEY_LENGTH + sizeof(unsigned char)
	+ CRYPTO_HMAC_BYTE_LENGTH + sizeof(unsigned int);

  if (len < min_len)
	return(-1);

  iv = ep = encrypted;
  cipher = crypto_cipher_open(CRYPTO_SYMMETRIC_DECRYPT, CRYPTO_CRYPT_ALG_NAME,
							  keys->auth_key, &iv, NULL);

  log_msg((LOG_TRACE_LEVEL, "IV:\n%s", hexdump(iv, AUTH_CRYPT_KEY_LENGTH)));

  ep += AUTH_CRYPT_KEY_LENGTH;

  crypto_cipher_decrypt(cipher, ep, 1, &rv1, NULL);
  log_msg((LOG_TRACE_LEVEL, "rv1=%u", (unsigned int) rv1));
  if (rv1 < AUTH_MIN_RANDOM_PADDING_LENGTH
	  || rv1 > AUTH_MAX_RANDOM_PADDING_LENGTH) {
	crypto_cipher_close(cipher, NULL, NULL);
	log_msg((LOG_ERROR_LEVEL, "Padding error, rv1"));
	return(-1);
  }

  ep++;
  min_len += rv1;

  /* Check again. */
  if (len < min_len) {
	log_msg((LOG_ALERT_LEVEL, "Error: len=%u (min_len=%u)", len, min_len));
	crypto_cipher_close(cipher, NULL, NULL);
	return(-1);
  }

  if (rv1) {
	int rlen;

	crypto_cipher_decrypt(cipher, ep, (int) rv1, rvbuf, &rlen);

	log_msg((LOG_TRACE_LEVEL, "rlen=%d", rlen));
	log_msg((LOG_TRACE_LEVEL, "rvbuf plain (%d):\n%s",
			 rlen, hexdump(rvbuf, rv1)));
	log_msg((LOG_TRACE_LEVEL, "rvbuf encrypted (%d):\n%s",
			 rlen, hexdump(ep, rv1)));
	ep += rv1;
  }

  plen = len - AUTH_CRYPT_KEY_LENGTH - sizeof(unsigned char)
	- rv1 - sizeof(olen) - CRYPTO_HMAC_BYTE_LENGTH;
  n = plen / 2;
  m = plen - n;
  log_msg((LOG_TRACE_LEVEL, "plen=%u, n=%d, m=%d", plen, n, m));

  pt = *plaintext = malloc(plen);

  crypto_cipher_decrypt(cipher, ep, m, pt + n, &dlen);
  log_msg((LOG_TRACE_LEVEL, "dlen=%d", dlen));
  crypto_cipher_decrypt(cipher, ep + m, n, pt, &dlen);
  log_msg((LOG_TRACE_LEVEL, "dlen=%d", dlen));
  ep += plen;

  crypto_cipher_decrypt(cipher, ep, sizeof(xlen), (unsigned char *) &xlen,
						&dlen);
  /* Note that ntohl() doesn't actually return a long */
  olen = ntohl((uint32_t) xlen);
  log_msg((LOG_TRACE_LEVEL, "xlen=%u, olen=%u, plen=%u, dlen=%d",
		   xlen, olen, plen, dlen));

  if (olen != plen) {
	/* The length of the plaintext has been altered. */
	log_msg((LOG_ALERT_LEVEL, "Error: olen=%u, xlen=%lu, plen=%u",
			 olen, xlen, plen));
	crypto_cipher_close(cipher, NULL, NULL);
	memzap(pt, plen);
	free(pt);
	return(-1);
  }
  ep += sizeof(olen);

  /* Validate the MAC */
  hmac = crypto_hmac_open(CRYPTO_HMAC_DIGEST_ALG_NAME, keys->hmac_key,
						  CRYPTO_HMAC_KEY_LENGTH);
  if (hmac == NULL) {
	log_msg((LOG_ALERT_LEVEL, "HMAC failed!"));
	return(-1);
  }
  crypto_hmac_hash(hmac, encrypted, ep - encrypted);
  crypto_hmac_close(hmac, expected_hmac, NULL);

  log_msg((LOG_TRACE_LEVEL, "hmac:\n%s",
		   hexdump(expected_hmac, CRYPTO_HMAC_BYTE_LENGTH)));

  if (memcmp(expected_hmac, ep, CRYPTO_HMAC_BYTE_LENGTH)) {
	log_msg((LOG_ALERT_LEVEL, "Error: expected_hmac != ep"));
	crypto_cipher_close(cipher, NULL, NULL);
	memzap(pt, plen);
	free(pt);
	return(-1);
  }
  ep += CRYPTO_HMAC_BYTE_LENGTH;

  crypto_cipher_close(cipher, NULL, NULL);

  if ((ep - encrypted) != len) {
	log_msg((LOG_ALERT_LEVEL, "Error: ep - encrypted = %u, len = %u",
			  ep - encrypted, len));
	memzap(pt, plen);
	free(pt);
	return(-1);
  }

  if (plaintext_length != NULL)
	*plaintext_length = plen;
  
  log_msg((LOG_TRACE_LEVEL, "Length = %u", plen));

  return(0);
}

/***************************************************************************/

/*
 * Fill BUF, a buffer of LEN bytes, with random material.
 * Return 0 if ok, -1 otherwise.
 */
int
crypto_randomize_buffer(unsigned char *buf, unsigned int len)
{

  /*
   * Note: OpenSSL's "RETURN VALUES" documentation for RAND_bytes() is
   * incorrect, stating that it "returns 1 on success, 0 otherwise", but
   * then later adding that it returns -1 if it is "not supported by the
   * current RAND method".
   * Therefore, check for a return value other than 1 to indicate an error.
   */
  if (RAND_bytes(buf, len) != 1) {
	crypto_log_error();
	return(-1);
  }

  return(0);
}

/*
 * Generate a random unsigned integer betwee LO and HI, inclusive, and
 * store it in UINT.
 * Return 0 if ok, -1 otherwise.
 */
int
crypto_random_uint(unsigned int lo, unsigned int hi, unsigned int *uint)
{
  unsigned int r;

  if (lo >= hi)
	return(-1);

  if (RAND_bytes((unsigned char *) &r, sizeof(r)) != 1) {
	crypto_log_error();
	return(-1);
  }

  *uint = (r % (hi - lo + 1)) + lo;

  return(0);
}

/*
 * Return a buffer comprised of LEN bytes of random material, or NULL if an
 * error occurs.
 */
unsigned char *
crypto_make_random_buffer(unsigned int len)
{
  unsigned char *ptr;

  ptr = (unsigned char *) malloc(len);
  if (RAND_bytes(ptr, len) != 1) {
	crypto_log_error();
	return(NULL);
  }

  return(ptr);
}

/*
 * Generate NBYTES bytes of random material, then create a printable string
 * consisting of PREFIX (if non-NULL) followed by NBYTES pairs of hex digits,
 * null terminated.
 * We require cryptographically strong random numbers, not run of the mill
 * junk.
 */
char *
crypto_make_random_string(char *prefix, int nbytes)
{
  int i;
  unsigned char *r;
  Ds ds;

  r = crypto_make_random_buffer(nbytes);

  ds_init(&ds);
  if (prefix != NULL && prefix[0] != '\0')
	ds_asprintf(&ds, "%s", prefix);

  for (i = 0; i < nbytes; i++)
	ds_asprintf(&ds, "%02x", r[i] & 0xff);

  memzap(r, nbytes);
  free(r);

  return(ds_buf(&ds));
}

/*
 * Like crypto_make_random_string() except format the random component
 * as a DACS base64 string.
 */
char *
crypto_make_random_a64(char *prefix, int nbytes)
{
  char *p, *s;
  unsigned char *r;

  r = crypto_make_random_buffer(nbytes);

  strba64(r, nbytes, &p);
  if (prefix != NULL && prefix[0] != '\0') {
	s = ds_xprintf("%s%s", prefix, p);
	free(p);
  }
  else
	s = p;

  memzap(r, nbytes);
  free(r);

  return(s);
}

/*
 * Generate a random string of length NBYTES from characters in TR_SPEC,
 * a tr(1) specification, or its complement (if CFLAG is non-zero).
 * Only printable characters, excluding the space, are allowed, regardless
 * of TR_SPEC.
 * Return the string, or NULL if an error occurs.
 */
char *
crypto_make_random_string_from_spec(char *tr_spec, int nbytes, int cflag)
{
  int ch, i, n;
  unsigned int r;
  char chars[256];
  Ds ds;

  if (nbytes <= 0)
	return(NULL);
	
  n = 0;
  for (ch = 041; ch < 0177; ch++) {
	if (strtr_char(ch, tr_spec, cflag))
	  chars[n++] = ch;
  }

  ds_init(&ds);
  for (i = 0; i < nbytes; i++) {
	if (crypto_random_uint(0, n - 1, &r) == -1)
	  return(NULL);
	ds_appendc(&ds, (int) chars[r]);
  }
  ds_appendc(&ds, (int) '\0');

  return(ds_buf(&ds));
}

/*
 * Helper for crypto_make_random_string_from_template() to append COUNT
 * FMT type characters to DS.
 * Suboptimal but adequate.
 * Return 0 if ok, -1 otherwise.
 */
static int
append_random_chars(Ds *ds, int fmt, unsigned int count)
{
  unsigned int i, w;
  unsigned long long rval;

  for (i = 0; i < count; i++) {
	if (RAND_bytes((unsigned char *) &rval, sizeof(rval)) != 1)
	  return(-1);

	/*
	 * XXX Selecting an integer randomly in this way (by remainder)
	 * does not always result in a perfect distribution, but the error
	 * is not terrible if the modulus is small relative to dividend.
	 * For example, if you are generating numbers in the range 0..2
	 * from four random bits (0..15) using val = Random % 3, you can get:
	 *   0%3=0,  1%3=1,  2%3=2,  3%3=0,  4%3=1,  5%3=2,  6%3=0, 7%3=1, 8%3=2,
	 *   9%3=0, 10%3=1, 11%3=2, 12%3=0, 13%3=1, 14%3=2, 15%3=0
	 * The probability of getting a 0 here is 6/16, but the probability of
	 * getting a 1 is 5/16, and 2 is 5/16.  But in this example the modulus
	 * is not small relative to the dividend.
	 *
	 * See NIST SP 800-90 (June 2006), Appendix B.5.1.3
	 */
	switch (fmt) {
	case 'l':
	  ds_appendc(ds, (int) ('a' + (rval % 26)));
	  break;

	case 'L':
	  ds_appendc(ds, (int) ('A' + (rval % 26)));
	  break;

	case 'a':
	  w = (unsigned int) (rval % 52);
	  if (w < 26)
		ds_appendc(ds, (int) ('A' + w));
	  else
		ds_appendc(ds, (int) ('a' + w - 26));
	  break;

	case 'A':
	  w = (unsigned int) (rval % 62);
	  if (w < 26)
		ds_appendc(ds, (int) ('A' + w));
	  else if (w < 52)
		ds_appendc(ds, (int) ('a' + w - 26));
	  else
		ds_appendc(ds, (int) ('0' + w - 52));
	  break;

	case 'x':
	case 'X':
	  w = (unsigned int) (rval % 16);
	  if (w < 10)
		ds_appendc(ds, (int) ('0' + w));
	  else if (fmt == 'x')
		ds_appendc(ds, (int) ('a' + w - 10));
	  else
		ds_appendc(ds, (int) ('A' + w - 10));
	  break;

	case 'd':
	  ds_appendc(ds, (int) ('0' + (rval % 10)));
	  break;
	  
	default:
	  ds_appendc(ds, fmt);
	  break;
	}
  }

  return(0);
}

/*
 * Return a random string according to the format string TEMPLATE.
 * TEMPLATE specifies the format of the corresponding
 * character in the output:
 *   '%l' - a lowercase letter
 *   '%L' - an uppercase letter
 *   '%d' - a digit
 *   '%x' - a lowercase hex digit
 *   '%X' - an uppercase hex digit
 *   '%a' - a lowercase or uppercase letter
 *   '%A' - a lowercase or uppercase letter, or a digit
 * A % can be followed by an unsigned integer, which is a repeat count
 * for the format character that follows.
 * A '%' followed by any other character copies that character.
 * A '%' at the end of the template is mapped to itself.
 * Other characters are copied verbatim.
 * Example: the template "%l-%d-%L" asks for a random lowercase letter,
 * hyphen, random digit, hyphen, and lastly a random uppercase character.
 * Example: "%2l%4d" asks for two lowercase letters followed by 4 digits.
 *
 * XXX should extend this to
 *   o recognize tr(1) type specifiers;
 *     e.g., for two random hex digits separated by a colon:
 *     "[0-9a-f]:[0-9a-f]"
 */
char *
crypto_make_random_string_from_template(char *template)
{
  char *t;
  unsigned int count;
  Ds ds;

  ds_init(&ds);
  for (t = template; *t != '\0'; t++) {
	if (*t == '%') {
	  if (*(t + 1) != '\0')
		t++;
	  if (isdigit((int) *t)) {
		char *endp;

		if (strnumx(t, STRNUM_UI, &count, &endp) == -1)
		  return(NULL);
		t = endp;
	  }
	  else
		count = 1;

	  if (append_random_chars(&ds, (int) *t, count) == -1)
		return(NULL);
	}
	else
	  ds_appendc(&ds, (int) *t);
  }

  ds_appendc(&ds, (int) '\0');

  return(ds_buf(&ds));
}

/***************************************************************************/

/*
 * XXX I'd like to implement NIST SP 800-90...
 * Recommendation for Random Number Generation Using Deterministic Random Bit
 * Generators
 */

#ifdef NOTDEF
static Rng_state *
rng_get_entropy_input(int min_entropy, int min_length, int max_length)
{

  return(NULL);
}

static int
rng_instantiate_function(Rng_state *state, int security_strength,
						 int prediction_resistance_flag,
						 char *personalization_str)
{

  return(-1);
}
#endif

Rng_state *
rng_init(char *seed)
{

  return(NULL);
}

Rng_state *
rng_reinit(char *new_seed, Rng_state *state)
{

  return(NULL);
}

int
rng_generate(Rng_state *state, unsigned char *buf, size_t len)
{

  if (state == NULL)
	return(RAND_bytes(buf, len) != 1 ? -1 : 0);

  return(-1);
}

char *
rng_generate_string(Rng_state *state, char *prefix, size_t len)
{
  int i;
  unsigned char *buf;
  Ds ds;

  buf = (unsigned char *) malloc(len);

  if (state == NULL) {
	if (RAND_bytes(buf, len) != 1)
	  return(NULL);
  }
  else
	return(NULL);

  ds_init(&ds);
  if (prefix != NULL)
	ds_asprintf(&ds, "%s", prefix);

  for (i = 0; i < len; i++)
    ds_asprintf(&ds, "%02x", buf[i] & 0xff);

  memzap(buf, len);
  free(buf);
  return(ds_buf(&ds));
}

void
rng_end(Rng_state *state)
{

}

#ifdef NOTDEF
/*
 * http://php.net/uniqid
 */
Ds *
crypto_uniqid(char *prefix)
{

  return(NULL);
}
#endif

/***************************************************************************/

/* Public key signing and verifying */

Digest_ctx *
crypto_sign_open(const char *digest_name)
{
  Digest_ctx *ctx;
  Digest_tab *dt;

  if ((dt = crypto_lookup_digest_by_name(digest_name)) == NULL)
	return(NULL);

  if (digest_implementation(dt->flags) != DIGEST_OPENSSL)
	return(NULL);

  if ((ctx = digest_make_ctx(dt)) == NULL)
	return(NULL);

  if (EVP_SignInit_ex(ctx->u.evp_ctx, dt->evp, NULL) == 0)
	return(NULL);

  return(ctx);
}

int
crypto_sign_update(Digest_ctx *ctx, unsigned char *buf, unsigned int len)
{

  if (EVP_SignUpdate(ctx->u.evp_ctx, buf, len) == 0)
	return(-1);

  return(0);
}

unsigned char *
crypto_sign_close(Digest_ctx *ctx, unsigned char *sign_buf,
				  unsigned int *sign_len, EVP_PKEY *private_key)
{

  if (EVP_SignFinal(ctx->u.evp_ctx, sign_buf, sign_len, private_key) == 0)
	return(NULL);

  return(sign_buf);
}

/*
 * Compute a digital signature for data BUF/LEN (use strlen(BUF) if LEN is 0)
 * using using DIGEST_NAME and SIGN_KEY.
 * Return a pointer to the signature and set SIGN_BUF (if non-NULL) and
 * SIGN_LEN; otherwise return NULL if an error occurs.
 */
unsigned char *
crypto_sign(char *digest_name, unsigned char *buf, unsigned int buflen,
			unsigned char **sign_buf, unsigned int *sign_len,
			EVP_PKEY *sign_key)
{
  unsigned int len, slen;
  unsigned char *sbuf;
  Digest_ctx *ctx;
  
  if ((ctx = crypto_sign_open(digest_name)) == NULL)
	return(NULL);

  if (buflen == 0)
	len = strlen((char *) buf) + 1;
  else
	len = buflen;

  if (crypto_sign_update(ctx, buf, len) == -1)
	return(NULL);

  slen = EVP_PKEY_size(sign_key);
  sbuf = (unsigned char *) malloc(slen);
  if (sign_buf != NULL)
	*sign_buf = sbuf;

  if (crypto_sign_close(ctx, sbuf, sign_len, sign_key) == NULL)
	return(NULL);

  EVP_MD_CTX_destroy(ctx->u.evp_ctx);

  return(sbuf);
}

/*
 * Generate a digital signature for DATA using private key PRIV_KEY.
 * If MAX_DATALEN is zero, compute over all of DATA, otherwise compute over
 * the smaller of MAX_DATALEN and the actual length.
 * This assumes that DATA is null-terminated.
 * If successful, the signature is returned (NOT null-terminated), otherwise
 * NULL is returned.
 */
Ds *
crypto_sign_buf_len(Ds *data, RSA *priv_key, unsigned int max_datalen)
{
  unsigned int datalen, siglen;
  unsigned char *sigbuf;
  Ds *ds;
  EVP_PKEY *pkey;

  pkey = EVP_PKEY_new();
  EVP_PKEY_assign_RSA(pkey, priv_key); 

  if ((datalen = ds_len(data) - 1) > max_datalen && max_datalen != 0)
	datalen = max_datalen;

  if (crypto_sign(CRYPTO_SIGNATURE_DIGEST_NAME, ds_ucbuf(data), datalen,
				  &sigbuf, &siglen, pkey) == NULL)
	return(NULL);

  ds = ds_setn(NULL, sigbuf, siglen);

  return(ds);
}

/*
 * A wrapper for a commonly used digital signature method.
 * It is assumed that DATA is null-terminated, and the null character is
 * not included in the signature.
 * RFC 3447
 */
Ds *
crypto_sign_buf(Ds *data, RSA *priv_key)
{
  Ds *ds;

  ds = crypto_sign_buf_len(data, priv_key, 0);

  return(ds);
}

/*
 * Sign BUF using SHA1 and the private key in CK.
 * Return the signature string base64 encoded, or NULL.
 */
static MAYBE_UNUSED char *
crypto_sign_test(char *buf, Crypt_keys *ck, unsigned int max_buflen)
{
  char *sbuf;
  Ds *ds, *sign;
  EVP_PKEY *sign_key;
  RSA *rrr;

  sign_key = ck->private_key;

  ds = ds_set(NULL, buf);
#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  rrr = EVP_PKEY_get1_RSA(sign_key);
#else
  rrr = sign_key->pkey.rsa;
#endif
  if ((sign = crypto_sign_buf_len(ds, rrr, max_buflen)) == NULL)
	return(NULL);

  /* The signature is not null-terminated. */
  mime_encode_base64(ds_ucbuf(sign), ds_len(sign), &sbuf);

  return(sbuf);
}

Digest_ctx *
crypto_signv_open(char *digest_name)
{
  Digest_tab *dt;
  Digest_ctx *ctx;

  if ((dt = crypto_lookup_digest_by_name(digest_name)) == NULL)
	return(NULL);

  if (digest_implementation(dt->flags) != DIGEST_OPENSSL)
	return(NULL);

  if ((ctx = digest_make_ctx(dt)) == NULL)
	return(NULL);

  if (EVP_VerifyInit_ex(ctx->u.evp_ctx, dt->evp, NULL) == 0)
	return(NULL);

  return(ctx);
}

int
crypto_signv_update(Digest_ctx *ctx, unsigned char *buf, unsigned int len)
{

  if (EVP_VerifyUpdate(ctx->u.evp_ctx, buf, len) == 0)
	return(-1);

  return(0);
}

int
crypto_signv_close(Digest_ctx *ctx, unsigned char *sign_buf,
				   unsigned int sign_len, EVP_PKEY *public_key)
{
  int st;

  st = EVP_VerifyFinal(ctx->u.evp_ctx, sign_buf, sign_len, public_key);

  return(st);
}

/*
 * Public key signature verification
 * Return 1 if SIGN_BUF/SIGN_LEN is a valid signature for BUF/LEN
 * using DIGEST_NAME and PUBLIC_KEY.
 * Return 0 if the verification fails, and -1 if an error occurs.
 */
int
crypto_signv(char *digest_name, unsigned char *buf, unsigned int blen,
			unsigned char *sign_buf, unsigned int sign_len,
			EVP_PKEY *public_key)
{
  int st;
  unsigned int len;
  Digest_ctx *ctx;

  if ((ctx = crypto_signv_open(digest_name)) == NULL)
	return(-1);

  if (blen == 0)
	len = strlen((char *) buf) + 1;
  else
	len = blen;
  if (crypto_signv_update(ctx, buf, len) == -1)
	return(-1);

  st = crypto_signv_close(ctx, sign_buf, sign_len, public_key);

  EVP_MD_CTX_destroy(ctx->u.evp_ctx);

  return(st);
}

/*
 * Sign the text in BUF using DIGEST_NAME and then verify the signature.
 */
static MAYBE_UNUSED int
crypto_signv_test(Crypt_keys *ck, char *digest_name, char *buf)
{
  int st;
  unsigned int sign_len;
  unsigned char *sign;

  fprintf(stderr, "Testing public key signature1... ");
  if (crypto_sign(digest_name, (unsigned char *) buf, strlen(buf),
				  &sign, &sign_len, ck->private_key) == NULL) {
	fprintf(stderr, "crypto_sign failed\n");
	return(-1);
  }

  st = crypto_signv(digest_name, (unsigned char *) buf, strlen(buf),
					sign, sign_len, ck->public_key);

  if (st == 1)
	fprintf(stderr, "ok\n");
  else if (st == 0)
	fprintf(stderr, "failed - signature did not verify\n");
  else
	fprintf(stderr, "failed - an error occurred\n");

  return(st);
}

/***************************************************************************/

/*
 * XXX THIS IS PURELY EXPERIMENTAL - THE QUALITY OF THE OUTPUT HASN'T BEEN
 * EVALUATED
 * XXX See PKCS #5 v2 or PKCS #12
 * ftp://ftp.rfc-editor.org/in-notes/rfc2898.txt
 *
 * Given PPLEN bytes of passphrase PP (which may be binary), generate
 * NEEDED pseudorandom bytes into an allocated buffer and set RBUF to point
 * to it.
 * The basic approach is to create an initial HMAC key from a SHA-1 of the
 * entire passphrase, then iterate through equal-sized chunks of the passphrase
 * using HMAC and a unique key for each iteration.
 */
void
crypto_make_randomized_from_passphrase(unsigned char *pp, unsigned int pplen,
									   unsigned int needed,
									   unsigned char **rbuf)
{
  unsigned int i, n, plen, rlen;
  unsigned char *ptr, *rptr;
  unsigned char *md, *sha1_key, *sha1_key2;
  unsigned int hmac_len, sha1_key_len, sha1_key2_len;
  Hmac_handle *hmac;

  /* Create an initial key using a digest of the passphrase. */
  sha1_key = crypto_digest(CRYPTO_RANDOMIZED_DIGEST_NAME, pp, pplen, NULL, &sha1_key_len);

  /* Allocate space for the result. */
  rlen = CRYPTO_HMAC_BYTE_LENGTH * ((needed + CRYPTO_HMAC_BYTE_LENGTH)
									/ CRYPTO_HMAC_BYTE_LENGTH);
  *rbuf = rptr = (unsigned char *) malloc(rlen);

  /* How many HMAC computations are required? */
  n = (needed + CRYPTO_HMAC_BYTE_LENGTH + 1) / CRYPTO_HMAC_BYTE_LENGTH;
  ptr = pp;
  /* XXX The remainder is lost, but the SHA1 is on the entire passphrase... */
  plen = pplen / n;

  sha1_key2_len = sha1_key_len + 8 + 1;
  sha1_key2 = (unsigned char *) malloc(sha1_key2_len);

  log_msg((LOG_TRACE_LEVEL,
		   "%d iteration%s, %d bytes each (%d)",
		   n, (n == 1) ? "" : "s", plen, sha1_key2_len));

  for (i = 0; i < n; i++) {
	memcpy(sha1_key2, sha1_key, sha1_key_len);
	sprintf((char *) (sha1_key2 + sha1_key_len), "%8.8u", i);
	/*
	log_msg((LOG_TRACE_LEVEL, "iter%d:\n%s",
			 i, sha1_key2 + sha1_key_len));
	log_msg((LOG_TRACE_LEVEL, "sha1_key (len=%d):\n%s",
			 sha1_key2_len, hexdump(sha1_key, sha1_key2_len)));
	*/
	if ((hmac = crypto_hmac_open(CRYPTO_RANDOMIZED_DIGEST_NAME, sha1_key2,
								 sha1_key2_len)) == NULL) {
	  log_msg((LOG_ALERT_LEVEL, "HMAC failed"));
	  goto done;
	}
	crypto_hmac_hash(hmac, ptr, plen);
	md = crypto_hmac_close(hmac, NULL, &hmac_len);
	memcpy(rptr, md, hmac_len);

	/*
	  log_msg((LOG_TRACE_LEVEL, "rptr(%d):\n%s",
	  i, hexdump(rptr, hmac_len)));
	*/
	memzap(md, hmac_len);
	free(md);
	rptr += hmac_len;
	ptr += plen;
  }

 done:
  memzap(sha1_key2, sha1_key2_len);
  free(sha1_key2);
  memzap(sha1_key, sha1_key_len);
  free(sha1_key);
  /*
	log_msg((LOG_TRACE_LEVEL, "result:\n%s",
	hexdump(*rbuf, needed)));
  */
}

/*
 * The block index counter, stored most significant byte first.
 * RFC 2898 S5.2
 */
#define PBKDF2_COUNTER_SIZE		4

/*
 * Set byte array COUNTER to the big endian encoding of integer VALUE
 * (reminder: the most significant byte of VALUE goes into COUNTER[0]).
 * Return COUNTER.
 *
 * This assumes the 4-byte counter used by PBKDF2 and 8 bits per byte.
 */
static unsigned char *
pbkdf2_set_counter(unsigned char *counter, uint32_t value)
{

  bendian_enc32(counter, value);

  return(counter);

#ifdef NOTDEF
  /*
   * XXX originally wanted to handle arbitrary-length counter values as simply as
   * possible but now that seems unnecessary and needlessly inefficient.
   */
  int cbyte, vbits;
  unsigned int bbit, vbit, vbitnum;

  memset(counter, 0, PBKDF2_COUNTER_SIZE);
  vbits = 8 * PBKDF2_COUNTER_SIZE;
  cbyte = PBKDF2_COUNTER_SIZE - 1;

  vbit = 1;
  bbit = 1;
  for (vbitnum = 0; vbitnum < vbits; vbitnum++) {
	if ((value & vbit) != 0) {
	  if (cbyte < 0)
		return(NULL);
	  counter[cbyte] |= bbit;
	}

	if (((vbitnum + 1) % 8) == 0) {
	  cbyte--;
	  bbit = 1;
	}
	else
	  bbit <<= 1;

	vbit <<= 1;
  }

  return(counter);
#endif
}

static void
pbkdf2_xor(unsigned char *dst, unsigned char *src, unsigned int hlen)
{
  unsigned int i;

  for (i = 0; i < hlen; i++)
	dst[i] ^= src[i];

}

/*
 * Helper function for crypto_pbkdf2().
 * For RFC 2898, ALGNAME should be "SHA1".
 */
static unsigned char *
pbkdf2_f(unsigned char *pwd, unsigned int pwdlen,
		 unsigned char *salt, unsigned int saltlen,
		 char *algname, unsigned int count, uint32_t block_index)
{
  unsigned int hlen, i, tlen;
  unsigned char *f, *text, *u, *u_prev;

  tlen = saltlen + PBKDF2_COUNTER_SIZE;
  text = (unsigned char *) malloc(tlen);
  memcpy(text, salt, saltlen);

  pbkdf2_set_counter(text + saltlen, block_index);

  u_prev = NULL;
  f = NULL;
  for (i = 0; i < count; i++) {
	u = crypto_hmac(algname, pwd, pwdlen, text, tlen, NULL, &hlen);
	if (u == NULL)
	  return(NULL);

	if (u_prev == NULL) {
	  f = (unsigned char *) malloc(hlen);
	  memcpy(f, u, hlen);
	  tlen = hlen;
	  free(text);
	}
	else {
	  pbkdf2_xor(f, u, hlen);
	  free(u_prev);
	}

	text = u_prev = u;
  }

  return(f);
}

/*
 * PBKDF2 - Password-Based Key Derivation Function
 *   RFC 2898, S5.2 (http://www.rfc-editor.org/rfc/rfc2898.txt)
 *   RFC 3962, App B test vectors (http://www.rfc-editor.org/rfc/rfc3962.txt)
 *
 * If ALGNAME is NULL, the default HMAC algorithm (CRYPTO_HMAC_DIGEST_ALG_NAME)
 * is used.
 * COUNT is the iteration count, which increases the amount of effort
 * needed by an attacker to search for a key but which also increases the
 * amount of time needed to generate a derived key.
 * DKLEN is the length, in bytes, of the derived key.
 */
unsigned char *
crypto_pbkdf2(unsigned char *pwd, int pwdlen,
			  unsigned char *salt, int saltlen,
			  char *algname, unsigned int count, unsigned int dklen)
{
  int len;
  char *dname;
  unsigned int ell, hlen, i, plen, r, rem, slen;
  uint32_t block_index;
  unsigned char *dk, *dkp;
  Digest_tab *dt;

  if (dklen == 0) {
	log_msg((LOG_ERROR_LEVEL, "derived key too short"));
	errno = EINVAL;
	return(NULL);
  }

  if (algname == NULL)
	dname = CRYPTO_HMAC_DIGEST_ALG_NAME;
  else
	dname = algname;

  if ((dt = crypto_lookup_hmac_digest_by_name(dname)) == NULL) {
	log_msg((LOG_ERROR_LEVEL,
			 "Cannot find HMAC digest algorithm \"%s\"", dname));
	errno = EINVAL;
	return(NULL);
  }

  if ((digest_usage(dt->flags) & DIGEST_USE_PBKDF2) == 0) {
	log_msg((LOG_ERROR_LEVEL,
			 "HMAC digest algorithm \"%s\" cannot be used for PBKDF", dname));
	errno = EINVAL;
	return(NULL);
  }

  /*
   * For HMAC-SHA1, hlen is 20 bytes.
   */
  if ((len = dt->digest_size) == 0) {
	log_msg((LOG_ERROR_LEVEL,
			 "Cannot find HMAC length for algorithm \"%s\"", dname));
	errno = EINVAL;
	return(NULL);
  }
  hlen = (unsigned int) len;

#ifdef NOTDEF
  IF dklen > (2^32 - 1) * hLen THEN
	log_msg((LOG_ERROR_LEVEL, "derived key too long"));
	errno = EINVAL;
	return(NULL);
  ENDIF
#endif

  if (pwdlen == -1)
	plen = strlen((char *) pwd);
  else
	plen = (unsigned int) pwdlen;

  if (saltlen == -1)
	slen = strlen((char *) salt);
  else
	slen = (unsigned int) saltlen;

  /*
   * ELL is the number of hlen blocks of bytes in the derived key, rounding up.
   * R is the number of bytes in the last block.
   */
  ell = (dklen + hlen - 1) / hlen;
  r = dklen - (ell - 1) * hlen;
  dk = (unsigned char *) malloc(dklen);

  dkp = dk;
  rem = dklen;
  block_index = 1;
  for (i = 1; i <= ell; i++) {
	unsigned int len;
	unsigned char *u;

	u = pbkdf2_f(pwd, plen, salt, slen, dname, count, block_index++);
	if (u == NULL)
	  return(NULL);
	len = (rem > hlen) ? hlen : rem;
	memcpy(dkp, u, len);
	dkp += len;
	rem -= len;
  }

  return(dk);
}

/*
 * Derive a key from other key material.
 * NBITS is the number of bits (which must be a multiple of 8) of derived
 * key required.
 *
 * One intended use of this within DACS is to support multiple symmetric
 * encryption key lengths by generating all of them from a common source.
 * Pre-generating keys of the various sizes seems unnecessary and inflexible,
 * and simply truncating a single long key to the desired length is probably
 * a weak approach.
 * Applying PBKDF2 appears to be the best recommended practice at present.
 * The mkkey utility should generate a very long, random key (say, 1024 bits)
 * from which keys of length 128-bits, 192-bits, 256-bits, and so on can be
 * created on-the-fly based on the run-time configuration.  This should not
 * impose a significant cost if the COUNT argument is suitably tuned.
 */
unsigned char *
crypto_kdf(unsigned char *pwd, int pwdlen,
		   unsigned char *salt, int saltlen,
		   char *algname, unsigned int count, unsigned int nbits)
{
  unsigned char *dk;

  dk = crypto_pbkdf2(pwd, pwdlen, salt, saltlen, algname, count, nbits / 8);

  return(dk);
}

/************/

/*
 * Expand a pseudorandom key (PRK of length PRKLEN bytes) to NEED bytes,
 * returning it in an allocated buffer.  PRKLEN must be at least the length
 * of the digest size of DIGEST_NAME.  If INFO is non-NULL and INFOLEN
 * (the length of INFO in bytes) is non-zero, incorporate it into the expansion.
 * The HMAC-compatible digest function is DIGEST_NAME.
 * The requested length (NEED bytes) must be less than 256 times the
 * digest size of DIGEST_NAME; e.g., for SHA-512, NEED must be less than
 * 256 * 64 (16,384).
 * Returns NULL if an error occurs, otherwise a new key of NEED bytes.
 */
unsigned char *
crypto_hkdf_expand(unsigned char *prk, unsigned int prklen,
				   unsigned char *info, unsigned int infolen,
				   unsigned int need, char *digest_name)
{
  unsigned char octet;
  unsigned int ilen, n, olen, t, tlen;
  unsigned char *okm, *okmp, *tbuf;
  Hmac_handle *h;

  if ((olen = crypto_hmac_hmac_length(digest_name)) == -1)
	return(NULL);

  if (prklen < olen)
	return(NULL);

  n = (need + olen - 1) / olen;
  if (n > 255)
	return(NULL);

  if (info == NULL || infolen == 0)
	ilen = 0;
  else
	ilen = infolen;

  tbuf = (unsigned char *) malloc(olen + ilen + 1);

  okm = (unsigned char *) malloc(olen * n);
  okmp = okm;

  for (t = 1; t <= n; t++) {
	unsigned int len;

	if ((h = crypto_hmac_open(digest_name, prk, prklen)) == NULL)
	  return(NULL);

	octet = t;
	if (t == 1) {
	  memcpy(tbuf, info, ilen);
	  memcpy(tbuf + ilen, &octet, 1);
	  tlen = ilen + 1;
	}
	else {
	  memcpy(tbuf, okmp, olen);
	  memcpy(tbuf + olen, info, ilen);
	  memcpy(tbuf + olen + ilen, &octet, 1);
	  tlen = olen + ilen + 1;
	  okmp += olen;
	}

	crypto_hmac_hash(h, tbuf, tlen);
	crypto_hmac_close(h, okmp, &len);
  }

  return(okm);
}

/*
 * Create a fixed-length pseudorandom key using IKMLEN bytes of input keying
 * material (IKM) and optional SALT (SALTLEN bytes).
 * If there is no salt component, SALT may be NULL or SALTLEN may be zero.
 * IKMLEN must be greater than zero.
 * The HMAC-compatible digest function is DIGEST_NAME.
 * The new key is returned in an allocated buffer; if HKDFBUF is non-NULL,
 * it is set to point to the buffer and if HKDFBUF_LEN is non-NULL, it is
 * set to the length of the new key, which will be the size in bytes of the
 * digest produced by DIGEST_NAME.
 * Returns NULL if an error occurs.
 */
unsigned char *
crypto_hkdf_extract(unsigned char *ikm, unsigned int ikmlen,
					unsigned char *salt, int saltlen,
					char *digest_name, unsigned char **hkdfbuf,
					unsigned int *hkdfbuf_len)
{
  unsigned int olen, prklen, slen;
  unsigned char *obuf, *prk, *sbuf;
  Hmac_handle *h;

  if (ikmlen == 0)
	return(NULL);

  if ((olen = crypto_hmac_hmac_length(digest_name)) == -1)
	return(NULL);

  if (salt == NULL || saltlen == 0) {
	slen = olen;
	sbuf = (unsigned char *) malloc(slen);
	memset(sbuf, 0, slen);
  }
  else {
	slen = saltlen;
	sbuf = salt;
  }

  if ((h = crypto_hmac_open(digest_name, sbuf, slen)) == NULL)
	return(NULL);

  crypto_hmac_hash(h, ikm, ikmlen);

  obuf = (unsigned char *) malloc(olen);

  if ((prk = crypto_hmac_close(h, obuf, &prklen)) == NULL)
	return(NULL);

  if (prklen != olen)
	return(NULL);

  if (hkdfbuf_len != NULL)
	*hkdfbuf_len = olen;

  if (hkdfbuf != NULL)
	*hkdfbuf = obuf;

  return(prk);
}

/*
 * Return a new pseudorandom key of NEED bytes by feeding the output
 * of crypto_hkdf_extract() to crypto_hkdf_expand().
 * The two components may be used separately under appropriate conditions
 * (refer to RFC 5869).
 * NULL is returned if an error occurs.
 *
 * See RFC 5869:
 * https://tools.ietf.org/html/rfc5869
 * And:
 * "Cryptographic Extraction and Key Derivation: The HKDF Scheme",
 * Hugo Krawczyk, Proceedings of CRYPTO 2010.
 * http://eprint.iacr.org/2010/264
 */
unsigned char *
crypto_hkdf(unsigned char *ikm, unsigned int ikmlen,
			unsigned char *salt, unsigned int saltlen,
			unsigned char *info, unsigned int infolen,
			unsigned int need, char *digest_name)
{
  unsigned int prklen;
  unsigned char *prk, *okm;

  if ((prk = crypto_hkdf_extract(ikm, ikmlen, salt, saltlen, digest_name,
								 NULL, &prklen)) == NULL)
	return(NULL);

  okm = crypto_hkdf_expand(prk, prklen, info, infolen, need, digest_name);

  return(okm);
}

typedef struct Hkdf_test {
  char *digest_name;
  char *ikm;
  char *salt;
  char *info;
  unsigned int need;
  char *prk_result;
  char *okm_result;
} Hkdf_test;

static Hkdf_test hkdf_vec[] = {
  /* Test cases from RFC 5869 Appendix A. */
  { "SHA-256", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
	"000102030405060708090a0b0c",
	"f0f1f2f3f4f5f6f7f8f9", 42,
	"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5",
	"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
  },
  { "SHA-256",
	"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
	"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
	"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
	82,
	"06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244",
	"b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87"
  },
  { "SHA-256", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
	"",
	"", 42,
	"19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04",
	"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8"
  },
  { "SHA1", "0b0b0b0b0b0b0b0b0b0b0b",
	"000102030405060708090a0b0c",
	"f0f1f2f3f4f5f6f7f8f9", 42,
	"9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243",
	"085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896"
  },
  { "SHA1", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
	"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
	"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
	82,
	"8adae09a2a307059478d309b26c4115a224cfaf6",
	"0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4"
  },
  { "SHA1", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
	"",
	"", 42,
	"da8c8a73c7fa77288ec6f5e7c297786aa0d32d01",
	"0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918"
  },
  { "SHA1", "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
	"",
	"", 42,
	"2adccada18779e7c2077ad2eb19d3f3e731385dd",
	"2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48"
  },

  /*
   * Tested against these vectors posted by the Kullo folks.  See:
   * https://www.kullo.net/blog/hkdf-sha-512-test-vectors/
   */

  /* Test A */
  { "SHA512", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
	"000102030405060708090a0b0c",
	"f0f1f2f3f4f5f6f7f8f9", 42,
	"665799823737ded04a88e47e54a5890bb2c3d247c7a4254a8e61350723590a26c36238127d8661b88cf80ef802d57e2f7cebcf1e00e083848be19929c61b4237",
	"832390086cda71fb47625bb5ceb168e4c8e26a1a16ed34d9fc7fe92c1481579338da362cb8d9f925d7cb"
  },

  /* Test B */
  { "SHA512",
	"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
	"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
	"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", 82,
	"35672542907d4e142c00e84499e74e1de08be86535f924e022804ad775dde27ec86cd1e5b7d178c74489bdbeb30712beb82d4f97416c5a94ea81ebdf3e629e4a",
	"ce6c97192805b346e6161e821ed165673b84f400a2b514b2fe23d84cd189ddf1b695b48cbd1c8388441137b3ce28f16aa64ba33ba466b24df6cfcb021ecff235f6a2056ce3af1de44d572097a8505d9e7a93"
  },

  /* Test C */
  { "SHA512", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
	"",
	"", 42,
	"fd200c4987ac491313bd4a2a13287121247239e11c9ef82802044b66ef357e5b194498d0682611382348572a7b1611de54764094286320578a863f36562b0df6",
	"f5fa02b18298a72a8c23898a8703472c6eb179dc204c03425c970e3b164bf90fff22d04836d0e2343bac"
  },

  /* Test D */
  { "SHA512", "0b0b0b0b0b0b0b0b0b0b0b",
	"000102030405060708090a0b0c",
	"f0f1f2f3f4f5f6f7f8f9", 42,
	"67409c9cac28b52ee9fad91c2fda999f7ca22e3434f0ae772863836568ad6a7f10cf113bfddd560129a594a8f52385c2d661d785d29ce93a11400c920683181d",
	"7413e8997e020610fbf6823f2ce14bff01875db1ca55f68cfcf3954dc8aff53559bd5e3028b080f7c068"
  },

  /* Test E */
  { "SHA512", "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
	"",
	"", 42,
	"5346b376bf3aa9f84f8f6ed5b1c4f489172e244dac303d12f68ecc766ea600aa88495e7fb605803122fa136924a840b1f0719d2d5f68e29b242299d758ed680c",
	"1407d46013d98bc6decefcfee55f0f90b0c7f63d68eb1a80eaf07e953cfc0a3a5240a155d6e4daa965bb"
  },

  { NULL, NULL, NULL, NULL, 0, NULL, NULL }
};

static int
do_hkdf_tests(void)
{
  int i;
  unsigned int hkdflen, ikmlen, infolen, saltlen;
  unsigned char *hkdfbuf, *ikm, *info, *okm, *salt;
  Hkdf_test *ht;

  i = 1;
  ht = &hkdf_vec[0];
  while (ht->digest_name != NULL) {
	char *p;

	ikm = strhextob(ht->ikm, &ikmlen);
	salt = strhextob(ht->salt, &saltlen);

	fprintf(stderr, "Test Case %d:\n", i++);
	if (crypto_hkdf_extract(ikm, ikmlen, salt, saltlen, ht->digest_name,
							&hkdfbuf, &hkdflen) == NULL)
	  return(-1);

	p = strbtohex(hkdfbuf, hkdflen, 0);
	fprintf(stderr, "PRK=0x%s\n", p);
	if (!streq(p, ht->prk_result))
	  return(-1);

	info = strhextob(ht->info, &infolen);
	if ((okm = crypto_hkdf_expand(hkdfbuf, hkdflen, info, infolen, ht->need,
								  ht->digest_name)) == NULL)
	  return(-1);

	p = strbtohex(okm, ht->need, 0);
	fprintf(stderr, "OKM=0x%s\n", p);
	if (!streq(p, ht->okm_result))
	  return(-1);

	/* Double check using the composing API. */
	if ((okm = crypto_hkdf(ikm, ikmlen, salt, saltlen, info, infolen,
						   ht->need, ht->digest_name)) == NULL)
	  return(-1);

	p = strbtohex(okm, ht->need, 0);
	if (!streq(p, ht->okm_result))
	  return(-1);

	ht++;
  }

  return(0);
}

/************/

/*
 * RFC 7539 - ChaCha20
 *
 * Adapted from D. J. Bernstein's public domain implementation
 * and the OpenBSD implementation.
 */

/*
 * Inlining these functions is a bit faster than using macros.
 * Remember to compile with full optimization enabled.
 */
#define U8TO32_LITTLE(P)		lendian_dec32(P)
#define U32TO8_LITTLE(P, V)		lendian_enc32(P, V)

#define U32C(V)			(V##U)
#define U32V(V)			((uint32_t)(V) & U32C(0xffffffff))
#define PLUS(v, w)		(U32V((v) + (w)))
#define PLUSONE(v)		(PLUS((v), 1))

#ifndef NOTDEF
#define R(a, b) (((a) << (b)) | ((a) >> (32 - (b))))

#define QUARTER_ROUND(A, B, C, D)	\
  { A += B; D ^= A; D = R(D, 16);	\
	C += D; B ^= C; B = R(B, 12);	\
	A += B; D ^= A; D = R(D, 8);	\
	C += D; B ^= C; B = R(B, 7);	\
  }
#else

#define U8C(V)			(V##U)
#define U8V(V)			((uint8_t)(V) & U8C(0xff))

#define XOR(v, w)		((v) ^ (w))
#define ROTL32(v, n) \
  (U32V((v) << (n)) | ((v) >> (32 - (n))))
#define ROTATE(v, c)	(ROTL32(v, c))
#define QUARTER_ROUND(A, B, C, D)	\
  { A = PLUS(A, B); D = ROTATE(XOR(D, A),16);		\
	C = PLUS(C, D); B = ROTATE(XOR(B, C),12);		\
	A = PLUS(A, B); D = ROTATE(XOR(D, A), 8);		\
	C = PLUS(C, D); B = ROTATE(XOR(B, C), 7);		\
  }
#endif

static inline void
blklendian_enc32(void *dest, const void *src, size_t count)
{
  int i;
  uint32_t *s;
  uint8_t *d;

  s = (uint32_t *) src;
  d = (uint8_t *) dest;
  for (i = 0; i < count; i++) {
	lendian_enc32(d, s[i]);
	d += sizeof(uint32_t);
  }
}

static inline int
blkeq_ui32(void *src1, void *src2, size_t count)
{
  int i;
  uint32_t *s1, *s2;

  s1 = src1;
  s2 = src2;
  
  for (i = 0; i < count; i++) {
	if (s1[i] != s2[i])
	  return(0);
  }

  return(1);
}

static inline void
blkxor(void *dest, const void *src, size_t len)
{
  size_t *d, ell, i;
  const size_t *s;

  d = dest;
  s = src;
  ell = len / sizeof(size_t);

  for (i = 0; i < ell; i++)
	d[i] ^= s[i];
}

static inline void
blkadd_ui32(void *dest, void *src, size_t count)
{
  int i;
  uint32_t *d, *s;

  d = dest;
  s = src;
  
  for (i = 0; i < count; i++)
	d[i] = PLUS(d[i], s[i]);
}

static inline void
blkcpy(void *dest, const void *src, size_t len)
{
  size_t *d, ell, i;
  const size_t *s;

  d = dest;
  s = src;
  ell = len / sizeof(size_t);
  
  for (i = 0; i < ell; i++)
	d[i] = s[i];
}

static int debug_chacha = 0;

/*
 * Initialization sets the 16 32-bit state registers to:
 *    0: cccccccc   1: cccccccc   2: cccccccc   3: cccccccc
 *    4: kkkkkkkk   5: kkkkkkkk   6: kkkkkkkk   7: kkkkkkkk
 *    8: kkkkkkkk   9: kkkkkkkk  10: kkkkkkkk  11: kkkkkkkk
 *   12: bbbbbbbb  13: nnnnnnnn  14: nnnnnnnn  15: nnnnnnnn
 *
 * c=constant k=key b=counter n=nonce
 */

/*
 * Initialize the IV (NONCE and COUNTER) state.
 */
static void
chacha_ivsetup(Chacha_ctx *ctx, const uint8_t *nonce, uint32_t counter)
{
  uint8_t *c;

  /*
   * Word 12 is a block counter.  Since each block is 64 bytes, a 32-bit
   * word is enough for 256GB of data.
   */
  c = (uint8_t *) &counter;
  ctx->input[12] = U8TO32_LITTLE(c + 0);

  /*
   * Words 13-15 are a nonce, which should not be repeated for the same
   * key.  The 13th word is the first 32 bits of the input nonce taken
   * as a little-endian integer, while the 15th word is the last 32 bits.
   */
  ctx->input[13] = U8TO32_LITTLE(nonce + 0);
  ctx->input[14] = U8TO32_LITTLE(nonce + 4);		/* S2.3 */
  ctx->input[15] = U8TO32_LITTLE(nonce + 8);
}

/*
 * Initialize context CTX for encryption or decryption using the
 * ChaCha20 cipher.
 * If both KEY and NONCE (the IV) are NULL, then this is a complete
 * initialization; both of these arguments are assumed to be appropriate
 * lengths.
 * If only NONCE is non-NULL, set the IV and counter without changing the
 * key.  If only KEY is non-NULL, set the KEY without changing the IV.
 *
 * KEY is 256 bits (32 bytes)
 * NONCE is 96 bits (12 bytes)
 * COUNTER is 32 bits (4 bytes)
 * (making the IV 16 bytes)
 */
void
chacha_init(Chacha_ctx *ctx, const uint8_t *key, const uint8_t *nonce,
			uint32_t counter)
{
  static const char sigma[16] = "expand 32-byte k";

  if (key != NULL && nonce != NULL)
	memset(ctx->input, 0, sizeof(ctx->input));

  if (key != NULL) {
	/*
	 * These eight words (4-11) are taken from the 256-bit key by
	 * reading the bytes in little-endian order, in 4-byte chunks.
	 */
	ctx->input[ 4] = U8TO32_LITTLE(key +  0);
	ctx->input[ 5] = U8TO32_LITTLE(key +  4);
	ctx->input[ 6] = U8TO32_LITTLE(key +  8);
	ctx->input[ 7] = U8TO32_LITTLE(key + 12);

	ctx->input[ 8] = U8TO32_LITTLE(key + 16);
	ctx->input[ 9] = U8TO32_LITTLE(key + 20);
	ctx->input[10] = U8TO32_LITTLE(key + 24);
	ctx->input[11] = U8TO32_LITTLE(key + 28);

	/*
	 * The first four words (0-3) are constants:
	 * 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574.
	 */
	ctx->input[ 0] = U8TO32_LITTLE(sigma +  0);
	ctx->input[ 1] = U8TO32_LITTLE(sigma +  4);
	ctx->input[ 2] = U8TO32_LITTLE(sigma +  8);
	ctx->input[ 3] = U8TO32_LITTLE(sigma + 12);
  }

  if (nonce != NULL)
	chacha_ivsetup(ctx, nonce, counter);
}

/*
 * Encipher (or decipher) MESSAGE of length NBYTES into buffer OUTPUT
 * using NROUNDS rounds based on context CTX.
 * It is assumed that CTX has been initialized or modified by a previous
 * call to this function, and that OUTPUT is large enough.
 * It is currently thought that using NROUNDS == 20 (CHACHA_ROUNDS)
 * is sufficient for security.
 * Decrypt by running this function on cipher text previously generated with
 * the same initial context.
 */
#ifdef ASSIGN
void
chacha_cipher(Chacha_ctx *ctx, const uint8_t *message, uint8_t *output,
			  uint32_t nrounds, uint32_t nbytes)
{
  uint8_t *ctarget, tmp[64];
  uint32_t x[16];
  uint32_t i;
  uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
  uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;

  if (nbytes == 0)
	return;

  ctarget = NULL;

  j0 = ctx->input[0];
  j1 = ctx->input[1];
  j2 = ctx->input[2];
  j3 = ctx->input[3];
  j4 = ctx->input[4];
  j5 = ctx->input[5];
  j6 = ctx->input[6];
  j7 = ctx->input[7];
  j8 = ctx->input[8];
  j9 = ctx->input[9];
  j10 = ctx->input[10];
  j11 = ctx->input[11];
  j12 = ctx->input[12];
  j13 = ctx->input[13];
  j14 = ctx->input[14];
  j15 = ctx->input[15];

  for (;;) {
    if (nbytes < 64) {
      for (i = 0; i < nbytes; i++)
		tmp[i] = message[i];
      message = tmp;
      ctarget = output;
      output = tmp;
    }

    x0 = j0;
    x1 = j1;
    x2 = j2;
    x3 = j3;
    x4 = j4;
    x5 = j5;
    x6 = j6;
    x7 = j7;
    x8 = j8;
    x9 = j9;
    x10 = j10;
    x11 = j11;
    x12 = j12;
    x13 = j13;
    x14 = j14;
    x15 = j15;

    for (i = 0; i < nrounds / 2; i++) {
      QUARTER_ROUND( x0, x4, x8,x12);
      QUARTER_ROUND( x1, x5, x9,x13);
      QUARTER_ROUND( x2, x6,x10,x14);
      QUARTER_ROUND( x3, x7,x11,x15);
      QUARTER_ROUND( x0, x5,x10,x15);
      QUARTER_ROUND( x1, x6,x11,x12);
      QUARTER_ROUND( x2, x7, x8,x13);
      QUARTER_ROUND( x3, x4, x9,x14);
    }

    x0 = PLUS(x0, j0);
    x1 = PLUS(x1, j1);
    x2 = PLUS(x2, j2);
    x3 = PLUS(x3, j3);
    x4 = PLUS(x4, j4);
    x5 = PLUS(x5, j5);
    x6 = PLUS(x6, j6);
    x7 = PLUS(x7, j7);
    x8 = PLUS(x8, j8);
    x9 = PLUS(x9, j9);
    x10 = PLUS(x10, j10);
    x11 = PLUS(x11, j11);
    x12 = PLUS(x12, j12);
    x13 = PLUS(x13, j13);
    x14 = PLUS(x14, j14);
    x15 = PLUS(x15, j15);

    x0 = XOR(x0, U8TO32_LITTLE(message + 0));
    x1 = XOR(x1, U8TO32_LITTLE(message + 4));
    x2 = XOR(x2, U8TO32_LITTLE(message + 8));
    x3 = XOR(x3, U8TO32_LITTLE(message + 12));
    x4 = XOR(x4, U8TO32_LITTLE(message + 16));
    x5 = XOR(x5, U8TO32_LITTLE(message + 20));
    x6 = XOR(x6, U8TO32_LITTLE(message + 24));
    x7 = XOR(x7, U8TO32_LITTLE(message + 28));
    x8 = XOR(x8, U8TO32_LITTLE(message + 32));
    x9 = XOR(x9, U8TO32_LITTLE(message + 36));
    x10 = XOR(x10, U8TO32_LITTLE(message + 40));
    x11 = XOR(x11, U8TO32_LITTLE(message + 44));
    x12 = XOR(x12, U8TO32_LITTLE(message + 48));
    x13 = XOR(x13, U8TO32_LITTLE(message + 52));
    x14 = XOR(x14, U8TO32_LITTLE(message + 56));
    x15 = XOR(x15, U8TO32_LITTLE(message + 60));

    j12 = PLUSONE(j12);
    if (!j12) {
      j13 = PLUSONE(j13);
      /* stopping at 2^70 bytes per nonce is user's responsibility */
    }

    U32TO8_LITTLE(output + 0,  x0);
    U32TO8_LITTLE(output + 4,  x1);
    U32TO8_LITTLE(output + 8,  x2);
    U32TO8_LITTLE(output + 12, x3);
    U32TO8_LITTLE(output + 16, x4);
    U32TO8_LITTLE(output + 20, x5);
    U32TO8_LITTLE(output + 24, x6);
    U32TO8_LITTLE(output + 28, x7);
    U32TO8_LITTLE(output + 32, x8);
    U32TO8_LITTLE(output + 36, x9);
    U32TO8_LITTLE(output + 40, x10);
    U32TO8_LITTLE(output + 44, x11);
    U32TO8_LITTLE(output + 48, x12);
    U32TO8_LITTLE(output + 52, x13);
    U32TO8_LITTLE(output + 56, x14);
    U32TO8_LITTLE(output + 60, x15);

    if (nbytes <= 64) {
      if (nbytes < 64) {
        for (i = 0; i < nbytes; i++)
		  ctarget[i] = output[i];
      }

      ctx->input[12] = j12;
      ctx->input[13] = j13;
      return;
    }

    nbytes -= 64;
    output += 64;
    message += 64;
  }

}

#else

void
chacha_cipher(Chacha_ctx *ctx, const uint8_t *message, uint8_t *output,
			  uint32_t nrounds, uint32_t nbytes)
{
  int i;
  uint8_t *ctarget, tmp[64];
  uint32_t x[16], jstate[16];
  uint32_t j12;

  if (nbytes == 0)
	return;

#define XVAR(IND)	(x[IND])
#define JVAR(IND)	(jstate[IND])

  memcpy(jstate, ctx->input, sizeof(ctx->input));

  ctarget = NULL;

  for (;;) {
    if (nbytes < 64) {
      for (i = 0; i < nbytes; i++)
		tmp[i] = message[i];

      message = tmp;
      ctarget = output;
      output = tmp;
    }

	memcpy(x, jstate, sizeof(jstate));

    for (i = 0; i < nrounds / 2; i++) {
      QUARTER_ROUND( XVAR(0), XVAR(4), XVAR(8),  XVAR(12));
      QUARTER_ROUND( XVAR(1), XVAR(5), XVAR(9),  XVAR(13));
      QUARTER_ROUND( XVAR(2), XVAR(6), XVAR(10), XVAR(14));
      QUARTER_ROUND( XVAR(3), XVAR(7), XVAR(11), XVAR(15));
      QUARTER_ROUND( XVAR(0), XVAR(5), XVAR(10), XVAR(15));
      QUARTER_ROUND( XVAR(1), XVAR(6), XVAR(11), XVAR(12));
      QUARTER_ROUND( XVAR(2), XVAR(7), XVAR(8),  XVAR(13));
      QUARTER_ROUND( XVAR(3), XVAR(4), XVAR(9),  XVAR(14));
    }

	blkadd_ui32(x, jstate, 16);

	blkxor(x, message, 64);

	j12 = JVAR(12) = PLUSONE(JVAR(12));

    if (!j12) {
      JVAR(13) = PLUSONE(JVAR(13));
      /* stopping at 2^70 bytes per nonce is user's responsibility */
    }

    U32TO8_LITTLE(output + 0,  XVAR(0));
    U32TO8_LITTLE(output + 4,  XVAR(1));
    U32TO8_LITTLE(output + 8,  XVAR(2));
    U32TO8_LITTLE(output + 12, XVAR(3));
    U32TO8_LITTLE(output + 16, XVAR(4));
    U32TO8_LITTLE(output + 20, XVAR(5));
    U32TO8_LITTLE(output + 24, XVAR(6));
    U32TO8_LITTLE(output + 28, XVAR(7));
    U32TO8_LITTLE(output + 32, XVAR(8));
    U32TO8_LITTLE(output + 36, XVAR(9));
    U32TO8_LITTLE(output + 40, XVAR(10));
    U32TO8_LITTLE(output + 44, XVAR(11));
    U32TO8_LITTLE(output + 48, XVAR(12));
    U32TO8_LITTLE(output + 52, XVAR(13));
    U32TO8_LITTLE(output + 56, XVAR(14));
    U32TO8_LITTLE(output + 60, XVAR(15));

    if (nbytes <= 64) {
      if (nbytes < 64) {
        for (i = 0; i < nbytes; i++)
		  ctarget[i] = output[i];
      }

      ctx->input[12] = JVAR(12);
      ctx->input[13] = JVAR(13);
      return;
    }

    nbytes -= 64;
    output += 64;
    message += 64;
  }

#undef XVAR
#undef JVAR
}
#endif

/*
 * One shot message encrypt/decrypt of MESSAGE (NBYTES long) to OUTPUT.
 */
void
chacha_cipher_message(const uint8_t *message, uint8_t *output,
					  uint32_t nrounds, uint32_t nbytes,
					  const uint8_t *key, const uint8_t *nonce,
					  uint32_t counter)
{
  Chacha_ctx ctx;

  chacha_init(&ctx, key, nonce, counter);

  chacha_cipher(&ctx, message, output, nrounds, nbytes);
}

static void
show(uint8_t *buf, size_t buflen)
{
  size_t i;

  for (i = 0; i < buflen; i++) {
	fprintf(stderr, "%02x", buf[i] & 0xff);
	if (((i + 1) % 16) == 0)
	  fprintf(stderr, "\n");
	else
	  fprintf(stderr, " ");
  }

  if (buflen % 16)
	fprintf(stderr, "\n");
}

static void
show32(uint32_t *buf, size_t count)
{
  size_t i;

  for (i = 0; i < count; i++) {
	fprintf(stderr, "0x%08x", buf[i] & 0xffffffff);
	if (((i + 1) % 4) == 0)
	  fprintf(stderr, "\n");
	else
	  fprintf(stderr, " ");
  }

  if (count % 4)
	fprintf(stderr, "\n");
}

/************/

/*
 * Adapted from Colin Percival's reference implementation of scrypt.
 * Copyright 2009 Colin Percival
 * All rights reserved.
 * http://www.tarsnap.com/scrypt.html
 * https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-03
 * See NOTICES.
 */

/*
 * Apply the salsa20/8 core to block B.
 * This is Daniel J. Bernstein's Salsa20 stream cipher reduced from 20 to 8
 * rounds.
 * http://cr.yp.to/snuffle/salsafamily-20071225.pdf
 */
static void
salsa20_8(uint32_t b[16])
{
  int nrounds = 8;
  uint32_t x[16];
  size_t i;

  blkcpy(x, b, sizeof(x));
  for (i = 0; i < nrounds; i += 2) {

#define R(a, b) (((a) << (b)) | ((a) >> (32 - (b))))

	/* Operate on columns. */
	x[ 4] ^= R(x[ 0]+x[12], 7);  x[ 8] ^= R(x[ 4]+x[ 0], 9);
	x[12] ^= R(x[ 8]+x[ 4],13);  x[ 0] ^= R(x[12]+x[ 8],18);

	x[ 9] ^= R(x[ 5]+x[ 1], 7);  x[13] ^= R(x[ 9]+x[ 5], 9);
	x[ 1] ^= R(x[13]+x[ 9],13);  x[ 5] ^= R(x[ 1]+x[13],18);

	x[14] ^= R(x[10]+x[ 6], 7);  x[ 2] ^= R(x[14]+x[10], 9);
	x[ 6] ^= R(x[ 2]+x[14],13);  x[10] ^= R(x[ 6]+x[ 2],18);

	x[ 3] ^= R(x[15]+x[11], 7);  x[ 7] ^= R(x[ 3]+x[15], 9);
	x[11] ^= R(x[ 7]+x[ 3],13);  x[15] ^= R(x[11]+x[ 7],18);

	/* Operate on rows. */
	x[ 1] ^= R(x[ 0]+x[ 3], 7);  x[ 2] ^= R(x[ 1]+x[ 0], 9);
	x[ 3] ^= R(x[ 2]+x[ 1],13);  x[ 0] ^= R(x[ 3]+x[ 2],18);

	x[ 6] ^= R(x[ 5]+x[ 4], 7);  x[ 7] ^= R(x[ 6]+x[ 5], 9);
	x[ 4] ^= R(x[ 7]+x[ 6],13);  x[ 5] ^= R(x[ 4]+x[ 7],18);

	x[11] ^= R(x[10]+x[ 9], 7);  x[ 8] ^= R(x[11]+x[10], 9);
	x[ 9] ^= R(x[ 8]+x[11],13);  x[10] ^= R(x[ 9]+x[ 8],18);

	x[12] ^= R(x[15]+x[14], 7);  x[13] ^= R(x[12]+x[15], 9);
	x[14] ^= R(x[13]+x[12],13);  x[15] ^= R(x[14]+x[13],18);
#undef R
  }

  for (i = 0; i < 16; i++)
	b[i] += x[i];
}

static int
salsa20_8_test(void)
{
  static uint8_t input[] = {
	0x7e, 0x87, 0x9a, 0x21, 0x4f, 0x3e, 0xc9, 0x86,
	0x7c, 0xa9, 0x40, 0xe6, 0x41, 0x71, 0x8f, 0x26,
	0xba, 0xee, 0x55, 0x5b, 0x8c, 0x61, 0xc1, 0xb5,
	0x0d, 0xf8, 0x46, 0x11, 0x6d, 0xcd, 0x3b, 0x1d,
	0xee, 0x24, 0xf3, 0x19, 0xdf, 0x9b, 0x3d, 0x85,
	0x14, 0x12, 0x1e, 0x4b, 0x5a, 0xc5, 0xaa, 0x32,
	0x76, 0x02, 0x1d, 0x29, 0x09, 0xc7, 0x48, 0x29,
	0xed, 0xeb, 0xc6, 0x8d, 0xb8, 0xb8, 0xc2, 0x5e
  };
  static uint8_t expected_output[] = {
	0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99,
	0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, 0xef, 0x05,
	0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d,
	0xfd, 0x7b, 0x1c, 0x63, 0x96, 0x68, 0x2f, 0x29,
	0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc,
	0xfe, 0x6b, 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba,
	0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c,
	0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81
  };

  salsa20_8((uint32_t *) input);

  if (memcmp(input, expected_output, sizeof(expected_output)) != 0)
	return(-1);

  return(0);
}

/*
 * Compute B_OUT = BlockMix_{salsa20/8, R}(B_IN).
 *
 * This is called scryptBlockMix is the description of the algorithm.
 * The input B_IN must be 128 * R bytes in length; the output B_OUT must also
 * be the same size.  The temporary space X must be 64 bytes.
 */
static void
blockmix_salsa8(uint32_t *b_in, uint32_t *b_out, uint32_t *x, size_t r)
{
  size_t i;

  /* 1: X <-- B_{2r - 1} */
  blkcpy(x, &b_in[(2 * r - 1) * 16], 64);

  /* 2: for i = 0 to 2r - 1 do */
  for (i = 0; i < 2 * r; i += 2) {
	/* 3: X <-- H(X \xor B_i) */
	blkxor(x, &b_in[i * 16], 64);
	salsa20_8(x);

	/* 4: Y_i <-- X */
	/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
	blkcpy(&b_out[i * 8], x, 64);

	/* 3: X <-- H(X \xor B_i) */
	blkxor(x, &b_in[i * 16 + 16], 64);
	salsa20_8(x);

	/* 4: Y_i <-- X */
	/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
	blkcpy(&b_out[i * 8 + r * 16], x, 64);
  }
}

/*
 * Return the result of parsing B_{2r-1} as a little-endian integer.
 */
static uint64_t
integerify(void *b, size_t r)
{
  uint32_t *x = (void *) ((uintptr_t) (b) + (2 * r - 1) * 64);

  return(((uint64_t) (x[1]) << 32) + x[0]);
}

/*
 * Compute B = SMix_r(B, N).
 * This is called scryptROMix in the description of the algorithm.
 * The input B must be 128 * R bytes in length;
 * the temporary storage V must be 128 * R * N bytes in length; the temporary
 * storage XY must be 256 * R + 64 bytes in length.  The value N must be a
 * power of 2 greater than 1.  The arrays B, V, and XY must be aligned to a
 * multiple of 64 bytes.
 */
static void
smix(uint8_t *b, size_t r, uint64_t n, uint32_t *v, uint32_t *xy)
{
  uint32_t *X, *Y, *Z;
  uint64_t i, j;
  size_t k;

  X = xy;
  Y = &xy[32 * r];
  Z = &xy[64 * r];

  /* 1: X <-- B */
  for (k = 0; k < 32 * r; k++)
	X[k] = lendian_dec32(&b[4 * k]);

  /* 2: for i = 0 to N - 1 do */
  for (i = 0; i < n; i += 2) {
	/* 3: V_i <-- X */
	blkcpy(&v[i * (32 * r)], X, 128 * r);

	/* 4: X <-- H(X) */
	blockmix_salsa8(X, Y, Z, r);

	/* 3: V_i <-- X */
	blkcpy(&v[(i + 1) * (32 * r)], Y, 128 * r);

	/* 4: X <-- H(X) */
	blockmix_salsa8(Y, X, Z, r);
  }

  /* 6: for i = 0 to N - 1 do */
  for (i = 0; i < n; i += 2) {
	/* 7: j <-- Integerify(X) mod N */
	j = integerify(X, r) & (n - 1);

	/* 8: X <-- H(X \xor V_j) */
	blkxor(X, &v[j * (32 * r)], 128 * r);
	blockmix_salsa8(X, Y, Z, r);

	/* 7: j <-- Integerify(X) mod N */
	j = integerify(Y, r) & (n - 1);

	/* 8: X <-- H(X \xor V_j) */
	blkxor(Y, &v[j * (32 * r)], 128 * r);
	blockmix_salsa8(Y, X, Z, r);
  }

  /* 10: B' <-- X */
  for (k = 0; k < 32 * r; k++)
	lendian_enc32(&b[4 * k], X[k]);
}

/*
 * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
 * p, buflen) and write the result into buf.  The parameters r, p, and buflen
 * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.  The parameter N
 * must be a power of 2 greater than 1.
 *
 * Return 0 on success; or -1 on error.
 */
unsigned char *
crypto_scrypt(unsigned char *passwd, unsigned int passwdlen,
			  unsigned char *salt, unsigned int saltlen,
			  uint64_t n, uint32_t r, uint32_t p, unsigned int dklen)
{
  int err;
  void *B0, *V0, *XY0;
  uint8_t *B1, *B2;
  uint32_t i, *V, *XY;

  B0 = B1 = B2 = NULL;
  V0 = XY0 = NULL;

  /* Sanity-check parameters. */
  if (dklen == 0) {
	fprintf(stderr, "crypto_scrypt: dklen == 0\n");
	DEBUG_ABORT(err, EINVAL);
	goto err0;
  }

#if SIZE_MAX > UINT32_MAX
  if ((uint64_t) dklen > (((uint64_t) (1) << 32) - 1) * 32) {
	DEBUG_ABORT(err, EFBIG);
	goto err0;
  }
#endif

  if ((uint64_t) (r) * (uint64_t) (p) >= (1 << 30)) {
	DEBUG_ABORT(err, EFBIG);
	goto err0;
  }

  /* Ensure that n is a power of 2. */
  if (((n & (n - 1)) != 0) || (n == 0)) {
	fprintf(stderr, "crypto_scrypt: n is not a power of two\n");
	DEBUG_ABORT(err, EINVAL);
	goto err0;
  }

  if ((r > SIZE_MAX / 128 / p) ||
#if SIZE_MAX / 256 <= UINT32_MAX 
	  (r > SIZE_MAX / 256) ||
#endif
	  (n > SIZE_MAX / 128 / r)) {
	DEBUG_ABORT(err, ENOMEM);
	goto err0;
  }

  /* Allocate memory. */
#ifdef HAVE_POSIX_MEMALIGN
  if ((err = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) {
	DEBUG_ABORT(err, err);
	goto err1;
  }
  XY = (uint32_t *) (XY0);
#ifndef MAP_ANON
  if ((err = posix_memalign(&V0, 64, 128 * r * n)) != 0) {
	DEBUG_ABORT(err, err);
	goto err2;
  }
  V = (uint32_t *) (V0);
#endif
#else
  if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) {
	DEBUG_ABORT(err, errno);
	goto err1;
  }

  XY = (uint32_t *) (((uintptr_t) (XY0) + 63) & ~ (uintptr_t) (63));
#ifndef MAP_ANON
  if ((V0 = malloc(128 * r * n + 63)) == NULL) {
	DEBUG_ABORT(err, errno);
	goto err2;
  }
  V = (uint32_t *) (((uintptr_t) (V0) + 63) & ~ (uintptr_t) (63));
#endif
#endif

#ifdef MAP_ANON
  if ((V0 = mmap(NULL, 128 * r * n, PROT_READ | PROT_WRITE,
#ifdef MAP_NOCORE
				 MAP_ANON | MAP_PRIVATE | MAP_NOCORE,
#else
				 MAP_ANON | MAP_PRIVATE,
#endif
				 -1, 0)) == MAP_FAILED) {
	DEBUG_ABORT(err, errno);
	goto err2;
  }
  V = (uint32_t *) (V0);
#endif

  /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
  B1 = crypto_pbkdf2(passwd, passwdlen, salt, saltlen, "SHA256", 1, p * 128 * r);
  if (B1 == NULL) {
	DEBUG_ABORT(err, errno);
	goto err2;
  }

  /* 2: for i = 0 to p - 1 do */
  for (i = 0; i < p; i++) {
	/* 3: B_i <-- MF(B_i, N) */
	smix(&B1[i * 128 * r], r, n, V, XY);
  }

  /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
  B2 = crypto_pbkdf2(passwd, passwdlen, B1, p * 128 * r, "SHA256", 1, dklen);
  if (B2 == NULL) {
	DEBUG_ABORT(err, errno);
	goto err2;
  }

  /* Free memory. */
#ifdef MAP_ANON
  if (munmap(V0, 128 * r * n)) {
	DEBUG_ABORT(err, errno);
	goto err2;
  }
#else
  if (V0 != NULL)
	free(V0);
#endif
  if (XY0 != NULL)
	free(XY0);

  if (B0 != NULL)
	free(B0);
  if (B1 != NULL)
	free(B1);

  /* Success! */
  return((unsigned char *) B2);

  /* Error returns. */

 err2:
  if (XY0 != NULL)
	free(XY0);

 err1:
  if (B0 != NULL)
	free(B0);
  if (B1 != NULL)
	free(B1);
  if (B2 != NULL)
	free(B2);

 err0:
  errno = err;
  return(NULL);
}

typedef struct Scrypt_test {
  char *pwd;
  int pwdlen;
  char *salt;
  int saltlen;
  char *alg_name;
  unsigned int N;
  unsigned int r;
  unsigned int p;
  unsigned int dklen;
  char *result;
} Scrypt_test;

static Scrypt_test scrypt_vec[] = {
  { "", 0, "", 0,
	"SHA256", 16, 1, 1, 64,
	"77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906" },
  { "password", -1, "NaCl", -1,
	"SHA256", 1024, 8, 16, 64,
	"fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640" },
  { "pleaseletmein", -1, "SodiumChloride", -1,
	"SHA256", 16384, 8, 1, 64,
	"7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887" },
  { "pleaseletmein", -1, "SodiumChloride", -1,
	"SHA256", 1048576, 8, 1, 64,
	"2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4" },

  { NULL, 0, NULL, 0, NULL, 0, 0, 0, 0, NULL }
};

static int
do_scrypt_tests(void)
{
  int i, plen, rc, slen;
  unsigned char *buf;
  char *p;
  Scrypt_test *test;
 
  if (salsa20_8_test() != 0) {
	fprintf(stderr, "Salsa20_8 test failed\n");
	return(-1);
  }

  for (i = 0; scrypt_vec[i].pwd != NULL; i++) {
	test = &scrypt_vec[i];
	if (test->pwdlen == -1)
	  plen = strlen((char *) test->pwd);
	else
	  plen = test->pwdlen;

	if (test->saltlen == -1)
	  slen = strlen((char *) test->salt);
	else
	  slen = test->saltlen;

	fprintf(stderr,
			"%2d. scrypt(%s, keylen=%d, saltlen=%d, N=%d, r=%d, p=%d, dklen=%d):\n",
			i, test->alg_name, plen, slen, test->N, test->r, test->p, test->dklen);
	if ((buf = crypto_scrypt((unsigned char *) test->pwd, plen,
							 (unsigned char *) test->salt, slen,
							 test->N, test->r, test->p, test->dklen)) == NULL) {
	  fprintf(stderr, "Test %d failed\n", i);
	  exit(1);
	}

	p = strbtohex(buf, test->dklen, 0);
	fprintf(stderr, " %s... ", p);
	if (!streq(p, test->result)) {
	  fprintf(stderr, "Failed!\n");
	  return(-1);
	}
	fprintf(stderr, "ok\n");
  }

  return(0);
}

/************/

static int debug_oauth = 0;

static char *
oauth_encode(char *str)
{
  char *enc_str;
  static char *oauth_chars =
	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";

  enc_str = percent_encode_other_chars(str, oauth_chars, 0);

  return(enc_str);
}

static int
oauth_param_compar(const void *ap, const void *bp)
{
  int st;
  Uri_query_arg *a, *b;

  a = *(Uri_query_arg **) ap;
  b = *(Uri_query_arg **) bp;

  if ((st = strcmp(a->name, b->name)) == 0)
	st = strcmp(a->value, b->value);

  return(st);
}

static char *
oauth_normalize_params(Dsvec *dsv_args, Dsvec *dsv_normalized_args)
{
  int i;
  Ds ds;
  Uri_query_arg *arg, *enc_arg;

  for (i = 0; i < dsvec_len(dsv_args); i++) {
	arg = (Uri_query_arg *) dsvec_ptr_index(dsv_args, i);
	enc_arg = ALLOC(Uri_query_arg);
	enc_arg->name = oauth_encode(arg->name);
	enc_arg->value = oauth_encode(arg->value);
	dsvec_add_ptr(dsv_normalized_args, enc_arg);
	if (debug_oauth > 1) {
	  fprintf(stderr,
			  "RawArg%d: %s=\"%s\"\n", i, arg->name, arg->value);
	  fprintf(stderr,
			  "EncArg%d: %s=\"%s\"\n", i, enc_arg->name, enc_arg->value);
	}
  }

  dsvec_sort(dsv_normalized_args, oauth_param_compar);

  ds_init(&ds);
  for (i = 0; i < dsvec_len(dsv_normalized_args); i++) {
	char *arg_str;

	arg = (Uri_query_arg *) dsvec_ptr_index(dsv_normalized_args, i);
	arg_str = ds_xprintf("%s=%s",
						 arg->name, (arg->value == NULL) ? "" : arg->value);
	ds_asprintf(&ds, "%s%s", (i == 0) ? "" : "&", arg_str);
  }

  return(ds_buf(&ds));
}

typedef enum {
  OAUTH_HMAC_SHA1 = 0,
  OAUTH_RSA_SHA1  = 1,
  OAUTH_PLAINTEXT = 2
} Oauth_sig_method;

/*
 * RFC 5849 S3.4
 *
 * XXX
 * http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/
 * http://tech.slashdot.org/story/12/07/28/1538212/oauth-20-standard-editor-quits-takes-name-off-spec
 * 
 */
static char *
crypto_oauth_signature(char *method, Uri *uri, Dsvec *oauth_args,
					   char *body_arg_str, Dsvec *normalized_oauth_args,
					   Oauth_sig_method sig_method, char *client_shared_secret,
					   char *token_shared_secret, char *private_key_file,
					   char *passphrase)
{
  int i;
  unsigned int outp_len;
  char *base_string_uri, *enc, *key;
  char *normalized_param_string, *signature_base_string;
  unsigned char *outp;
  Ds ds;
  Dsvec *args, *body_args;

  ds_init(&ds);
  ds_asprintf(&ds, "%s://%s", uri->scheme, uri->host);
  if (uri->port_given != NULL) {
	if (streq(uri->scheme, "http")) {
	  if (uri->port != HTTP_DEFAULT_PORT)
		ds_asprintf(&ds, ":%s", uri->port_given);
	}
	else if (streq(uri->scheme, "https")) {
	  if (uri->port != HTTP_DEFAULT_SSL_PORT)
		ds_asprintf(&ds, ":%s", uri->port_given);
	}
	else
	  ds_asprintf(&ds, ":%s", uri->port_given);
  }
  if (uri->path == NULL || *uri->path == '\0')
	ds_asprintf(&ds, "/");
  else
	ds_asprintf(&ds, "%s", uri->path);

  base_string_uri = ds_buf(&ds);
  if (debug_oauth)
	fprintf(stderr, "base_string_uri=\"%s\"\n", base_string_uri);

  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  dsvec_append(args, uri->query_args);
  dsvec_append(args, oauth_args);
  if (body_arg_str != NULL) {
	if ((body_args = parse_query_string(strdup(body_arg_str))) == NULL)
	  return(NULL);
	dsvec_append(args, body_args);
  }

  normalized_param_string = oauth_normalize_params(args, normalized_oauth_args);
  if (debug_oauth)
	fprintf(stderr, "normalized_params=\"%s\"\n", normalized_param_string);

  ds_reset_buf(&ds);
  ds_asprintf(&ds, "%s&%s&%s",
			  oauth_encode(strtoupper(method)),
			  oauth_encode(base_string_uri),
			  oauth_encode(normalized_param_string));
			  

  signature_base_string = ds_buf(&ds);
  if (debug_oauth)
	fprintf(stderr, "signature_base_string=\"%s\"\n", signature_base_string);

  if (sig_method == OAUTH_HMAC_SHA1) {
	key = ds_xprintf("%s&%s",
					 oauth_encode(client_shared_secret),
					 oauth_encode(token_shared_secret));
	outp = crypto_hmac("SHA1", (unsigned char *) key, strlen(key),
					   ds_ucbuf(&ds), ds_len(&ds) - 1,
					   NULL, &outp_len);
	mime_encode_base64(outp, outp_len, &enc);
	if (debug_oauth)
	  fprintf(stderr, "HMAC-SHA1(\"%s\", \"%s\") = \"%s\"\n",
			  key, signature_base_string, enc);
  }
  else if (sig_method == OAUTH_RSA_SHA1) {
	Ds *sig;
	RSA *priv_key;

	priv_key = pem_load_private_key(private_key_file, NULL);
	if (priv_key == NULL)
	  return(NULL);
	if ((sig = crypto_sign_buf(&ds, priv_key)) == NULL)
	  return(NULL);
	mime_encode_base64(ds_ucbuf(sig), ds_len(sig), &enc);
	if (debug_oauth)
	  fprintf(stderr, "RSA-SHA1(\"%s\", \"%s\") = \"%s\"\n",
			  private_key_file, signature_base_string, enc);
  }
  else {
	enc = ds_xprintf("%s&%s",
					 oauth_encode(client_shared_secret),
					 oauth_encode(token_shared_secret));
	if (debug_oauth)
	  fprintf(stderr, "PLAINTEXT(\"%s\") = \"%s\"\n",
			  signature_base_string, enc);
  }

  return(enc);
}

/*
 * Create a query argument, without encoding.
 */
Uri_query_arg *
uri_make_query_arg(char *name, char *value)
{
  Uri_query_arg *arg;

  arg = ALLOC(Uri_query_arg);
  arg->name = name;
  arg->value = value;

  return(arg);
}

/*
 * Create and add a query argument element, without encoding anything.
 */
Uri_query_arg *
uri_add_query_arg(Dsvec *dsv, char *name, char *value)
{
  Uri_query_arg *arg;

  arg = uri_make_query_arg(name, value);
  dsvec_add_ptr(dsv, arg);

  return(arg);
}

/*
 * Also see:
 * http://oauth.googlecode.com/svn/code/javascript/example/signature.html
 */
static int
do_oauth_test(void)
{
  int i, testnum;
  unsigned int outp_len;
  char *client_shared_secret, *enc, *signature, *token_shared_secret;
  char *oauth_signature;
  unsigned char *outp;
  Dsvec *args, *normalized_args;
  Uri *uri;
  Uri_query_arg *arg;

  testnum = 0;

  /*
   * Tests from: http://wiki.oauth.net/TestCases
   */
  /* Test S3.1 and 3.4.1.1 */
  fprintf(stderr, "Test %d (HMAC-SHA1)... ", ++testnum);
  outp = crypto_hmac("SHA1", (unsigned char *) "cs&", 3,
					 (unsigned char *) "bs", 2, NULL, &outp_len);
  mime_encode_base64(outp, outp_len, &enc);
  if (!streq(enc, "egQqG5AJep5sJ7anhXju1unge2I=")) {
	fprintf(stderr, "failed\n");
	return(-1);
  }
  fprintf(stderr, "ok\n");

  fprintf(stderr, "Test %d (HMAC-SHA1)... ", ++testnum);
  outp = crypto_hmac("SHA1", (unsigned char *) "cs&ts", 5,
					 (unsigned char *) "bs", 2, NULL, &outp_len);
  mime_encode_base64(outp, outp_len, &enc);
  if (!streq(enc, "VZVjXceV7JgPq/dOTnNmEfO0Fv8=")) {
	fprintf(stderr, "failed\n");
	return(-1);
  }
  fprintf(stderr, "ok\n");

  fprintf(stderr, "Test %d (vacation.jpg)... ", ++testnum);
  client_shared_secret = "kd94hf93k423kf44";
  token_shared_secret = "pfkkdhi9sl3r4s00";
  uri = uri_parse("http://photos.example.net/photos");
  uri_add_query_arg(uri->query_args, "file", "vacation.jpg");
  uri_add_query_arg(uri->query_args, "size", "original");

  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  uri_add_query_arg(args, "oauth_consumer_key", "dpf43f3p2l4k3l03");
  uri_add_query_arg(args, "oauth_token", "nnch734d00sl2jdk");
  uri_add_query_arg(args, "oauth_signature_method", "HMAC-SHA1");
  uri_add_query_arg(args, "oauth_timestamp", "1191242096");
  uri_add_query_arg(args, "oauth_nonce", "kllo9940pd9333jh");
  uri_add_query_arg(args, "oauth_version", "1.0");

  normalized_args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  signature = crypto_oauth_signature("GET", uri, args,
									 NULL,
									 normalized_args, OAUTH_HMAC_SHA1,
									 client_shared_secret, token_shared_secret,
									 NULL, NULL);
  if (debug_oauth) {
	fprintf(stderr, "Authorization: OAuth\n");
	for (i = 0; i < dsvec_len(normalized_args); i++) {
	  arg = dsvec_ptr_index(normalized_args, i);
	  fprintf(stderr, "  %s=\"%s\",\n", arg->name, arg->value);
	}
  }
  if (!streq(signature, "tR3+Ty81lMeYAr/Fid0kMTYa/WM=")) {
	fprintf(stderr, "failed\n");
	fprintf(stderr, "client_shared_secret=\"%s\", token_shared_secret=\"%s\"\n",
			client_shared_secret, token_shared_secret);
	return(-1);
  }
  fprintf(stderr, "ok\n");

  fprintf(stderr, "Test %d (vacaction.jpg)... ", ++testnum);
  client_shared_secret = "";
  token_shared_secret = "";
  uri = uri_parse("http://photos.example.net/photos");
  uri_add_query_arg(uri->query_args, "file", "vacaction.jpg");
  uri_add_query_arg(uri->query_args, "size", "original");

  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  uri_add_query_arg(args, "oauth_consumer_key", "dpf43f3p2l4k3l03");
  uri_add_query_arg(args, "oauth_signature_method", "RSA-SHA1");
  uri_add_query_arg(args, "oauth_timestamp", "1196666512");
  uri_add_query_arg(args, "oauth_nonce", "13917289812797014437");
  uri_add_query_arg(args, "oauth_version", "1.0");

  normalized_args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  signature = crypto_oauth_signature("GET", uri, args,
									 NULL,
									 normalized_args, OAUTH_RSA_SHA1,
									 client_shared_secret, token_shared_secret,
									 "tests/oauth_test_private_key.pem", NULL);
  if (debug_oauth) {
	fprintf(stderr, "Authorization: OAuth\n");
	for (i = 0; i < dsvec_len(normalized_args); i++) {
	  arg = dsvec_ptr_index(normalized_args, i);
	  fprintf(stderr, "  %s=\"%s\",\n", arg->name, arg->value);
	}
  }
  if (!streq(signature, "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=")) {
	fprintf(stderr, "failed\n");
	fprintf(stderr, "client_shared_secret=\"%s\", token_shared_secret=\"%s\"\n",
			client_shared_secret, token_shared_secret);
	return(-1);
  }
  fprintf(stderr, "ok\n");

  /* RFC 5849 test - S2.1 */
  fprintf(stderr, "Test %d (S2.1)... ", ++testnum);
  client_shared_secret = "ja893SD9";
  token_shared_secret = "";

  uri = uri_parse("https://server.example.com/request_temp_credentials");

  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  uri_add_query_arg(args, "oauth_consumer_key", "jd83jd92dhsh93js");
  uri_add_query_arg(args, "oauth_signature_method", "PLAINTEXT");
  uri_add_query_arg(args, "oauth_callback", "http://client.example.net/cb?x=1");

  normalized_args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  signature = crypto_oauth_signature("POST", uri, args,
									 NULL,
									 normalized_args, OAUTH_PLAINTEXT,
									 client_shared_secret, token_shared_secret,
									 NULL, NULL);
  oauth_signature = oauth_encode(signature);
  if (debug_oauth) {
	fprintf(stderr, "Authorization: OAuth\n");
	for (i = 0; i < dsvec_len(normalized_args); i++) {
	  arg = dsvec_ptr_index(normalized_args, i);
	  fprintf(stderr, "  %s=\"%s\",\n", arg->name, arg->value);
	}
	fprintf(stderr, "  oauth_signature=\"%s\"\n", oauth_signature);
  }
  if (!streq(oauth_signature, "ja893SD9%26")) {
	fprintf(stderr, "failed\n");
	fprintf(stderr, "client_shared_secret=\"%s\", token_shared_secret=\"%s\"\n",
			client_shared_secret, token_shared_secret);
	return(-1);
  }
  fprintf(stderr, "ok\n");

  /* RFC 5849 test - S2.3 */
  fprintf(stderr, "Test %d (S2.3)... ", ++testnum);
  client_shared_secret = "ja893SD9";
  token_shared_secret = "xyz4992k83j47x0b";

  uri = uri_parse("https://server.example.com/request_token");

  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  uri_add_query_arg(args, "oauth_consumer_key", "jd83jd92dhsh93js");
  uri_add_query_arg(args, "oauth_token", "hdk48Djdsa");
  uri_add_query_arg(args, "oauth_signature_method", "PLAINTEXT");
  uri_add_query_arg(args, "oauth_verifier", "473f82d3");

  normalized_args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  signature = crypto_oauth_signature("POST", uri, args,
									 NULL,
									 normalized_args, OAUTH_PLAINTEXT,
									 client_shared_secret, token_shared_secret,
									 NULL, NULL);
  oauth_signature = oauth_encode(signature);
  if (debug_oauth) {
	fprintf(stderr, "Authorization: OAuth\n");
	for (i = 0; i < dsvec_len(normalized_args); i++) {
	  arg = dsvec_ptr_index(normalized_args, i);
	  fprintf(stderr, "  %s=\"%s\",\n", arg->name, arg->value);
	}
	fprintf(stderr, "  oauth_signature=\"%s\"\n", oauth_signature);
  }
  if (!streq(oauth_signature, "ja893SD9%26xyz4992k83j47x0b")) {
	fprintf(stderr, "failed\n");
	fprintf(stderr, "client_shared_secret=\"%s\", token_shared_secret=\"%s\"\n",
			client_shared_secret, token_shared_secret);
	return(-1);
  }
  fprintf(stderr, "ok\n");

  /* RFC 5849 test - S3.1 and 3.4.1.1 */
  fprintf(stderr, "Test %d (S3.1/3.4.1.1)... ", ++testnum);
  client_shared_secret = "j49sk3j29djd";
  token_shared_secret = "dh893hdasih9";

  uri = uri_parse("http://example.com/request");
  uri_add_query_arg(uri->query_args, "b5", "=%3D");
  uri_add_query_arg(uri->query_args, "a3", "a");
  uri_add_query_arg(uri->query_args, "c@", "");
  uri_add_query_arg(uri->query_args, "a2", "r b");

  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  uri_add_query_arg(args, "oauth_consumer_key", "9djdj82h48djs9d2");
  uri_add_query_arg(args, "oauth_token", "kkk9d7dh3k39sjv7");
  uri_add_query_arg(args, "oauth_signature_method", "HMAC-SHA1");
  uri_add_query_arg(args, "oauth_timestamp", "137131201");
  uri_add_query_arg(args, "oauth_nonce", "7d8f3e4a");

  normalized_args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  signature = crypto_oauth_signature("POST", uri, args,
									 "c2&a3=2 q",
									 normalized_args, OAUTH_HMAC_SHA1,
									 client_shared_secret, token_shared_secret,
									 NULL, NULL);
  oauth_signature = oauth_encode(signature);
  if (debug_oauth) {
	fprintf(stderr, "Authorization: OAuth\n");
	for (i = 0; i < dsvec_len(normalized_args); i++) {
	  arg = dsvec_ptr_index(normalized_args, i);
	  fprintf(stderr, "  %s=\"%s\",\n", arg->name, arg->value);
	}
	fprintf(stderr, "  oauth_signature=\"%s\"\n", oauth_signature);
  }
  if (!streq(oauth_signature, "r6%2FTJjbCOr97%2F%2BUU0NsvSne7s5g%3D")) {
	fprintf(stderr, "failed\n");
	fprintf(stderr, "client_shared_secret=\"%s\", token_shared_secret=\"%s\"\n",
			client_shared_secret, token_shared_secret);
	return(-1);
  }
  fprintf(stderr, "ok\n");

  /* RFC 5849 test - S1.2 */
  fprintf(stderr, "Test %d (S1.2-1)... ", ++testnum);
  client_shared_secret = "kd94hf93k423kf44";
  token_shared_secret = "";

  uri = uri_parse("https://photos.example.net/initiate");
  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  uri_add_query_arg(args, "oauth_consumer_key", "dpf43f3p2l4k3l03");
  uri_add_query_arg(args, "oauth_signature_method", "HMAC-SHA1");
  uri_add_query_arg(args, "oauth_timestamp", "137131200");
  uri_add_query_arg(args, "oauth_nonce", "wIjqoS");
  uri_add_query_arg(args, "oauth_callback", "http://printer.example.com/ready");

  normalized_args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  signature = crypto_oauth_signature("post", uri, args, NULL,
									 normalized_args, OAUTH_HMAC_SHA1,
									 client_shared_secret, token_shared_secret,
									 NULL, NULL);
  oauth_signature = oauth_encode(signature);
  if (debug_oauth) {
	fprintf(stderr, "Authorization: OAuth\n");
	for (i = 0; i < dsvec_len(normalized_args); i++) {
	  arg = dsvec_ptr_index(normalized_args, i);
	  fprintf(stderr, "  %s=\"%s\",\n", arg->name, arg->value);
	}
	fprintf(stderr, "  oauth_signature=\"%s\"\n", oauth_signature);
  }
  if (!streq(oauth_signature, "74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D")) {
	fprintf(stderr, "failed\n");
	return(-1);
  }
  fprintf(stderr, "ok\n");

  /* RFC 5849 test -  S1.2 */
  fprintf(stderr, "Test %d (S1.2-2)... ", ++testnum);
  client_shared_secret = "kd94hf93k423kf44";
  token_shared_secret = "hdhd0244k9j7ao03";
  uri = uri_parse("https://photos.example.net/token");
  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  uri_add_query_arg(args, "oauth_consumer_key", "dpf43f3p2l4k3l03");
  uri_add_query_arg(args, "oauth_token", "hh5s93j4hdidpola");
  uri_add_query_arg(args, "oauth_signature_method", "HMAC-SHA1");
  uri_add_query_arg(args, "oauth_timestamp", "137131201");
  uri_add_query_arg(args, "oauth_nonce", "walatlh");
  uri_add_query_arg(args, "oauth_verifier", "hfdp7dh39dks9884");

  normalized_args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  signature = crypto_oauth_signature("post", uri, args, NULL,
									 normalized_args, OAUTH_HMAC_SHA1,
									 client_shared_secret, token_shared_secret,
									 NULL, NULL);
  oauth_signature = oauth_encode(signature);
  if (debug_oauth) {
	fprintf(stderr, "Authorization: OAuth\n");
	for (i = 0; i < dsvec_len(normalized_args); i++) {
	  arg = dsvec_ptr_index(normalized_args, i);
	  fprintf(stderr, "  %s=\"%s\",\n", arg->name, arg->value);
	}
	fprintf(stderr, "  oauth_signature=\"%s\"\n", oauth_signature);
  }
  if (!streq(oauth_signature, "gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D")) {
	fprintf(stderr, "failed\n");
	fprintf(stderr, "client_shared_secret=\"%s\", token_shared_secret=\"%s\"\n",
			client_shared_secret, token_shared_secret);
	return(-1);
  }
  fprintf(stderr, "ok\n");

  /* RFC 5849 test -  S1.2 */
  fprintf(stderr, "Test %d (S1.2-3)... ", ++testnum);
  client_shared_secret = "kd94hf93k423kf44";
  token_shared_secret = "pfkkdhi9sl3r4s00";
  uri = uri_parse("http://photos.example.net/photos");
  uri_add_query_arg(uri->query_args, "file", "vacation.jpg");
  uri_add_query_arg(uri->query_args, "size", "original");
  args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  uri_add_query_arg(args, "oauth_consumer_key", "dpf43f3p2l4k3l03");
  uri_add_query_arg(args, "oauth_token", "nnch734d00sl2jdk");
  uri_add_query_arg(args, "oauth_signature_method", "HMAC-SHA1");
  uri_add_query_arg(args, "oauth_timestamp", "137131202");
  uri_add_query_arg(args, "oauth_nonce", "chapoH");

  normalized_args = dsvec_init(NULL, sizeof(Uri_query_arg *));
  signature = crypto_oauth_signature("get", uri, args, NULL,
									 normalized_args, OAUTH_HMAC_SHA1,
									 client_shared_secret, token_shared_secret,
									 NULL, NULL);
  oauth_signature = oauth_encode(signature);
  if (debug_oauth) {
	fprintf(stderr, "Authorization: OAuth\n");
	for (i = 0; i < dsvec_len(normalized_args); i++) {
	  arg = dsvec_ptr_index(normalized_args, i);
	  fprintf(stderr, "  %s=\"%s\",\n", arg->name, arg->value);
	}
	fprintf(stderr, "  oauth_signature=\"%s\"\n", oauth_signature);
  }
  if (!streq(oauth_signature, "MdpQcU8iPSUjWoN%2FUDMsK2sui9I%3D")) {
	fprintf(stderr, "failed\n");
	fprintf(stderr, "client_shared_secret=\"%s\", token_shared_secret=\"%s\"\n",
			client_shared_secret, token_shared_secret);
	return(-1);
  }
  fprintf(stderr, "ok\n");

  return(0);
}

char *
dacs_approval_create(char *uri, char *method, char *ident, char *dn,
					 EVP_PKEY *private_key)
{
  char *approval, *astr, *astr_enc, *signature, *stamp;
  unsigned int s_astr_len;
  unsigned char *s_astr;

  if (dn == NULL)
	dn = ACS_DEFAULT_APPROVAL_DIGEST_NAME;

  if ((stamp = ustamp_clock(dacs_current_jurisdiction(), NULL)) == NULL)
	return(NULL);

  astr
	= ds_xprintf("j=\"%s\"\nh=\"%s\"\ns=\"%s\"\nu=\"%s\"\nm=\"%s\"\ni=\"%s\"",
				 dacs_current_jurisdiction(),
				 dn, strtolower(stamp), uri, strtolower(method),
				 (ident == NULL) ? "unauth" : ident);
  if ((s_astr = crypto_sign(dn, (unsigned char *) astr, strlen(astr), NULL,
							&s_astr_len, private_key)) == NULL)
	return(NULL);

  strba64(s_astr, s_astr_len, &signature);
  strba64((unsigned char *) astr, strlen(astr) + 1, &astr_enc);
  log_msg((LOG_TRACE_LEVEL | LOG_SENSITIVE_FLAG,
		   "Approval signature(%s)=%s", astr, signature));

  approval = ds_xprintf("a=\"%s\", s=\"%s\"", astr_enc, signature);
  log_msg((LOG_TRACE_LEVEL, "Approval: %s", approval));

  return(approval);
}

#ifdef PROG

#include "dacs.h"

#define CRYPTO_TEST_CIPHER_NAME		"AES-256-CFB"

typedef struct Hmac_test {
  int size;
  int count;
  unsigned int key_len;
  unsigned int hmac_len;
  char *key_str;
  char *msg_str;
  char *hmac_str;
} Hmac_test;

static int
do_hmac_test(Hmac_test *test)
{
  char *outp_str;
  unsigned int key_len, msg_len;
  unsigned char *key, *msg, *outp;
  Digest_tab *dt;
  Hmac_handle *hmac;

  if ((key = strhextob(test->key_str, &key_len)) == NULL) {
	fprintf(stderr, "Invalid Key hex string\n");
	return(-1);
  }

  if (key_len != test->key_len) {
	fprintf(stderr, "Key length mismatch\n");
	return(-1);
  }

  if ((msg = strhextob(test->msg_str, &msg_len)) == NULL) {
	fprintf(stderr, "Invalid Msg hex string\n");
	return(-1);
  }
  
  if ((dt = crypto_lookup_hmac_digest_by_size(test->size)) == NULL) {
	fprintf(stderr, "HMAC lookup failed for size %d\n", test->size);
	return(-1);
  }

  if ((hmac = crypto_hmac_open(dt->name, key, key_len)) == NULL)
	return(-1);

  crypto_hmac_hash(hmac, msg, msg_len);

  outp = crypto_hmac_close(hmac, NULL, NULL);
  outp_str = strbtohex(outp, test->hmac_len, 0);
  if (!strneq(outp_str, test->hmac_str, test->hmac_len)) {
	fprintf(stderr, "HMAC comparison failed for size %d\n", test->size);
	fprintf(stderr, "Want: %s\n", test->hmac_str);
	fprintf(stderr, "Got:  %s\n", outp_str);
	return(-1);
  }

  return(0);
}

static int
do_hmac_tests(FILE *fp)
{
  int in_test, n;
  char *p;
  Ds *ds;
  Hmac_test test;

  ds = ds_init(NULL);
  ds->delnl_flag = 1;

  test.size = 0;
  test.count = 0;
  test.key_len = 0;
  test.hmac_len = 0;
  test.key_str = NULL;
  test.msg_str = NULL;
  test.hmac_str = NULL;

  in_test = 0;
  n = 0;
  while (1) {
	if (ds_gets(ds, fp) == NULL)
	  break;
	p = ds_buf(ds);

	if (*p == '#')
	  continue;

	if (*p == '\0') {
	  if (in_test) {
		if (do_hmac_test(&test) == -1) {
		  fprintf(stderr, "HMAC test failed!\n");
		  fprintf(stderr, "L     = %d\n", test.size);
		  fprintf(stderr, "Count = %d\n", test.count);
		  fprintf(stderr, "Klen  = %d\n", test.key_len);
		  fprintf(stderr, "Tlen  = %d\n", test.hmac_len);
		  return(-1);
		}
		fprintf(stderr, ".");
		n++;
		test.count = 0;
		test.key_len = test.hmac_len = 0;
		test.key_str = test.msg_str = test.hmac_str = NULL;
		in_test = 0;
	  }
	  continue;
	}

	if (strprefix(p, "[L=") != NULL) {
	  test.size = atoi(p + 3);
	  fprintf(stderr, "%s[L=%d] ", n ? "\n" : "", test.size);
	  continue;
	}

	in_test = 1;
	if (strprefix(p, "Count = ") != NULL)
	  test.count = atoi(p + 8);
	else if (strprefix(p, "Klen = ") != NULL)
	  test.key_len = atoi(p + 7);
	else if (strprefix(p, "Tlen = ") != NULL)
	  test.hmac_len = atoi(p + 7);
	else if (strprefix(p, "Key = ") != NULL)
	  test.key_str = strdup(p + 6);
	else if (strprefix(p, "Msg = ") != NULL)
	  test.msg_str = strdup(p + 6);
	else if (strprefix(p, "Mac = ") != NULL)
	  test.hmac_str = strdup(p + 6);
	else {
	  fprintf(stderr, "Parse error: %s\n", p);
	  return(-1);
	}
  }

  fprintf(stderr, "\n");
  return(0);
}

/*
 * HMAC test vectors from:
 *   RFC 4231
 *   http://wolfgang-ehrhardt.de/hmac-sha3-testvectors.html (for SHA-3)
 */
typedef struct RFC4231_hmac_test_data {
  char *key_str;
  char *msg_str;
  struct {
	char *digest_name;
	char *hmac_str;
	int match_prefix;
  } t[9];
} RFC4231_hmac_test_data;

static RFC4231_hmac_test_data rfc4231_hmac_test_data[] = {
  { /* 4.2. Test Case 1 */
	"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
	"4869205468657265",
	{
	  { "SHA224",
		"896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22", 0
	  },
	  { "SHA256",
		"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", 0
	  },
	  { "SHA384",
		"afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6", 0
	  },
	  { "SHA512",
		"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854", 0
	  },
	  { "SHA3-224",
		"3b16546bbc7be2706a031dcafd56373d9884367641d8c59af3c860f7", 0
	  },
	  { "SHA3-256",
		"ba85192310dffa96e2a3a40e69774351140bb7185e1202cdcc917589f95e16bb", 0
	  },
	  { "SHA3-384",
		"68d2dcf7fd4ddd0a2240c8a437305f61fb7334cfb5d0226e1bc27dc10a2e723a20d370b47743130e26ac7e3d532886bd", 0
	  },
	  { "SHA3-512",
		"eb3fbd4b2eaab8f5c504bd3a41465aacec15770a7cabac531e482f860b5ec7ba47ccb2c6f2afce8f88d22b6dc61380f23a668fd3888bb80537c0a0b86407689e", 0
	  },
	  { NULL, NULL, 0 }
	}
  },

  { /* 4.3. Test Case 2 */
	"4a656665",
	"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
	{
	  { "SHA224",
		"a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44", 0
	  },
	  { "SHA256",
		"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", 0
	  },
	  { "SHA384",
		"af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649", 0
	  },
	  { "SHA512",
		"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737", 0
	  },
	  { "SHA3-224",
		"7fdb8dd88bd2f60d1b798634ad386811c2cfc85bfaf5d52bbace5e66", 0
	  },
	  { "SHA3-256",
		"c7d4072e788877ae3596bbb0da73b887c9171f93095b294ae857fbe2645e1ba5", 0
	  },
	  { "SHA3-384",
		"f1101f8cbf9766fd6764d2ed61903f21ca9b18f57cf3e1a23ca13508a93243ce48c045dc007f26a21b3f5e0e9df4c20a", 0
	  },
	  { "SHA3-512",
		"5a4bfeab6166427c7a3647b747292b8384537cdb89afb3bf5665e4c5e709350b287baec921fd7ca0ee7a0c31d022a95e1fc92ba9d77df883960275beb4e62024", 0
	  },
	  { NULL, NULL, 0 }
	}
  },
	  
  { /* 4.4. Test Case 3 */
	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
	{
	  { "SHA224",
		"7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea", 0
	  },
	  { "SHA256",
		"773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", 0
	  },
	  { "SHA384",
		"88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27", 0
	  },
	  { "SHA512",
		"fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb", 0
	  },
	  { "SHA3-224",
		"676cfc7d16153638780390692be142d2df7ce924b909c0c08dbfdc1a", 0
	  },
	  { "SHA3-256",
		"84ec79124a27107865cedd8bd82da9965e5ed8c37b0ac98005a7f39ed58a4207", 0
	  },
	  { "SHA3-384",
		"275cd0e661bb8b151c64d288f1f782fb91a8abd56858d72babb2d476f0458373b41b6ab5bf174bec422e53fc3135ac6e", 0
	  },
	  { "SHA3-512",
		"309e99f9ec075ec6c6d475eda1180687fcf1531195802a99b5677449a8625182851cb332afb6a89c411325fbcbcd42afcb7b6e5aab7ea42c660f97fd8584bf03", 0
	  },
	  { NULL, NULL, 0 }
	}
  },

  { /* 4.5. Test Case 4 */
	"0102030405060708090a0b0c0d0e0f10111213141516171819",
	"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
	{
	  { "SHA224",
		"6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a", 0
	  },
	  { "SHA256",
		"82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b", 0
	  },
	  { "SHA384",
		"3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb", 0
	  },
	  { "SHA512",
		"b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd", 0
	  },
	  { "SHA3-224",
		"a9d7685a19c4e0dbd9df2556cc8a7d2a7733b67625ce594c78270eeb", 0
	  },
	  { "SHA3-256",
		"57366a45e2305321a4bc5aa5fe2ef8a921f6af8273d7fe7be6cfedb3f0aea6d7", 0
	  },
	  { "SHA3-384",
		"3a5d7a879702c086bc96d1dd8aa15d9c46446b95521311c606fdc4e308f4b984da2d0f9449b3ba8425ec7fb8c31bc136", 0
	  },
	  { "SHA3-512",
		"b27eab1d6e8d87461c29f7f5739dd58e98aa35f8e823ad38c5492a2088fa0281993bbfff9a0e9c6bf121ae9ec9bb09d84a5ebac817182ea974673fb133ca0d1d", 0
	  },
	  { NULL, NULL, 0 }
	}
  },

  { /* 4.6. Test Case 5 */
	"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
	"546573742057697468205472756e636174696f6e",
	{
	  { "SHA224",
		"0e2aea68a90c8d37c988bcdb9fca6fa8", 1
	  },
	  { "SHA256",
		"a3b6167473100ee06e0c796c2955552b", 1
	  },
	  { "SHA384",
		"3abf34c3503b2a23a46efc619baef897", 1
	  },
	  { "SHA512",
		"415fad6271580a531d4179bc891d87a6", 1
	  },
	  { "SHA3-224",
		"49fdd3abd005ebb8ae63fea946d1883c", 1
	  },
	  { "SHA3-256",
		"6e02c64537fb118057abb7fb66a23b3c", 1
	  },
	  { "SHA3-384",
		"47c51ace1ffacffd7494724682615783", 1
	  },
	  { "SHA3-512",
		"0fa7475948f43f48ca0516671e18978c", 1
	  },
	  { NULL, NULL, 0 }
	}
  },

  { /* 4.7. Test Case 6 */
	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b6579202d2048617368204b6579204669727374",
	{
	  { "SHA224",
		"95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e", 0
	  },
	  { "SHA256",
		"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54", 0
	  },
	  { "SHA384",
		"4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952", 0
	  },
	  { "SHA512",
		"80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598", 0
	  },
	  { "SHA3-224",
		"b4a1f04c00287a9b7f6075b313d279b833bc8f75124352d05fb9995f", 0
	  },
	  { "SHA3-256",
		"ed73a374b96c005235f948032f09674a58c0ce555cfc1f223b02356560312c3b", 0
	  },
	  { "SHA3-384",
		"0fc19513bf6bd878037016706a0e57bc528139836b9a42c3d419e498e0e1fb9616fd669138d33a1105e07c72b6953bcc", 0
	  },
	  { "SHA3-512",
		"00f751a9e50695b090ed6911a4b65524951cdc15a73a5d58bb55215ea2cd839ac79d2b44a39bafab27e83fde9e11f6340b11d991b1b91bf2eee7fc872426c3a4", 0
	  },
	  { NULL, NULL, 0 }
	}
  },

  { /* 4.7. Test Case 6a */
	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b6579202d2048617368204b6579204669727374",
	{
	  { "SHA3-224",
		"b96d730c148c2daad8649d83defaa3719738d34775397b7571c38515", 0
	  },
	  { "SHA3-256",
		"a6072f86de52b38bb349fe84cd6d97fb6a37c4c0f62aae93981193a7229d3467", 0
	  },
	  { "SHA3-384",
		"713dff0302c85086ec5ad0768dd65a13ddd79068d8d4c6212b712e41649449111480230044185a99103ed82004ddbfcc", 0
	  },
	  { "SHA3-512",
		"b14835c819a290efb010ace6d8568dc6b84de60bc49b004c3b13eda763589451e5dd74292884d1bdce64e6b919dd61dc9c56a282a81c0bd14f1f365b49b83a5b", 0
	  },
	  { NULL, NULL, 0 }
	}
  },

  { /* 4.8. Test Case 7 */
	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"5468697320697320612074657374207573696e672061206c6172676572207468616e20626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520686173686564206265666f7265206265696e6720757365642062792074686520484d414320616c676f726974686d2e",
	{
	  { "SHA224",
		"3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1", 0
	  },
	  { "SHA256",
		"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2", 0
	  },
	  { "SHA384",
		"6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e", 0
	  },
	  { "SHA512",
		"e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58", 0
	  },
	  { "SHA3-224",
		"05d8cd6d00faea8d1eb68ade28730bbd3cbab6929f0a086b29cd62a0", 0
	  },
	  { "SHA3-256",
		"65c5b06d4c3de32a7aef8763261e49adb6e2293ec8e7c61e8de61701fc63e123", 0
	  },
	  { "SHA3-384",
		"026fdf6b50741e373899c9f7d5406d4eb09fc6665636fc1a530029ddf5cf3ca5a900edce01f5f61e2f408cdf2fd3e7e8", 0
	  },
	  { "SHA3-512",
		"38a456a004bd10d32c9ab8336684112862c3db61adcca31829355eaf46fd5c73d06a1f0d13fec9a652fb3811b577b1b1d1b9789f97ae5b83c6f44dfcf1d67eba", 0
	  },
	  { NULL, NULL, 0 }
	}
  },

  { /* 4.8. Test Case 7a */
	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"5468697320697320612074657374207573696e672061206c6172676572207468616e20626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520686173686564206265666f7265206265696e6720757365642062792074686520484d414320616c676f726974686d2e",
	{
	  { "SHA3-224",
		"c79c9b093424e588a9878bbcb089e018270096e9b4b1a9e8220c866a", 0
	  },
	  { "SHA3-256",
		"e6a36d9b915f86a093cac7d110e9e04cf1d6100d30475509c2475f571b758b5a", 0
	  },
	  { "SHA3-384",
		"cad18a8ff6c4cc3ad487b95f9769e9b61c062aefd6952569e6e6421897054cfc70b5fdc6605c18457112fc6aaad45585", 0
	  },
	  { "SHA3-512",
		"dc030ee7887034f32cf402df34622f311f3e6cf04860c6bbd7fa488674782b4659fdbdf3fd877852885cfe6e22185fe7b2ee952043629bc9d5f3298a41d02c66", 0
	  },
	  { NULL, NULL, 0 }
	}
  },

  { NULL, NULL, { { NULL, NULL, 0 } } }
};

#ifdef NOTDEF

#endif

/*
 * http://www.rfc-editor.org/rfc/rfc4231.txt
 */
static int
rfc4231_hmac_test(void)
{
  int count, i, matched;
  char *outp_str;
  unsigned int key_len, hmac_len, msg_len;
  unsigned char *key, *msg, *outp;
  Digest_tab *dt;
  Hmac_handle *hmac;
  RFC4231_hmac_test_data *data;

  fprintf(stderr, "\nDoing RFC 4231 HMAC (SHA-2 and SHA-3) test vectors:\n");
  fprintf(stderr, "(see http://www.rfc-editor.org/rfc/rfc4231.txt)\n");

  count = 0;
  for (data = &rfc4231_hmac_test_data[0]; data->key_str != NULL; data++) {
	if ((key = strhextob(data->key_str, &key_len)) == NULL) {
	  fprintf(stderr, "Invalid Key hex string\n");
	  exit(1);
	}

	if ((msg = strhextob(data->msg_str, &msg_len)) == NULL) {
	  fprintf(stderr, "Invalid Msg hex string\n");
	  exit(1);
	}

	for (i = 0; data->t[i].digest_name != NULL; i++) {
	  if ((dt = crypto_lookup_hmac_digest_by_name(data->t[i].digest_name))
		  == NULL) {
		fprintf(stderr, "HMAC lookup failed for %s\n", data->t[i].digest_name);
		exit(1);
	  }

	  if ((hmac = crypto_hmac_open(dt->name, key, key_len)) == NULL)
		exit(1);
	  crypto_hmac_hash(hmac, msg, msg_len);
	  outp = crypto_hmac_close(hmac, NULL, &hmac_len);
	  outp_str = strbtohex(outp, hmac_len, 0);
	  if (data->t[i].match_prefix)
		matched = (strprefix(outp_str, data->t[i].hmac_str)) != NULL;
	  else
		matched = streq(outp_str, data->t[i].hmac_str);

	  if (!matched) {
		fprintf(stderr, "\nHMAC comparison failed for %s (test %d)\n",
				data->t[i].digest_name, count + 1);
		fprintf(stderr, "Want: %s\n", data->t[i].hmac_str);
		fprintf(stderr, "Got:  %s\n", outp_str);
		exit(1);
	  }
	  count++;
	  fprintf(stderr, ".");
	}
  }

  fprintf(stderr, "\nAll HMAC test vectors succeeded (%d tests)\n\n", count);
  return(0);
}

/*
 * See: http://csrc.nist.gov/groups/STM/cavp/documents/mac/hmactestvectors.zip
 */
static int
nist_hmac_test(char *filename)
{
  FILE *fp;

  if ((fp = fopen(filename, "r")) == NULL) {
	fprintf(stderr, "Cannot open \"%s\"\n", filename);
	return(0);
  }

  fprintf(stderr, "\nDoing NIST HMAC test vector tests:\n");
  fprintf(stderr,
		  "(see http://csrc.nist.gov/groups/STM/cavp/documents/mac/hmactestvectors.zip)\n");

  if (do_hmac_tests(fp) == -1)
	exit(1);

  fclose(fp);
  fprintf(stderr, "All HMAC test vectors succeeded\n");
  return(0);
}

static Crypt_keys *
get_keys(char *filename)
{
  char *buf;
  Crypt_keys *ck;

  if (load_file("tests/test_keys", &buf, NULL) == -1)
	return(NULL);

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

  free(buf);

  return(ck);
}

static int
write_privatekey(Crypt_keys *ck, char *filename, const EVP_CIPHER *enc,
				 unsigned char *kstr, int klen, pem_password_cb callback,
				 void *cb_arg)
{
  FILE *fp;
  RSA *rrr;

  if ((fp = fopen(filename, "w")) == NULL) {
	log_err((LOG_ERROR_LEVEL, "write_privatekey: \"%s\"", filename));
	return(-1);
  }

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  rrr = EVP_PKEY_get1_RSA(ck->private_key);
#else
  rrr = ck->private_key->pkey.rsa;
#endif

  if (PEM_write_RSAPrivateKey(fp, rrr,
							  enc, kstr, klen, callback, cb_arg) == 0) {
	crypto_log_error();
	return(-1);
  }

  fclose(fp);
  return(0);
}

static int
write_publickey(Crypt_keys *ck, char *filename)
{
  FILE *fp;
  RSA *rrr;

  if ((fp = fopen(filename, "w")) == NULL) {
	log_err((LOG_ERROR_LEVEL, "write_publickey: \"%s\"", filename));
	return(-1);
  }

#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
  rrr = EVP_PKEY_get1_RSA(ck->private_key);
#else
  rrr = ck->private_key->pkey.rsa;
#endif

  if (PEM_write_RSAPublicKey(fp, rrr) == 0) {
	crypto_log_error();
	return(-1);
  }

  fclose(fp);
  return(0);
}

typedef struct Pbkdf2_test {
  unsigned char *pwd;
  int pwdlen;
  unsigned char *salt;
  int saltlen;
  char *algname;
  unsigned int count;
  unsigned int dklen;
  char *result;
} Pbkdf2_test;

static unsigned char pbkdf2_salt1[] = {
  0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0x12
};

static unsigned char pbkdf2_g_clef[] = {
  0xf0, 0x9d, 0x84, 0x9e
};

typedef unsigned char *UC;

#undef TEST_LONG_PBKDF2

/*
 * Assembled from RFC 6070, draft-josefsson-scrypt-kdf-03, and various
 * public sources (with many thanks).
 * [ Anyone may freely use or copy these test cases -bjb ]
 *
 * Look ma, I'm 6070 famous!
 */
static Pbkdf2_test pbkdf2_vec[] = {
  /* All "qwerty" tests were validated against libgcrypt-1.7.1 */
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA224", 2, 16,
	"4f40bc10265b3cd7300e57ec3f86b5b6" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA224", 1024, 16,
	"95c5e7185c6b5ccd555e517599505cc1" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA224", 4096, 20,
	"c7237c4ec5762a70e2198d8ec620fef406d3160a" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA224", 2, 32,
	"4f40bc10265b3cd7300e57ec3f86b5b6f3a6716ff00e0113f1babf2ef39b9adc" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA224", 2, 64,
	"4f40bc10265b3cd7300e57ec3f86b5b6f3a6716ff00e0113f1babf2ef39b9adca23d22c1424d7a88a2a85af071563c4612a67ea037a329d779932e7e145230af" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA224", 80000, 16,
	"991a1a5d6ef48f6553b4978ccc2a40f6" },

  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA384", 2, 16,
	"f42af4d9efd3841bfe8facc18673ebb8" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA384", 1024, 16,
	"58f166b880668546f305f7ae745580f8" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA384", 4096, 20,
	"05a67829c8c05afd3a4aa1dface637ff9c07c99d" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA384", 2, 32,
	"f42af4d9efd3841bfe8facc18673ebb8e115c5f3d99b68a1f9bbef0c45d8965d" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA384", 2, 64,
	"f42af4d9efd3841bfe8facc18673ebb8e115c5f3d99b68a1f9bbef0c45d8965dd77d1f9dba2149018b5409e7f1c753e49d73504c81819e7e203ba09ce168d7c3" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA384", 80000, 16,
	"4eb418ef9586bece9e67f408e8aebb6e" },

  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-224", 2, 16,
	"9e12e35ebbaad4164be7cacd78ff5bd0" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-224", 1024, 16,
	"61263c782840277e576ddbdfb18a3e29" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-224", 4096, 20,
	"2688ab3514a285ba51b90544480ec6d1eec79d69" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-224", 2, 32,
	"9e12e35ebbaad4164be7cacd78ff5bd02978f133787645b10753e871434eefcb" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-224", 2, 64,
	"9e12e35ebbaad4164be7cacd78ff5bd02978f133787645b10753e871434eefcbb6f3af4a8a403e0847f3c508fbb0024be70ff84fbbb49d2fb2cc20c3a79ccc33" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-224", 80000, 16,
	"99cf4f2d4a3b375eb9f584a5376a86ca" },

  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-256", 2, 16,
	"564353c2de52e0e899f8a6ba7fd5d8ce" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-256", 1024, 16,
	"b404cb4190415750f3db347dc7303a78" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-256", 4096, 20,
	"f0c8697ca57b75c54bb63adc694f445beebdb202" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-256", 2, 32,
	"564353c2de52e0e899f8a6ba7fd5d8ce6386d0cc42505f68253e259508f54662" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-256", 2, 64,
	"564353c2de52e0e899f8a6ba7fd5d8ce6386d0cc42505f68253e259508f54662f6ca4ba5e05f1306f368d9e785a1d17863c0fc257a4b9e68a2bab12c09cc3f3f" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-256", 80000, 16,
	"6d0946ef9cd06fb478d76268b3675008" },

  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-384", 2, 16,
	"a83ba39f799c071816956f181118239d" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-384", 1024, 16,
	"5295c172bfd2d5009e456a2247d0f3ae" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-384", 4096, 20,
	"7277a0e8e21b552466819c82ffa0df8507327536" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-384", 2, 32,
	"a83ba39f799c071816956f181118239d4f3ddda925fa807ab1adec7cfab34ca2" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-384", 2, 64,
	"a83ba39f799c071816956f181118239d4f3ddda925fa807ab1adec7cfab34ca207619a40bcec08df74e0297ba1a9852b16a006b5f74fceb1e0b9ef78c8a7eb49" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-384", 80000, 16,
	"67ebd132daf5c8916010c5e725128922" },

  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-512", 2, 16,
	"c8b5c439ed3ed0c0a4afa8c39fea2180" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-512", 1024, 16,
	"e7127c11121d52311019b0d8164bbdc4" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-512", 4096, 20,
	"e7e34baffd06bab49dc596e9fbba8ab47bb7ede8" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-512", 2, 32,
	"c8b5c439ed3ed0c0a4afa8c39fea21804a553dacbff2803716d563d923943974" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-512", 2, 64,
	"c8b5c439ed3ed0c0a4afa8c39fea21804a553dacbff2803716d563d9239439745d0ed9c7a932cb1b8792b85d074ef0d5e4ba7b3ba8a41925190ee2e9516c3054" },
  { (UC) "qwerty", -1,
	(unsigned char  *) "01234567", -1, "SHA3-512", 80000, 16,
	"7b8e05da6bfb0218d72f3e6be15bb19b" },

  /* HMAC-SHA1 tests. */
  { (UC) "password", -1,
	(unsigned char *) "ATHENA.MIT.EDUraeburn", -1, "SHA1", 1, 128 / 8,
	"cdedb5281bb2f801565a1122b2563515" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "ATHENA.MIT.EDUraeburn", -1, "SHA1", 1, 256 / 8,
	"cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "ATHENA.MIT.EDUraeburn", -1, "SHA1", 2, 128 / 8,
	"01dbee7f4a9e243e988b62c73cda935d" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "ATHENA.MIT.EDUraeburn", -1, "SHA1", 2, 256 / 8,
	"01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "ATHENA.MIT.EDUraeburn", -1, "SHA1", 1200, 128 / 8,
	"5c08eb61fdf71e4e4ec3cf6ba1f5512b" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "ATHENA.MIT.EDUraeburn", -1, "SHA1", 1200, 256 / 8,
	"5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13" },
  { (unsigned char *) "password", -1,
	pbkdf2_salt1, sizeof(pbkdf2_salt1), "SHA1", 5, 128 / 8,
	"d1daa78615f287e6a1c8b120d7062a49" },
  { (unsigned char *) "password", -1,
	pbkdf2_salt1, sizeof(pbkdf2_salt1), "SHA1", 5, 256 / 8,
	"d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee" },
  { (unsigned char *)
	"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", -1,
	(unsigned char *) "pass phrase equals block size", -1, "SHA1", 1200,  128 / 8,
	"139c30c0966bc32ba55fdbf212530ac9" },
  { (unsigned char *)
	"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", -1,
	(unsigned char *) "pass phrase equals block size", -1, "SHA1", 1200,  256 / 8,
	"139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1" },
  { (unsigned char *)
	"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", -1,
	(unsigned char *) "pass phrase exceeds block size", -1, "SHA1", 1200,  128 / 8,
	"9ccad6d468770cd51b10e6a68721be61" },
  { (unsigned char *)
	"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", -1,
	(unsigned char *) "pass phrase exceeds block size", -1, "SHA1", 1200,  256 / 8,
	"9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a" },
  { pbkdf2_g_clef, sizeof(pbkdf2_g_clef),
	(unsigned char *) "EXAMPLE.COMpianist", -1, "SHA1", 50, 128 / 8,
	"6b9cf26d45455a43a5b8bb276a403b39" },
  { pbkdf2_g_clef, sizeof(pbkdf2_g_clef),
	(unsigned char *) "EXAMPLE.COMpianist", -1, "SHA1", 50, 256 / 8,
	"6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA1", 1, 20,
	"0c60c80f961f0e71f3a9b524af6012062fe037a6" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA1", 2, 20,
	"ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA1", 4096, 20,
	"4b007901b765489abead49d926f721d065a429c1" },
#ifdef TEST_LONG_PBKDF2
  /* Takes much too long... */
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA1", 16777216, 20,
	"eefe3d61cd4da4e4e9945b3d6ba2158c2634e984" },
#endif
  { (unsigned char *) "passwordPASSWORDpassword", -1,
	(unsigned char *) "saltSALTsaltSALTsaltSALTsaltSALTsalt", -1, "SHA1", 4096, 25,
	"3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038" },
  { (unsigned char *) "pass\0word", 9,
	(unsigned char *) "sa\0lt", 5, "SHA1", 4096, 16,
	"56fa6aa75548099dcc37d7f03425e0c3" },

  /* HMAC-SHA256 tests. */
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA256", 1, 20,
	"120fb6cffcf8b32c43e7225256c4f837a86548c9" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA256", 1, 32,
	"120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b" },
  { (unsigned char *) "passwd", -1,
	(unsigned char *) "salt", -1, "SHA256", 1, 64,
	"55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783" },
  { (unsigned char *) "Password", -1,
	(unsigned char *) "NaCl", -1, "SHA256", 80000, 64,
	"4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA256", 2, 20,
	"ae4d0c95af6b46d32d0adff928f06dd02a303f8e" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA256", 2, 32,
	"ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA256", 4096, 20,
	"c5e478d59288c841aa530db6845c4c8d962893a0" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA256", 4096, 32,
	"c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a" },
#ifdef TEST_LONG_PBKDF2
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA256", 16777216, 20,
	"cf81c66fe8cfc04d1f31ecb65dab4089f7f179e8" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA256", 16777216, 32,
	"cf81c66fe8cfc04d1f31ecb65dab4089f7f179e89b3b0bcb17ad10e3ac6eba46" },
#endif
  { (unsigned char *) "passwordPASSWORDpassword", -1,
	(unsigned char *) "saltSALTsaltSALTsaltSALTsaltSALTsalt", -1, "SHA256", 4096, 40,
	"348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9" },
  { (unsigned char *) "pass\0word", 9,
	(unsigned char *) "sa\0lt", 5, "SHA256", 4096, 16,
	"89b69d0516f829893c696226650a8687" },

  /* HMAC-SHA512 tests. */
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA512", 1, 64,
	"867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501b\
ad999bfe943c08f050235d7d68b1da55e63f73b60a57fce" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA512", 2, 64,
	"e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f7\
840edce4fef5a82be67335c77a6068e04112754f27ccf4e" },
  { (unsigned char *) "password", -1,
	(unsigned char *) "salt", -1, "SHA512", 4096, 64,
	"d197b1b33db0143e018b12f3d1d1479e6cdebdcc97c5c0f87f6902e072f457b5143f30602641b3d55\
cd335988cb36b84376060ecd532e039b742a239434af2d5" },
  { (unsigned char *) "passwordPASSWORDpassword", -1,
	(unsigned char *) "saltSALTsaltSALTsaltSALTsaltSALTsalt", -1, "SHA512", 4096, 64,
	"8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b59f9e60cd9532\
fa33e0f75aefe30225c583a186cd82bd4daea9724a3d3b8" },
  { (unsigned char *) "1", -1,
	(unsigned char *) "2", -1, "SHA512", 1024, 64,
	"a180451f4618df9515ab0be2c56ac3420287cb8fc015f78494c9394a62ef6e6657fdd1811ffb8c54e\
b892ea594a7e1f6ed81f27264a8e9b1c6ed639f35bb12e8" },
  { (unsigned char *) "", 0,
	(unsigned char *) "", 0, "SHA512", 1024, 64,
	"fbaa2acb43d1e1afff858358af2843cc578db4e22b94211809c0bf4729d26c2f654a2d6ce6d52bea0\
620a7db62321c9638aeb2d11661467fee968104cc1b51a4" },
  { (unsigned char *) "Bitcoin uses peer to peer technology to operate with no central authority", -1,
	(unsigned char *) "Common salt has the chemical formula NaCl", -1,
	"SHA512", 1024, 64,
	"345c92b1aaf0c19efd22fd806d273587dce89cfae6dc02509e5a40bed84d6f66ed24269fd5cce06e1\
ff4f58983c56e60dab29cd8b57ee324f537e7e639838037" },
  { (unsigned char *) "Bitcoin uses peer to peer technology to operate with no central authority", -1,
	(unsigned char *) "", 0,
	"SHA512", 1024, 64,
	"5d8f770fc9a78c9f7d3dde8449e066ca3d6227979f218e18931687da0cfc8fd513dd1db4cb298b2ca\
2e7343dfdf3694f026924eadd025b0b47cd39d31b67421d" },
  { (unsigned char *) "", 0,
	(unsigned char *) "Common salt has the chemical formula NaCl", -1,
	"SHA512", 1024, 64,
	"127794e00dd310ccdb58c3c03592e40d54c410af499839ee91c40fe62feae3c49fa06a799a2de8294\
8eb5f4340e9936499fb633819071ba27a72747cb043d57a" },
  { (unsigned char *) "Pass\0word", 9,
	(unsigned char *) "Sa\0lt", 5, "SHA512", 1024, 64,
	"aa55b05f2c7711bb83ea01e562c25c8d9fc1dcb2c5ac7a3620708681b5151129f1311ba0ed88f71aa\
90188f322130ff16a683d47427c2f3175fce47a8bc2a0f5" },
  { (unsigned char *) "\x00\x01\xff\xfe", 4,
	(unsigned char *) "\x00\x01\xff\xfe", 4, "SHA512", 1024, 64,
	"58ee8ae370c4ed6288f04850f7226ee182f369aadcf11e6bfcf5270cec909c90d5ee782615d1c3456\
189ba59d1dee4f0aa284538f115159cad6a15e6ff56e7df" },
  { (unsigned char *) "Test vector", -1,
	(unsigned char *) "This is a maximum length salt ..ABCDEFGH[][][][][][][]\\\\!!!!", -1,
	"SHA512", 1024, 64,
	"e0c10648fc64a93ad1c8351a2c036bc616f57596c1345d7363e76a3f47d0977281f77865014e4311a\
59d3ab6e8bce02c97375ce899f74f81d6bbdb1c03520c43" },
  { (unsigned char *) "LongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLong\
LongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLong\
LongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLong\
LongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLong\
LongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLong\
LongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLong\
LongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLong\
LongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLongLongCatIsLong\
LongCatIsLongLongCatIsLong", -1,
	(unsigned char *) "salt", -1, "SHA512", 1024, 64,
	"46dd09e7172af3936f36b55acc51c809f6ccd31e8e5317eaa79cabab28c7e6169fa9560ac94b53201\
fbc0604d6a5f0c2394feea9917361d7f5b63adb300fdc85" },
  { (unsigned char *) "passDATAb00AB7YxDTT", -1,
	(unsigned char *) "saltKEYbcTcXHCBxtjD", -1, "SHA512", 1, 64,
	"cbe6088ad4359af42e603c2a33760ef9d4017a7b2aad10af46f992c660a0b461ecb0dc2a79c257094\
1bea6a08d15d6887e79f32b132e1c134e9525eeddd744fa" },
  { (unsigned char *) "passDATAb00AB7YxDTT", -1,
	(unsigned char *) "saltKEYbcTcXHCBxtjD", -1, "SHA512", 100000, 64,
	"accdcd8798ae5cd85804739015ef2a11e32591b7b7d16f76819b30b0d49d80e1abea6c9822b80a1fd\
fe421e26f5603eca8a47a64c9a004fb5af8229f762ff41f" },

  { NULL, -1, NULL, -1, NULL, 0, 0, NULL }
};

static int
do_pbkdf2_test(int num, Pbkdf2_test *test)
{
  int plen, slen;
  char *p;
  unsigned char *kdf2;

  if (test->pwdlen == -1)
	plen = strlen((char *) test->pwd);
  else
	plen = test->pwdlen;

  if (test->saltlen == -1)
	slen = strlen((char *) test->salt);
  else
	slen = test->saltlen;

  fprintf(stderr, "%2d. pbkdf2(%s, keylen=%d, saltlen=%d, c=%d, dklen=%d):\n",
		  num, test->algname, plen, slen, test->count, test->dklen);
  kdf2 = crypto_pbkdf2(test->pwd, plen, test->salt, slen,
					   test->algname, test->count, test->dklen);
  p = strbtohex(kdf2, test->dklen, 0);
  fprintf(stderr, " %s... ", p);
  if (!streq(p, test->result)) {
	fprintf(stderr, "Failed!\n");
	return(-1);
  }
  fprintf(stderr, "ok\n");
  return(0);
}

typedef struct Chacha_test {
  char *label;
  int nrounds;
  uint32_t counter;
  uint8_t key[32];
  uint8_t nonce[12];		/* IV. */
  uint8_t cipher1[64];		/* Keystream block 1. */
  uint8_t cipher2[64];		/* Keystream block 2. */
} Chacha_test;

/*
 * https://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-01
 * Version 01 (31-Dec-2013)
 */
static Chacha_test chacha_test[] = {
  /* All zero IV, all zero key. */
  { "Chacha/8, TC1", 8, 0,
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x3e, 0x00, 0xef, 0x2f, 0x89, 0x5f, 0x40, 0xd6,
	  0x7f, 0x5b, 0xb8, 0xe8, 0x1f, 0x09, 0xa5, 0xa1,
	  0x2c, 0x84, 0x0e, 0xc3, 0xce, 0x9a, 0x7f, 0x3b,
	  0x18, 0x1b, 0xe1, 0x88, 0xef, 0x71, 0x1a, 0x1e,
	  0x98, 0x4c, 0xe1, 0x72, 0xb9, 0x21, 0x6f, 0x41,
	  0x9f, 0x44, 0x53, 0x67, 0x45, 0x6d, 0x56, 0x19,
	  0x31, 0x4a, 0x42, 0xa3, 0xda, 0x86, 0xb0, 0x01,
	  0x38, 0x7b, 0xfd, 0xb8, 0x0e, 0x0c, 0xfe, 0x42 },
	{ 0xd2, 0xae, 0xfa, 0x0d, 0xea, 0xa5, 0xc1, 0x51,
	  0xbf, 0x0a, 0xdb, 0x6c, 0x01, 0xf2, 0xa5, 0xad,
	  0xc0, 0xfd, 0x58, 0x12, 0x59, 0xf9, 0xa2, 0xaa,
	  0xdc, 0xf2, 0x0f, 0x8f, 0xd5, 0x66, 0xa2, 0x6b,
	  0x50, 0x32, 0xec, 0x38, 0xbb, 0xc5, 0xda, 0x98,
	  0xee, 0x0c, 0x6f, 0x56, 0x8b, 0x87, 0x2a, 0x65,
	  0xa0, 0x8a, 0xbf, 0x25, 0x1d, 0xeb, 0x21, 0xbb,
	  0x4b, 0x56, 0xe5, 0xd8, 0x82, 0x1e, 0x68, 0xaa }
  },

  { "Chacha/12, TC1", 12, 0,
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x9b, 0xf4, 0x9a, 0x6a, 0x07, 0x55, 0xf9, 0x53,
	  0x81, 0x1f, 0xce, 0x12, 0x5f, 0x26, 0x83, 0xd5,
	  0x04, 0x29, 0xc3, 0xbb, 0x49, 0xe0, 0x74, 0x14,
	  0x7e, 0x00, 0x89, 0xa5, 0x2e, 0xae, 0x15, 0x5f,
	  0x05, 0x64, 0xf8, 0x79, 0xd2, 0x7a, 0xe3, 0xc0,
	  0x2c, 0xe8, 0x28, 0x34, 0xac, 0xfa, 0x8c, 0x79,
	  0x3a, 0x62, 0x9f, 0x2c, 0xa0, 0xde, 0x69, 0x19,
	  0x61, 0x0b, 0xe8, 0x2f, 0x41, 0x13, 0x26, 0xbe },
	{ 0x0b, 0xd5, 0x88, 0x41, 0x20, 0x3e, 0x74, 0xfe,
	  0x86, 0xfc, 0x71, 0x33, 0x8c, 0xe0, 0x17, 0x3d,
	  0xc6, 0x28, 0xeb, 0xb7, 0x19, 0xbd, 0xcb, 0xcc,
	  0x15, 0x15, 0x85, 0x21, 0x4c, 0xc0, 0x89, 0xb4,
	  0x42, 0x25, 0x8d, 0xcd, 0xa1, 0x4c, 0xf1, 0x11,
	  0xc6, 0x02, 0xb8, 0x97, 0x1b, 0x8c, 0xc8, 0x43,
	  0xe9, 0x1e, 0x46, 0xca, 0x90, 0x51, 0x51, 0xc0,
	  0x27, 0x44, 0xa6, 0xb0, 0x17, 0xe6, 0x93, 0x16
	}
  },

  { "Chacha/20, TC1", 20, 0,
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
	  0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
	  0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
	  0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7,
	  0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d,
	  0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37,
	  0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
	  0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86 },
	{ 0x9f, 0x07, 0xe7, 0xbe, 0x55, 0x51, 0x38, 0x7a,
	  0x98, 0xba, 0x97, 0x7c, 0x73, 0x2d, 0x08, 0x0d,
	  0xcb, 0x0f, 0x29, 0xa0, 0x48, 0xe3, 0x65, 0x69,
	  0x12, 0xc6, 0x53, 0x3e, 0x32, 0xee, 0x7a, 0xed,
	  0x29, 0xb7, 0x21, 0x76, 0x9c, 0xe6, 0x4e, 0x43,
	  0xd5, 0x71, 0x33, 0xb0, 0x74, 0xd8, 0x39, 0xd5,
	  0x31, 0xed, 0x1f, 0x28, 0x51, 0x0a, 0xfb, 0x45,
	  0xac, 0xe1, 0x0a, 0x1f, 0x4b, 0x79, 0x4d, 0x6f }
  },

  /* Single bit in key set, all zero IV. */
  { "Chacha/8, TC2", 8, 0,
	{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00 },
	{ 0xcf, 0x5e, 0xe9, 0xa0, 0x49, 0x4a, 0xa9, 0x61,
	  0x3e, 0x05, 0xd5, 0xed, 0x72, 0x5b, 0x80, 0x4b,
	  0x12, 0xf4, 0xa4, 0x65, 0xee, 0x63, 0x5a, 0xcc,
	  0x3a, 0x31, 0x1d, 0xe8, 0x74, 0x04, 0x89, 0xea,
	  0x28, 0x9d, 0x04, 0xf4, 0x3c, 0x75, 0x18, 0xdb,
	  0x56, 0xeb, 0x44, 0x33, 0xe4, 0x98, 0xa1, 0x23,
	  0x8c, 0xd8, 0x46, 0x4d, 0x37, 0x63, 0xdd, 0xbb,
	  0x92, 0x22, 0xee, 0x3b, 0xd8, 0xfa, 0xe3, 0xc8 },
	{ 0xb4, 0x35, 0x5a, 0x7d, 0x93, 0xdd, 0x88, 0x67,
	  0x08, 0x9e, 0xe6, 0x43, 0x55, 0x8b, 0x95, 0x75,
	  0x4e, 0xfa, 0x2b, 0xd1, 0xa8, 0xa1, 0xe2, 0xd7,
	  0x5b, 0xcd, 0xb3, 0x20, 0x15, 0x54, 0x26, 0x38,
	  0x29, 0x19, 0x41, 0xfe, 0xb4, 0x99, 0x65, 0x58,
	  0x7c, 0x4f, 0xdf, 0xe2, 0x19, 0xcf, 0x0e, 0xc1,
	  0x32, 0xa6, 0xcd, 0x4d, 0xc0, 0x67, 0x39, 0x2e,
	  0x67, 0x98, 0x2f, 0xe5, 0x32, 0x78, 0xc0, 0xb4  }
  },

  { "Chacha/12, TC2", 12, 0,
	{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00 },
	{ 0x12, 0x05, 0x6e, 0x59, 0x5d, 0x56, 0xb0, 0xf6,
	  0xee, 0xf0, 0x90, 0xf0, 0xcd, 0x25, 0xa2, 0x09,
	  0x49, 0x24, 0x8c, 0x27, 0x90, 0x52, 0x5d, 0x0f,
	  0x93, 0x02, 0x18, 0xff, 0x0b, 0x4d, 0xdd, 0x10,
	  0xa6, 0x00, 0x22, 0x39, 0xd9, 0xa4, 0x54, 0xe2,
	  0x9e, 0x10, 0x7a, 0x7d, 0x06, 0xfe, 0xfd, 0xfe,
	  0xf0, 0x21, 0x0f, 0xeb, 0xa0, 0x44, 0xf9, 0xf2,
	  0x9b, 0x17, 0x72, 0xc9, 0x60, 0xdc, 0x29, 0xc0  },
	{ 0x0c, 0x73, 0x66, 0xc5, 0xcb, 0xc6, 0x04, 0x24,
	  0x0e, 0x66, 0x5e, 0xb0, 0x2a, 0x69, 0x37, 0x2a,
	  0x7a, 0xf9, 0x79, 0xb2, 0x6f, 0xbb, 0x78, 0x09,
	  0x2a, 0xc7, 0xc4, 0xb8, 0x80, 0x29, 0xa7, 0xc8,
	  0x54, 0x51, 0x3b, 0xc2, 0x17, 0xbb, 0xfc, 0x7d,
	  0x90, 0x43, 0x2e, 0x30, 0x8e, 0xba, 0x15, 0xaf,
	  0xc6, 0x5a, 0xeb, 0x48, 0xef, 0x10, 0x0d, 0x56,
	  0x01, 0xe6, 0xaf, 0xba, 0x25, 0x71, 0x17, 0xa9  }
  },

  { "Chacha/20, TC2", 20, 0,
	{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00 },
	{ 0xc5, 0xd3, 0x0a, 0x7c, 0xe1, 0xec, 0x11, 0x93,
	  0x78, 0xc8, 0x4f, 0x48, 0x7d, 0x77, 0x5a, 0x85,
	  0x42, 0xf1, 0x3e, 0xce, 0x23, 0x8a, 0x94, 0x55,
	  0xe8, 0x22, 0x9e, 0x88, 0x8d, 0xe8, 0x5b, 0xbd,
	  0x29, 0xeb, 0x63, 0xd0, 0xa1, 0x7a, 0x5b, 0x99,
	  0x9b, 0x52, 0xda, 0x22, 0xbe, 0x40, 0x23, 0xeb,
	  0x07, 0x62, 0x0a, 0x54, 0xf6, 0xfa, 0x6a, 0xd8,
	  0x73, 0x7b, 0x71, 0xeb, 0x04, 0x64, 0xda, 0xc0 },
	{ 0x10, 0xf6, 0x56, 0xe6, 0xd1, 0xfd, 0x55, 0x05,
	  0x3e, 0x50, 0xc4, 0x87, 0x5c, 0x99, 0x30, 0xa3,
	  0x3f, 0x6d, 0x02, 0x63, 0xbd, 0x14, 0xdf, 0xd6,
	  0xab, 0x8c, 0x70, 0x52, 0x1c, 0x19, 0x33, 0x8b,
	  0x23, 0x08, 0xb9, 0x5c, 0xf8, 0xd0, 0xbb, 0x7d,
	  0x20, 0x2d, 0x21, 0x02, 0x78, 0x0e, 0xa3, 0x52,
	  0x8f, 0x1c, 0xb4, 0x85, 0x60, 0xf7, 0x6b, 0x20,
	  0xf3, 0x82, 0xb9, 0x42, 0x50, 0x0f, 0xce, 0xac }
  },

  /* Single bit in IV set, all zero key. */
  { "Chacha/8, TC3", 8, 0,
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x01, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00 },
	{ 0x2b, 0x8f, 0x4b, 0xb3, 0x79, 0x83, 0x06, 0xca,
	  0x51, 0x30, 0xd4, 0x7c, 0x4f, 0x8d, 0x4e, 0xd1,
	  0x3a, 0xa0, 0xed, 0xcc, 0xc1, 0xbe, 0x69, 0x42,
	  0x09, 0x0f, 0xae, 0xec, 0xa0, 0xd7, 0x59, 0x9b,
	  0x7f, 0xf0, 0xfe, 0x61, 0x6b, 0xb2, 0x5a, 0xa0,
	  0x15, 0x3a, 0xd6, 0xfd, 0xc8, 0x8b, 0x95, 0x49,
	  0x03, 0xc2, 0x24, 0x26, 0xd4, 0x78, 0xb9, 0x7b,
	  0x22, 0xb8, 0xf9, 0xb1, 0xdb, 0x00, 0xcf, 0x06 },
	{ 0x47, 0x0b, 0xdf, 0xfb, 0xc4, 0x88, 0xa8, 0xb7,
	  0xc7, 0x01, 0xeb, 0xf4, 0x06, 0x1d, 0x75, 0xc5,
	  0x96, 0x91, 0x86, 0x49, 0x7c, 0x95, 0x36, 0x78,
	  0x09, 0xaf, 0xa8, 0x0b, 0xd8, 0x43, 0xb0, 0x40,
	  0xa7, 0x9a, 0xbc, 0x6e, 0x73, 0xa9, 0x17, 0x57,
	  0xf1, 0xdb, 0x73, 0xc8, 0xea, 0xcf, 0xa5, 0x43,
	  0xb3, 0x8f, 0x28, 0x9d, 0x06, 0x5a, 0xb2, 0xf3,
	  0x03, 0x2d, 0x37, 0x7b, 0x8c, 0x37, 0xfe, 0x46 }
  },

  { "Chacha/12, TC3", 12, 0,
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x01, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00 },
	{ 0x64, 0xb8, 0xbd, 0xf8, 0x7b, 0x82, 0x8c, 0x4b,
	  0x6d, 0xba, 0xf7, 0xef, 0x69, 0x8d, 0xe0, 0x3d,
	  0xf8, 0xb3, 0x3f, 0x63, 0x57, 0x14, 0x41, 0x8f,
	  0x98, 0x36, 0xad, 0xe5, 0x9b, 0xe1, 0x29, 0x69,
	  0x46, 0xc9, 0x53, 0xa0, 0xf3, 0x8e, 0xcf, 0xfc,
	  0x9e, 0xcb, 0x98, 0xe8, 0x1d, 0x5d, 0x99, 0xa5,
	  0xed, 0xfc, 0x8f, 0x9a, 0x0a, 0x45, 0xb9, 0xe4,
	  0x1e, 0xf3, 0xb3, 0x1f, 0x02, 0x8f, 0x1d, 0x0f },
	{ 0x55, 0x9d, 0xb4, 0xa7, 0xf2, 0x22, 0xc4, 0x42,
	  0xfe, 0x23, 0xb9, 0xa2, 0x59, 0x6a, 0x88, 0x28,
	  0x51, 0x22, 0xee, 0x4f, 0x13, 0x63, 0x89, 0x6e,
	  0xa7, 0x7c, 0xa1, 0x50, 0x91, 0x2a, 0xc7, 0x23,
	  0xbf, 0xf0, 0x4b, 0x02, 0x6a, 0x2f, 0x80, 0x7e,
	  0x03, 0xb2, 0x9c, 0x02, 0x07, 0x7d, 0x7b, 0x06,
	  0xfc, 0x1a, 0xb9, 0x82, 0x7c, 0x13, 0xc8, 0x01,
	  0x3a, 0x6d, 0x83, 0xbd, 0x3b, 0x52, 0xa2, 0x6f }
  },

  { "Chacha/20, TC3", 20, 0,
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x01, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00 },
	{ 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb,
	  0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80,
	  0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac,
	  0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32,
	  0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c,
	  0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54,
	  0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
	  0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b },
	{ 0x53, 0x05, 0xe5, 0xe4, 0x4a, 0xff, 0x19, 0xb2,
	  0x35, 0x93, 0x61, 0x44, 0x67, 0x5e, 0xfb, 0xe4,
	  0x40, 0x9e, 0xb7, 0xe8, 0xe5, 0xf1, 0x43, 0x0f,
	  0x5f, 0x58, 0x36, 0xae, 0xb4, 0x9b, 0xb5, 0x32,
	  0x8b, 0x01, 0x7c, 0x4b, 0x9d, 0xc1, 0x1f, 0x8a,
	  0x03, 0x86, 0x3f, 0xa8, 0x03, 0xdc, 0x71, 0xd5,
	  0x72, 0x6b, 0x2b, 0x6b, 0x31, 0xaa, 0x32, 0x70,
	  0x8a, 0xfe, 0x5a, 0xf1, 0xd6, 0xb6, 0x90, 0x58 }
  },

  /* All bits in IV and key set. */
  { "Chacha/8, TC4", 8, 0,
	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
	{ 0x00, 0x00, 0x00, 0x00,
	  0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff },
	{ 0xe1, 0x63, 0xbb, 0xf8, 0xc9, 0xa7, 0x39, 0xd1,
	  0x89, 0x25, 0xee, 0x83, 0x62, 0xda, 0xd2, 0xcd,
	  0xc9, 0x73, 0xdf, 0x05, 0x22, 0x5a, 0xfb, 0x2a,
	  0xa2, 0x63, 0x96, 0xf2, 0xa9, 0x84, 0x9a, 0x4a,
	  0x44, 0x5e, 0x05, 0x47, 0xd3, 0x1c, 0x16, 0x23,
	  0xc5, 0x37, 0xdf, 0x4b, 0xa8, 0x5c, 0x70, 0xa9,
	  0x88, 0x4a, 0x35, 0xbc, 0xbf, 0x3d, 0xfa, 0xb0,
	  0x77, 0xe9, 0x8b, 0x0f, 0x68, 0x13, 0x5f, 0x54 },
	{ 0x81, 0xd4, 0x93, 0x3f, 0x8b, 0x32, 0x2a, 0xc0,
	  0xcd, 0x76, 0x2c, 0x27, 0x23, 0x5c, 0xe2, 0xb3,
	  0x15, 0x34, 0xe0, 0x24, 0x4a, 0x9a, 0x2f, 0x1f,
	  0xd5, 0xe9, 0x44, 0x98, 0xd4, 0x7f, 0xf1, 0x08,
	  0x79, 0x0c, 0x00, 0x9c, 0xf9, 0xe1, 0xa3, 0x48,
	  0x03, 0x2a, 0x76, 0x94, 0xcb, 0x28, 0x02, 0x4c,
	  0xd9, 0x6d, 0x34, 0x98, 0x36, 0x1e, 0xdb, 0x17,
	  0x85, 0xaf, 0x75, 0x2d, 0x18, 0x7a, 0xb5, 0x4b }
  },

  { "Chacha/12, TC4", 12, 0,
	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
	{ 0x00, 0x00, 0x00, 0x00,
	  0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff },
	{ 0x04, 0xbf, 0x88, 0xda, 0xe8, 0xe4, 0x7a, 0x22,
	  0x8f, 0xa4, 0x7b, 0x7e, 0x63, 0x79, 0x43, 0x4b,
	  0xa6, 0x64, 0xa7, 0xd2, 0x8f, 0x4d, 0xab, 0x84,
	  0xe5, 0xf8, 0xb4, 0x64, 0xad, 0xd2, 0x0c, 0x3a,
	  0xca, 0xa6, 0x9c, 0x5a, 0xb2, 0x21, 0xa2, 0x3a,
	  0x57, 0xeb, 0x5f, 0x34, 0x5c, 0x96, 0xf4, 0xd1,
	  0x32, 0x2d, 0x0a, 0x2f, 0xf7, 0xa9, 0xcd, 0x43,
	  0x40, 0x1c, 0xd5, 0x36, 0x63, 0x9a, 0x61, 0x5a  },
	{ 0x5c, 0x94, 0x29, 0xb5, 0x5c, 0xa3, 0xc1, 0xb5,
	  0x53, 0x54, 0x55, 0x96, 0x69, 0xa1, 0x54, 0xac,
	  0xa4, 0x6c, 0xd7, 0x61, 0xc4, 0x1a, 0xb8, 0xac,
	  0xe3, 0x85, 0x36, 0x3b, 0x95, 0x67, 0x5f, 0x06,
	  0x8e, 0x18, 0xdb, 0x5a, 0x67, 0x3c, 0x11, 0x29,
	  0x1b, 0xd4, 0x18, 0x78, 0x92, 0xa9, 0xa3, 0xa3,
	  0x35, 0x14, 0xf3, 0x71, 0x2b, 0x26, 0xc1, 0x30,
	  0x26, 0x10, 0x32, 0x98, 0xed, 0x76, 0xbc, 0x9a }
  },

  { "Chacha/20, TC4", 20, 0,
	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
	{ 0x00, 0x00, 0x00, 0x00,
	  0xff, 0xff, 0xff, 0xff,
	  0xff, 0xff, 0xff, 0xff },
	{ 0xd9, 0xbf, 0x3f, 0x6b, 0xce, 0x6e, 0xd0, 0xb5,
	  0x42, 0x54, 0x55, 0x77, 0x67, 0xfb, 0x57, 0x44,
	  0x3d, 0xd4, 0x77, 0x89, 0x11, 0xb6, 0x06, 0x05,
	  0x5c, 0x39, 0xcc, 0x25, 0xe6, 0x74, 0xb8, 0x36,
	  0x3f, 0xea, 0xbc, 0x57, 0xfd, 0xe5, 0x4f, 0x79,
	  0x0c, 0x52, 0xc8, 0xae, 0x43, 0x24, 0x0b, 0x79,
	  0xd4, 0x90, 0x42, 0xb7, 0x77, 0xbf, 0xd6, 0xcb,
	  0x80, 0xe9, 0x31, 0x27, 0x0b, 0x7f, 0x50, 0xeb },
	{ 0x5b, 0xac, 0x2a, 0xcd, 0x86, 0xa8, 0x36, 0xc5,
	  0xdc, 0x98, 0xc1, 0x16, 0xc1, 0x21, 0x7e, 0xc3,
	  0x1d, 0x3a, 0x63, 0xa9, 0x45, 0x13, 0x19, 0xf0,
	  0x97, 0xf3, 0xb4, 0xd6, 0xda, 0xb0, 0x77, 0x87,
	  0x19, 0x47, 0x7d, 0x24, 0xd2, 0x4b, 0x40, 0x3a,
	  0x12, 0x24, 0x1d, 0x7c, 0xca, 0x06, 0x4f, 0x79,
	  0x0f, 0x1d, 0x51, 0xcc, 0xaf, 0xf6, 0xb1, 0x66,
	  0x7d, 0x4b, 0xbc, 0xa1, 0x95, 0x8c, 0x43, 0x06 }
  },

  /* All even bits set in key and IV. */
  { "Chacha/8, TC5", 8, 0,
	{ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55 },
	{ 0x7c, 0xb7, 0x82, 0x14, 0xe4, 0xd3, 0x46, 0x5b,
	  0x6d, 0xc6, 0x2c, 0xf7, 0xa1, 0x53, 0x8c, 0x88,
	  0x99, 0x69, 0x52, 0xb4, 0xfb, 0x72, 0xcb, 0x61,
	  0x05, 0xf1, 0x24, 0x3c, 0xe3, 0x44, 0x2e, 0x29,
	  0x75, 0xa5, 0x9e, 0xbc, 0xd2, 0xb2, 0xa5, 0x98,
	  0x29, 0x0d, 0x75, 0x38, 0x49, 0x1f, 0xe6, 0x5b,
	  0xdb, 0xfe, 0xfd, 0x06, 0x0d, 0x88, 0x79, 0x81,
	  0x20, 0xa7, 0x0d, 0x04, 0x9d, 0xc2, 0x67, 0x7d },
	{ 0xd4, 0x8f, 0xf5, 0xa2, 0x51, 0x3e, 0x49, 0x7a,
	  0x5d, 0x54, 0x80, 0x2d, 0x74, 0x84, 0xc4, 0xf1,
	  0x08, 0x39, 0x44, 0xd8, 0xd0, 0xd1, 0x4d, 0x64,
	  0x82, 0xce, 0x09, 0xf7, 0xe5, 0xeb, 0xf2, 0x0b,
	  0x29, 0x80, 0x7d, 0x62, 0xc3, 0x18, 0x74, 0xd0,
	  0x2f, 0x5d, 0x3c, 0xc8, 0x53, 0x81, 0xa7, 0x45,
	  0xec, 0xbc, 0x60, 0x52, 0x52, 0x05, 0xe3, 0x00,
	  0xa7, 0x69, 0x61, 0xbf, 0xe5, 0x1a, 0xc0, 0x7c }
  },

  { "Chacha/12, TC5", 12, 0,
	{ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55 },
	{ 0xa6, 0x00, 0xf0, 0x77, 0x27, 0xff, 0x93, 0xf3,
	  0xda, 0x00, 0xdd, 0x74, 0xcc, 0x3e, 0x8b, 0xfb,
	  0x5c, 0xa7, 0x30, 0x2f, 0x6a, 0x0a, 0x29, 0x44,
	  0x95, 0x3d, 0xe0, 0x04, 0x50, 0xee, 0xcd, 0x40,
	  0xb8, 0x60, 0xf6, 0x60, 0x49, 0xf2, 0xea, 0xed,
	  0x63, 0xb2, 0xef, 0x39, 0xcc, 0x31, 0x0d, 0x2c,
	  0x48, 0x8f, 0x5d, 0x9a, 0x24, 0x1b, 0x61, 0x5d,
	  0xc0, 0xab, 0x70, 0xf9, 0x21, 0xb9, 0x1b, 0x95 },
	{ 0x14, 0x0e, 0xff, 0x4a, 0xa4, 0x95, 0xac, 0x61,
	 0x28, 0x9b, 0x6b, 0xc5, 0x7d, 0xe0, 0x72, 0x41,
	 0x9d, 0x09, 0xda, 0xa7, 0xa7, 0x24, 0x39, 0x90,
	 0xda, 0xf3, 0x48, 0xa8, 0xf2, 0x83, 0x1e, 0x59,
	 0x7c, 0xf3, 0x79, 0xb3, 0xb2, 0x84, 0xf0, 0x0b,
	 0xda, 0x27, 0xa4, 0xc6, 0x80, 0x85, 0x37, 0x4a,
	 0x8a, 0x5c, 0x38, 0xde, 0xd6, 0x2d, 0x11, 0x41,
	 0xca, 0xe0, 0xbb, 0x83, 0x8d, 0xdc, 0x22, 0x32 }
  },

  { "Chacha/20, TC5", 20, 0,
	{ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x55, 0x55, 0x55, 0x55,
	  0x55, 0x55, 0x55, 0x55 },
	{ 0xbe, 0xa9, 0x41, 0x1a, 0xa4, 0x53, 0xc5, 0x43,
	  0x4a, 0x5a, 0xe8, 0xc9, 0x28, 0x62, 0xf5, 0x64,
	  0x39, 0x68, 0x55, 0xa9, 0xea, 0x6e, 0x22, 0xd6,
	  0xd3, 0xb5, 0x0a, 0xe1, 0xb3, 0x66, 0x33, 0x11,
	  0xa4, 0xa3, 0x60, 0x6c, 0x67, 0x1d, 0x60, 0x5c,
	  0xe1, 0x6c, 0x3a, 0xec, 0xe8, 0xe6, 0x1e, 0xa1,
	  0x45, 0xc5, 0x97, 0x75, 0x01, 0x7b, 0xee, 0x2f,
	  0xa6, 0xf8, 0x8a, 0xfc, 0x75, 0x80, 0x69, 0xf7 },
	{ 0xe0, 0xb8, 0xf6, 0x76, 0xe6, 0x44, 0x21, 0x6f,
	  0x4d, 0x2a, 0x34, 0x22, 0xd7, 0xfa, 0x36, 0xc6,
	  0xc4, 0x93, 0x1a, 0xca, 0x95, 0x0e, 0x9d, 0xa4,
	  0x27, 0x88, 0xe6, 0xd0, 0xb6, 0xd1, 0xcd, 0x83,
	  0x8e, 0xf6, 0x52, 0xe9, 0x7b, 0x14, 0x5b, 0x14,
	  0x87, 0x1e, 0xae, 0x6c, 0x68, 0x04, 0xc7, 0x00,
	  0x4d, 0xb5, 0xac, 0x2f, 0xce, 0x4c, 0x68, 0xc7,
	  0x26, 0xd0, 0x04, 0xb1, 0x0f, 0xca, 0xba, 0x86 }
  },

  /* All odd bits set in key and IV. */
  { "Chacha/8, TC6", 8, 0,
	{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
	{ 0x00, 0x00, 0x00, 0x00,
	  0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa },
	{ 0x40, 0xf9, 0xab, 0x86, 0xc8, 0xf9, 0xa1, 0xa0,
	  0xcd, 0xc0, 0x5a, 0x75, 0xe5, 0x53, 0x1b, 0x61,
	  0x2d, 0x71, 0xef, 0x7f, 0x0c, 0xf9, 0xe3, 0x87,
	  0xdf, 0x6e, 0xd6, 0x97, 0x2f, 0x0a, 0xae, 0x21,
	  0x31, 0x1a, 0xa5, 0x81, 0xf8, 0x16, 0xc9, 0x0e,
	  0x8a, 0x99, 0xde, 0x99, 0x0b, 0x6b, 0x95, 0xaa,
	  0xc9, 0x24, 0x50, 0xf4, 0xe1, 0x12, 0x71, 0x26,
	  0x67, 0xb8, 0x04, 0xc9, 0x9e, 0x9c, 0x6e, 0xda },
	{ 0xf8, 0xd1, 0x44, 0xf5, 0x60, 0xc8, 0xc0, 0xea,
	  0x36, 0x88, 0x0d, 0x3b, 0x77, 0x87, 0x4c, 0x9a,
	  0x91, 0x03, 0xd1, 0x47, 0xf6, 0xde, 0xd3, 0x86,
	  0x28, 0x48, 0x01, 0xa4, 0xee, 0x15, 0x8e, 0x5e,
	  0xa4, 0xf9, 0xc0, 0x93, 0xfc, 0x55, 0xfd, 0x34,
	  0x4c, 0x33, 0x34, 0x9d, 0xc5, 0xb6, 0x99, 0xe2,
	  0x1d, 0xc8, 0x3b, 0x42, 0x96, 0xf9, 0x2e, 0xe3,
	  0xec, 0xab, 0xf3, 0xd5, 0x1f, 0x95, 0xfe, 0x3f }
  },

  { "Chacha/12, TC6", 12, 0,
	{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
	{ 0x00, 0x00, 0x00, 0x00,
	  0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa },
	{ 0x85, 0x65, 0x05, 0xb0, 0x1d, 0x3b, 0x47, 0xaa,
	  0xe0, 0x3d, 0x6a, 0x97, 0xaa, 0x0f, 0x03, 0x3a,
	  0x9a, 0xdc, 0xc9, 0x43, 0x77, 0xba, 0xbd, 0x86,
	  0x08, 0x86, 0x4f, 0xb3, 0xf6, 0x25, 0xb6, 0xe3,
	  0x14, 0xf0, 0x86, 0x15, 0x8f, 0x9f, 0x72, 0x5d,
	  0x81, 0x1e, 0xeb, 0x95, 0x3b, 0x7f, 0x74, 0x70,
	  0x76, 0xe4, 0xc3, 0xf6, 0x39, 0xfa, 0x84, 0x1f,
	  0xad, 0x6c, 0x9a, 0x70, 0x9e, 0x62, 0x13, 0x97 },
	{ 0x6d, 0xd6, 0xee, 0x9b, 0x5e, 0x1e, 0x2e, 0x67,
	  0x6b, 0x1c, 0x9e, 0x2b, 0x82, 0xc2, 0xe9, 0x6c,
	  0x16, 0x48, 0x43, 0x7b, 0xff, 0x2f, 0x01, 0x26,
	  0xb7, 0x4e, 0x8c, 0xe0, 0xa9, 0xb0, 0x6d, 0x17,
	  0x20, 0xac, 0x0b, 0x6f, 0x09, 0x08, 0x6f, 0x28,
	  0xbc, 0x20, 0x15, 0x87, 0xf0, 0x53, 0x5e, 0xd9,
	  0x38, 0x52, 0x70, 0xd0, 0x8b, 0x4a, 0x93, 0x82,
	  0xf1, 0x8f, 0x82, 0xdb, 0xde, 0x18, 0x21, 0x0e }
  },

  { "Chacha/20, TC6", 20, 0,
	{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
	{ 0x00, 0x00, 0x00, 0x00,
	  0xaa, 0xaa, 0xaa, 0xaa,
	  0xaa, 0xaa, 0xaa, 0xaa },
	{ 0x9a, 0xa2, 0xa9, 0xf6, 0x56, 0xef, 0xde, 0x5a,
	  0xa7, 0x59, 0x1c, 0x5f, 0xed, 0x4b, 0x35, 0xae,
	  0xa2, 0x89, 0x5d, 0xec, 0x7c, 0xb4, 0x54, 0x3b,
	  0x9e, 0x9f, 0x21, 0xf5, 0xe7, 0xbc, 0xbc, 0xf3,
	  0xc4, 0x3c, 0x74, 0x8a, 0x97, 0x08, 0x88, 0xf8,
	  0x24, 0x83, 0x93, 0xa0, 0x9d, 0x43, 0xe0, 0xb7,
	  0xe1, 0x64, 0xbc, 0x4d, 0x0b, 0x0f, 0xb2, 0x40,
	  0xa2, 0xd7, 0x21, 0x15, 0xc4, 0x80, 0x89, 0x06 },
	{ 0x72, 0x18, 0x44, 0x89, 0x44, 0x05, 0x45, 0xd0,
	  0x21, 0xd9, 0x7e, 0xf6, 0xb6, 0x93, 0xdf, 0xe5,
	  0xb2, 0xc1, 0x32, 0xd4, 0x7e, 0x6f, 0x04, 0x1c,
	  0x90, 0x63, 0x65, 0x1f, 0x96, 0xb6, 0x23, 0xe6,
	  0x2a, 0x11, 0x99, 0x9a, 0x23, 0xb6, 0xf7, 0xc4,
	  0x61, 0xb2, 0x15, 0x30, 0x26, 0xad, 0x5e, 0x86,
	  0x6a, 0x2e, 0x59, 0x7e, 0xd0, 0x7b, 0x84, 0x01,
	  0xde, 0xc6, 0x3a, 0x09, 0x34, 0xc6, 0xb2, 0xa9 }
  },

  /* Sequence patterns in key and IV. */
  { "Chacha/8, TC7", 8, 0,
	{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
	  0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	  0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
	  0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x0f, 0x1e, 0x2d, 0x3c,
	  0x4b, 0x5a, 0x69, 0x78 },
	{ 0xdb, 0x43, 0xad, 0x9d, 0x1e, 0x84, 0x2d, 0x12,
	  0x72, 0xe4, 0x53, 0x0e, 0x27, 0x6b, 0x3f, 0x56,
	  0x8f, 0x88, 0x59, 0xb3, 0xf7, 0xcf, 0x6d, 0x9d,
	  0x2c, 0x74, 0xfa, 0x53, 0x80, 0x8c, 0xb5, 0x15,
	  0x7a, 0x8e, 0xbf, 0x46, 0xad, 0x3d, 0xcc, 0x4b,
	  0x6c, 0x7d, 0xad, 0xde, 0x13, 0x17, 0x84, 0xb0,
	  0x12, 0x0e, 0x0e, 0x22, 0xf6, 0xd5, 0xf9, 0xff,
	  0xa7, 0x40, 0x7d, 0x4a, 0x21, 0xb6, 0x95, 0xd9 },
	{ 0xc5, 0xdd, 0x30, 0xbf, 0x55, 0x61, 0x2f, 0xab,
	  0x9b, 0xdd, 0x11, 0x89, 0x20, 0xc1, 0x98, 0x16,
	  0x47, 0x0c, 0x7f, 0x5d, 0xcd, 0x42, 0x32, 0x5d,
	  0xbb, 0xed, 0x8c, 0x57, 0xa5, 0x62, 0x81, 0xc1,
	  0x44, 0xcb, 0x0f, 0x03, 0xe8, 0x1b, 0x30, 0x04,
	  0x62, 0x4e, 0x06, 0x50, 0xa1, 0xce, 0x5a, 0xfa,
	  0xf9, 0xa7, 0xcd, 0x81, 0x63, 0xf6, 0xdb, 0xd7,
	  0x26, 0x02, 0x25, 0x7d, 0xd9, 0x6e, 0x47, 0x1e }
  },

  { "Chacha/12, TC7", 12, 0,
	{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
	  0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	  0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
	  0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x0f, 0x1e, 0x2d, 0x3c,
	  0x4b, 0x5a, 0x69, 0x78 },
	{ 0x7e, 0xd1, 0x2a, 0x3a, 0x63, 0x91, 0x2a, 0xe9,
	  0x41, 0xba, 0x6d, 0x4c, 0x0d, 0x5e, 0x86, 0x2e,
	  0x56, 0x8b, 0x0e, 0x55, 0x89, 0x34, 0x69, 0x35,
	  0x50, 0x5f, 0x06, 0x4b, 0x8c, 0x26, 0x98, 0xdb,
	  0xf7, 0xd8, 0x50, 0x66, 0x7d, 0x8e, 0x67, 0xbe,
	  0x63, 0x9f, 0x3b, 0x4f, 0x6a, 0x16, 0xf9, 0x2e,
	  0x65, 0xea, 0x80, 0xf6, 0xc7, 0x42, 0x94, 0x45,
	  0xda, 0x1f, 0xc2, 0xc1, 0xb9, 0x36, 0x50, 0x40 },
	{ 0xe3, 0x2e, 0x50, 0xc4, 0x10, 0x6f, 0x3b, 0x3d,
	  0xa1, 0xce, 0x7c, 0xcb, 0x1e, 0x71, 0x40, 0xb1,
	  0x53, 0x49, 0x3c, 0x0f, 0x3a, 0xd9, 0xa9, 0xbc,
	  0xff, 0x07, 0x7e, 0xc4, 0x59, 0x6f, 0x1d, 0x0f,
	  0x29, 0xbf, 0x9c, 0xba, 0xa5, 0x02, 0x82, 0x0f,
	  0x73, 0x2a, 0xf5, 0xa9, 0x3c, 0x49, 0xee, 0xe3,
	  0x3d, 0x1c, 0x4f, 0x12, 0xaf, 0x3b, 0x42, 0x97,
	  0xaf, 0x91, 0xfe, 0x41, 0xea, 0x9e, 0x94, 0xa2 }
  },

  { "Chacha/20, TC7", 20, 0,
	{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
	  0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
	  0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
	  0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x0f, 0x1e, 0x2d, 0x3c,
	  0x4b, 0x5a, 0x69, 0x78 },
	{ 0x9f, 0xad, 0xf4, 0x09, 0xc0, 0x08, 0x11, 0xd0,
	  0x04, 0x31, 0xd6, 0x7e, 0xfb, 0xd8, 0x8f, 0xba,
	  0x59, 0x21, 0x8d, 0x5d, 0x67, 0x08, 0xb1, 0xd6,
	  0x85, 0x86, 0x3f, 0xab, 0xbb, 0x0e, 0x96, 0x1e,
	  0xea, 0x48, 0x0f, 0xd6, 0xfb, 0x53, 0x2b, 0xfd,
	  0x49, 0x4b, 0x21, 0x51, 0x01, 0x50, 0x57, 0x42,
	  0x3a, 0xb6, 0x0a, 0x63, 0xfe, 0x4f, 0x55, 0xf7,
	  0xa2, 0x12, 0xe2, 0x16, 0x7c, 0xca, 0xb9, 0x31 },
	{ 0xfb, 0xfd, 0x29, 0xcf, 0x7b, 0xc1, 0xd2, 0x79,
	  0xed, 0xdf, 0x25, 0xdd, 0x31, 0x6b, 0xb8, 0x84,
	  0x3d, 0x6e, 0xde, 0xe0, 0xbd, 0x1e, 0xf1, 0x21,
	  0xd1, 0x2f, 0xa1, 0x7c, 0xbc, 0x2c, 0x57, 0x4c,
	  0xcc, 0xab, 0x5e, 0x27, 0x51, 0x67, 0xb0, 0x8b,
	  0xd6, 0x86, 0xf8, 0xa0, 0x9d, 0xf8, 0x7e, 0xc3,
	  0xff, 0xb3, 0x53, 0x61, 0xb9, 0x4e, 0xbf, 0xa1,
	  0x3f, 0xec, 0x0e, 0x48, 0x89, 0xd1, 0x8d, 0xa5 },
  },

  /* Text strings for key and IV. */
  { "Chacha/8, TC8", 8, 0,
	{ 0xc4, 0x6e, 0xc1, 0xb1, 0x8c, 0xe8, 0xa8, 0x78,
	  0x72, 0x5a, 0x37, 0xe7, 0x80, 0xdf, 0xb7, 0x35,
	  0x1f, 0x68, 0xed, 0x2e, 0x19, 0x4c, 0x79, 0xfb,
	  0xc6, 0xae, 0xbe, 0xe1, 0xa6, 0x67, 0x97, 0x5d },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x1a, 0xda, 0x31, 0xd5,
	  0xcf, 0x68, 0x82, 0x21
	},
	{ 0x83, 0x87, 0x51, 0xb4, 0x2d, 0x8d, 0xdd, 0x8a,
	  0x3d, 0x77, 0xf4, 0x88, 0x25, 0xa2, 0xba, 0x75,
	  0x2c, 0xf4, 0x04, 0x7c, 0xb3, 0x08, 0xa5, 0x97,
	  0x8e, 0xf2, 0x74, 0x97, 0x3b, 0xe3, 0x74, 0xc9,
	  0x6a, 0xd8, 0x48, 0x06, 0x58, 0x71, 0x41, 0x7b,
	  0x08, 0xf0, 0x34, 0xe6, 0x81, 0xfe, 0x46, 0xa9,
	  0x3f, 0x7d, 0x5c, 0x61, 0xd1, 0x30, 0x66, 0x14,
	  0xd4, 0xaa, 0xf2, 0x57, 0xa7, 0xcf, 0xf0, 0x8b },
	{ 0x16, 0xf2, 0xfd, 0xa1, 0x70, 0xcc, 0x18, 0xa4,
	  0xb5, 0x8a, 0x26, 0x67, 0xed, 0x96, 0x27, 0x74,
	  0xaf, 0x79, 0x2a, 0x6e, 0x7f, 0x3c, 0x77, 0x99,
	  0x25, 0x40, 0x71, 0x1a, 0x7a, 0x13, 0x6d, 0x7e,
	  0x8a, 0x2f, 0x8d, 0x3f, 0x93, 0x81, 0x67, 0x09,
	  0xd4, 0x5a, 0x3f, 0xa5, 0xf8, 0xce, 0x72, 0xfd,
	  0xe1, 0x5b, 0xe7, 0xb8, 0x41, 0xac, 0xba, 0x3a,
	  0x2a, 0xbd, 0x55, 0x72, 0x28, 0xd9, 0xfe, 0x4f }
  },

  { "Chacha/12, TC8", 12, 0,
	{ 0xc4, 0x6e, 0xc1, 0xb1, 0x8c, 0xe8, 0xa8, 0x78,
	  0x72, 0x5a, 0x37, 0xe7, 0x80, 0xdf, 0xb7, 0x35,
	  0x1f, 0x68, 0xed, 0x2e, 0x19, 0x4c, 0x79, 0xfb,
	  0xc6, 0xae, 0xbe, 0xe1, 0xa6, 0x67, 0x97, 0x5d },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x1a, 0xda, 0x31, 0xd5,
	  0xcf, 0x68, 0x82, 0x21 },
	{ 0x14, 0x82, 0x07, 0x27, 0x84, 0xbc, 0x6d, 0x06,
	  0xb4, 0xe7, 0x3b, 0xdc, 0x11, 0x8b, 0xc0, 0x10,
	  0x3c, 0x79, 0x76, 0x78, 0x6c, 0xa9, 0x18, 0xe0,
	  0x69, 0x86, 0xaa, 0x25, 0x1f, 0x7e, 0x9c, 0xc1,
	  0xb2, 0x74, 0x9a, 0x0a, 0x16, 0xee, 0x83, 0xb4,
	  0x24, 0x2d, 0x2e, 0x99, 0xb0, 0x8d, 0x7c, 0x20,
	  0x09, 0x2b, 0x80, 0xbc, 0x46, 0x6c, 0x87, 0x28,
	  0x3b, 0x61, 0xb1, 0xb3, 0x9d, 0x0f, 0xfb, 0xab },
	{ 0xd9, 0x4b, 0x11, 0x6b, 0xc1, 0xeb, 0xdb, 0x32,
	  0x9b, 0x9e, 0x4f, 0x62, 0x0d, 0xb6, 0x95, 0x54,
	  0x4a, 0x8e, 0x3d, 0x9b, 0x68, 0x47, 0x3d, 0x0c,
	  0x97, 0x5a, 0x46, 0xad, 0x96, 0x6e, 0xd6, 0x31,
	  0xe4, 0x2a, 0xff, 0x53, 0x0a, 0xd5, 0xea, 0xc7,
	  0xd8, 0x04, 0x7a, 0xdf, 0xa1, 0xe5, 0x11, 0x3c,
	  0x91, 0xf3, 0xe3, 0xb8, 0x83, 0xf1, 0xd1, 0x89,
	  0xac, 0x1c, 0x8f, 0xe0, 0x7b, 0xa5, 0xa4, 0x2b }
  },

  { "Chacha/20, TC8", 20, 0,
	{ 0xc4, 0x6e, 0xc1, 0xb1, 0x8c, 0xe8, 0xa8, 0x78,
	  0x72, 0x5a, 0x37, 0xe7, 0x80, 0xdf, 0xb7, 0x35,
	  0x1f, 0x68, 0xed, 0x2e, 0x19, 0x4c, 0x79, 0xfb,
	  0xc6, 0xae, 0xbe, 0xe1, 0xa6, 0x67, 0x97, 0x5d },
	{ 0x00, 0x00, 0x00, 0x00,
	  0x1a, 0xda, 0x31, 0xd5,
	  0xcf, 0x68, 0x82, 0x21 },
	{ 0xf6, 0x3a, 0x89, 0xb7, 0x5c, 0x22, 0x71, 0xf9,
	  0x36, 0x88, 0x16, 0x54, 0x2b, 0xa5, 0x2f, 0x06,
	  0xed, 0x49, 0x24, 0x17, 0x92, 0x30, 0x2b, 0x00,
	  0xb5, 0xe8, 0xf8, 0x0a, 0xe9, 0xa4, 0x73, 0xaf,
	  0xc2, 0x5b, 0x21, 0x8f, 0x51, 0x9a, 0xf0, 0xfd,
	  0xd4, 0x06, 0x36, 0x2e, 0x8d, 0x69, 0xde, 0x7f,
	  0x54, 0xc6, 0x04, 0xa6, 0xe0, 0x0f, 0x35, 0x3f,
	  0x11, 0x0f, 0x77, 0x1b, 0xdc, 0xa8, 0xab, 0x92 },
	{ 0xe5, 0xfb, 0xc3, 0x4e, 0x60, 0xa1, 0xd9, 0xa9,
	  0xdb, 0x17, 0x34, 0x5b, 0x0a, 0x40, 0x27, 0x36,
	  0x85, 0x3b, 0xf9, 0x10, 0xb0, 0x60, 0xbd, 0xf1,
	  0xf8, 0x97, 0xb6, 0x29, 0x0f, 0x01, 0xd1, 0x38,
	  0xae, 0x2c, 0x4c, 0x90, 0x22, 0x5b, 0xa9, 0xea,
	  0x14, 0xd5, 0x18, 0xf5, 0x59, 0x29, 0xde, 0xa0,
	  0x98, 0xca, 0x7a, 0x6c, 0xcf, 0xe6, 0x12, 0x27,
	  0x05, 0x3c, 0x84, 0xe4, 0x9a, 0x4a, 0x33, 0x32 },
  },

  { NULL, 0, 0,
	{ 0x00 },
	{ 0x00 },
	{ 0x00 },
	{ 0x00 }
  }
};

static int
do_chacha_strombergson(void)
{
  uint8_t ct[64], pt[64];
  Chacha_ctx ctx;
  Chacha_test *test;

  fprintf(stderr, "ChaCha Strombergson Test Vectors 01...\n");
  for (test = chacha_test; test->nrounds != 0; test++) {
	printf("%s: ", test->label);
	chacha_init(&ctx, test->key, test->nonce, test->counter);

	memset(pt, 0, sizeof(pt));

	/* Keystream block 1 */
	chacha_cipher(&ctx, pt, ct, test->nrounds, sizeof(pt));
	if (memcmp(ct, test->cipher1, sizeof(test->cipher1)) != 0) {
	  fprintf(stderr, "failed\n");
	  return(-1);
	}

	/* Keystream block 2 */
	chacha_cipher(&ctx, pt, ct, test->nrounds, sizeof(pt));
	if (memcmp(ct, test->cipher2, sizeof(test->cipher2)) != 0) {
	  fprintf(stderr, "failed\n");
	  return(-1);
	}
	printf("ok\n");
  }

  return(0);
}

static int
do_chacha_core_tests(void)
{
  Chacha_ctx ctx;
  uint32_t counter;
  uint32_t a, b, c, d;
  uint8_t ct[64], iv[12], key[32], plain[64];

  fprintf(stderr, "ChaCha Core Tests...\n");
  {
	/*
	 * https://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-01
	 * Expect:
	 * Keystream block 1:
	 * Keystream block 2:
	 */
	static uint8_t cipher1[] = {
	  0x3e, 0x00, 0xef, 0x2f, 0x89, 0x5f, 0x40, 0xd6,
	  0x7f, 0x5b, 0xb8, 0xe8, 0x1f, 0x09, 0xa5, 0xa1,
	  0x2c, 0x84, 0x0e, 0xc3, 0xce, 0x9a, 0x7f, 0x3b,
	  0x18, 0x1b, 0xe1, 0x88, 0xef, 0x71, 0x1a, 0x1e,
	  0x98, 0x4c, 0xe1, 0x72, 0xb9, 0x21, 0x6f, 0x41,
	  0x9f, 0x44, 0x53, 0x67, 0x45, 0x6d, 0x56, 0x19,
	  0x31, 0x4a, 0x42, 0xa3, 0xda, 0x86, 0xb0, 0x01,
	  0x38, 0x7b, 0xfd, 0xb8, 0x0e, 0x0c, 0xfe, 0x42
	};
	static uint8_t cipher2[] = {
	  0xd2, 0xae, 0xfa, 0x0d, 0xea, 0xa5, 0xc1, 0x51,
	  0xbf, 0x0a, 0xdb, 0x6c, 0x01, 0xf2, 0xa5, 0xad,
	  0xc0, 0xfd, 0x58, 0x12, 0x59, 0xf9, 0xa2, 0xaa,
	  0xdc, 0xf2, 0x0f, 0x8f, 0xd5, 0x66, 0xa2, 0x6b,
	  0x50, 0x32, 0xec, 0x38, 0xbb, 0xc5, 0xda, 0x98,
	  0xee, 0x0c, 0x6f, 0x56, 0x8b, 0x87, 0x2a, 0x65,
	  0xa0, 0x8a, 0xbf, 0x25, 0x1d, 0xeb, 0x21, 0xbb,
	  0x4b, 0x56, 0xe5, 0xd8, 0x82, 0x1e, 0x68, 0xaa
	};

	fprintf(stderr, "Core Test 1: ");
	memset(iv, 0, sizeof(iv));
	memset(key, 0, sizeof(key));

	counter = 0;
	chacha_init(&ctx, key, iv, counter);

	memset(plain, 0, sizeof(plain));
	chacha_cipher(&ctx, plain, ct, 8, sizeof(plain));
	if (memcmp(ct, cipher1, sizeof(cipher1)) != 0) {
	  fprintf(stderr, "failed!\n");
	  show((uint8_t *) ct, sizeof(ct));
	  printf("\n");
	  return(-1);
	}

	chacha_cipher(&ctx, plain, ct, 8, sizeof(plain));
	if (memcmp(ct, cipher2, sizeof(cipher2)) != 0) {
	  fprintf(stderr, "failed!\n");
	  show((uint8_t *) ct, sizeof(ct));
	  printf("\n");
	  return(-1);
	}

	fprintf(stderr, "ok\n");
  }

  {
	/* https://tools.ietf.org/html/rfc7539 */
	int i, nrounds;
	uint32_t state[16];
	uint8_t *p;
	static uint8_t key[32] = {
	  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
	  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
	  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
	};
	static uint8_t nonce[12] = {
	  0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00
	};
	static uint32_t counter = 1;
	static uint32_t expect1[] = {
	  0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
	  0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
	  0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
	  0x00000001, 0x09000000, 0x4a000000, 0x00000000
	};
	static uint32_t expect2[] = {
	  0x837778ab, 0xe238d763, 0xa67ae21e, 0x5950bb2f,
	  0xc4f2d0c7, 0xfc62bb2f, 0x8fa018fc, 0x3f5ec7b7,
	  0x335271c2, 0xf29489f3, 0xeabda8fc, 0x82e46ebd, 
	  0xd19c12b4, 0xb04e16de, 0x9e83d0cb, 0x4e3c50a2
	};
	static uint32_t expect3[] = {
	  0xe4e7f110, 0x15593bd1, 0x1fdd0f50, 0xc47120a3,
	  0xc7f4d1c7, 0x0368c033, 0x9aaa2204, 0x4e6cd4c3,
	  0x466482d2, 0x09aa9f07, 0x05d7c214, 0xa2028bd9,
	  0xd19c12b5, 0xb94e16de, 0xe883d0cb, 0x4e3c50a2
	};
	static uint8_t expect4[] = {
	  0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15,
	  0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
	  0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03,
	  0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
	  0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09,
	  0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
	  0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9,
	  0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e
	};

	fprintf(stderr, "Core Test 2: ");
	chacha_init(&ctx, key, nonce, counter);
	if (!blkeq_ui32(ctx.input, expect1, 16)) {
	  fprintf(stderr, "failed!\n");
	  show32(ctx.input, 16);
	  return(-1);
	}
	fprintf(stderr, "ok\n");

	/* Make a copy. */
	memcpy(state, ctx.input, sizeof(ctx.input));

	fprintf(stderr, "Core Test 3: ");
	nrounds = 10;
#define R(a, b) (((a) << (b)) | ((a) >> (32 - (b))))
#define X(N)	ctx.input[N]
    for (i = 0; i < nrounds; i++) {
      QUARTER_ROUND( X(0), X(4), X(8),X(12));
      QUARTER_ROUND( X(1), X(5), X(9),X(13));
      QUARTER_ROUND( X(2), X(6),X(10),X(14));
      QUARTER_ROUND( X(3), X(7),X(11),X(15));
      QUARTER_ROUND( X(0), X(5),X(10),X(15));
      QUARTER_ROUND( X(1), X(6),X(11),X(12));
      QUARTER_ROUND( X(2), X(7), X(8),X(13));
      QUARTER_ROUND( X(3), X(4), X(9),X(14));
    }
#undef X

	if (!blkeq_ui32(ctx.input, expect2, 16)) {
	  fprintf(stderr, "failed!\n");
	  show32(ctx.input, 16);
	  return(-1);
	}
	fprintf(stderr, "ok\n");

	fprintf(stderr, "Core Test 4: ");
	blkadd_ui32(ctx.input, state, 16);
	if (!blkeq_ui32(ctx.input, expect3, 16)) {
	  fprintf(stderr, "failed!\n");
	  show32(ctx.input, 16);
	  return(-1);
	}
	fprintf(stderr, "ok\n");

	fprintf(stderr, "Core Test 5: ");
	p = (uint8_t *) &ctx.input[0];
	if (memcmp(p, expect4, sizeof(ctx.input)) != 0) {
	  fprintf(stderr, "failed!\n");
	  show(p, sizeof(ctx.input));
	  return(-1);
	}
	fprintf(stderr, "ok\n");
  }

  {
	uint32_t *x;
	uint32_t state_2_2_1[16] = {
	  0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a,
	  0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c,
	  0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963,
	  0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320
	};

	fprintf(stderr, "Core Test 6: ");
	memcpy(ctx.input, (void *) state_2_2_1, sizeof(state_2_2_1));
	x = &ctx.input[0];
	QUARTER_ROUND(x[2], x[7], x[8], x[13]);
	if (x[2] != 0xbdb886dc
		|| x[7] != 0xcfacafd2
		|| x[8] != 0xe46bea80
		|| x[13] != 0xccc07c79) {
	  fprintf(stderr, "failed!\n");
	  fprintf(stderr, "0x%08x 0x%08x 0x%08x 0x%08x\n", x[2], x[7], x[8], x[13]);
	  return(-1);
	}
	fprintf(stderr, "ok\n");
  }

  fprintf(stderr, "Core Test 7: ");
  a = 0x11111111;
  b = 0x01020304;
  c = 0x9b8d6f43;
  d = 0x01234567;
  QUARTER_ROUND(a, b, c, d);
  if (a != 0xea2a92f4 || b != 0xcb1cf8ce || c != 0x4581472e || d != 0x5881c4bb) {
	fprintf(stderr, "failed!\n");
	fprintf(stderr, "0x%08x 0x%08x 0x%08x 0x%08x\n", a, b, c, d);
	return(-1);
  }
  fprintf(stderr, "ok\n");

  return(0);
#undef R
}

static int
do_chacha_perf_test(void)
{
  int i;
  size_t need;
  char *inl, *opt;
  uint8_t *ct, *pt, *mem_in, *mem_out;
  struct timespec end_time, start_time;
  unsigned long diff_msecs, end_msecs, start_msecs;
  Chacha_ctx ctx;
  static uint32_t counter = 17;
  static uint8_t nonce[16] = {
	0x01, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
	0x17, 0x1d, 0x1f, 0x25, 0x27, 0x29, 0x2b, 0x2f
  };
  uint8_t key[32];

#ifdef __OPTIMIZE__
  opt = "optimized";
#else
  opt = "not optimized";
#endif

#ifdef __NO_INLINE__
  inl = "disabled";
#else
  inl = "enabled";
#endif

  fprintf(stderr, "ChaCha performance test (%s, inlining %s): ", opt, inl);

#define BYTES_PER_MB		(1024 * 1024)
  need = BYTES_PER_MB * 64;
  if ((mem_in = (uint8_t *) malloc(need)) == NULL) {
	fprintf(stderr, "failed!\n");
	return(-1);
  }

  if ((mem_out = (uint8_t *) malloc(need)) == NULL) {
	fprintf(stderr, "failed!\n");
	return(-1);
  }

  memset(mem_in, 17, need);
  crypto_randomize_buffer(key, sizeof(key));

  start_time.tv_sec = 0;
  start_time.tv_nsec = 0;
  clock_gettime(CLOCK_VIRTUAL, &start_time);

  chacha_init(&ctx, key, nonce, counter);

  pt = mem_in;
  ct = mem_out;
  for (i = 0; i < 20; i++) {
	uint8_t *tmp;

	chacha_cipher(&ctx, pt, ct, 20, need);
	tmp = pt;
	pt = ct;
	ct = tmp;
  }

  clock_gettime(CLOCK_VIRTUAL, &end_time);
  start_msecs = (start_time.tv_sec * 1000) + (start_time.tv_nsec / 1000000);
  end_msecs = (end_time.tv_sec * 1000) + (end_time.tv_nsec / 1000000);
  diff_msecs = end_msecs - start_msecs;

  fprintf(stderr, "ok\n");
  fprintf(stderr, "Throughput: %.2f MB/sec\n",
		  ((need / BYTES_PER_MB)) / (diff_msecs / 1000.0));

#ifdef NOTDEF
  {
	int iv_len, key_len, len;
	unsigned char *ivp;
	Cipher_handle *ch;

	fprintf(stderr, "%s: ", CRYPTO_TEST_CIPHER_NAME);
	ivp = nonce;
	if ((ch = crypto_cipher_open(CRYPTO_SYMMETRIC_ENCRYPT,
								 CRYPTO_TEST_CIPHER_NAME,
								 NULL, NULL, NULL)) == NULL) {
	  fprintf(stderr, "open failed\n");
	  return(-1);
	}

	key_len = EVP_CIPHER_CTX_key_length(ch->ctx);
	iv_len = EVP_CIPHER_CTX_iv_length(ch->ctx);
	fprintf(stderr, "key_len=%d, iv_len=%d: ", key_len, iv_len);
	if (key_len > sizeof(key)) {
	  fprintf(stderr, "Key is too small for cipher\n");
	  return(-1);
	}
	if (iv_len > sizeof(nonce)) {
	  fprintf(stderr, "Nonce is too small for cipher\n");
	  return(-1);
	}
	EVP_CipherInit_ex(ch->ctx, NULL, NULL, key, ivp, 1);

	start_time.tv_sec = 0;
	start_time.tv_nsec = 0;
	clock_gettime(CLOCK_VIRTUAL, &start_time);

	pt = mem_in;
	ct = mem_out;
	for (i = 0; i < 20; i++) {
	  uint8_t *tmp;

	  crypto_cipher_encrypt(ch, pt, need, ct, &len);
	  tmp = pt;
	  pt = ct;
	  ct = tmp;
	}
	crypto_cipher_close(ch, NULL, &len);

	clock_gettime(CLOCK_VIRTUAL, &end_time);
	start_msecs = (start_time.tv_sec * 1000) + (start_time.tv_nsec / 1000000);
	end_msecs = (end_time.tv_sec * 1000) + (end_time.tv_nsec / 1000000);
	diff_msecs = end_msecs - start_msecs;

	fprintf(stderr, "ok\n");
	fprintf(stderr, "Throughput: %.2f MB/sec\n",
			((need / BYTES_PER_MB)) / (diff_msecs / 1000.0));
  }
#endif

  free(mem_in);
  free(mem_out);

  return(0);
}

/*
 * Using random parameters, verify that decrypting an encrypted random message
 * yields the original message.
 */
static int
do_chacha_cha(void)
{
  uint8_t *message, *output1, *output2;
  uint8_t key[32], nonce[12];
  uint32_t counter;

  fprintf(stderr, "ChaCha random encipher/decipher test... ");
  crypto_randomize_buffer(key, sizeof(key));
  crypto_randomize_buffer(nonce, sizeof(nonce));
  crypto_randomize_buffer((unsigned char *) &counter, sizeof(counter));

#define CHACHA_TEST_MESSAGE_NBYTES		6089		/* Arbitrary but large-ish */

  message = crypto_make_random_string(NULL, CHACHA_TEST_MESSAGE_NBYTES);
  output1 = malloc(CHACHA_TEST_MESSAGE_NBYTES);
  output2 = malloc(CHACHA_TEST_MESSAGE_NBYTES);

  chacha_cipher_message(message, output1, CHACHA_ROUNDS,
						CHACHA_TEST_MESSAGE_NBYTES,
						key, nonce, counter);

  chacha_cipher_message(output1, output2, CHACHA_ROUNDS,
						CHACHA_TEST_MESSAGE_NBYTES,
						key, nonce, counter);

  if (memcmp(message, output2, CHACHA_TEST_MESSAGE_NBYTES) != 0) {
	fprintf(stderr, "failed!\n");
	fprintf(stderr, "Please copy this output and submit with a bug report:\n");
	fprintf(stderr, "key:\n");
	show(key, sizeof(key));
	fprintf(stderr, "nonce:\n");
	show(nonce, sizeof(nonce));
	fprintf(stderr, "counter: %u\n", counter);
	fprintf(stderr, "message:\n");
	show(message, CHACHA_TEST_MESSAGE_NBYTES);
	return(-1);
  }

  fprintf(stderr, "ok\n");
  return(0);
}

static int
do_chacha_tests(void)
{

  if (do_chacha_core_tests() == -1)
	return(-1);

  if (do_chacha_strombergson() == -1)
	return(-1);

  if (do_chacha_cha() == -1)
	return(-1);

  if (do_chacha_perf_test() == -1)
	return(-1);

  return(0);
}
#undef R

/*
 * This signature value was obtained using:
 *   echo "Please sign me" | openssl dgst -sha1 \
 *     -sign tests/private_test_key.pem -binary | gbase64
 */
static char *sign_test2_sig_value =
  "UjYbp3PDQK25XALNU+yl5sk9KC0nEQrvIfEX3atg/P8ITNUSC+hUG8T7q4mWqTVpQJAzes55gmHLm/t4IFmWLMyWJ1wtu6gSl18uXqJXRNA9dIuQ3DpXRaJ19SLMQEfp9HIPHZX/R/U3W2ZhsZb+UCz6ychVVefJuyXoZbQDoKY=";

int
main(int argc, char **argv)
{
  int i, declen, enclen, one_only;
  int avoid_argon2;
  char *buf, *errmsg, *p;
  unsigned char *decp, *encp;
  Crypt_keys *ck;

  avoid_argon2 = 0;
  one_only = 0;
  errmsg = NULL;

  if (dacs_init(DACS_STANDALONE, &argc, &argv, NULL, &errmsg) == -1) {
  fail:
    if (errmsg != NULL)
      fprintf(stderr, "crypto: %s\n", errmsg);

	exit(1);
  }

  if (argv[1] != NULL) {
	if (argv[2] != NULL) {
	  errmsg = "Unrecognized argument";
	  goto fail;
	}

	/*
	 * Argon2, which uses POSIX threads, seems to get hung up on something
	 * on occasion.  Provide a flag to suppress its tests, just in case.
	 */
	if (strcaseeq(argv[1], "noargon2")) {
	  avoid_argon2 = 1;
	  goto do_tests;
	}

	one_only = 1;
	if (strcaseeq(argv[1], "list"))
	  goto do_list;
	if (strcaseprefix(argv[1], "blake") != NULL)
	  goto do_blake2;
	if (strcaseprefix(argv[1], "chacha") != NULL)
	  goto do_chacha;
	if (strcaseprefix(argv[1], "argon") != NULL)
	  goto do_argon2;
	if (strcaseprefix(argv[1], "sha512") != NULL)
	  goto do_sha512t;
	if (strcaseprefix(argv[1], "pbkdf") != NULL)
	  goto do_pbkdf2;
	if (strcaseprefix(argv[1], "sha3") != NULL)
	  goto do_sha3;
	if (strcaseprefix(argv[1], "hkdf") != NULL)
	  goto do_hkdf;
	if (strcaseprefix(argv[1], "scrypt") != NULL)
	  goto do_scrypt;
	if (strcaseprefix(argv[1], "oauth") != NULL)
	  goto do_oauth;
	if (strcaseprefix(argv[1], "ivs") != NULL)
	  goto do_ivs;
	if (strcaseprefix(argv[1], "hmac") != NULL)
	  goto do_hmac;
	if (strcaseprefix(argv[1], "encrypt") != NULL)
	  goto do_encrypt;
	if (strcaseprefix(argv[1], "keys") != NULL)
	  goto do_keys;
	fprintf(stderr, "Unrecognized argument: \"%s\"\n", argv[1]);
	exit(1);
  }

 do_tests:
  fprintf(stderr, "Testing crypto...\n");

 do_list:
  fprintf(stderr, "Available digest functions:\n");
  crypto_show_digest_info(stderr, NULL);
  fprintf(stderr, "\n");
  if (one_only)
	exit(0);

 do_chacha:
  fprintf(stderr, "Doing ChaCha tests:\n");
  if (do_chacha_tests() == -1)
	exit(1);
  fprintf(stderr, "All ChaCha tests succeeded\n\n");
  if (one_only)
	exit(0);

 do_blake2:
  fprintf(stderr, "Doing Blake2 tests:\n");
  if (do_blake2_tests() == -1)
	exit(1);
  fprintf(stderr, "All Blake2 tests succeeded\n\n");
  if (one_only)
	exit(0);

 do_argon2:
  if  (!avoid_argon2) {
	fprintf(stderr, "Doing Argon2 tests:\n");
	if (do_argon2_tests() == -1)
	  exit(1);
	fprintf(stderr, "All Argon2 tests succeeded\n\n");
	if (one_only)
	  exit(0);
  }
  else
	fprintf(stderr, "Not doing Argon2 tests.\n");

 do_scrypt:
  /* XXX this can fail if too much memory is required for any one of the tests */
  fprintf(stderr, "Doing scrypt tests:\n");
  errno = 0;
  if (do_scrypt_tests() == -1) {
	if (errno != 0)
	  fprintf(stderr, "Error: %s\n", strerror(errno));
	fprintf(stderr, "These tests may fail if there is insufficient memory.\n");
	fprintf(stderr, "Everything might be ok if you try again.\n");
	exit(1);
  }
  fprintf(stderr, "All scrypt tests succeeded\n\n");
  if (one_only)
	exit(0);
  
 do_sha512t:
  fprintf(stderr, "Doing SHA512t tests:\n");
  if (do_sha512t_test() == -1)
	exit(1);
  fprintf(stderr, "All SHA512t tests succeeded\n\n");
  if (one_only)
	exit(0);

  /*
   * Test PBKDF2 against RFC 3962
   * and http://www.ietf.org/id/draft-josefsson-pbkdf2-test-vectors-00.txt
   */
 do_pbkdf2:
  fprintf(stderr, "Doing PBKDF2 tests:\n");
  for (i = 0; pbkdf2_vec[i].pwd != NULL; i++) {
	if (do_pbkdf2_test(i + 1, pbkdf2_vec + i) == -1)
	  exit(1);
  }
  fprintf(stderr, "All PBKDF2 tests succeeded\n\n");
  if (one_only)
	exit(0);

 do_sha3:
  fprintf(stderr, "Doing SHA3 tests:\n");
  if (do_sha3_tests() == -1)
	exit(1);
  if (do_sha3_bit_tests() == -1)
	exit(1);
  fprintf(stderr, "All SHA3 tests succeeded\n\n");
  if (one_only)
	exit(0);

 do_hkdf:
  fprintf(stderr, "Doing HKDF tests:\n");
  if (do_hkdf_tests() == -1)
	exit(1);
  fprintf(stderr, "All HKDF tests succeeded\n\n");
  if (one_only)
	exit(0);

 do_oauth:
  fprintf(stderr, "Doing OAuth tests:\n");
  if (do_oauth_test() == -1)
	exit(1);
  fprintf(stderr, "All OAuth tests succeeded\n\n");
  if (one_only)
	exit(0);

 do_ivs:
  fprintf(stderr, "Random IVs:\n");
  for (i = 0; i < 4; i++) {
	make_iv((unsigned char *) "hello, world", 20, &encp);
	fprintf(stderr, "%s", hexdump(encp, 20));
  }
  if (one_only)
	exit(0);

 do_hmac:
  fprintf(stderr, "\n");
  hmac_test();
  nist_hmac_test("tests/HMAC.txt");
  rfc4231_hmac_test();
  if (one_only)
	exit(0);

 do_encrypt:
  fprintf(stderr, "Testing symmetric encryption... ");
  encp = crypto_encipher(CRYPTO_SYMMETRIC_ENCRYPT, CRYPTO_TEST_CIPHER_NAME,
						 (unsigned char *) "hello, world", NULL,  NULL,
						 (unsigned char *) "abcdefghijkl", 13, NULL, &enclen);
  decp = crypto_decipher(CRYPTO_SYMMETRIC_DECRYPT, CRYPTO_TEST_CIPHER_NAME,
						 (unsigned char *) "hello, world", NULL, NULL,
						 encp, enclen, NULL, &declen);
  p = ds_xprintf("*%s*%d", decp, declen);
  if (!streq(p, "*abcdefghijkl*13")) {
	fprintf(stderr, "Failed!\n");
	exit(1);
  }
  fprintf(stderr, "ok\n");
  if (one_only)
	exit(0);

 do_keys:
  if ((ck = get_keys("tests/test_keys")) == NULL)
	fprintf(stderr, "Cannot load test keys for PKI testing, skipping.\n");
  else {
	int st;
	char *d_tmp, *e_tmp, *fn, *plain, *sig;
	unsigned char *iv;
	PKI_session_keys sk;

	/* Write the test keys in PEM format. */
	fprintf(stderr, "Writing test keys in PEM format... ");
	fn = "tests/private_test_key.pem";
	st = write_privatekey(ck, fn, NULL, NULL, 0, NULL, NULL);
	if (st == -1) {
	  fprintf(stderr, "Could not write private test key to %s\n", fn);
	  exit(1);
	}
	fn = "tests/public_test_key.pem";
	st = write_publickey(ck, fn);
	if (st == -1) {
	  fprintf(stderr, "Could not write public test key to %s\n", fn);
	  exit(1);
	}
	fprintf(stderr, "ok\n");

	crypto_signv_test(ck, "sha256", "Please sign me");

	fprintf(stderr, "Testing public key signature2... ");
	if ((sig = crypto_sign_test("Please sign me\n", ck, 0)) == NULL) {
	  fprintf(stderr, "Failed!\n");
	  exit(1);
	}
	if (!streq(sig, sign_test2_sig_value)) {
	  fprintf(stderr, "Failed!\n");
	  exit(1);
	}
	fprintf(stderr, "ok, sig=%s\n", sig);

	fprintf(stderr, "Testing public key encryption... ");
	plain = "abcdefghijkl";
	iv = NULL;
	encp = crypto_encipher(CRYPTO_PKI_ENCRYPT, CRYPTO_TEST_CIPHER_NAME,
						   (unsigned char *) ck->public_key, &iv, &sk,
						   (unsigned char *) plain, strlen(plain) + 1,
						   NULL, &enclen);
	strba64(encp, enclen, &e_tmp);
	decp = crypto_decipher(CRYPTO_PKI_DECRYPT, CRYPTO_TEST_CIPHER_NAME,
						   (unsigned char *) ck->private_key, &iv, &sk,
						   encp, enclen, NULL, &declen);
	if (declen != strlen(plain) + 1 || memcmp(plain, decp, declen)) {
	  fprintf(stderr, "Failed!\n");
	  exit(1);
	}
	fprintf(stderr, "ok\n");

	fprintf(stderr, "Testing public key encryption export/import... ");
	if ((st = crypto_pki_test(CRYPTO_TEST_CIPHER_NAME, ck->public_key,
							  ck->private_key, "tests/HMAC.txt")) == 1)
	  fprintf(stderr, "ok\n");
	else {
	  fprintf(stderr, "Failed!\n");
	  exit(1);
	}
  }
  if (one_only)
	exit(0);

  fprintf(stderr, "All crypto tests succeeded!\n");

  exit(0);
}
#endif
