changeset 6431:076accb971ef

Add MD2 and hash fixes.
author Simon Josefsson <simon@josefsson.org>
date Fri, 28 Oct 2005 12:09:31 +0000
parents cc9cb5980e61
children 13e8dcd2e4d7
files ChangeLog lib/ChangeLog lib/gc-gnulib.c lib/gc-libgcrypt.c lib/gc.h lib/md2.c lib/md2.h m4/ChangeLog m4/gc-md2.m4 m4/md2.m4 modules/gc-md2 modules/gc-md2-tests modules/md2 modules/md2-tests tests/test-gc-md2.c tests/test-md2.c
diffstat 16 files changed, 1025 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2005-10-28  Simon Josefsson  <jas@extundo.com>
+
+	* tests/test-gc-md2.c, tests/test-md2.c: New files.
+
+	* modules/md2, modules/md2-tests: New files.
+
 2005-10-27  Paul Eggert  <eggert@cs.ucla.edu>
 
 	* modules/verify (License): Change from GPL to LGPL.  This is a
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,3 +1,14 @@
+2005-10-28  Simon Josefsson  <jas@extundo.com>
+
+	* gc.h: Add MD2 and RMD160 length defines.  Add prototypes.
+
+	* gc-libgcrypt.c: Add MD2 (which is not available through
+	libgcrypt).
+
+	* gc-gnulib.c: Add MD2.  Implement gc_hash_* API.
+
+	* md2.h, md2.c: New files.
+
 2005-10-24  Simon Josefsson  <jas@extundo.com>
 
 	* md4.h: Shrink buffer size, now that we changed the type.
--- a/lib/gc-gnulib.c
+++ b/lib/gc-gnulib.c
@@ -38,6 +38,9 @@
 #include <errno.h>
 
 /* Hashes. */
+#ifdef GC_USE_MD2
+# include "md2.h"
+#endif
 #ifdef GC_USE_MD4
 # include "md4.h"
 #endif
@@ -537,11 +540,233 @@
 
 /* Hashes. */
 
+#define MAX_DIGEST_SIZE 20
+
+typedef struct _gc_hash_ctx {
+  Gc_hash alg;
+  Gc_hash_mode mode;
+  char hash[MAX_DIGEST_SIZE];
+#ifdef GC_USE_MD2
+  struct md2_ctx md2Context;
+#endif
+#ifdef GC_USE_MD4
+  struct md4_ctx md4Context;
+#endif
+#ifdef GC_USE_MD5
+  struct md5_ctx md5Context;
+#endif
+#ifdef GC_USE_SHA1
+  struct sha1_ctx sha1Context;
+#endif
+} _gc_hash_ctx;
+
+Gc_rc
+gc_hash_open (Gc_hash hash, Gc_hash_mode mode, gc_hash_handle * outhandle)
+{
+  _gc_hash_ctx *ctx;
+  Gc_rc rc = GC_OK;
+
+  ctx = calloc (sizeof (*ctx), 1);
+
+  ctx->alg = hash;
+  ctx->mode = mode;
+
+  switch (hash)
+    {
+#ifdef GC_USE_MD2
+    case GC_MD2:
+      md2_init_ctx (&ctx->md2Context);
+      break;
+#endif
+
+#ifdef GC_USE_MD4
+    case GC_MD4:
+      md4_init_ctx (&ctx->md4Context);
+      break;
+#endif
+
+#ifdef GC_USE_MD5
+    case GC_MD5:
+      md5_init_ctx (&ctx->md5Context);
+      break;
+#endif
+
+#ifdef GC_USE_SHA1
+    case GC_SHA1:
+      sha1_init_ctx (&ctx->sha1Context);
+      break;
+#endif
+
+    default:
+      rc = GC_INVALID_HASH;
+      break;
+    }
+
+  switch (mode)
+    {
+    case 0:
+      break;
+
+    default:
+      rc = GC_INVALID_HASH;
+      break;
+    }
+
+  if (rc == GC_OK)
+    *outhandle = ctx;
+  else
+    free (ctx);
+
+  return rc;
+}
+
+Gc_rc
+gc_hash_clone (gc_hash_handle handle, gc_hash_handle * outhandle)
+{
+  _gc_hash_ctx *in = handle;
+  _gc_hash_ctx *out;
+
+  *outhandle = out = calloc (sizeof (*out), 1);
+  if (!out)
+    return GC_MALLOC_ERROR;
+
+  memcpy (out, in, sizeof (*out));
+
+  return GC_OK;
+}
+
+size_t
+gc_hash_digest_length (Gc_hash hash)
+{
+  size_t len;
+
+  switch (hash)
+    {
+    case GC_MD2:
+      len = GC_MD2_DIGEST_SIZE;
+      break;
+
+    case GC_MD4:
+      len = GC_MD4_DIGEST_SIZE;
+      break;
+
+    case GC_MD5:
+      len = GC_MD5_DIGEST_SIZE;
+      break;
+
+    case GC_RMD160:
+      len = GC_RMD160_DIGEST_SIZE;
+      break;
+
+    case GC_SHA1:
+      len = GC_SHA1_DIGEST_SIZE;
+      break;
+
+    default:
+      return 0;
+    }
+
+  return len;
+}
+
+void
+gc_hash_write (gc_hash_handle handle, size_t len, const char *data)
+{
+  _gc_hash_ctx *ctx = handle;
+
+  switch (ctx->alg)
+    {
+#ifdef GC_USE_MD2
+    case GC_MD2:
+      md2_process_bytes (data, len, &ctx->md2Context);
+      break;
+#endif
+
+#ifdef GC_USE_MD4
+    case GC_MD4:
+      md4_process_bytes (data, len, &ctx->md4Context);
+      break;
+#endif
+
+#ifdef GC_USE_MD5
+    case GC_MD5:
+      md5_process_bytes (data, len, &ctx->md5Context);
+      break;
+#endif
+
+#ifdef GC_USE_SHA1
+    case GC_SHA1:
+      sha1_process_bytes (data, len, &ctx->sha1Context);
+      break;
+#endif
+
+    default:
+      break;
+    }
+}
+
+const char *
+gc_hash_read (gc_hash_handle handle)
+{
+  _gc_hash_ctx *ctx = handle;
+  const char *ret = NULL;
+
+  switch (ctx->alg)
+    {
+#ifdef GC_USE_MD2
+    case GC_MD2:
+      md2_finish_ctx (&ctx->md2Context, ctx->hash);
+      ret = ctx->hash;
+      break;
+#endif
+
+#ifdef GC_USE_MD4
+    case GC_MD4:
+      md4_finish_ctx (&ctx->md4Context, ctx->hash);
+      ret = ctx->hash;
+      break;
+#endif
+
+#ifdef GC_USE_MD5
+    case GC_MD5:
+      md5_finish_ctx (&ctx->md5Context, ctx->hash);
+      ret = ctx->hash;
+      break;
+#endif
+
+#ifdef GC_USE_SHA1
+    case GC_SHA1:
+      sha1_finish_ctx (&ctx->sha1Context, ctx->hash);
+      ret = ctx->hash;
+      break;
+#endif
+
+    default:
+      return NULL;
+    }
+
+  return ret;
+}
+
+void
+gc_hash_close (gc_hash_handle handle)
+{
+  _gc_hash_ctx *ctx = handle;
+
+  free (ctx);
+}
+
 Gc_rc
 gc_hash_buffer (Gc_hash hash, const void *in, size_t inlen, char *resbuf)
 {
   switch (hash)
     {
+#ifdef GC_USE_MD2
+    case GC_MD2:
+      md2_buffer (in, inlen, resbuf);
+      break;
+#endif
+
 #ifdef GC_USE_MD4
     case GC_MD4:
       md4_buffer (in, inlen, resbuf);
@@ -567,6 +792,15 @@
   return GC_OK;
 }
 
+#ifdef GC_USE_MD2
+Gc_rc
+gc_md2 (const void *in, size_t inlen, void *resbuf)
+{
+  md2_buffer (in, inlen, resbuf);
+  return GC_OK;
+}
+#endif
+
 #ifdef GC_USE_MD4
 Gc_rc
 gc_md4 (const void *in, size_t inlen, void *resbuf)
--- a/lib/gc-libgcrypt.c
+++ b/lib/gc-libgcrypt.c
@@ -27,8 +27,14 @@
 /* Get prototype. */
 #include "gc.h"
 
+#include <stdlib.h>
+#include <string.h>
+
 /* Get libgcrypt API. */
 #include <gcrypt.h>
+#ifdef GC_USE_MD2
+# include "md2.h"
+#endif
 
 #include <assert.h>
 
@@ -218,14 +224,35 @@
 
 /* Hashes. */
 
+typedef struct _gc_hash_ctx {
+  Gc_hash alg;
+  Gc_hash_mode mode;
+  gcry_md_hd_t gch;
+#ifdef GC_USE_MD2
+  char hash[GC_MD2_DIGEST_SIZE];
+  struct md2_ctx md2Context;
+#endif
+} _gc_hash_ctx;
+
 Gc_rc
 gc_hash_open (Gc_hash hash, Gc_hash_mode mode, gc_hash_handle * outhandle)
 {
+  _gc_hash_ctx *ctx;
   int gcryalg, gcrymode;
   gcry_error_t err;
+  Gc_rc rc = GC_OK;
+
+  ctx = calloc (sizeof (*ctx), 1);
+
+  ctx->alg = hash;
+  ctx->mode = mode;
 
   switch (hash)
     {
+    case GC_MD2:
+      gcryalg = GCRY_MD_NONE;
+      break;
+
     case GC_MD4:
       gcryalg = GCRY_MD_MD4;
       break;
@@ -243,7 +270,7 @@
       break;
 
     default:
-      return GC_INVALID_HASH;
+      rc = GC_INVALID_HASH;
     }
 
   switch (mode)
@@ -257,24 +284,43 @@
       break;
 
     default:
-      return GC_INVALID_HASH;
+      rc = GC_INVALID_HASH;
     }
 
-  err = gcry_md_open ((gcry_md_hd_t *) outhandle, gcryalg, gcrymode);
-  if (gcry_err_code (err))
-    return GC_INVALID_HASH;
+  if (rc == GC_OK && gcryalg != GCRY_MD_NONE)
+    {
+      err = gcry_md_open (&ctx->gch, gcryalg, gcrymode);
+      if (gcry_err_code (err))
+	rc = GC_INVALID_HASH;
+    }
 
-  return GC_OK;
+  if (rc == GC_OK)
+    *outhandle = ctx;
+  else
+    free (ctx);
+
+  return rc;
 }
 
 Gc_rc
 gc_hash_clone (gc_hash_handle handle, gc_hash_handle * outhandle)
 {
+  _gc_hash_ctx *in = handle;
+  _gc_hash_ctx *out;
   int err;
 
-  err = gcry_md_copy ((gcry_md_hd_t *) outhandle, (gcry_md_hd_t) handle);
+  *outhandle = out = calloc (sizeof (*out), 1);
+  if (!out)
+    return GC_MALLOC_ERROR;
+
+  memcpy (out, in, sizeof (*out));
+
+  err = gcry_md_copy (&out->gch, in->gch);
   if (err)
-    return GC_INVALID_HASH;
+    {
+      free (out);
+      return GC_INVALID_HASH;
+    }
 
   return GC_OK;
 }
@@ -282,52 +328,78 @@
 size_t
 gc_hash_digest_length (Gc_hash hash)
 {
-  int gcryalg;
+  size_t len;
 
   switch (hash)
     {
+    case GC_MD2:
+      len = GC_MD2_DIGEST_SIZE;
+      break;
+
     case GC_MD4:
-      gcryalg = GCRY_MD_MD4;
+      len = GC_MD4_DIGEST_SIZE;
       break;
 
     case GC_MD5:
-      gcryalg = GCRY_MD_MD5;
+      len = GC_MD5_DIGEST_SIZE;
+      break;
+
+    case GC_RMD160:
+      len = GC_RMD160_DIGEST_SIZE;
       break;
 
     case GC_SHA1:
-      gcryalg = GCRY_MD_SHA1;
-      break;
-
-    case GC_RMD160:
-      gcryalg = GCRY_MD_RMD160;
+      len = GC_SHA1_DIGEST_SIZE;
       break;
 
     default:
       return 0;
     }
 
-  return gcry_md_get_algo_dlen (gcryalg);
+  return len;
 }
 
 void
 gc_hash_hmac_setkey (gc_hash_handle handle, size_t len, const char *key)
 {
-  gcry_md_setkey ((gcry_md_hd_t) handle, key, len);
+  _gc_hash_ctx *ctx = handle;
+#ifdef GC_USE_MD2
+  if (ctx->alg != GC_MD2)
+#endif
+    gcry_md_setkey (ctx->gch, key, len);
 }
 
 void
 gc_hash_write (gc_hash_handle handle, size_t len, const char *data)
 {
-  gcry_md_write ((gcry_md_hd_t) handle, data, len);
+  _gc_hash_ctx *ctx = handle;
+
+#ifdef GC_USE_MD2
+  if (ctx->alg == GC_MD2)
+    md2_process_bytes (data, len, &ctx->md2Context);
+  else
+#endif
+    gcry_md_write (ctx->gch, data, len);
 }
 
 const char *
 gc_hash_read (gc_hash_handle handle)
 {
+  _gc_hash_ctx *ctx = handle;
   const char *digest;
 
-  gcry_md_final ((gcry_md_hd_t) handle);
-  digest = gcry_md_read ((gcry_md_hd_t) handle, 0);
+#ifdef GC_USE_MD2
+  if (ctx->alg == GC_MD2)
+    {
+      md2_finish_ctx (&ctx->md2Context, ctx->hash);
+      digest = ctx->hash;
+    }
+  else
+#endif
+    {
+      gcry_md_final (ctx->gch);
+      digest = gcry_md_read (ctx->gch, 0);
+    }
 
   return digest;
 }
@@ -335,7 +407,14 @@
 void
 gc_hash_close (gc_hash_handle handle)
 {
-  gcry_md_close ((gcry_md_hd_t) handle);
+  _gc_hash_ctx *ctx = handle;
+
+#ifdef GC_USE_MD2
+  if (ctx->alg != GC_MD2)
+#endif
+    gcry_md_close (ctx->gch);
+
+  free (ctx);
 }
 
 Gc_rc
@@ -345,6 +424,13 @@
 
   switch (hash)
     {
+#ifdef GC_USE_MD2
+    case GC_MD2:
+      md2_buffer (in, inlen, resbuf);
+      return GC_OK;
+      break;
+#endif
+
 #ifdef GC_USE_MD4
     case GC_MD4:
       gcryalg = GCRY_MD_MD4;
@@ -380,6 +466,15 @@
 
 /* One-call interface. */
 
+#ifdef GC_USE_MD2
+Gc_rc
+gc_md2 (const void *in, size_t inlen, void *resbuf)
+{
+  md2_buffer (in, inlen, resbuf);
+  return GC_OK;
+}
+#endif
+
 #ifdef GC_USE_MD4
 Gc_rc
 gc_md4 (const void *in, size_t inlen, void *resbuf)
--- a/lib/gc.h
+++ b/lib/gc.h
@@ -57,8 +57,10 @@
 
 typedef void *gc_hash_handle;
 
+#define GC_MD2_DIGEST_SIZE 16
 #define GC_MD4_DIGEST_SIZE 16
 #define GC_MD5_DIGEST_SIZE 16
+#define GC_RMD160_DIGEST_SIZE 20
 #define GC_SHA1_DIGEST_SIZE 20
 
 /* Cipher types. */
@@ -141,6 +143,8 @@
 gc_hash_buffer (Gc_hash hash, const void *in, size_t inlen, char *out);
 
 /* One-call interface. */
+extern Gc_rc gc_md2 (const void *in, size_t inlen, void *resbuf);
+extern Gc_rc gc_md4 (const void *in, size_t inlen, void *resbuf);
 extern Gc_rc gc_md5 (const void *in, size_t inlen, void *resbuf);
 extern Gc_rc gc_sha1 (const void *in, size_t inlen, void *resbuf);
 extern Gc_rc gc_hmac_md5 (const void *key, size_t keylen,
new file mode 100644
--- /dev/null
+++ b/lib/md2.c
@@ -0,0 +1,275 @@
+/* Functions to compute MD2 message digest of files or memory blocks.
+   according to the definition of MD2 in RFC 1319 from April 1992.
+   Copyright (C) 1995,1996,1997,1999,2000,2001,2002,2003,2005
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Adapted by Simon Josefsson from public domain Libtomcrypt 1.06 by
+   Tom St Denis. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "md2.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <minmax.h>
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+#define BLOCKSIZE 4096
+#if BLOCKSIZE % 64 != 0
+# error "invalid BLOCKSIZE"
+#endif
+
+static void md2_update_chksum (struct md2_ctx *md);
+static void md2_compress (struct md2_ctx *md);
+
+/* Initialize structure containing state of computation.
+   (RFC 1319, 3.3: Step 3)  */
+void
+md2_init_ctx (struct md2_ctx *ctx)
+{
+  memset (ctx->X, 0, sizeof (ctx->X));
+  memset (ctx->chksum, 0, sizeof (ctx->chksum));
+  memset (ctx->buf, 0, sizeof (ctx->buf));
+  ctx->curlen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result
+   must be in little endian byte order.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *
+md2_read_ctx (const struct md2_ctx *ctx, void *resbuf)
+{
+  memcpy (resbuf, ctx->X, 16);
+
+  return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *
+md2_finish_ctx (struct md2_ctx *ctx, void *resbuf)
+{
+  unsigned long i, k;
+
+  /* pad the message */
+  k = 16 - ctx->curlen;
+  for (i = ctx->curlen; i < 16; i++)
+    {
+      ctx->buf[i] = (unsigned char) k;
+    }
+
+  /* hash and update */
+  md2_compress (ctx);
+  md2_update_chksum (ctx);
+
+  /* hash checksum */
+  memcpy (ctx->buf, ctx->chksum, 16);
+  md2_compress (ctx);
+
+  return md2_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD2 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+int
+md2_stream (FILE *stream, void *resblock)
+{
+  struct md2_ctx ctx;
+  char buffer[BLOCKSIZE + 72];
+  size_t sum;
+
+  /* Initialize the computation context.  */
+  md2_init_ctx (&ctx);
+
+  /* Iterate over full file contents.  */
+  while (1)
+    {
+      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
+         computation function processes the whole buffer so that with the
+         next round of the loop another block can be read.  */
+      size_t n;
+      sum = 0;
+
+      /* Read block.  Take care for partial reads.  */
+      while (1)
+	{
+	  n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+	  sum += n;
+
+	  if (sum == BLOCKSIZE)
+	    break;
+
+	  if (n == 0)
+	    {
+	      /* Check for the error flag IFF N == 0, so that we don't
+	         exit the loop after a partial read due to e.g., EAGAIN
+	         or EWOULDBLOCK.  */
+	      if (ferror (stream))
+		return 1;
+	      goto process_partial_block;
+	    }
+
+	  /* We've read at least one byte, so ignore errors.  But always
+	     check for EOF, since feof may be true even though N > 0.
+	     Otherwise, we could end up calling fread after EOF.  */
+	  if (feof (stream))
+	    goto process_partial_block;
+	}
+
+      /* Process buffer with BLOCKSIZE bytes.  Note that
+         BLOCKSIZE % 64 == 0
+       */
+      md2_process_block (buffer, BLOCKSIZE, &ctx);
+    }
+
+process_partial_block:;
+
+  /* Process any remaining bytes.  */
+  if (sum > 0)
+    md2_process_bytes (buffer, sum, &ctx);
+
+  /* Construct result in desired memory.  */
+  md2_finish_ctx (&ctx, resblock);
+  return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+void *
+md2_buffer (const char *buffer, size_t len, void *resblock)
+{
+  struct md2_ctx ctx;
+
+  /* Initialize the computation context.  */
+  md2_init_ctx (&ctx);
+
+  /* Process whole buffer but last len % 64 bytes.  */
+  md2_process_block (buffer, len, &ctx);
+
+  /* Put result in desired memory area.  */
+  return md2_finish_ctx (&ctx, resblock);
+}
+
+void
+md2_process_bytes (const void *buffer, size_t len, struct md2_ctx *ctx)
+{
+  const char *in = buffer;
+  unsigned long n;
+
+  while (len > 0)
+    {
+      n = MIN (len, (16 - ctx->curlen));
+      memcpy (ctx->buf + ctx->curlen, in, (size_t) n);
+      ctx->curlen += n;
+      in += n;
+      len -= n;
+
+      /* is 16 bytes full? */
+      if (ctx->curlen == 16)
+	{
+	  md2_compress (ctx);
+	  md2_update_chksum (ctx);
+	  ctx->curlen = 0;
+	}
+    }
+}
+
+static const unsigned char PI_SUBST[256] = {
+  41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
+  19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
+  76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
+  138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
+  245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
+  148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
+  39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
+  181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
+  150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
+  112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
+  96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
+  85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
+  234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
+  129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
+  8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
+  203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
+  166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
+  31, 26, 219, 153, 141, 51, 159, 17, 131, 20
+};
+
+/* adds 16 bytes to the checksum */
+static void
+md2_update_chksum (struct md2_ctx *ctx)
+{
+  int j;
+  unsigned char L;
+
+  L = ctx->chksum[15];
+  for (j = 0; j < 16; j++)
+    {
+      /* caution, the RFC says its "C[j] = S[M[i*16+j] xor L]" but the
+         reference source code [and test vectors] say otherwise. */
+      L = (ctx->chksum[j] ^= PI_SUBST[(int) (ctx->buf[j] ^ L)] & 255);
+    }
+}
+
+static void
+md2_compress (struct md2_ctx *ctx)
+{
+  size_t j, k;
+  unsigned char t;
+
+  /* copy block */
+  for (j = 0; j < 16; j++)
+    {
+      ctx->X[16 + j] = ctx->buf[j];
+      ctx->X[32 + j] = ctx->X[j] ^ ctx->X[16 + j];
+    }
+
+  t = (unsigned char) 0;
+
+  /* do 18 rounds */
+  for (j = 0; j < 18; j++)
+    {
+      for (k = 0; k < 48; k++)
+	{
+	  t = (ctx->X[k] ^= PI_SUBST[(int) (t & 255)]);
+	}
+      t = (t + (unsigned char) j) & 255;
+    }
+}
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.  */
+void
+md2_process_block (const void *buffer, size_t len, struct md2_ctx *ctx)
+{
+  md2_process_bytes (buffer, len, ctx);
+}
new file mode 100644
--- /dev/null
+++ b/lib/md2.h
@@ -0,0 +1,82 @@
+/* Declarations of functions and data types used for MD2 sum
+   library functions.
+   Copyright (C) 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef MD2_H
+# define MD2_H 1
+
+# include <stdio.h>
+# include <stddef.h>
+
+# define MD2_DIGEST_SIZE 16
+
+/* Structure to save state of computation between the single steps.  */
+struct md2_ctx
+{
+  unsigned char chksum[16], X[48], buf[16];
+  size_t curlen;
+};
+
+
+/* Initialize structure containing state of computation. */
+extern void md2_init_ctx (struct md2_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 64.  */
+extern void md2_process_block (const void *buffer, size_t len,
+			       struct md2_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 64.  */
+extern void md2_process_bytes (const void *buffer, size_t len,
+			       struct md2_ctx *ctx);
+
+/* Process the remaining bytes in the buffer and put result from CTX
+   in first 16 bytes following RESBUF.  The result is always in little
+   endian byte order, so that a byte-wise output yields to the wanted
+   ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF be correctly
+   aligned for a 32 bits value.  */
+extern void *md2_finish_ctx (struct md2_ctx *ctx, void *resbuf);
+
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result is
+   always in little endian byte order, so that a byte-wise output yields
+   to the wanted ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *md2_read_ctx (const struct md2_ctx *ctx, void *resbuf);
+
+
+/* Compute MD2 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+extern int md2_stream (FILE *stream, void *resblock);
+
+/* Compute MD2 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+extern void *md2_buffer (const char *buffer, size_t len, void *resblock);
+
+#endif
--- a/m4/ChangeLog
+++ b/m4/ChangeLog
@@ -1,3 +1,7 @@
+2005-10-28  Simon Josefsson  <jas@extundo.com>
+
+	* gc-md2.m4, md2.m4: New file.
+
 2005-10-22  Simon Josefsson  <jas@extundo.com>
 
 	* gc.m4: Don't be fooled by --disable-*random-device parameters,
new file mode 100644
--- /dev/null
+++ b/m4/gc-md2.m4
@@ -0,0 +1,10 @@
+# gc-md2.m4 serial 1
+dnl Copyright (C) 2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_GC_MD2],
+[
+  AC_DEFINE(GC_USE_MD2, 1, [Define if you want to support MD2 through GC.])
+])
new file mode 100644
--- /dev/null
+++ b/m4/md2.m4
@@ -0,0 +1,11 @@
+# md2.m4 serial 1
+dnl Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_MD2],
+[
+  AC_LIBSOURCES([md2.c, md2.h])
+  AC_LIBOBJ([md2])
+])
new file mode 100644
--- /dev/null
+++ b/modules/gc-md2
@@ -0,0 +1,25 @@
+Description:
+Generic crypto wrappers for MD2 functions.
+
+Files:
+m4/gc-md2.m4
+
+Depends-on:
+stdint
+gc
+minmax
+md2
+
+configure.ac:
+gl_GC_MD2
+
+Makefile.am:
+
+Include:
+"gc.h"
+
+License:
+LGPL
+
+Maintainer:
+Simon Josefsson
new file mode 100644
--- /dev/null
+++ b/modules/gc-md2-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-gc-md2.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-gc-md2
+noinst_PROGRAMS += test-gc-md2
+test_gc_md2_SOURCES = test-gc-md2.c
new file mode 100644
--- /dev/null
+++ b/modules/md2
@@ -0,0 +1,24 @@
+Description:
+Compute MD2 checksum.
+
+Files:
+lib/md2.h
+lib/md2.c
+m4/md2.m4
+
+Depends-on:
+minmax
+
+configure.ac:
+gl_MD2
+
+Makefile.am:
+
+Include:
+"md2.h"
+
+License:
+LGPL
+
+Maintainer:
+Simon Josefsson
new file mode 100644
--- /dev/null
+++ b/modules/md2-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-md2.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-md2
+noinst_PROGRAMS += test-md2
+test_md2_SOURCES = test-md2.c
new file mode 100644
--- /dev/null
+++ b/tests/test-gc-md2.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2005 Free Software Foundation
+ * Written by Simon Josefsson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gc.h"
+
+int
+main (int argc, char *argv[])
+{
+  Gc_rc rc;
+  gc_hash_handle h;
+
+  rc = gc_init ();
+  if (rc != GC_OK)
+    {
+      printf ("gc_init() failed\n");
+      return 1;
+    }
+
+  /* Test vectors from RFC 1319. */
+
+  {
+    char *in = "abcdefghijklmnopqrstuvwxyz";
+    size_t inlen = strlen (in);
+    char *expect =
+      "\x4e\x8d\xdf\xf3\x65\x02\x92\xab\x5a\x41\x08\xc3\xaa\x47\x94\x0b";
+    char out[16];
+    const char *p;
+
+    if (gc_md2 (in, inlen, out) != 0)
+      {
+	printf ("gc_md2 call failed\n");
+	return 1;
+      }
+
+    if (memcmp (out, expect, 16) != 0)
+      {
+	size_t i;
+	printf ("md2 1 missmatch. expected:\n");
+	for (i = 0; i < 16; i++)
+	  printf ("%02x ", expect[i] & 0xFF);
+	printf ("\ncomputed:\n");
+	for (i = 0; i < 16; i++)
+	  printf ("%02x ", out[i] & 0xFF);
+	printf ("\n");
+	return 1;
+      }
+
+    if (gc_hash_buffer (GC_MD2, in, inlen, out) != 0)
+      {
+	printf ("gc_hash_buffer(MD2) call failed\n");
+	return 1;
+      }
+
+    if (memcmp (out, expect, 16) != 0)
+      {
+	size_t i;
+	printf ("md2 2 missmatch. expected:\n");
+	for (i = 0; i < 16; i++)
+	  printf ("%02x ", expect[i] & 0xFF);
+	printf ("\ncomputed:\n");
+	for (i = 0; i < 16; i++)
+	  printf ("%02x ", out[i] & 0xFF);
+	printf ("\n");
+	return 1;
+      }
+
+    if (gc_hash_digest_length (GC_MD2) != 16)
+      {
+	printf ("gc_hash_digest_length (GC_MD2) failed\n");
+	return 1;
+      }
+
+    if ((rc = gc_hash_open (GC_MD2, 0, &h)) != GC_OK)
+      {
+	printf ("gc_hash_open(GC_MD2) failed (%d)\n", rc);
+	return 1;
+      }
+
+    gc_hash_write (h, inlen, in);
+
+    p = gc_hash_read (h);
+
+    if (!p)
+      {
+	printf ("gc_hash_read failed\n");
+	return 1;
+      }
+
+    if (memcmp (p, expect, 16) != 0)
+	{
+	size_t i;
+	printf ("md2 3 missmatch. expected:\n");
+	for (i = 0; i < 16; i++)
+	  printf ("%02x ", expect[i] & 0xFF);
+	printf ("\ncomputed:\n");
+	for (i = 0; i < 16; i++)
+	  printf ("%02x ", p[i] & 0xFF);
+	printf ("\n");
+	return 1;
+      }
+
+    gc_hash_close (h);
+  }
+
+  gc_done ();
+
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/tests/test-md2.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.  */
+
+/* Written by Simon Josefsson. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "md2.h"
+
+int
+main (int argc, char *argv[])
+{
+  const char *in1 = "abc";
+  const char *out1 =
+    "\xda\x85\x3b\x0d\x3f\x88\xd9\x9b\x30\x28\x3a\x69\xe6\xde\xd6\xbb";
+  const char *in2 = "abcdefghijklmnopqrstuvwxyz";
+  const char *out2 =
+    "\x4e\x8d\xdf\xf3\x65\x02\x92\xab\x5a\x41\x08\xc3\xaa\x47\x94\x0b";
+  char buf[MD2_DIGEST_SIZE];
+
+  if (memcmp (md2_buffer (in1, strlen (in1), buf), out1, MD2_DIGEST_SIZE) !=
+      0)
+    {
+      size_t i;
+      printf ("expected:\n");
+      for (i = 0; i < MD2_DIGEST_SIZE; i++)
+	printf ("%02x ", out1[i] & 0xFF);
+      printf ("\ncomputed:\n");
+      for (i = 0; i < MD2_DIGEST_SIZE; i++)
+	printf ("%02x ", buf[i] & 0xFF);
+      printf ("\n");
+      return 1;
+    }
+
+  if (memcmp (md2_buffer (in2, strlen (in2), buf), out2, MD2_DIGEST_SIZE) !=
+      0)
+    {
+      size_t i;
+      printf ("expected:\n");
+      for (i = 0; i < MD2_DIGEST_SIZE; i++)
+	printf ("%02x ", out2[i] & 0xFF);
+      printf ("\ncomputed:\n");
+      for (i = 0; i < MD2_DIGEST_SIZE; i++)
+	printf ("%02x ", buf[i] & 0xFF);
+      printf ("\n");
+      return 1;
+    }
+
+  return 0;
+}