
/*
 * Auxiliary cryptographic functions
 * These are mainly older functions that are needed for interoperability
 * or completeness reasons (such as for NTLM authentication) and for which we'd
 * rather not require any additional dependencies (e.g., OpenSSL).
 * Efficiency is not much of a consideration.
 *
 * Note: this should be standalone - do not bring in any DACS dependencies.
 *
 * RFC 2104 Appendix
 * https://tools.ietf.org/html/rfc2104
 */

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

#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "crypto_aux.h"

#ifndef MAYBE_UNUSED
#define MAYBE_UNUSED   __attribute__ ((__unused__))
#endif

#ifndef lint
static MAYBE_UNUSED const char copyright[] =
"Copyright (c) 2003-2018\n\
Distributed Systems Software.  All rights reserved.";
static MAYBE_UNUSED const char revid[] =
  "$Id: crypto_aux.c 2981 2018-01-23 22:43:42Z brachman $";
#endif

/*
 * rc4.c
 *
 * Copyright (c) 1996-2000 Whistle Communications, Inc.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty, use and
 * redistribution of this software, in source or object code forms, with or
 * without modifications are expressly permitted by Whistle Communications;
 * provided, however, that:
 * 1. Any and all reproductions of the source or object code must include the
 *    copyright notice above and the following disclaimer of warranties; and
 * 2. No rights are granted, in any manner or form, to use Whistle
 *    Communications, Inc. trademarks, including the mark "WHISTLE
 *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
 *    such appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

/*
 * This implementation of the Alleged RC4 (ARC4) stream cipher is from FreeBSD:
 * /usr/src/sys/crypto/rc4/rc4.[ch]
 * $FreeBSD: releng/10.3/sys/crypto/rc4/rc4.c 241394 2012-10-10 08:36:38Z kevlo $
 * $FreeBSD: src/sys/crypto/rc4/rc4.h,v 1.3 2000/07/16 05:53:14 peter Exp $
 *
 * NOTE:
 * RC4 has a variety of severe cryptographic weaknesses and is considered to be
 * insecure, particularly if the beginning of the generated keystream is not
 * discarded or if nonrandom or related keys are used.
 *
 * It is included here only for interoperation with NTLM and should
 * not be used for any (other) applications that require secure encryption.
 */

static __inline void
swap_bytes(uint8_t *a, uint8_t *b)
{
  uint8_t temp;

  temp = *a;
  *a = *b;
  *b = temp;
}

/*
 * Initialize an RC4 state buffer using the supplied key,
 * which can have arbitrary length.
 */
RC4_ctx *
rc4_init(RC4_ctx *const state, const uint8_t *key, size_t keylen)
{
  size_t i, k;
  uint8_t j;

  /* Initialize state with identity permutation. */
  for (i = 0; i < 256; i++)
	state->perm[i] = (uint8_t) i; 

  state->index1 = 0;
  state->index2 = 0;
  
  /* Randomize the permutation using key data. */
  for (j = i = k = 0; i < 256; i++) {
	j += state->perm[i] + key[k]; 
	swap_bytes(&state->perm[i], &state->perm[j]);
	if (++k >= keylen)
	  k = 0;
  }

  return(state);
}

/*
 * Encrypt some data using the supplied RC4 state buffer.
 * The input and output buffers may be the same buffer.
 * Since RC4 is a stream cipher, this function is used
 * for both encryption and decryption.
 */
void
rc4_crypt(RC4_ctx *const state, const uint8_t *inbuf, uint8_t *outbuf,
		  size_t buflen)
{
  size_t i;
  uint8_t j;

  for (i = 0; i < buflen; i++) {
	/* Update modification indices. */
	state->index1++;
	state->index2 += state->perm[state->index1];

	/* Modify permutation. */
	swap_bytes(&state->perm[state->index1], &state->perm[state->index2]);

	/* Encrypt (or decrypt) next byte. */
	j = state->perm[state->index1] + state->perm[state->index2];
	outbuf[i] = inbuf[i] ^ state->perm[j];
  }
}

/*
 * This code implements the MD5 message-digest algorithm.
 * The algorithm is due to Ron Rivest.  This code was
 * written by Colin Plumb in 1993, no copyright is claimed.
 * This code is in the public domain; do with it what you wish.
 *
 * Equivalent code is available from RSA Data Security, Inc.
 * This code has been tested against that, and is equivalent,
 * except that you don't need to include two pages of legalese
 * with every copy.
 *
 * To compute the message digest of a chunk of bytes,
 * pass an MD5 context structure to MD5_init(), pass MD5_update()
 * a buffer of bytes as many times as necessary, and then call MD5_final to
 * fill the supplied 16-byte (MD5_DIGEST_LEN) array with the computed digest.
 *
 * Note: this digest algorithm is no longer considered secure and
 * is used here only for compatibility reasons.  Use a better cryptographic
 * digest algorithm for other applications.
 *
 * RFC 1321 https://tools.ietf.org/html/rfc1321
 */

/*
 * Includes minor modifications for DACS.
 */

static void MD5_transform(uint32_t buf[4], uint32_t const in[16]);

/*
 * Note: this code is harmless on little-endian machines.
 */
static void
byteReverse(uint8_t *buf, unsigned int longs)
{
  uint32_t t;

  do {
    t = (uint32_t) ((unsigned int) buf[3] << 8 | buf[2]) << 16
	  | ((unsigned int) buf[1] << 8 | buf[0]);
    *(uint32_t *) buf = t;
    buf += 4;
  } while (--longs);
}

/*
 * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
 * initialization constants.
 */
void
MD5_init(MD5_ctx *ctx)
{

  ctx->buf[0] = 0x67452301;
  ctx->buf[1] = 0xefcdab89;
  ctx->buf[2] = 0x98badcfe;
  ctx->buf[3] = 0x10325476;

  ctx->bits[0] = 0;
  ctx->bits[1] = 0;
}

/*
 * Update context to reflect the concatenation of another buffer full of bytes.
 */
void
MD5_update(MD5_ctx *ctx, const uint8_t *buf, size_t len)
{
  uint32_t t;

  /* Update bitcount */

  t = ctx->bits[0];
  if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
    ctx->bits[1]++;     /* Carry from low to high */
  ctx->bits[1] += len >> 29;

  t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data. */

  /* Handle any leading odd-sized chunks. */
  if (t) {
    uint8_t *p = (uint8_t *) ctx->in + t;

    t = 64 - t;
    if (len < t) {
	  memmove(p, buf, len);
	  return;
    }

    memmove(p, buf, t);
    byteReverse(ctx->in, 16);
    MD5_transform(ctx->buf, (uint32_t *) ctx->in);
    buf += t;
    len -= t;
  }

  /* Process data in 64-byte chunks. */
  while (len >= 64) {
    memmove(ctx->in, buf, 64);
    byteReverse(ctx->in, 16);
    MD5_transform(ctx->buf, (uint32_t *) ctx->in);
    buf += 64;
    len -= 64;
  }

  /* Handle any remaining bytes of data. */
  memmove(ctx->in, buf, len);
}

/*
 * Final wrapup - pad to 64-byte boundary with the bit pattern 
 * 1 0* (64-bit count of bits processed, MSB-first).
 * DIGEST is a 16-byte buffer (MD5_DIGEST_LEN) where the result is copied.
 */
uint8_t *
MD5_final(MD5_ctx *ctx, uint8_t *digest)
{
  unsigned int count;
  uint8_t *p;

  /* Compute number of bytes mod 64. */
  count = (ctx->bits[0] >> 3) & 0x3F;

  /*
   * Set the first char of padding to 0x80.  This is safe since there is
   * always at least one byte free.
   */
  p = ctx->in + count;
  *p++ = 0x80;

  /* Bytes of padding needed to make 64 bytes. */
  count = 64 - 1 - count;

  /* Pad out to 56 mod 64. */
  if (count < 8) {
    /* Two lots of padding:  Pad the first block to 64 bytes. */
    memset(p, 0, count);
    byteReverse(ctx->in, 16);
    MD5_transform(ctx->buf, (uint32_t *) ctx->in);

    /* Now fill the next block with 56 bytes. */
    memset(ctx->in, 0, 56);
  } else {
    /* Pad block to 56 bytes. */
    memset(p, 0, count - 8);
  }
  byteReverse(ctx->in, 14);

  /* Append length in bits and transform. */
  ((uint32_t *) ctx->in)[14] = ctx->bits[0];
  ((uint32_t *) ctx->in)[15] = ctx->bits[1];

  MD5_transform(ctx->buf, (uint32_t *) ctx->in);
  byteReverse((uint8_t *) ctx->buf, 4);
  memmove(digest, ctx->buf, 16);
  memset(ctx, 0, sizeof(*ctx));   /* In case it's sensitive. */

  return(digest);
}

/* The four core functions - F1 is optimized somewhat. */

/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))

/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s)							\
  ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )

/*
 * The core of the MD5 algorithm, this alters an existing MD5 hash to
 * reflect the addition of 16 longwords of new data.  MD5Update blocks
 * the data and converts bytes into longwords for this routine.
 */
static void
MD5_transform(uint32_t buf[4], uint32_t const in[16])
{
  uint32_t a, b, c, d;

  a = buf[0];
  b = buf[1];
  c = buf[2];
  d = buf[3];

  MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
  MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
  MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
  MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
  MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
  MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
  MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
  MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
  MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
  MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);

  MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
  MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
  MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
  MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
  MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
  MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
  MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
  MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
  MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
  MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);

  MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
  MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
  MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
  MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
  MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
  MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
  MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
  MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
  MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
  MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23);

  MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
  MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10);
  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
  MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21);
  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
  MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10);
  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
  MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21);
  MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f, 6);
  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
  MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15);
  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
  MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82, 6);
  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
  MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15);
  MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21);

  buf[0] += a;
  buf[1] += b;
  buf[2] += c;
  buf[3] += d;
}

uint8_t *
crypto_hmac_md5(uint8_t *key, size_t key_len, uint8_t *text, size_t text_len,
				uint8_t **inner, size_t *ilen, uint8_t **hmac, size_t *hlen)
{
  int i;
  MD5_ctx context;
  uint8_t *digest;
  uint8_t k_ipad[65];    /* inner padding - key XORd with ipad */
  uint8_t k_opad[65];    /* outer padding - key XORd with opad */
  uint8_t tk[16];

  /* If key is longer than 64 bytes, reset it to key=MD5(key). */
  if (key_len > 64) {
	MD5_ctx tctx;

	MD5_init(&tctx);
	MD5_update(&tctx, key, key_len);
	MD5_final(&tctx, tk);

	key = tk;
	key_len = 16;
  }

  /*
   * The HMAC_MD5 transform looks like:
   *
   * MD5(K XOR opad, MD5(K XOR ipad, text))
   *
   * where K is an n byte key
   * ipad is the byte 0x36 repeated 64 times
   * opad is the byte 0x5c repeated 64 times
   * and text is the data being protected
   */

  /* Start out by storing key in pads. */
  memset(k_ipad, 0, sizeof(k_ipad));
  memset(k_opad, 0, sizeof(k_opad));
  memcpy(k_ipad, key, key_len);
  memcpy(k_opad, key, key_len);

  /* XOR key with ipad and opad values. */
  for (i = 0; i < 64; i++) {
	k_ipad[i] ^= 0x36;
	k_opad[i] ^= 0x5c;
  }

  digest = (uint8_t *) malloc(16);
  *hmac = digest;
  *hlen = 16;

  /* Perform inner MD5. */
  MD5_init(&context);                   /* init context for 1st pass */
  MD5_update(&context, k_ipad, 64);      /* start with inner pad */
  MD5_update(&context, text, text_len); /* then text of datagram */
  MD5_final(&context, digest);          /* finish up 1st pass */

  if (inner != NULL) {
	*inner = (uint8_t *) malloc(16);
	memcpy(*inner, digest, 16);
	*ilen = 16;
  }

  /* Perform outer MD5. */
  MD5_init(&context);                   /* init context for 2nd pass */
  MD5_update(&context, k_opad, 64);     /* start with outer pad */
  MD5_update(&context, digest, 16);     /* then results of 1st hash */
  MD5_final(&context, digest);          /* finish up 2nd pass */

  return(digest);
}

/*
 * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
 * MD4 Message-Digest Algorithm (RFC 1320).
 * https://tools.ietf.org/html/rfc1320
 *
 * Homepage:
 * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4
 *
 * Author:
 * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
 *
 * This software was written by Alexander Peslyak in 2001.  No copyright is
 * claimed, and the software is hereby placed in the public domain.
 * In case this attempt to disclaim copyright and place the software in the
 * public domain is deemed null and void, then the software is
 * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
 * general public under the following terms:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted.
 *
 * There's ABSOLUTELY NO WARRANTY, express or implied.
 *
 * (This is a heavily cut-down "BSD license".)
 *
 * This differs from Colin Plumb's older public domain implementation in that
 * no exactly 32-bit integer data type is required (any 32-bit or wider
 * unsigned integer data type will do), there's no compile-time endianness
 * configuration, and the function prototypes match OpenSSL's.  No code from
 * Colin Plumb's implementation has been reused; this comment merely compares
 * the properties of the two independent implementations.
 *
 * The primary goals of this implementation are portability and ease of use.
 * It is meant to be fast, but not as fast as possible.  Some known
 * optimizations are not included to reduce source code size and avoid
 * compile-time configuration.
 */

/*
 * Includes minor modifications for DACS.
 */

/*
 * The basic MD4 functions.
 *
 * F and G are optimized compared to their RFC 1320 definitions, with the
 * optimization for F borrowed from Colin Plumb's MD5 implementation.
 */
#define F(x, y, z)          ((z) ^ ((x) & ((y) ^ (z))))
#define G(x, y, z)          (((x) & ((y) | (z))) | ((y) & (z)))
#define H(x, y, z)          ((x) ^ (y) ^ (z))

/*
 * The MD4 transformation for all three rounds.
 */
#define STEP(f, a, b, c, d, x, s)								\
  (a) += f((b), (c), (d)) + (x);								\
  (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));

/*
 * SET reads 4 input bytes in little-endian byte order and stores them
 * in a properly aligned word in host byte order.
 *
 * The check for little-endian architectures that tolerate unaligned
 * memory accesses is just an optimization.  Nothing will break if it
 * doesn't work.
 */
#if defined(__i386__) || defined(__x86_64__)
#define SET(n)				(*(MD4_u32plus *) &ptr[(n) * 4])
#define GET(n)				SET(n)

#else

#define SET(n)								\
  (ctx->block[(n)] =						\
   (MD4_u32plus)ptr[(n) * 4] |				\
   ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) |	\
   ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) |	\
   ((MD4_u32plus)ptr[(n) * 4 + 3] << 24))
#define GET(n)				(ctx->block[(n)])
#endif

/*
 * This processes one or more 64-byte data blocks, but does NOT update
 * the bit counters.  There are no alignment requirements.
 */
static const void *
body(MD4_ctx *ctx, const void *data, unsigned long size)
{
  const unsigned char *ptr;
  MD4_u32plus a, b, c, d;
  MD4_u32plus saved_a, saved_b, saved_c, saved_d;

  ptr = (const unsigned char *) data;

  a = ctx->a;
  b = ctx->b;
  c = ctx->c;
  d = ctx->d;

  do {
	saved_a = a;
	saved_b = b;
	saved_c = c;
	saved_d = d;

	/* Round 1 */
	STEP(F, a, b, c, d, SET(0), 3);
	STEP(F, d, a, b, c, SET(1), 7);
	STEP(F, c, d, a, b, SET(2), 11);
	STEP(F, b, c, d, a, SET(3), 19);
	STEP(F, a, b, c, d, SET(4), 3);
	STEP(F, d, a, b, c, SET(5), 7);
	STEP(F, c, d, a, b, SET(6), 11);
	STEP(F, b, c, d, a, SET(7), 19);
	STEP(F, a, b, c, d, SET(8), 3);
	STEP(F, d, a, b, c, SET(9), 7);
	STEP(F, c, d, a, b, SET(10), 11);
	STEP(F, b, c, d, a, SET(11), 19);
	STEP(F, a, b, c, d, SET(12), 3);
	STEP(F, d, a, b, c, SET(13), 7);
	STEP(F, c, d, a, b, SET(14), 11);
	STEP(F, b, c, d, a, SET(15), 19);

	/* Round 2 */
	STEP(G, a, b, c, d, GET(0) + 0x5a827999, 3);
	STEP(G, d, a, b, c, GET(4) + 0x5a827999, 5);
	STEP(G, c, d, a, b, GET(8) + 0x5a827999, 9);
	STEP(G, b, c, d, a, GET(12) + 0x5a827999, 13);
	STEP(G, a, b, c, d, GET(1) + 0x5a827999, 3);
	STEP(G, d, a, b, c, GET(5) + 0x5a827999, 5);
	STEP(G, c, d, a, b, GET(9) + 0x5a827999, 9);
	STEP(G, b, c, d, a, GET(13) + 0x5a827999, 13);
	STEP(G, a, b, c, d, GET(2) + 0x5a827999, 3);
	STEP(G, d, a, b, c, GET(6) + 0x5a827999, 5);
	STEP(G, c, d, a, b, GET(10) + 0x5a827999, 9);
	STEP(G, b, c, d, a, GET(14) + 0x5a827999, 13);
	STEP(G, a, b, c, d, GET(3) + 0x5a827999, 3);
	STEP(G, d, a, b, c, GET(7) + 0x5a827999, 5);
	STEP(G, c, d, a, b, GET(11) + 0x5a827999, 9);
	STEP(G, b, c, d, a, GET(15) + 0x5a827999, 13);

	/* Round 3 */
	STEP(H, a, b, c, d, GET(0) + 0x6ed9eba1, 3);
	STEP(H, d, a, b, c, GET(8) + 0x6ed9eba1, 9);
	STEP(H, c, d, a, b, GET(4) + 0x6ed9eba1, 11);
	STEP(H, b, c, d, a, GET(12) + 0x6ed9eba1, 15);
	STEP(H, a, b, c, d, GET(2) + 0x6ed9eba1, 3);
	STEP(H, d, a, b, c, GET(10) + 0x6ed9eba1, 9);
	STEP(H, c, d, a, b, GET(6) + 0x6ed9eba1, 11);
	STEP(H, b, c, d, a, GET(14) + 0x6ed9eba1, 15);
	STEP(H, a, b, c, d, GET(1) + 0x6ed9eba1, 3);
	STEP(H, d, a, b, c, GET(9) + 0x6ed9eba1, 9);
	STEP(H, c, d, a, b, GET(5) + 0x6ed9eba1, 11);
	STEP(H, b, c, d, a, GET(13) + 0x6ed9eba1, 15);
	STEP(H, a, b, c, d, GET(3) + 0x6ed9eba1, 3);
	STEP(H, d, a, b, c, GET(11) + 0x6ed9eba1, 9);
	STEP(H, c, d, a, b, GET(7) + 0x6ed9eba1, 11);
	STEP(H, b, c, d, a, GET(15) + 0x6ed9eba1, 15);

	a += saved_a;
	b += saved_b;
	c += saved_c;
	d += saved_d;

	ptr += 64;
  } while (size -= 64);

  ctx->a = a;
  ctx->b = b;
  ctx->c = c;
  ctx->d = d;

  return(ptr);
}

void
MD4_init(MD4_ctx *ctx)
{

  ctx->a = 0x67452301;
  ctx->b = 0xefcdab89;
  ctx->c = 0x98badcfe;
  ctx->d = 0x10325476;

  ctx->lo = 0;
  ctx->hi = 0;
}

void
MD4_update(MD4_ctx *ctx, const void *data, unsigned long size)
{
  MD4_u32plus saved_lo;
  unsigned long used, available;

  saved_lo = ctx->lo;
  if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
	ctx->hi++;
  ctx->hi += size >> 29;

  used = saved_lo & 0x3f;

  if (used) {
	available = 64 - used;

	if (size < available) {
	  memcpy(&ctx->buffer[used], data, size);
	  return;
	}

	memcpy(&ctx->buffer[used], data, available);
	data = (const unsigned char *) data + available;
	size -= available;
	body(ctx, ctx->buffer, 64);
  }

  if (size >= 64) {
	data = body(ctx, data, size & ~(unsigned long) 0x3f);
	size &= 0x3f;
  }

  memcpy(ctx->buffer, data, size);
}

void
MD4_final(MD4_ctx *ctx, unsigned char *result)
{
  unsigned long used, available;

  used = ctx->lo & 0x3f;

  ctx->buffer[used++] = 0x80;

  available = 64 - used;

  if (available < 8) {
	memset(&ctx->buffer[used], 0, available);
	body(ctx, ctx->buffer, 64);
	used = 0;
	available = 64;
  }

  memset(&ctx->buffer[used], 0, available - 8);

  ctx->lo <<= 3;
  ctx->buffer[56] = ctx->lo;
  ctx->buffer[57] = ctx->lo >> 8;
  ctx->buffer[58] = ctx->lo >> 16;
  ctx->buffer[59] = ctx->lo >> 24;
  ctx->buffer[60] = ctx->hi;
  ctx->buffer[61] = ctx->hi >> 8;
  ctx->buffer[62] = ctx->hi >> 16;
  ctx->buffer[63] = ctx->hi >> 24;

  body(ctx, ctx->buffer, 64);

  result[ 0] = ctx->a;
  result[ 1] = ctx->a >> 8;
  result[ 2] = ctx->a >> 16;
  result[ 3] = ctx->a >> 24;
  result[ 4] = ctx->b;
  result[ 5] = ctx->b >> 8;
  result[ 6] = ctx->b >> 16;
  result[ 7] = ctx->b >> 24;
  result[ 8] = ctx->c;
  result[ 9] = ctx->c >> 8;
  result[10] = ctx->c >> 16;
  result[11] = ctx->c >> 24;
  result[12] = ctx->d;
  result[13] = ctx->d >> 8;
  result[14] = ctx->d >> 16;
  result[15] = ctx->d >> 24;

  memset(ctx, 0, sizeof(*ctx));
}

/*
 * Without any arguments, a series of self-tests are run.
 * If the single argument "md5" (or "md4") is given, the program instead
 * reads stdin and emits the hex encoded MD5 (MD4) digest to stdout.
 * Exit status is zero iff no error occurs.
 */

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

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

  return(buf);
}

#include <sys/types.h>
#include <unistd.h>

/* https://en.wikipedia.org/wiki/MD5 */
typedef struct md5_test {
  char *msg;
  uint8_t digest[MD5_DIGEST_LEN];
} md5_test;

static md5_test md5_self_test[] = {  
  { "The quick brown fox jumps over the lazy dog",
	{ 0x9e, 0x10, 0x7d, 0x9d, 0x37, 0x2b, 0xb6, 0x82,
	  0x6b, 0xd8, 0x1d, 0x35, 0x42, 0xa4, 0x19, 0xd6 },
  },

  { "The quick brown fox jumps over the lazy dog.",
	{
	  0xe4, 0xd9, 0x09, 0xc2, 0x90, 0xd0, 0xfb, 0x1c,
	  0xa0, 0x68, 0xff, 0xad, 0xdf, 0x22, 0xcb, 0xd0
	},
  },

  { "",
	{
	  0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
	  0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e
	},
  },

  { NULL, { 0x00 } }
};

static void
md5_str(char *str, uint8_t *digestp)
{
  MD5_ctx ctx;

  MD5_init(&ctx);
  MD5_update(&ctx, (uint8_t *) str, strlen(str));
  MD5_final(&ctx, digestp);
}

static int
do_md5_self_test(void)
{
  uint8_t digest[64];
  md5_test *test;

  fprintf(stderr, "Doing MD5 tests:\n");

  for (test = &md5_self_test[0]; test->msg != NULL; test++) {
	md5_str(test->msg, digest);
	fprintf(stderr, "\"%s\": ", test->msg);
	if (memcmp(digest, test->digest, MD5_DIGEST_LEN) != 0) {
	  fprintf(stderr, "failed!\n");
	  fprintf(stderr, "expected: %s\n",
			  to_hex(test->digest, MD5_DIGEST_LEN));
	  fprintf(stderr, "got:      %s\n", to_hex(digest, MD5_DIGEST_LEN));
	  return(-1);
	}
	fprintf(stderr, "ok\n");
  }
  fprintf(stderr, "All ok\n");
  return(0);
}

typedef struct md4_test {
  char *msg;
  uint8_t digest[MD4_DIGEST_LEN];
} md4_test;

/* Test vectors are from RFC 1320. */
static md4_test md4_self_test[] = {  
  { "",
	{ 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
	  0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 }
  },

  { "a",
	{ 0xbd, 0xe5, 0x2c, 0xb3, 0x1d, 0xe3, 0x3e, 0x46,
	  0x24, 0x5e, 0x05, 0xfb, 0xdb, 0xd6, 0xfb, 0x24 }
  },

  { "abc",
	{ 0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52,
	  0x5f, 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d }
  },

  { "message digest",
	{ 0xd9, 0x13, 0x0a, 0x81, 0x64, 0x54, 0x9f, 0xe8,
	  0x18, 0x87, 0x48, 0x06, 0xe1, 0xc7, 0x01, 0x4b }
  },

  { "abcdefghijklmnopqrstuvwxyz",
	{ 0xd7, 0x9e, 0x1c, 0x30, 0x8a, 0xa5, 0xbb, 0xcd,
	  0xee, 0xa8, 0xed, 0x63, 0xdf, 0x41, 0x2d, 0xa9 }
  },

  { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
	{ 0x04, 0x3f, 0x85, 0x82, 0xf2, 0x41, 0xdb, 0x35,
	  0x1c, 0xe6, 0x27, 0xe1, 0x53, 0xe7, 0xf0, 0xe4 }
  },

  { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
	{ 0xe3, 0x3b, 0x4d, 0xdc, 0x9c, 0x38, 0xf2, 0x19,
	  0x9c, 0x3e, 0x7b, 0x16, 0x4f, 0xcc, 0x05, 0x36 }
  },

  { NULL, { 0x00 } }
};

static void
md4_str(char *str, uint8_t *digestp)
{
  MD4_ctx ctx;

  MD4_init(&ctx);
  MD4_update(&ctx, str, strlen(str));
  MD4_final(&ctx, digestp);
}

static int
do_md4_self_test(void)
{
  uint8_t digest[64];
  md4_test *test;

  fprintf(stderr, "Doing MD4 tests:\n");

  for (test = &md4_self_test[0]; test->msg != NULL; test++) {
	md4_str(test->msg, digest);
	fprintf(stderr, "\"%s\": ", test->msg);
	if (memcmp(digest, test->digest, MD4_DIGEST_LEN) != 0) {
	  fprintf(stderr, "failed!\n");
	  fprintf(stderr, "expected: %s\n",
			  to_hex(test->digest, MD4_DIGEST_LEN));
	  fprintf(stderr, "got:      %s\n", to_hex(digest, MD4_DIGEST_LEN));
	  return(-1);
	}
	fprintf(stderr, "ok\n");
  }
  fprintf(stderr, "All ok\n");
  return(0);
}

/*
 * Test vectors are from various sources.
 * See RFC 6229 https://tools.ietf.org/html/rfc6229
 */
typedef struct rc4_test {
  size_t key_len;
  size_t offset;		/* Starting offset for key stream test. */
  size_t slen;			/* Length in bytes of key stream test. */
  uint8_t key[128];		/* Key. */
  size_t msg_len;		/* If zero, this is a key stream test. */
  uint8_t msg[128];		/* Message to encipher/decipher. */
  uint8_t exp[128];		/* Expected cipher output or key stream. */
} rc4_test;

static rc4_test rc4_self_test[] = {
  { 8, 0, 0,
	{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef },
	8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0x74, 0x94, 0xc2, 0xe7, 0x10, 0x4b, 0x08, 0x79 }
  },

  { 8, 0, 0,
	{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef },
	8, { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef },
	{ 0x75, 0xb7, 0x87, 0x80, 0x99, 0xe0, 0xc5, 0x96 }
  },

  { 8, 0, 0,
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	{ 0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a }
  },

  { 5, 0, 0,
	{ 0x61, 0x8a, 0x63, 0xd2, 0xfb },
	5, { 0xdc, 0xee, 0x4c, 0xf9, 0x2c },
	{ 0xf1, 0x38, 0x29, 0xc9, 0xde }
  },

  { 16, 0, 16,
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
	0, { 0x00 },
	{ 0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a,
	  0x8a, 0x06, 0x1e, 0x67, 0x57, 0x6e, 0x92, 0x6d }
  },

  { 5, 0, 16,
	{ 0x01, 0x02, 0x03, 0x04, 0x05 },
	0, { 0x00 },
	{ 0xb2, 0x39, 0x63, 0x05, 0xf0, 0x3d, 0xc0, 0x27,
	  0xcc, 0xc3, 0x52, 0x4a, 0x0a, 0x11, 0x18, 0xa8 }
  },

  { 5, 0, 16,
	{ 0x01, 0x02, 0x03, 0x04, 0x05 },
	0, { 0x00 },
	{ 0xb2, 0x39, 0x63, 0x05, 0xf0, 0x3d, 0xc0, 0x27,
	  0xcc, 0xc3, 0x52, 0x4a, 0x0a, 0x11, 0x18, 0xa8 }
  },

  { 5, 0, 16,
	{ 0x01, 0x02, 0x03, 0x04, 0x05 },
	0, { 0x00 },
	{ 0xb2, 0x39, 0x63, 0x05, 0xf0, 0x3d, 0xc0, 0x27,
	  0xcc, 0xc3, 0x52, 0x4a, 0x0a, 0x11, 0x18, 0xa8 }
  },

  { 5, 1536, 16,
	{ 0x01, 0x02, 0x03, 0x04, 0x05 },
	0, { 0x00 },
	{ 0xd8, 0x72, 0x9d, 0xb4, 0x18, 0x82, 0x25, 0x9b,
	  0xee, 0x4f, 0x82, 0x53, 0x25, 0xf5, 0xa1, 0x30 }
  },

  { 10, 4096, 16,
	{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a },
	0, { 0x00 },
	{ 0x08, 0xb6, 0xbe, 0x45, 0x12, 0x4a, 0x43, 0xe2,
	  0xeb, 0x77, 0x95, 0x3f, 0x84, 0xdc, 0x85, 0x53 }
  },

  { 5, 0, 16,
	{ 0x83, 0x32, 0x22, 0x77, 0x2a },
	0, { 0x00 },
	{ 0x80, 0xad, 0x97, 0xbd, 0xc9, 0x73, 0xdf, 0x8a,
	  0x2e, 0x87, 0x9e, 0x92, 0xa4, 0x97, 0xef, 0xda }
  },

  { 5, 3056, 16,
	{ 0x83, 0x32, 0x22, 0x77, 0x2a },
	0, { 0x00 },
	{ 0xaf, 0xec, 0xc0, 0x37, 0xfd, 0xb7, 0xb0, 0x83,
	  0x8e, 0xb3, 0xd7, 0x0b, 0xcd, 0x26, 0x83, 0x82 }
  },

  { 32, 0, 16,
	{ 0x1a, 0xda, 0x31, 0xd5, 0xcf, 0x68, 0x82, 0x21,
	  0xc1, 0x09, 0x16, 0x39, 0x08, 0xeb, 0xe5, 0x1d,
	  0xeb, 0xb4, 0x62, 0x27, 0xc6, 0xcc, 0x8b, 0x37,
	  0x64, 0x19, 0x10, 0x83, 0x32, 0x22, 0x77, 0x2a },
	0, { 0x00 },
	{ 0xdd, 0x5b, 0xcb, 0x00, 0x18, 0xe9, 0x22, 0xd4,
	  0x94, 0x75, 0x9d, 0x7c, 0x39, 0x5d, 0x02, 0xd3 }
  },

  { 32, 768, 16,
	{ 0x1a, 0xda, 0x31, 0xd5, 0xcf, 0x68, 0x82, 0x21,
	  0xc1, 0x09, 0x16, 0x39, 0x08, 0xeb, 0xe5, 0x1d,
	  0xeb, 0xb4, 0x62, 0x27, 0xc6, 0xcc, 0x8b, 0x37,
	  0x64, 0x19, 0x10, 0x83, 0x32, 0x22, 0x77, 0x2a },
	0, { 0x00 },
	{ 0x85, 0x14, 0xa5, 0x49, 0x58, 0x58, 0x09, 0x6f,
	  0x59, 0x6e, 0x4b, 0xcd, 0x66, 0xb1, 0x06, 0x65 }
  },

  { 0, 0, 0,
	{ 0x00 },
	0, { 0x00 },
	{ 0x00 }
  }
};

/*
 * For testing purposes, copy LEN bytes of the key stream for the given STATE
 * into SBUF, starting OFFSET bytes from the current stream position.
 */
static void
rc4_key_stream(RC4_ctx *const state, uint8_t *sbuf, size_t offset, size_t len)
{
  size_t i, s;
  uint8_t j;

  for (i = s = 0; i < (offset + len); i++) {
	/* Update modification indices. */
	state->index1++;
	state->index2 += state->perm[state->index1];

	/* Modify permutation. */
	swap_bytes(&state->perm[state->index1], &state->perm[state->index2]);

	/* Encrypt (or decrypt) next byte. */
	j = state->perm[state->index1] + state->perm[state->index2];
	if (i >= offset)
	  sbuf[s++] = state->perm[j];
  }
}

static int
do_rc4_self_test(void)
{
  uint8_t buf[512];
  RC4_ctx ctx;
  rc4_test *test;

  fprintf(stderr, "Doing RC4 tests:\n");

  for (test = &rc4_self_test[0]; test->key_len != 0; test++) {
	rc4_init(&ctx, test->key, test->key_len);
	if (test->msg_len) {
	  rc4_crypt(&ctx, test->msg, buf, test->msg_len);
	  if (memcmp(buf, test->exp, test->msg_len) != 0) {
		fprintf(stderr, "failed!\n");
		fprintf(stderr, "expected: %s\n",
				to_hex(test->exp, test->msg_len));
		fprintf(stderr, "got:      %s\n", to_hex(buf, test->msg_len));
		return(-1);
	  }
	}
	else {
	  rc4_init(&ctx, test->key, test->key_len);
	  rc4_key_stream(&ctx, buf, test->offset, test->slen);
	  if (memcmp(buf, test->exp, test->slen) != 0) {
		fprintf(stderr, "failed!\n");
		fprintf(stderr, "expected: %s\n", to_hex(test->exp, test->slen));
		fprintf(stderr, "got:      %s\n", to_hex(buf, test->slen));
		return(-1);
	  }
	}
	fprintf(stderr, ".");
  }
  fprintf(stderr, "\nAll ok\n");
  return(0);
}

static int
do_hmac_md5_self_test(void)
{
  size_t hlen;
  uint8_t *hmac;
  static uint8_t key1[] = {
	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
  };
  static uint8_t exp1[] = {
	0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,
	0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d
  };
  static uint8_t key2[] = {
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
  };
  static uint8_t data2[] = {
	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 uint8_t exp2[] = {
	0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,
	0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6
  };

  fprintf(stderr, "Doing HMAC-MD5 tests:\n");

  crypto_hmac_md5(key1, sizeof(key1), (uint8_t *) "Hi There", 8,
				  NULL, NULL, &hmac, &hlen);
  if (memcmp(hmac, exp1, hlen) != 0) {
	fprintf(stderr, "failed!\n");
	fprintf(stderr, "expected: %s\n", to_hex(exp1, sizeof(exp1)));
	fprintf(stderr, "got:      %s\n", to_hex(hmac, hlen));
	return(-1);
  }
  fprintf(stderr, "%s\n", to_hex(hmac, hlen));

  {
	static uint8_t exp[] = {
	  0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,
	  0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38
	};

	crypto_hmac_md5((uint8_t *) "Jefe", 4,
					(uint8_t *) "what do ya want for nothing?", 28,
					NULL, NULL, &hmac, &hlen);
	if (memcmp(hmac, exp, hlen) != 0) {
	  fprintf(stderr, "failed!\n");
	  fprintf(stderr, "expected: %s\n", to_hex(exp, sizeof(exp)));
	  fprintf(stderr, "got:      %s\n", to_hex(hmac, hlen));
	  return(-1);
	}
	fprintf(stderr, "%s\n", to_hex(hmac, hlen));
  }

  crypto_hmac_md5(key2, sizeof(key2), data2, sizeof(data2), NULL, NULL,
				  &hmac, &hlen);
  if (memcmp(hmac, exp2, hlen) != 0) {
	fprintf(stderr, "failed!\n");
	fprintf(stderr, "expected: %s\n", to_hex(exp2, sizeof(exp2)));
	fprintf(stderr, "got:      %s\n", to_hex(hmac, hlen));
	return(-1);
  }
  fprintf(stderr, "%s\n", to_hex(hmac, hlen));

  fprintf(stderr, "All ok\n");
  return(0);
}

int
do_crypto_aux_tests(void)
{

  if (do_md4_self_test() == -1)
	return(-1);
  if (do_md5_self_test() == -1)
	return(-1);
  if (do_rc4_self_test() == -1)
	return(-1);
  if (do_hmac_md5_self_test() == -1)
	return(-1);

  return(0);
}

#ifdef PROG

int
main(int argc, char **argv)
{
  int i;
  uint8_t buf[BUFSIZ], digest[64];
  ssize_t n;

  if (argc == 2) {
	if (strcasecmp(argv[1], "md5") == 0) {
	  MD5_ctx ctx;

	  MD5_init(&ctx);
	  while ((n = read(0, (void *) buf, sizeof(buf))) > 0)
		MD5_update(&ctx, buf, n);
	  MD5_final(&ctx, digest);

	  fprintf(stdout, "%s\n", to_hex(digest, MD5_DIGEST_LEN));

	  exit(0);
	}

	if (strcasecmp(argv[1], "md4") == 0) {
	  MD4_ctx ctx;

	  MD4_init(&ctx);
	  while ((n = read(0, (void *) buf, sizeof(buf))) > 0)
		MD4_update(&ctx, buf, n);
	  MD4_final(&ctx, digest);

	  fprintf(stdout, "%s\n", to_hex(digest, MD4_DIGEST_LEN));

	  exit(0);
	}

	fprintf(stderr, "?\n");
	exit(1);
  }

  fprintf(stderr, "Doing auxiliary cryptographic function tests\n");
  do_md4_self_test();
  do_md5_self_test();
  do_rc4_self_test();
  do_hmac_md5_self_test();
  fprintf(stderr, "All auxiliary cryptographic function tests succeeded\n");

  exit(0);
}
#endif
