summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2014-01-09 19:14:09 +0100
committerWerner Koch <wk@gnupg.org>2014-01-19 15:13:03 +0100
commit94030e44aaff805d754e368507f16dd51a531b72 (patch)
treea25c8cdcab25df13e58fbe43d1bf099c91b7cb17
parentc3b30bae7d1e157f8b65e32ba1b3a516f2bbf58b (diff)
downloadlibgcrypt-94030e44aaff805d754e368507f16dd51a531b72.tar.gz
md: Add Whirlpool bug emulation feature.
* src/gcrypt.h.in (GCRY_MD_FLAG_BUGEMU1): New. * src/cipher-proto.h (gcry_md_init_t): Add arg FLAGS. Change all code to implement that flag. * cipher/md.c (gcry_md_context): Replace SECURE and FINALIZED by bit field FLAGS. Add flag BUGEMU1. Change all users. (md_open): Replace args SECURE and HMAC by FLAGS. Init flags.bugemu1. (_gcry_md_open): Add for GCRY_MD_FLAG_BUGEMU1. (md_enable): Pass bugemu1 flag to the hash init function. (_gcry_md_reset): Ditto. -- This problem is for example exhibited in the Linux cryptsetup tool. See https://bbs.archlinux.org/viewtopic.php?id=175737 . It has be been tracked down by Milan Broz. The suggested way of using the flag is: if (whirlpool_bug_assumed) { #if GCRYPT_VERSION_NUMBER >= 0x010601 err = gcry_md_open (&hd, GCRY_MD_WHIRLPOOL, GCRY_MD_FLAG_BUGEMU1) if (gpg_err_code (err) == GPG_ERR_INV_ARG) error ("Need at least Libggcrypt 1.6.1 for the fix"); else { do_hash (hd); gcry_md_close (hd); } #endif } Signed-off-by: Werner Koch <wk@gnupg.org>
-rw-r--r--NEWS3
-rw-r--r--cipher/crc.c15
-rw-r--r--cipher/gostr3411-94.c4
-rw-r--r--cipher/md.c56
-rw-r--r--cipher/md4.c4
-rw-r--r--cipher/md5.c4
-rw-r--r--cipher/rmd160.c14
-rw-r--r--cipher/sha1.c8
-rw-r--r--cipher/sha256.c8
-rw-r--r--cipher/sha512.c8
-rw-r--r--cipher/stribog.c9
-rw-r--r--cipher/whirlpool.c132
-rw-r--r--doc/gcrypt.texi16
-rw-r--r--src/cipher-proto.h2
-rw-r--r--src/gcrypt.h.in3
-rw-r--r--tests/basic.c53
16 files changed, 291 insertions, 48 deletions
diff --git a/NEWS b/NEWS
index 4bf4a066..5e21eb6b 100644
--- a/NEWS
+++ b/NEWS
@@ -8,10 +8,13 @@ Noteworthy changes in version 1.7.0 (unreleased)
* Support curves GOST R 34.10-2001 and GOST R 34.10-2012.
+ * Add emulation from broken Whirlpool code prior to 1.6.0.
+
* Interface changes relative to the 1.6.0 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcry_mac_get_algo NEW.
GCRY_MAC_HMAC_MD2 NEW.
+ GCRY_MD_FLAG_BUGEMU1 NEW.
Noteworthy changes in version 1.6.0 (2013-12-16)
diff --git a/cipher/crc.c b/cipher/crc.c
index 4f72ffb2..1322f0da 100644
--- a/cipher/crc.c
+++ b/cipher/crc.c
@@ -149,9 +149,12 @@ CRC_CONTEXT;
/* CRC32 */
static void
-crc32_init (void *context)
+crc32_init (void *context, unsigned int flags)
{
CRC_CONTEXT *ctx = (CRC_CONTEXT *) context;
+
+ (void)flags;
+
ctx->CRC = 0 ^ 0xffffffffL;
}
@@ -184,9 +187,12 @@ crc32_final (void *context)
/* CRC32 a'la RFC 1510 */
static void
-crc32rfc1510_init (void *context)
+crc32rfc1510_init (void *context, unsigned int flags)
{
CRC_CONTEXT *ctx = (CRC_CONTEXT *) context;
+
+ (void)flags;
+
ctx->CRC = 0;
}
@@ -237,9 +243,12 @@ crc32rfc1510_final (void *context)
#define CRC24_POLY 0x1864cfbL
static void
-crc24rfc2440_init (void *context)
+crc24rfc2440_init (void *context, unsigned int flags)
{
CRC_CONTEXT *ctx = (CRC_CONTEXT *) context;
+
+ (void)flags;
+
ctx->CRC = CRC24_INIT;
}
diff --git a/cipher/gostr3411-94.c b/cipher/gostr3411-94.c
index b3326aa5..9a39733d 100644
--- a/cipher/gostr3411-94.c
+++ b/cipher/gostr3411-94.c
@@ -44,10 +44,12 @@ static unsigned int
transform (void *c, const unsigned char *data, size_t nblks);
static void
-gost3411_init (void *context)
+gost3411_init (void *context, unsigned int flags)
{
GOSTR3411_CONTEXT *hd = context;
+ (void)flags;
+
memset (&hd->hd, 0, sizeof(hd->hd));
memset (hd->h, 0, 32);
memset (hd->sigma, 0, 32);
diff --git a/cipher/md.c b/cipher/md.c
index f4fb1294..a332e039 100644
--- a/cipher/md.c
+++ b/cipher/md.c
@@ -1,7 +1,7 @@
/* md.c - message digest dispatcher
* Copyright (C) 1998, 1999, 2002, 2003, 2006,
* 2008 Free Software Foundation, Inc.
- * Copyright (C) 2013 g10 Code GmbH
+ * Copyright (C) 2013, 2014 g10 Code GmbH
*
* This file is part of Libgcrypt.
*
@@ -93,9 +93,12 @@ struct gcry_md_context
{
int magic;
size_t actual_handle_size; /* Allocated size of this handle. */
- int secure;
FILE *debug;
- int finalized;
+ struct {
+ unsigned int secure: 1;
+ unsigned int finalized:1;
+ unsigned int bugemu1:1;
+ } flags;
GcryDigestEntry *list;
byte *macpads;
int macpads_Bsize; /* Blocksize as used for the HMAC pads. */
@@ -269,9 +272,11 @@ check_digest_algo (int algorithm)
* may be 0.
*/
static gcry_err_code_t
-md_open (gcry_md_hd_t *h, int algo, int secure, int hmac)
+md_open (gcry_md_hd_t *h, int algo, unsigned int flags)
{
- gcry_err_code_t err = GPG_ERR_NO_ERROR;
+ gcry_err_code_t err = 0;
+ int secure = !!(flags & GCRY_MD_FLAG_SECURE);
+ int hmac = !!(flags & GCRY_MD_FLAG_HMAC);
int bufsize = secure ? 512 : 1024;
struct gcry_md_context *ctx;
gcry_md_hd_t hd;
@@ -315,7 +320,8 @@ md_open (gcry_md_hd_t *h, int algo, int secure, int hmac)
memset (hd->ctx, 0, sizeof *hd->ctx);
ctx->magic = secure ? CTX_MAGIC_SECURE : CTX_MAGIC_NORMAL;
ctx->actual_handle_size = n + sizeof (struct gcry_md_context);
- ctx->secure = secure;
+ ctx->flags.secure = secure;
+ ctx->flags.bugemu1 = !!(flags & GCRY_MD_FLAG_BUGEMU1);
if (hmac)
{
@@ -371,13 +377,12 @@ _gcry_md_open (gcry_md_hd_t *h, int algo, unsigned int flags)
gcry_err_code_t rc;
gcry_md_hd_t hd;
- if ((flags & ~(GCRY_MD_FLAG_SECURE | GCRY_MD_FLAG_HMAC)))
+ if ((flags & ~(GCRY_MD_FLAG_SECURE
+ | GCRY_MD_FLAG_HMAC
+ | GCRY_MD_FLAG_BUGEMU1)))
rc = GPG_ERR_INV_ARG;
else
- {
- rc = md_open (&hd, algo, (flags & GCRY_MD_FLAG_SECURE),
- (flags & GCRY_MD_FLAG_HMAC));
- }
+ rc = md_open (&hd, algo, flags);
*h = rc? NULL : hd;
return rc;
@@ -423,7 +428,7 @@ md_enable (gcry_md_hd_t hd, int algorithm)
- sizeof (entry->context));
/* And allocate a new list entry. */
- if (h->secure)
+ if (h->flags.secure)
entry = xtrymalloc_secure (size);
else
entry = xtrymalloc (size);
@@ -438,7 +443,8 @@ md_enable (gcry_md_hd_t hd, int algorithm)
h->list = entry;
/* And init this instance. */
- entry->spec->init (&entry->context.c);
+ entry->spec->init (&entry->context.c,
+ h->flags.bugemu1? GCRY_MD_FLAG_BUGEMU1:0);
}
}
@@ -467,7 +473,7 @@ md_copy (gcry_md_hd_t ahd, gcry_md_hd_t *b_hd)
md_write (ahd, NULL, 0);
n = (char *) ahd->ctx - (char *) ahd;
- if (a->secure)
+ if (a->flags.secure)
bhd = xtrymalloc_secure (n + sizeof (struct gcry_md_context));
else
bhd = xtrymalloc (n + sizeof (struct gcry_md_context));
@@ -505,7 +511,7 @@ md_copy (gcry_md_hd_t ahd, gcry_md_hd_t *b_hd)
{
for (ar = a->list; ar; ar = ar->next)
{
- if (a->secure)
+ if (a->flags.secure)
br = xtrymalloc_secure (sizeof *br
+ ar->spec->contextsize
- sizeof(ar->context));
@@ -560,12 +566,13 @@ _gcry_md_reset (gcry_md_hd_t a)
/* Note: We allow this even in fips non operational mode. */
- a->bufpos = a->ctx->finalized = 0;
+ a->bufpos = a->ctx->flags.finalized = 0;
for (r = a->ctx->list; r; r = r->next)
{
memset (r->context.c, 0, r->spec->contextsize);
- (*r->spec->init) (&r->context.c);
+ (*r->spec->init) (&r->context.c,
+ a->ctx->flags.bugemu1? GCRY_MD_FLAG_BUGEMU1:0);
}
if (a->ctx->macpads)
md_write (a, a->ctx->macpads, a->ctx->macpads_Bsize); /* inner pad */
@@ -642,7 +649,7 @@ md_final (gcry_md_hd_t a)
{
GcryDigestEntry *r;
- if (a->ctx->finalized)
+ if (a->ctx->flags.finalized)
return;
if (a->bufpos)
@@ -651,7 +658,7 @@ md_final (gcry_md_hd_t a)
for (r = a->ctx->list; r; r = r->next)
(*r->spec->final) (&r->context.c);
- a->ctx->finalized = 1;
+ a->ctx->flags.finalized = 1;
if (a->ctx->macpads)
{
@@ -660,8 +667,11 @@ md_final (gcry_md_hd_t a)
byte *p = md_read (a, algo);
size_t dlen = md_digest_length (algo);
gcry_md_hd_t om;
- gcry_err_code_t err = md_open (&om, algo, a->ctx->secure, 0);
+ gcry_err_code_t err;
+ err = md_open (&om, algo,
+ ((a->ctx->flags.secure? GCRY_MD_FLAG_SECURE:0)
+ | (a->ctx->flags.bugemu1? GCRY_MD_FLAG_BUGEMU1:0)));
if (err)
_gcry_fatal_error (err, NULL);
md_write (om,
@@ -864,7 +874,7 @@ _gcry_md_hash_buffer (int algo, void *digest,
}
}
- err = md_open (&h, algo, 0, 0);
+ err = md_open (&h, algo, 0);
if (err)
log_bug ("gcry_md_open failed for algo %d: %s",
algo, gpg_strerror (gcry_error(err)));
@@ -925,7 +935,7 @@ _gcry_md_hash_buffers (int algo, unsigned int flags, void *digest,
}
}
- rc = md_open (&h, algo, 0, hmac);
+ rc = md_open (&h, algo, (hmac? GCRY_MD_FLAG_HMAC:0));
if (rc)
return rc;
@@ -1158,7 +1168,7 @@ _gcry_md_info (gcry_md_hd_t h, int cmd, void *buffer, size_t *nbytes)
switch (cmd)
{
case GCRYCTL_IS_SECURE:
- *nbytes = h->ctx->secure;
+ *nbytes = h->ctx->flags.secure;
break;
case GCRYCTL_IS_ALGO_ENABLED:
diff --git a/cipher/md4.c b/cipher/md4.c
index 40dc0586..72912545 100644
--- a/cipher/md4.c
+++ b/cipher/md4.c
@@ -69,10 +69,12 @@ static unsigned int
transform ( void *c, const unsigned char *data, size_t nblks );
static void
-md4_init( void *context )
+md4_init (void *context, unsigned int flags)
{
MD4_CONTEXT *ctx = context;
+ (void)flags;
+
ctx->A = 0x67452301;
ctx->B = 0xefcdab89;
ctx->C = 0x98badcfe;
diff --git a/cipher/md5.c b/cipher/md5.c
index d06d3f78..73ad968b 100644
--- a/cipher/md5.c
+++ b/cipher/md5.c
@@ -53,10 +53,12 @@ static unsigned int
transform ( void *ctx, const unsigned char *data, size_t datalen );
static void
-md5_init( void *context )
+md5_init( void *context, unsigned int flags)
{
MD5_CONTEXT *ctx = context;
+ (void)flags;
+
ctx->A = 0x67452301;
ctx->B = 0xefcdab89;
ctx->C = 0x98badcfe;
diff --git a/cipher/rmd160.c b/cipher/rmd160.c
index 224694f2..1a58ba61 100644
--- a/cipher/rmd160.c
+++ b/cipher/rmd160.c
@@ -143,11 +143,13 @@
static unsigned int
transform ( void *ctx, const unsigned char *data, size_t nblks );
-void
-_gcry_rmd160_init (void *context)
+static void
+rmd160_init (void *context, unsigned int flags)
{
RMD160_CONTEXT *hd = context;
+ (void)flags;
+
hd->h0 = 0x67452301;
hd->h1 = 0xEFCDAB89;
hd->h2 = 0x98BADCFE;
@@ -162,6 +164,12 @@ _gcry_rmd160_init (void *context)
}
+void
+_gcry_rmd160_init (void *context)
+{
+ rmd160_init (context, 0);
+}
+
/****************
* Transform the message X which consists of 16 32-bit-words
@@ -528,6 +536,6 @@ gcry_md_spec_t _gcry_digest_spec_rmd160 =
{
GCRY_MD_RMD160, {0, 0},
"RIPEMD160", asn, DIM (asn), oid_spec_rmd160, 20,
- _gcry_rmd160_init, _gcry_md_block_write, rmd160_final, rmd160_read,
+ rmd160_init, _gcry_md_block_write, rmd160_final, rmd160_read,
sizeof (RMD160_CONTEXT)
};
diff --git a/cipher/sha1.c b/cipher/sha1.c
index 889a7ea1..65bd6866 100644
--- a/cipher/sha1.c
+++ b/cipher/sha1.c
@@ -106,11 +106,13 @@ transform (void *c, const unsigned char *data, size_t nblks);
static void
-sha1_init (void *context)
+sha1_init (void *context, unsigned int flags)
{
SHA1_CONTEXT *hd = context;
unsigned int features = _gcry_get_hw_features ();
+ (void)flags;
+
hd->h0 = 0x67452301;
hd->h1 = 0xefcdab89;
hd->h2 = 0x98badcfe;
@@ -425,7 +427,7 @@ _gcry_sha1_hash_buffer (void *outbuf, const void *buffer, size_t length)
{
SHA1_CONTEXT hd;
- sha1_init (&hd);
+ sha1_init (&hd, 0);
_gcry_md_block_write (&hd, buffer, length);
sha1_final (&hd);
memcpy (outbuf, hd.bctx.buf, 20);
@@ -438,7 +440,7 @@ _gcry_sha1_hash_buffers (void *outbuf, const gcry_buffer_t *iov, int iovcnt)
{
SHA1_CONTEXT hd;
- sha1_init (&hd);
+ sha1_init (&hd, 0);
for (;iovcnt > 0; iov++, iovcnt--)
_gcry_md_block_write (&hd,
(const char*)iov[0].data + iov[0].off, iov[0].len);
diff --git a/cipher/sha256.c b/cipher/sha256.c
index 601e9c0b..4efaec6f 100644
--- a/cipher/sha256.c
+++ b/cipher/sha256.c
@@ -92,11 +92,13 @@ transform (void *c, const unsigned char *data, size_t nblks);
static void
-sha256_init (void *context)
+sha256_init (void *context, unsigned int flags)
{
SHA256_CONTEXT *hd = context;
unsigned int features = _gcry_get_hw_features ();
+ (void)flags;
+
hd->h0 = 0x6a09e667;
hd->h1 = 0xbb67ae85;
hd->h2 = 0x3c6ef372;
@@ -128,11 +130,13 @@ sha256_init (void *context)
static void
-sha224_init (void *context)
+sha224_init (void *context, unsigned int flags)
{
SHA256_CONTEXT *hd = context;
unsigned int features = _gcry_get_hw_features ();
+ (void)flags;
+
hd->h0 = 0xc1059ed8;
hd->h1 = 0x367cd507;
hd->h2 = 0x3070dd17;
diff --git a/cipher/sha512.c b/cipher/sha512.c
index 3474694c..92b49131 100644
--- a/cipher/sha512.c
+++ b/cipher/sha512.c
@@ -119,12 +119,14 @@ static unsigned int
transform (void *context, const unsigned char *data, size_t nblks);
static void
-sha512_init (void *context)
+sha512_init (void *context, unsigned int flags)
{
SHA512_CONTEXT *ctx = context;
SHA512_STATE *hd = &ctx->state;
unsigned int features = _gcry_get_hw_features ();
+ (void)flags;
+
hd->h0 = U64_C(0x6a09e667f3bcc908);
hd->h1 = U64_C(0xbb67ae8584caa73b);
hd->h2 = U64_C(0x3c6ef372fe94f82b);
@@ -157,12 +159,14 @@ sha512_init (void *context)
}
static void
-sha384_init (void *context)
+sha384_init (void *context, unsigned int flags)
{
SHA512_CONTEXT *ctx = context;
SHA512_STATE *hd = &ctx->state;
unsigned int features = _gcry_get_hw_features ();
+ (void)flags;
+
hd->h0 = U64_C(0xcbbb9d5dc1059ed8);
hd->h1 = U64_C(0x629a292a367cd507);
hd->h2 = U64_C(0x9159015a3070dd17);
diff --git a/cipher/stribog.c b/cipher/stribog.c
index 6d1d3422..942bbf4c 100644
--- a/cipher/stribog.c
+++ b/cipher/stribog.c
@@ -1198,10 +1198,12 @@ transform (void *context, const unsigned char *inbuf_arg, size_t datalen);
static void
-stribog_init_512 (void *context)
+stribog_init_512 (void *context, unsigned int flags)
{
STRIBOG_CONTEXT *hd = context;
+ (void)flags;
+
memset (hd, 0, sizeof (*hd));
hd->bctx.blocksize = 64;
@@ -1209,10 +1211,11 @@ stribog_init_512 (void *context)
}
static void
-stribog_init_256 (void *context)
+stribog_init_256 (void *context, unsigned int flags)
{
STRIBOG_CONTEXT *hd = context;
- stribog_init_512 (context);
+
+ stribog_init_512 (context, flags);
memset (hd->h, 1, 64);
}
diff --git a/cipher/whirlpool.c b/cipher/whirlpool.c
index 57ca8826..ffc6662c 100644
--- a/cipher/whirlpool.c
+++ b/cipher/whirlpool.c
@@ -54,6 +54,11 @@ typedef u64 whirlpool_block_t[BLOCK_SIZE / 8];
typedef struct {
gcry_md_block_ctx_t bctx;
whirlpool_block_t hash_state;
+ int use_bugemu;
+ struct {
+ size_t count;
+ unsigned char length[32];
+ } bugemu;
} whirlpool_context_t;
@@ -1166,7 +1171,7 @@ whirlpool_transform (void *ctx, const unsigned char *data, size_t nblks);
static void
-whirlpool_init (void *ctx)
+whirlpool_init (void *ctx, unsigned int flags)
{
whirlpool_context_t *context = ctx;
@@ -1174,9 +1179,17 @@ whirlpool_init (void *ctx)
context->bctx.blocksize = BLOCK_SIZE;
context->bctx.bwrite = whirlpool_transform;
+ if ((flags & GCRY_MD_FLAG_BUGEMU1))
+ {
+ memset (&context->bugemu, 0, sizeof context->bugemu);
+ context->use_bugemu = 1;
+ }
+ else
+ context->use_bugemu = 0;
}
+
/*
* Transform block.
*/
@@ -1295,15 +1308,120 @@ whirlpool_transform ( void *c, const unsigned char *data, size_t nblks )
return burn;
}
+
+/* Bug compatibility Whirlpool version. */
+static void
+whirlpool_add_bugemu (whirlpool_context_t *context,
+ const void *buffer_arg, size_t buffer_n)
+{
+ const unsigned char *buffer = buffer_arg;
+ u64 buffer_size;
+ unsigned int carry;
+ unsigned int i;
+
+ buffer_size = buffer_n;
+
+ if (context->bugemu.count == BLOCK_SIZE)
+ {
+ /* Flush the buffer. */
+ whirlpool_transform (context, context->bctx.buf, 1);
+ context->bugemu.count = 0;
+ }
+ if (! buffer)
+ return; /* Nothing to add. */
+
+ if (context->bugemu.count)
+ {
+ while (buffer_n && (context->bugemu.count < BLOCK_SIZE))
+ {
+ context->bctx.buf[context->bugemu.count++] = *buffer++;
+ buffer_n--;
+ }
+ whirlpool_add_bugemu (context, NULL, 0);
+ if (!buffer_n)
+ return; /* Done. This is the bug we emulate. */
+ }
+
+ while (buffer_n >= BLOCK_SIZE)
+ {
+ whirlpool_transform (context, buffer, 1);
+ context->bugemu.count = 0;
+ buffer_n -= BLOCK_SIZE;
+ buffer += BLOCK_SIZE;
+ }
+ while (buffer_n && (context->bugemu.count < BLOCK_SIZE))
+ {
+ context->bctx.buf[context->bugemu.count++] = *buffer++;
+ buffer_n--;
+ }
+
+ /* Update bit counter. */
+ carry = 0;
+ buffer_size <<= 3;
+ for (i = 1; i <= 32; i++)
+ {
+ if (! (buffer_size || carry))
+ break;
+
+ carry += context->bugemu.length[32 - i] + (buffer_size & 0xFF);
+ context->bugemu.length[32 - i] = carry;
+ buffer_size >>= 8;
+ carry >>= 8;
+ }
+ gcry_assert (! (buffer_size || carry));
+}
+
+
+/* Bug compatibility Whirlpool version. */
+static void
+whirlpool_final_bugemu (void *ctx)
+{
+ whirlpool_context_t *context = ctx;
+ unsigned int i;
+
+ /* Flush. */
+ whirlpool_add_bugemu (context, NULL, 0);
+
+ /* Pad. */
+ context->bctx.buf[context->bugemu.count++] = 0x80;
+
+ if (context->bugemu.count > 32)
+ {
+ /* An extra block is necessary. */
+ while (context->bugemu.count < 64)
+ context->bctx.buf[context->bugemu.count++] = 0;
+ whirlpool_add_bugemu (context, NULL, 0);
+ }
+ while (context->bugemu.count < 32)
+ context->bctx.buf[context->bugemu.count++] = 0;
+
+ /* Add length of message. */
+ memcpy (context->bctx.buf + context->bugemu.count,
+ context->bugemu.length, 32);
+ context->bugemu.count += 32;
+ whirlpool_add_bugemu (context, NULL, 0);
+
+ block_to_buffer (context->bctx.buf, context->hash_state, i);
+}
+
+
static void
whirlpool_write (void *ctx, const void *buffer, size_t buffer_n)
{
whirlpool_context_t *context = ctx;
- u64 old_nblocks = context->bctx.nblocks;
- _gcry_md_block_write (context, buffer, buffer_n);
+ if (context->use_bugemu)
+ {
+ whirlpool_add_bugemu (context, buffer, buffer_n);
+ }
+ else
+ {
+ u64 old_nblocks = context->bctx.nblocks;
+
+ _gcry_md_block_write (context, buffer, buffer_n);
- gcry_assert (old_nblocks <= context->bctx.nblocks);
+ gcry_assert (old_nblocks <= context->bctx.nblocks);
+ }
}
static void
@@ -1314,6 +1432,12 @@ whirlpool_final (void *ctx)
u64 t, th, lsb, msb;
unsigned char *length;
+ if (context->use_bugemu)
+ {
+ whirlpool_final_bugemu (ctx);
+ return;
+ }
+
t = context->bctx.nblocks;
/* if (sizeof t == sizeof context->bctx.nblocks) */
th = context->bctx.nblocks_high;
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 7712b80a..4a917901 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -3111,6 +3111,22 @@ The size of the MAC is equal to the message digest of the underlying
hash algorithm. If you want CBC message authentication codes based on
a cipher, see @xref{Working with cipher handles}.
+@item GCRY_MD_FLAG_BUGEMU1
+@cindex bug emulation
+Versions of Libgcrypt before 1.6.0 had a bug in the Whirlpool code
+which led to a wrong result for certain input sizes and write
+patterns. Using this flag emulates that bug. This may for example be
+useful for applications which use Whirlpool as part of their key
+generation. It is strongly suggested to use this flag only if really
+needed and if possible to the data should be re-processed using the
+regular Whirlpool algorithm.
+
+Note that this flag works for the entire hash context. If needed
+arises it may be used to enable bug emulation for other hash
+algorithms. Thus you should not use this flag for a multi-algorithm
+hash context.
+
+
@end table
@c begin table of hash flags
diff --git a/src/cipher-proto.h b/src/cipher-proto.h
index 0955ef59..82677917 100644
--- a/src/cipher-proto.h
+++ b/src/cipher-proto.h
@@ -204,7 +204,7 @@ typedef struct gcry_cipher_spec
*/
/* Type for the md_init function. */
-typedef void (*gcry_md_init_t) (void *c);
+typedef void (*gcry_md_init_t) (void *c, unsigned int flags);
/* Type for the md_write function. */
typedef void (*gcry_md_write_t) (void *c, const void *buf, size_t nbytes);
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index f8318c00..b06f2592 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1151,7 +1151,8 @@ enum gcry_md_algos
enum gcry_md_flags
{
GCRY_MD_FLAG_SECURE = 1, /* Allocate all buffers in "secure" memory. */
- GCRY_MD_FLAG_HMAC = 2 /* Make an HMAC out of this algorithm. */
+ GCRY_MD_FLAG_HMAC = 2, /* Make an HMAC out of this algorithm. */
+ GCRY_MD_FLAG_BUGEMU1 = 0x0100
};
/* (Forward declaration.) */
diff --git a/tests/basic.c b/tests/basic.c
index 697485ed..5fd71313 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -4046,6 +4046,7 @@ check_digests (void)
#endif
{ 0 }
};
+ gcry_error_t err;
int i;
if (verbose)
@@ -4074,6 +4075,58 @@ check_digests (void)
algos[i].expect);
}
+ /* Check the Whirlpool bug emulation. */
+ if (!gcry_md_test_algo (GCRY_MD_WHIRLPOOL) && !in_fips_mode)
+ {
+ static const char expect[] =
+ "\x35\x28\xd6\x4c\x56\x2c\x55\x2e\x3b\x91\x93\x95\x7b\xdd\xcc\x6e"
+ "\x6f\xb7\xbf\x76\x22\x9c\xc6\x23\xda\x3e\x09\x9b\x36\xe8\x6d\x76"
+ "\x2f\x94\x3b\x0c\x63\xa0\xba\xa3\x4d\x66\x71\xe6\x5d\x26\x67\x28"
+ "\x36\x1f\x0e\x1a\x40\xf0\xce\x83\x50\x90\x1f\xfa\x3f\xed\x6f\xfd";
+ gcry_md_hd_t hd;
+ int algo = GCRY_MD_WHIRLPOOL;
+ unsigned char *p;
+ int mdlen;
+
+ err = gcry_md_open (&hd, GCRY_MD_WHIRLPOOL, GCRY_MD_FLAG_BUGEMU1);
+ if (err)
+ {
+ fail ("algo %d, gcry_md_open failed: %s\n", algo, gpg_strerror (err));
+ goto leave;
+ }
+
+ mdlen = gcry_md_get_algo_dlen (algo);
+ if (mdlen < 1 || mdlen > 500)
+ {
+ fail ("algo %d, gcry_md_get_algo_dlen failed: %d\n", algo, mdlen);
+ gcry_md_close (hd);
+ goto leave;
+ }
+
+ /* Hash 62 byes in chunks. */
+ gcry_md_write (hd, "1234567890", 10);
+ gcry_md_write (hd, "1234567890123456789012345678901234567890123456789012",
+ 52);
+
+ p = gcry_md_read (hd, algo);
+
+ if (memcmp (p, expect, mdlen))
+ {
+ printf ("computed: ");
+ for (i = 0; i < mdlen; i++)
+ printf ("%02x ", p[i] & 0xFF);
+ printf ("\nexpected: ");
+ for (i = 0; i < mdlen; i++)
+ printf ("%02x ", expect[i] & 0xFF);
+ printf ("\n");
+
+ fail ("algo %d, digest mismatch\n", algo);
+ }
+
+ gcry_md_close (hd);
+ }
+
+ leave:
if (verbose)
fprintf (stderr, "Completed hash checks.\n");
}