Mailing List Archive

[PATCH] Message digest test suite and incomplete MD4 support
I need MD4 support so I wrote the below, but it didn't work so I added
a test suite for message digests (thus removing a TODO in
tests/basic.c), but wasn't able to get it to work anyway (the dynamic
algorithm loading stuff is rather convoluted IMHO). The test case
fails the same way for the TIGER algorithm, so perhaps there is
something genuinely wrong. I'm thankful for any help in getting this
to work, and to get it included in the distribution (I have signed
papers now).

Index: configure.ac
===================================================================
RCS file: /cvs/gnupg/libgcrypt/configure.ac,v retrieving revision 1.23
diff -u -p -r1.23 configure.ac --- configure.ac 20 Sep 2002 11:23:57
-0000 1.23
+++ configure.ac 2 Nov 2002 21:39:01 -0000
@@ -70,7 +70,7 @@ AC_SUBST(VERSION)
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package])

-static_modules="sha1 md5 rmd160"
+static_modules="sha1 md4 md5 rmd160"
static_random_module=""

AC_PROG_AWK
Index: cipher/Makefile.am
===================================================================
RCS file: /cvs/gnupg/libgcrypt/cipher/Makefile.am,v
retrieving revision 1.66
diff -u -p -r1.66 Makefile.am
--- cipher/Makefile.am 17 May 2002 08:33:16 -0000 1.66
+++ cipher/Makefile.am 2 Nov 2002 21:39:01 -0000
@@ -28,12 +28,13 @@ noinst_LTLIBRARIES = libcipher.la


# The configure script greps the module names from the EXTRA_PROGRAMS line
-EXTRA_PROGRAMS = rndlinux rndunix rndegd rndw32 sha1 rmd160 md5 tiger
+EXTRA_PROGRAMS = rndlinux rndunix rndegd rndw32 sha1 rmd160 md4 md5 tiger

EXTRA_rndlinux_SOURCES = rndlinux.c
EXTRA_rndunix_SOURCES = rndunix.c
EXTRA_rndegd_SOURCES = rndegd.c
EXTRA_rndw32_SOURCES = rndw32.c
+EXTRA_md4_SOURCES = md4.c
EXTRA_md5_SOURCES = md5.c
EXTRA_rmd160_SOURCES = rmd160.c
EXTRA_sha1_SOURCES = sha1.c
Index: cipher/md.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/cipher/md.c,v
retrieving revision 1.55
diff -u -p -r1.55 md.c
--- cipher/md.c 14 Oct 2002 18:07:00 -0000 1.55
+++ cipher/md.c 2 Nov 2002 21:39:01 -0000
@@ -49,6 +49,8 @@ static struct {
{ "1.2.840.113549.2.5", GCRY_MD_MD5 },
/* GNU.digestAlgorithm TIGER */
{ "1.3.6.1.4.1.11591.12.2", GCRY_MD_TIGER },
+ /* iso.member-body.us.rsadsi.digestAlgorithm.md4 */
+ { "1.2.840.113549.2.4", GCRY_MD_MD4 },
{NULL}
};

@@ -819,6 +821,7 @@ gcry_md_get_algo_dlen( int algo )
/* we do some very quick checks here */
switch( algo )
{
+ case GCRY_MD_MD4:
case GCRY_MD_MD5: return 16;
case GCRY_MD_SHA1:
case GCRY_MD_RMD160: return 20;
Index: cipher/md4.c
===================================================================
RCS file: cipher/md4.c
diff -N cipher/md4.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ cipher/md4.c 2 Nov 2002 21:39:01 -0000
@@ -0,0 +1,397 @@
+/* md4.c - MD4 Message-Digest Algorithm
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Based on md5.c in libgcrypt, but rewritten to compute md4 checksums
+ * using a public domain md4 implementation with the following comments:
+ *
+ * Modified by Wei Dai from Andrew M. Kuchling's md4.c
+ * The original code and all modifications are in the public domain.
+ *
+ * This is the original introductory comment:
+ *
+ * md4.c : MD4 hash algorithm.
+ *
+ * Part of the Python Cryptography Toolkit, version 1.1
+ *
+ * Distribute and use freely; there are no restrictions on further
+ * dissemination and usage except those imposed by the laws of your
+ * country of residence.
+ *
+ */
+
+/* MD4 test suite:
+ * MD4 ("") = 31d6cfe0d16ae931b73c59d7e0c089c0
+ * MD4 ("a") = bde52cb31de33e46245e05fbdbd6fb24
+ * MD4 ("abc") = a448017aaf21d8525fc10ae87aa6729d
+ * MD4 ("message digest") = d9130a8164549fe818874806e1c7014b
+ * MD4 ("abcdefghijklmnopqrstuvwxyz") = d79e1c308aa5bbcdeea8ed63df412da9
+ * MD4 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =
+ * 043f8582f241db351ce627e153e7f0e4
+ * MD4 ("123456789012345678901234567890123456789012345678901234567890123456
+ * 78901234567890") = e33b4ddc9c38f2199c3e7b164fcc0536
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "g10lib.h"
+#include "memory.h"
+#include "dynload.h"
+
+#include "bithelp.h"
+
+
+typedef struct {
+ u32 A,B,C,D; /* chaining variables */
+ u32 nblocks;
+ byte buf[64];
+ int count;
+} MD4_CONTEXT;
+
+
+static void
+md4_init( MD4_CONTEXT *ctx )
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->nblocks = 0;
+ ctx->count = 0;
+}
+
+static void
+burn_stack (int bytes)
+{
+ char buf[128];
+
+ memset (buf, 0, sizeof buf);
+ bytes -= sizeof buf;
+ if (bytes > 0)
+ burn_stack (bytes);
+}
+
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+
+/****************
+ * transform n*64 bytes
+ */
+static void
+/*transform( MD4_CONTEXT *ctx, const void *buffer, size_t len )*/
+transform( MD4_CONTEXT *ctx, byte *data )
+{
+ u32 correct_words[16];
+ register u32 A = ctx->A;
+ register u32 B = ctx->B;
+ register u32 C = ctx->C;
+ register u32 D = ctx->D;
+ u32 *cwp = correct_words;
+
+#ifdef BIG_ENDIAN_HOST
+ { int i;
+ byte *p2, *p1;
+ for(i=0, p1=data, p2=(byte*)correct_words; i < 16; i++, p2 += 4 ) {
+ p2[3] = *p1++;
+ p2[2] = *p1++;
+ p2[1] = *p1++;
+ p2[0] = *p1++;
+ }
+ }
+#else
+ memcpy( correct_words, data, 64 );
+#endif
+
+ /* Round 1. */
+#define function(a,b,c,d,k,s) a=rol(a+F(b,c,d)+data[k],s);
+ function(A,B,C,D, 0, 3);
+ function(D,A,B,C, 1, 7);
+ function(C,D,A,B, 2,11);
+ function(B,C,D,A, 3,19);
+ function(A,B,C,D, 4, 3);
+ function(D,A,B,C, 5, 7);
+ function(C,D,A,B, 6,11);
+ function(B,C,D,A, 7,19);
+ function(A,B,C,D, 8, 3);
+ function(D,A,B,C, 9, 7);
+ function(C,D,A,B,10,11);
+ function(B,C,D,A,11,19);
+ function(A,B,C,D,12, 3);
+ function(D,A,B,C,13, 7);
+ function(C,D,A,B,14,11);
+ function(B,C,D,A,15,19);
+
+#undef function
+
+ /* Round 2. */
+#define function(a,b,c,d,k,s) a=rol(a+G(b,c,d)+data[k]+0x5a827999,s);
+
+ function(A,B,C,D, 0, 3);
+ function(D,A,B,C, 4, 5);
+ function(C,D,A,B, 8, 9);
+ function(B,C,D,A,12,13);
+ function(A,B,C,D, 1, 3);
+ function(D,A,B,C, 5, 5);
+ function(C,D,A,B, 9, 9);
+ function(B,C,D,A,13,13);
+ function(A,B,C,D, 2, 3);
+ function(D,A,B,C, 6, 5);
+ function(C,D,A,B,10, 9);
+ function(B,C,D,A,14,13);
+ function(A,B,C,D, 3, 3);
+ function(D,A,B,C, 7, 5);
+ function(C,D,A,B,11, 9);
+ function(B,C,D,A,15,13);
+
+#undef function
+
+ /* Round 3. */
+#define function(a,b,c,d,k,s) a=rol(a+H(b,c,d)+data[k]+0x6ed9eba1,s);
+
+ function(A,B,C,D, 0, 3);
+ function(D,A,B,C, 8, 9);
+ function(C,D,A,B, 4,11);
+ function(B,C,D,A,12,15);
+ function(A,B,C,D, 2, 3);
+ function(D,A,B,C,10, 9);
+ function(C,D,A,B, 6,11);
+ function(B,C,D,A,14,15);
+ function(A,B,C,D, 1, 3);
+ function(D,A,B,C, 9, 9);
+ function(C,D,A,B, 5,11);
+ function(B,C,D,A,13,15);
+ function(A,B,C,D, 3, 3);
+ function(D,A,B,C,11, 9);
+ function(C,D,A,B, 7,11);
+ function(B,C,D,A,15,15);
+
+
+ /* Put checksum in context given as argument. */
+ ctx->A += A;
+ ctx->B += B;
+ ctx->C += C;
+ ctx->D += D;
+}
+
+
+
+/* The routine updates the message-digest context to
+ * account for the presence of each of the characters inBuf[0..inLen-1]
+ * in the message whose digest is being computed.
+ */
+static void
+md4_write( MD4_CONTEXT *hd, byte *inbuf, size_t inlen)
+{
+ if( hd->count == 64 ) { /* flush the buffer */
+ transform( hd, hd->buf );
+ burn_stack (80+6*sizeof(void*));
+ hd->count = 0;
+ hd->nblocks++;
+ }
+ if( !inbuf )
+ return;
+ if( hd->count ) {
+ for( ; inlen && hd->count < 64; inlen-- )
+ hd->buf[hd->count++] = *inbuf++;
+ md4_write( hd, NULL, 0 );
+ if( !inlen )
+ return;
+ }
+ burn_stack (80+6*sizeof(void*));
+
+ while( inlen >= 64 ) {
+ transform( hd, inbuf );
+ hd->count = 0;
+ hd->nblocks++;
+ inlen -= 64;
+ inbuf += 64;
+ }
+ for( ; inlen && hd->count < 64; inlen-- )
+ hd->buf[hd->count++] = *inbuf++;
+
+}
+
+
+
+/* The routine final terminates the message-digest computation and
+ * ends with the desired message digest in mdContext->digest[0...15].
+ * The handle is prepared for a new MD4 cycle.
+ * Returns 16 bytes representing the digest.
+ */
+
+static void
+md4_final( MD4_CONTEXT *hd )
+{
+ u32 t, msb, lsb;
+ byte *p;
+
+ md4_write(hd, NULL, 0); /* flush */;
+
+ t = hd->nblocks;
+ /* multiply by 64 to make a byte count */
+ lsb = t << 6;
+ msb = t >> 26;
+ /* add the count */
+ t = lsb;
+ if( (lsb += hd->count) < t )
+ msb++;
+ /* multiply by 8 to make a bit count */
+ t = lsb;
+ lsb <<= 3;
+ msb <<= 3;
+ msb |= t >> 29;
+
+ if( hd->count < 56 ) { /* enough room */
+ hd->buf[hd->count++] = 0x80; /* pad */
+ while( hd->count < 56 )
+ hd->buf[hd->count++] = 0; /* pad */
+ }
+ else { /* need one extra block */
+ hd->buf[hd->count++] = 0x80; /* pad character */
+ while( hd->count < 64 )
+ hd->buf[hd->count++] = 0;
+ md4_write(hd, NULL, 0); /* flush */;
+ memset(hd->buf, 0, 56 ); /* fill next block with zeroes */
+ }
+ /* append the 64 bit count */
+ hd->buf[56] = lsb ;
+ hd->buf[57] = lsb >> 8;
+ hd->buf[58] = lsb >> 16;
+ hd->buf[59] = lsb >> 24;
+ hd->buf[60] = msb ;
+ hd->buf[61] = msb >> 8;
+ hd->buf[62] = msb >> 16;
+ hd->buf[63] = msb >> 24;
+ transform( hd, hd->buf );
+ burn_stack (80+6*sizeof(void*));
+
+ p = hd->buf;
+ #ifdef BIG_ENDIAN_HOST
+ #define X(a) do { *p++ = hd->a ; *p++ = hd->a >> 8; \
+ *p++ = hd->a >> 16; *p++ = hd->a >> 24; } while(0)
+ #else /* little endian */
+ #define X(a) do { *(u32*)p = (*hd).a ; p += 4; } while(0)
+ #endif
+ X(A);
+ X(B);
+ X(C);
+ X(D);
+ #undef X
+
+}
+
+static byte *
+md4_read( MD4_CONTEXT *hd )
+{
+ return hd->buf;
+}
+
+/****************
+ * Return some information about the algorithm. We need algo here to
+ * distinguish different flavors of the algorithm.
+ * Returns: A pointer to string describing the algorithm or NULL if
+ * the ALGO is invalid.
+ */
+static const char *
+md4_get_info( int algo, size_t *contextsize,
+ byte **r_asnoid, int *r_asnlen, int *r_mdlen,
+ void (**r_init)( void *c ),
+ void (**r_write)( void *c, byte *buf, size_t nbytes ),
+ void (**r_final)( void *c ),
+ byte *(**r_read)( void *c )
+ )
+{
+ static byte asn[18] = /* Object ID is 1.2.840.113549.2.4 */
+ { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86,0x48,
+ 0x86, 0xf7, 0x0d, 0x02, 0x04, 0x05, 0x00, 0x04, 0x10 };
+
+ if( algo != 11 )
+ return NULL;
+
+ *contextsize = sizeof(MD4_CONTEXT);
+ *r_asnoid = asn;
+ *r_asnlen = DIM(asn);
+ *r_mdlen = 16;
+ *(void (**)(MD4_CONTEXT *))r_init = md4_init;
+ *(void (**)(MD4_CONTEXT *, byte*, size_t))r_write = md4_write;
+ *(void (**)(MD4_CONTEXT *))r_final = md4_final;
+ *(byte *(**)(MD4_CONTEXT *))r_read = md4_read;
+
+ return "MD4";
+}
+
+
+#ifndef IS_MODULE
+static
+#endif
+const char * const gnupgext_version = "MD4 ($Revision: 1.23 $)";
+
+static struct {
+ int class;
+ int version;
+ int value;
+ void (*func)(void);
+} func_table[] = {
+ { 10, 1, 0, (void(*)(void))md4_get_info },
+ { 11, 1, 1 },
+};
+
+
+#ifndef IS_MODULE
+static
+#endif
+void *
+gnupgext_enum_func( int what, int *sequence, int *class, int *vers )
+{
+ void *ret;
+ int i = *sequence;
+
+ do {
+ if( i >= DIM(func_table) || i < 0 )
+ return NULL;
+ *class = func_table[i].class;
+ *vers = func_table[i].version;
+ switch( *class ) {
+ case 11: case 21: case 31: ret = &func_table[i].value; break;
+ default: ret = func_table[i].func; break;
+ }
+ i++;
+ } while( what && what != *class );
+
+ *sequence = i;
+ return ret;
+}
+
+
+
+
+#ifndef IS_MODULE
+void
+_gcry_md4_constructor(void)
+{
+ _gcry_register_internal_cipher_extension( gnupgext_version, gnupgext_enum_func );
+}
+#endif
+
+/* end of file */
Index: doc/gcrypt.texi
===================================================================
RCS file: /cvs/gnupg/libgcrypt/doc/gcrypt.texi,v
retrieving revision 1.3
diff -u -p -r1.3 gcrypt.texi
--- doc/gcrypt.texi 14 Aug 2002 19:07:55 -0000 1.3
+++ doc/gcrypt.texi 2 Nov 2002 21:39:01 -0000
@@ -232,7 +232,7 @@ directory in which the header file is lo
file search path (via the @option{-I} option).

However, the path to the include file is determined at the time the
-source is configured. To solve this problem, `Libgrypt' ships with a small
+source is configured. To solve this problem, `Libgcrypt' ships with a small
helper program @command{libgcrypt-config} that knows the path to the
include file and other configuration options. The options that need
to be added to the compiler invocation at compile time are output by
@@ -943,7 +943,7 @@ using a random quality as defined by @va
@deftypefun void *gcry_random_bytes_secure (size_t @var{nbytes}, enum gcry_random_level @var{level})

Allocate a memory block consisting of @var{nbytes} fresh random bytes
-using a random quality as defined by @var{level}. This fuinction
+using a random quality as defined by @var{level}. This function
differs from @code{gcry_random_bytes} in that the returned buffer is
allcated in a ``secure'' area of the memory.
@end deftypefun
Index: src/gcrypt.h
===================================================================
RCS file: /cvs/gnupg/libgcrypt/src/gcrypt.h,v
retrieving revision 1.63
diff -u -p -r1.63 gcrypt.h
--- src/gcrypt.h 20 Sep 2002 11:10:06 -0000 1.63
+++ src/gcrypt.h 2 Nov 2002 21:39:01 -0000
@@ -675,7 +675,8 @@ enum gcry_md_algos
GCRY_MD_HAVAL = 7, /* HAVAL, 5 pass, 160 bit. */
GCRY_MD_SHA256 = 8,
GCRY_MD_SHA384 = 9,
- GCRY_MD_SHA512 = 10
+ GCRY_MD_SHA512 = 10,
+ GCRY_MD_MD4 = 11
};

/* Flags used with the open function. */
Index: tests/basic.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/tests/basic.c,v
retrieving revision 1.4
diff -u -p -r1.4 basic.c
--- tests/basic.c 26 Aug 2002 10:36:31 -0000 1.4
+++ tests/basic.c 2 Nov 2002 21:39:01 -0000
@@ -160,11 +160,121 @@ check_ciphers (void)
/* TODO: add some extra encryption to test the higher level functions */
}

+static void
+check_one_md (int algo, char *data, int len, char *expect)
+{
+ GCRY_MD_HD hd;
+ char *p;
+ int mdlen;
+
+ hd = gcry_md_open (algo, 0);
+ if (!hd) {
+ fail ("algo %d, grcy_md_open failed: %s\n",
+ algo, gcry_strerror (-1) );
+ return;
+ }
+
+ mdlen = gcry_md_get_algo_dlen(algo);
+ if (mdlen < 1 || mdlen > 500) {
+ fail ("algo %d, grcy_md_get_algo_dlen failed: %d\n", algo, mdlen);
+ return;
+ }
+
+ gcry_md_write (hd, data, len);
+
+ p = gcry_md_read (hd, algo);
+
+ if ( memcmp (p, expect, mdlen) )
+ fail ("algo %d, digest mismatch\n", algo);
+
+ gcry_md_close (hd);
+}

static void
check_digests ()
{
- /* TODO */
+ static struct algos {
+ int md;
+ char *data;
+ char *expect;
+ } algos[] = {
+ { GCRY_MD_MD4, "",
+ "\x31\xD6\xCF\xE0\xD1\x6A\xE9\x31\xB7\x3C\x59\xD7\xE0\xC0\x89\xC0" },
+ { GCRY_MD_MD4, "a",
+ "\xBD\xE5\x2C\xB3\x1D\xE3\x3E\x46\x24\x5E\x05\xFB\xDB\xD6\xFB\x24" },
+ { GCRY_MD_MD5, "",
+ "\xD4\x1D\x8C\xD9\x8F\x00\xB2\x04\xE9\x80\x09\x98\xEC\xF8\x42\x7E" },
+ { GCRY_MD_MD5, "a",
+ "\x0C\xC1\x75\xB9\xC0\xF1\xB6\xA8\x31\xC3\x99\xE2\x69\x77\x26\x61" },
+ { GCRY_MD_MD5, "abc",
+ "\x90\x01\x50\x98\x3C\xD2\x4F\xB0\xD6\x96\x3F\x7D\x28\xE1\x7F\x72" },
+ { GCRY_MD_MD5, "message digest",
+ "\xF9\x6B\x69\x7D\x7C\xB7\x93\x8D\x52\x5A\x2F\x31\xAA\xF1\x61\xD0"},
+ { GCRY_MD_SHA1, "abc",
+ "\xA9\x99\x3E\x36\x47\x06\x81\x6A\xBA\x3E"
+ "\x25\x71\x78\x50\xC2\x6C\x9C\xD0\xD8\x9D"},
+ { GCRY_MD_SHA1, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "\x84\x98\x3E\x44\x1C\x3B\xD2\x6E\xBA\xAE"
+ "\x4A\xA1\xF9\x51\x29\xE5\xE5\x46\x70\xF1" },
+ { GCRY_MD_RMD160, "",
+ "\x9c\x11\x85\xa5\xc5\xe9\xfc\x54\x61\x28"
+ "\x08\x97\x7e\xe8\xf5\x48\xb2\x25\x8d\x31" },
+ { GCRY_MD_RMD160, "a",
+ "\x0b\xdc\x9d\x2d\x25\x6b\x3e\xe9\xda\xae"
+ "\x34\x7b\xe6\xf4\xdc\x83\x5a\x46\x7f\xfe" },
+ { GCRY_MD_RMD160, "abc",
+ "\x8e\xb2\x08\xf7\xe0\x5d\x98\x7a\x9b\x04"
+ "\x4a\x8e\x98\xc6\xb0\x87\xf1\x5a\x0b\xfc" },
+ { GCRY_MD_RMD160, "message digest",
+ "\x5d\x06\x89\xef\x49\xd2\xfa\xe5\x72\xb8"
+ "\x81\xb1\x23\xa8\x5f\xfa\x21\x59\x5f\x36" },
+#if 0
+ { GCRY_MD_TIGER, "",
+ "\x24\xF0\x13\x0C\x63\xAC\x93\x32\x16\x16\x6E\x76"
+ "\xB1\xBB\x92\x5F\xF3\x73\xDE\x2D\x49\x58\x4E\x7A" },
+ { GCRY_MD_TIGER, "abc",
+ "\xF2\x58\xC1\xE8\x84\x14\xAB\x2A\x52\x7A\xB5\x41"
+ "\xFF\xC5\xB8\xBF\x93\x5F\x7B\x95\x1C\x13\x29\x51" },
+ { GCRY_MD_TIGER, "Tiger",
+ "\x9F\x00\xF5\x99\x07\x23\x00\xDD\x27\x6A\xBB\x38"
+ "\xC8\xEB\x6D\xEC\x37\x79\x0C\x11\x6F\x9D\x2B\xDF" },
+ { GCRY_MD_TIGER, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg"
+ "hijklmnopqrstuvwxyz0123456789+-",
+ "\x87\xFB\x2A\x90\x83\x85\x1C\xF7\x47\x0D\x2C\xF8"
+ "\x10\xE6\xDF\x9E\xB5\x86\x44\x50\x34\xA5\xA3\x86" },
+ { GCRY_MD_TIGER, "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdef"
+ "ghijklmnopqrstuvwxyz+0123456789",
+ "467DB80863EBCE488DF1CD1261655DE957896565975F9197" },
+ { GCRY_MD_TIGER, "Tiger - A Fast New Hash Function, "
+ "by Ross Anderson and Eli Biham",
+ "0C410A042968868A1671DA5A3FD29A725EC1E457D3CDB303" },
+ { GCRY_MD_TIGER, "Tiger - A Fast New Hash Function, "
+ "by Ross Anderson and Eli Biham, proceedings of Fa"
+ "st Software Encryption 3, Cambridge.",
+ "EBF591D5AFA655CE7F22894FF87F54AC89C811B6B0DA3193" },
+ { GCRY_MD_TIGER, "Tiger - A Fast New Hash Function, "
+ "by Ross Anderson and Eli Biham, proceedings of Fa"
+ "st Software Encryption 3, Cambridge, 1996.",
+ "3D9AEB03D1BD1A6357B2774DFD6D5B24DD68151D503974FC" },
+ { GCRY_MD_TIGER, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
+ "ijklmnopqrstuvwxyz0123456789+-ABCDEFGHIJKLMNOPQRS"
+ "TUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-",
+ "00B83EB4E53440C5 76AC6AAEE0A74858 25FD15E70A59FFE4" },
+#endif
+ { 0 }
+ };
+ int i;
+
+ for (i=0; algos[i].md; i++ )
+ {
+ if (verbose)
+ fprintf (stderr, "checking `%s'\n", gcry_md_algo_name (algos[i].md));
+
+ check_one_md (algos[i].md, algos[i].data, strlen(algos[i].data),
+ algos[i].expect);
+ }
+
+ /* TODO: test HMAC mode */
}
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
On Sat, 02 Nov 2002 22:45:21 +0100, Simon Josefsson said:

> I need MD4 support so I wrote the below, but it didn't work so I added

I have commited that changes but I changed the value of the
GCRY_MD_MD4 macro. Thanks.

> a test suite for message digests (thus removing a TODO in
> tests/basic.c), but wasn't able to get it to work anyway (the dynamic
> algorithm loading stuff is rather convoluted IMHO). The test case

Agreed. It has been removed from GnuPG so we will probably also
remove it form libgcrypt. There is no real point in having loadable
algorithms, given that the text is usually small and the large tables
some algorithms use are anywat paged in on demand.

> fails the same way for the TIGER algorithm, so perhaps there is
> something genuinely wrong. I'm thankful for any help in getting this
> to work, and to get it included in the distribution (I have signed
> papers now).


Salam-Shalom,

Werner
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
Werner Koch <wk@gnupg.org> writes:

> On Sat, 02 Nov 2002 22:45:21 +0100, Simon Josefsson said:
>
>> I need MD4 support so I wrote the below, but it didn't work so I added
>
> I have commited that changes but I changed the value of the
> GCRY_MD_MD4 macro. Thanks.
>
>> a test suite for message digests (thus removing a TODO in
>> tests/basic.c), but wasn't able to get it to work anyway (the dynamic
>> algorithm loading stuff is rather convoluted IMHO). The test case
>
> Agreed. It has been removed from GnuPG so we will probably also
> remove it form libgcrypt. There is no real point in having loadable
> algorithms, given that the text is usually small and the large tables
> some algorithms use are anywat paged in on demand.

Thanks for adding it, but it doesn't work for me, make check says:

algo 301, grcy_md_open failed: invalid hash algorithm
algo 301, grcy_md_open failed: invalid hash algorithm

Does it work for you? Note that my original patch didn't work for me
either, so my original post asked how to fix it. I tried to debug why
it didn't work, but I lost myself in the dynamic module loading code,
in which I assume the problem also lies. MD4 seems to be properly
statically linked though.
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
On Thu, 21 Nov 2002 17:34:47 +0100, Simon Josefsson said:

> algo 301, grcy_md_open failed: invalid hash algorithm

> Does it work for you? Note that my original patch didn't work for me

No. I had some hope that someone else debugs this ;-). Should be not
that complicated. I am currently hacking on a Mutt version and the
time is pressing. I'll fix it asap and make a new release.


Salam-Shalom,

Werner
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
Werner Koch <wk@gnupg.org> writes:

> On Thu, 21 Nov 2002 17:34:47 +0100, Simon Josefsson said:
>
>> algo 301, grcy_md_open failed: invalid hash algorithm
>
>> Does it work for you? Note that my original patch didn't work for me
>
> No. I had some hope that someone else debugs this ;-). Should be not
> that complicated. I am currently hacking on a Mutt version and the
> time is pressing. I'll fix it asap and make a new release.

Thanks. I'll try to debug it too.

Btw, is there some inconsistency between this NEWS entry

* It is possible to use libgcrypt w/o intialized secure memory.

and the following output?

jas@latte:~$ cat baba.c
#include <gcrypt.h>

int main()
{
GCRY_MD_HD md5h;

md5h = gcry_md_open (GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
puts("ok");
}
jas@latte:~$ gcc -o baba baba.c `libgcrypt-config --libs --cflags`
jas@latte:~$ ./baba
operation is not possible without initialized secure memory
(you may have used the wrong program for this task)
jas@latte:~$
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
> int main()
> {
> GCRY_MD_HD md5h;

gcry_control (GCRYCTL_DISABLE_SECMEM);

> md5h = gcry_md_open (GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);

The default is to use the "secure" memory - in this case we use a key
and we assume that a MAC key is somewhat sensitive.


Shalom-Salam,

Werner
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
Werner Koch <wk@gnupg.org> writes:

>> int main()
>> {
>> GCRY_MD_HD md5h;
>
> gcry_control (GCRYCTL_DISABLE_SECMEM);
>
>> md5h = gcry_md_open (GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
>
> The default is to use the "secure" memory - in this case we use a key
> and we assume that a MAC key is somewhat sensitive.

Ok. This usage is a bit problematic if libgcrypt is used in a third
party library, as the application might use libgcrypt too, and might
want secure memory. Maybe the third party library has to document
that the application must initialize or disable secure memory in
libgcrypt, but OTOH this is weird if the application is not using
libgcrypt, and even more so if libgcrypt is not always used by the
third party library (i.e. it is an optional compile time feature in
the third party library). (Right now my third party library
initializes secure memory, which causes libgcrypt warnings because it
is invoked multiple times. It also overrides the application's
preference whether to use secure memory or not, if the application
happens to use libgcrypt.)

Global states are always a pain...
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
Simon Josefsson <jas@extundo.com> writes:

> Werner Koch <wk@gnupg.org> writes:
>
>> On Thu, 21 Nov 2002 17:34:47 +0100, Simon Josefsson said:
>>
>>> algo 301, grcy_md_open failed: invalid hash algorithm
>>
>>> Does it work for you? Note that my original patch didn't work for me
>>
>> No. I had some hope that someone else debugs this ;-). Should be not
>> that complicated. I am currently hacking on a Mutt version and the
>> time is pressing. I'll fix it asap and make a new release.
>
> Thanks. I'll try to debug it too.

Now it works.

Index: cipher/md.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/cipher/md.c,v
retrieving revision 1.56
diff -u -p -r1.56 md.c
--- cipher/md.c 10 Nov 2002 18:03:28 -0000 1.56
+++ cipher/md.c 23 Nov 2002 02:37:08 -0000
@@ -145,7 +145,7 @@ static int
load_digest_module( int req_algo )
{
static int initialized = 0;
- static u32 checked_algos[256/32];
+ static u32 checked_algos[512/32];
static int checked_all = 0;
struct md_digest_list_s *r;
void *context = NULL;
@@ -161,7 +161,7 @@ load_digest_module( int req_algo )
initialized = 1;
}
algo = req_algo;
- if( algo > 255 || !algo )
+ if( algo > 512 || !algo )
return 0; /* algorithm number too high (does not fit into out bitmap)*/
if( checked_all )
return 0; /* already called with -1 */
Index: cipher/md4.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/cipher/md4.c,v
retrieving revision 1.2
diff -u -p -r1.2 md4.c
--- cipher/md4.c 10 Nov 2002 19:04:54 -0000 1.2
+++ cipher/md4.c 23 Nov 2002 02:37:08 -0000
@@ -90,11 +90,11 @@ burn_stack (int bytes)
burn_stack (bytes);
}

+// #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))

-
/****************
* transform n*64 bytes
*/
@@ -102,29 +102,14 @@ static void
/*transform( MD4_CONTEXT *ctx, const void *buffer, size_t len )*/
transform( MD4_CONTEXT *ctx, byte *data )
{
- u32 correct_words[16];
register u32 A = ctx->A;
register u32 B = ctx->B;
register u32 C = ctx->C;
register u32 D = ctx->D;
- u32 *cwp = correct_words;
-
-#ifdef BIG_ENDIAN_HOST
- { int i;
- byte *p2, *p1;
- for(i=0, p1=data, p2=(byte*)correct_words; i < 16; i++, p2 += 4 ) {
- p2[3] = *p1++;
- p2[2] = *p1++;
- p2[1] = *p1++;
- p2[0] = *p1++;
- }
- }
-#else
- memcpy( correct_words, data, 64 );
-#endif
+ u32 *in = (u32*)data;

/* Round 1. */
-#define function(a,b,c,d,k,s) a=rol(a+F(b,c,d)+data[k],s);
+#define function(a,b,c,d,k,s) a=rol(a+F(b,c,d)+in[k],s);
function(A,B,C,D, 0, 3);
function(D,A,B,C, 1, 7);
function(C,D,A,B, 2,11);
@@ -145,7 +130,7 @@ transform( MD4_CONTEXT *ctx, byte *data
#undef function

/* Round 2. */
-#define function(a,b,c,d,k,s) a=rol(a+G(b,c,d)+data[k]+0x5a827999,s);
+#define function(a,b,c,d,k,s) a=rol(a+G(b,c,d)+in[k]+0x5a827999,s);

function(A,B,C,D, 0, 3);
function(D,A,B,C, 4, 5);
@@ -167,7 +152,7 @@ transform( MD4_CONTEXT *ctx, byte *data
#undef function

/* Round 3. */
-#define function(a,b,c,d,k,s) a=rol(a+H(b,c,d)+data[k]+0x6ed9eba1,s);
+#define function(a,b,c,d,k,s) a=rol(a+H(b,c,d)+in[k]+0x6ed9eba1,s);

function(A,B,C,D, 0, 3);
function(D,A,B,C, 8, 9);
@@ -354,7 +339,7 @@ static struct {
void (*func)(void);
} func_table[] = {
{ 10, 1, 0, (void(*)(void))md4_get_info },
- { 11, 1, 1 },
+ { 11, 1, 301 },
};


Index: tests/basic.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/tests/basic.c,v
retrieving revision 1.5
diff -u -p -r1.5 basic.c
--- tests/basic.c 10 Nov 2002 18:01:50 -0000 1.5
+++ tests/basic.c 23 Nov 2002 02:37:09 -0000
@@ -287,7 +287,9 @@ check_digests ()
{ GCRY_MD_MD4, "",
"\x31\xD6\xCF\xE0\xD1\x6A\xE9\x31\xB7\x3C\x59\xD7\xE0\xC0\x89\xC0" },
{ GCRY_MD_MD4, "a",
- "\xBD\xE5\x2C\xB3\x1D\xE3\x3E\x46\x24\x5E\x05\xFB\xDB\xD6\xFB\x24" },
+ "\xbd\xe5\x2c\xb3\x1d\xe3\x3e\x46\x24\x5e\x05\xfb\xdb\xd6\xfb\x24" },
+ { GCRY_MD_MD4, "message digest",
+ "\xd9\x13\x0a\x81\x64\x54\x9f\xe8\x18\x87\x48\x06\xe1\xc7\x01\x4b" },
{ GCRY_MD_MD5, "",
"\xD4\x1D\x8C\xD9\x8F\x00\xB2\x04\xE9\x80\x09\x98\xEC\xF8\x42\x7E" },
{ GCRY_MD_MD5, "a",
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
On Sat, 23 Nov 2002 03:40:16 +0100, Simon Josefsson said:

> - static u32 checked_algos[256/32];
> + static u32 checked_algos[512/32];

Thanks.

> - if( algo > 255 || !algo )
> + if( algo > 512 || !algo )

It should be 511 or >=

> +// #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))

[please don't use C99 comments]

> #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
> #define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
> #define H(x, y, z) ((x) ^ (y) ^ (z))

> - memcpy( correct_words, data, 64 );
> -#endif
> + u32 *in = (u32*)data;

This bears 2 problems: It does not work on big endian machines and
DATA might not be aligned on a 4 byte boundary which results in a bus
error or unpleasant software emulation of the instructions by the CPU
(I once got complains from a Alpha admin, that I should fix my programs
because the kernel log was full with unaligned instructions warnings ;-).

I have fixed and tested this for ia32.

Thanks,

Werner
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
Werner Koch <wk@gnupg.org> writes:

>> - memcpy( correct_words, data, 64 );
>> -#endif
>> + u32 *in = (u32*)data;
>
> This bears 2 problems: It does not work on big endian machines and
> DATA might not be aligned on a 4 byte boundary which results in a bus
> error or unpleasant software emulation of the instructions by the CPU
> (I once got complains from a Alpha admin, that I should fix my programs
> because the kernel log was full with unaligned instructions warnings ;-).
>
> I have fixed and tested this for ia32.

Thanks, I should have done more tests. CVS works on
alphaev67-dec-osf5.0 and sparc-sun-solaris2.8 with non-GNU tools.
scripts/depcomp was not included by a "make dist" here, but it may be
a local problem. The self tests core dump on rs6000-ibm-aix4.3.2.0
and mips-sgi-irix6.5 with non-GNU tools, I'll try to debug it.
Re: [PATCH] Message digest test suite and incomplete MD4 support [ In reply to ]
Simon Josefsson <jas@extundo.com> writes:

> The self tests core dump on rs6000-ibm-aix4.3.2.0

Seems to be caused by autoconf guessing the wrong values. If I use
the config.h from libgcrypt-1.1.10 (which works) with 1.1.11 (after
modifying version defines) the new version works too. Diff below for
reference, but it is probably a local problem as I'm using the latest
auto* tools in Debian Sid.

> and mips-sgi-irix6.5 with non-GNU tools, I'll try to debug it.

Libgcrypt 1.1.10 also crashed, and my debugging environment on the
host is not enjoyable so I'm leaving this for someone else.

--- config.h Sun Nov 24 20:20:35 2002
+++ ../libgcrypt-1.1.10/config.h Sun Nov 24 20:02:04 2002
@@ -26,10 +26,6 @@
/* Define to 1 if you have the `clock_gettime' function. */
#define HAVE_CLOCK_GETTIME 1

-/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you
- don't. */
-#define HAVE_DECL_SYS_SIGLIST 0
-
/* defined if the system supports a random device */
/* #undef HAVE_DEV_RANDOM */

@@ -56,7 +52,7 @@
#define HAVE_FSTAT64 1

/* Define to 1 if you have the `gethrtime' function. */
-#define HAVE_GETHRTIME 1
+/* #undef HAVE_GETHRTIME */

/* Define to 1 if you have the `getpagesize' function. */
#define HAVE_GETPAGESIZE 1
@@ -122,7 +118,7 @@
#define HAVE_STDLIB_H 1

/* Define to 1 if you have the `stpcpy' function. */
-#define HAVE_STPCPY 1
+/* #undef HAVE_STPCPY */

/* Define to 1 if you have the `strcasecmp' function. */
#define HAVE_STRCASECMP 1
@@ -134,7 +130,7 @@
#define HAVE_STRFTIME 1

/* Define to 1 if you have the `stricmp' function. */
-#define HAVE_STRICMP 1
+/* #undef HAVE_STRICMP */

/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
@@ -143,10 +139,10 @@
#define HAVE_STRING_H 1

/* Define to 1 if you have the `strlwr' function. */
-#define HAVE_STRLWR 1
+/* #undef HAVE_STRLWR */

/* Define to 1 if you have the `strsep' function. */
-#define HAVE_STRSEP 1
+/* #undef HAVE_STRSEP */

/* Define to 1 if you have the `strtoul' function. */
#define HAVE_STRTOUL 1
@@ -197,7 +193,7 @@
#define HAVE_VPRINTF 1

/* Define to 1 if you have the `wait4' function. */
-#define HAVE_WAIT4 1
+/* #undef HAVE_WAIT4 */

/* Define to 1 if you have the `waitpid' function. */
#define HAVE_WAITPID 1
@@ -236,13 +232,13 @@
#define PACKAGE_NAME "libgcrypt"

/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "libgcrypt 1.1.11"
+#define PACKAGE_STRING "libgcrypt 1.1.10"

/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libgcrypt"

/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.1.11"
+#define PACKAGE_VERSION "1.1.10"

/* A human readable text with the name of the OS */
#define PRINTABLE_OS_NAME "AIX"
@@ -265,6 +261,9 @@
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1

+/* Define to 1 if `sys_siglist' is declared by <signal.h> or <unistd.h>. */
+/* #undef SYS_SIGLIST_DECLARED */
+
/* define if capabilities should be used */
/* #undef USE_CAPABILITIES */

@@ -279,7 +278,7 @@
/* #undef USE_STATIC_RNDW32 */

/* Version of this package */
-#define VERSION "1.1.11"
+#define VERSION "1.1.10"

/* define if compiled symbols have a leading underscore */
/* #undef WITH_SYMBOL_UNDERSCORE */