[PATCH 3/5] Add basic implementation of GOST R 34.11-94 message digest
Dmitry Eremin-Solenikov
dbaryshkov at gmail.com
Mon Sep 2 11:28:50 CEST 2013
* src/gcrypt.h.in (GCRY_MD_GOSTR3411_94): New.
* cipher/gostr3411-94.c: New.
* configure.ac (available_digests): Add gostr3411-94.
* src/cipher.h: Add gostr3411-94 definitions.
* cipher/md.c: Register GOST R 34.11-94.
* tests/basic.c (check_digests): Add 4 tests for GOST R 34.11-94
hash algo. Two are defined in the standard itself, two other are
more or less common tests - an empty string an exclamation mark.
* doc/gcrypt.texi: Add an entry describing GOST R 34.11-94 to the MD
algorithms table.
--
Add simple implementation of GOST R 34.11-94 hash function. Currently
there is no way to specify hash parameters (it always uses GOST R 34.11-94
test parameters).
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov at gmail.com>
---
cipher/Makefile.am | 1 +
cipher/gostr3411-94.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++
cipher/md.c | 4 +
configure.ac | 12 ++-
doc/gcrypt.texi | 4 +
src/cipher.h | 1 +
src/gcrypt.h.in | 3 +-
tests/basic.c | 18 ++++
8 files changed, 312 insertions(+), 2 deletions(-)
create mode 100644 cipher/gostr3411-94.c
diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index f3bd71c..f0aaebe 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -63,6 +63,7 @@ elgamal.c \
ecc.c ecc-curves.c ecc-misc.c ecc-common.h \
idea.c \
gost28147.c gost.h \
+gostr3411-94.c \
md4.c \
md5.c \
rijndael.c rijndael-tables.h rijndael-amd64.S rijndael-armv6.S \
diff --git a/cipher/gostr3411-94.c b/cipher/gostr3411-94.c
new file mode 100644
index 0000000..fa4bfb2
--- /dev/null
+++ b/cipher/gostr3411-94.c
@@ -0,0 +1,271 @@
+/* gostr3411-94.c - GOST R 34.11-94 hash function
+ * Copyright (C) 2012 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "g10lib.h"
+#include "bithelp.h"
+#include "cipher.h"
+#include "hash-common.h"
+
+#include "gost.h"
+
+typedef struct {
+ gcry_md_block_ctx_t bctx;
+ GOST28147_context hd;
+ byte h[32];
+ byte sigma[32];
+ u32 len;
+} GOSTR3411_CONTEXT;
+
+static void
+transform (void *c, const unsigned char *data);
+
+static void
+gost3411_init (void *context)
+{
+ GOSTR3411_CONTEXT *hd = context;
+
+ memset (&hd->hd, 0, sizeof(hd->hd));
+ memset (hd->h, 0, 32);
+ memset (hd->sigma, 0, 32);
+
+ hd->bctx.nblocks = 0;
+ hd->bctx.count = 0;
+ hd->bctx.blocksize = 32;
+ hd->bctx.bwrite = transform;
+}
+
+static void
+do_p (unsigned char *p, unsigned char *u, unsigned char *v)
+{
+ int i, k;
+ for (k = 0; k < 8; k++)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ p[i + 4 * k] = u[8 * i + k] ^ v[8 * i + k];
+ }
+ }
+}
+
+static void
+do_a (unsigned char *u)
+{
+ unsigned char temp[8];
+ int i;
+ memcpy (temp, u, 8);
+ memmove (u, u+8, 24);
+ for (i = 0; i < 8; i++)
+ {
+ u[24 + i] = u[i] ^ temp[i];
+ }
+}
+/* apply do_a twice: 1 2 3 4 -> 3 4 1^2 2^3 */
+static void
+do_a2 (unsigned char *u)
+{
+ unsigned char temp[16];
+ int i;
+ memcpy (temp, u, 16);
+ memcpy (u, u + 16, 16);
+ for (i = 0; i < 8; i++)
+ {
+ u[16 + i] = temp[i] ^ temp[8 + i];
+ u[24 + i] = u[i] ^ temp[8 + i];
+ }
+}
+
+static void
+do_apply_c2 (unsigned char *u)
+{
+ u[ 1] ^= 0xff;
+ u[ 3] ^= 0xff;
+ u[ 5] ^= 0xff;
+ u[ 7] ^= 0xff;
+
+ u[ 8] ^= 0xff;
+ u[10] ^= 0xff;
+ u[12] ^= 0xff;
+ u[14] ^= 0xff;
+
+ u[17] ^= 0xff;
+ u[18] ^= 0xff;
+ u[20] ^= 0xff;
+ u[23] ^= 0xff;
+
+ u[24] ^= 0xff;
+ u[28] ^= 0xff;
+ u[29] ^= 0xff;
+ u[31] ^= 0xff;
+}
+
+#define do_phi_step(e, i) \
+ e[(0 + 2*i) % 32] ^= e[(2 + 2*i) % 32] ^ e[(4 + 2*i) % 32] ^ e[(6 + 2*i) % 32] ^ e[(24 + 2*i) % 32] ^ e[(30 + 2*i) % 32]; \
+ e[(1 + 2*i) % 32] ^= e[(3 + 2*i) % 32] ^ e[(5 + 2*i) % 32] ^ e[(7 + 2*i) % 32] ^ e[(25 + 2*i) % 32] ^ e[(31 + 2*i) % 32];
+
+static void
+do_phi_submix (unsigned char *e, unsigned char *x, int round)
+{
+ int i;
+ round *= 2;
+ for (i = 0; i < 32; i++)
+ {
+ e[(i + round) % 32] ^= x[i];
+ }
+}
+
+static void
+do_add (unsigned char *s, unsigned char *a)
+{
+ unsigned temp = 0;
+ int i;
+
+ for (i = 0; i < 32; i++)
+ {
+ temp = s[i] + a[i] + (temp >> 8);
+ s[i] = temp & 0xff;
+ }
+}
+
+static void
+do_hash_step (GOST28147_context *hd, unsigned char *h, unsigned char *m)
+{
+ unsigned char u[32], v[32], s[32];
+ unsigned char k[32];
+ int i;
+
+ memcpy (u, h, 32);
+ memcpy (v, m, 32);
+
+ for (i = 0; i < 4; i++) {
+ do_p (k, u, v);
+
+ _gcry_gost_enc_one (hd, k, s + i*8, h + i*8);
+
+ do_a (u);
+ if (i == 1)
+ do_apply_c2 (u);
+ do_a2 (v);
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ do_phi_step (s, 0);
+ do_phi_step (s, 1);
+ do_phi_step (s, 2);
+ do_phi_step (s, 3);
+ do_phi_step (s, 4);
+ do_phi_step (s, 5);
+ do_phi_step (s, 6);
+ do_phi_step (s, 7);
+ do_phi_step (s, 8);
+ do_phi_step (s, 9);
+ /* That is in total 12 + 1 + 61 = 74 = 16 * 4 + 10 rounds */
+ if (i == 4)
+ break;
+ do_phi_step (s, 10);
+ do_phi_step (s, 11);
+ if (i == 0)
+ do_phi_submix(s, m, 12);
+ do_phi_step (s, 12);
+ if (i == 0)
+ do_phi_submix(s, h, 13);
+ do_phi_step (s, 13);
+ do_phi_step (s, 14);
+ do_phi_step (s, 15);
+ }
+
+ memcpy (h, s+20, 12);
+ memcpy (h+12, s, 20);
+}
+
+
+static void
+transform (void *ctx, const unsigned char *data)
+{
+ GOSTR3411_CONTEXT *hd = ctx;
+ byte m[32];
+
+ memcpy (m, data, 32);
+ do_hash_step (&hd->hd, hd->h, m);
+ do_add (hd->sigma, m);
+}
+
+/*
+ The routine finally terminates the computation and returns the
+ digest. The handle is prepared for a new cycle, but adding bytes
+ to the handle will the destroy the returned buffer. Returns: 32
+ bytes with the message the digest. */
+static void
+gost3411_final (void *context)
+{
+ GOSTR3411_CONTEXT *hd = context;
+ size_t padlen = 0;
+ byte l[32];
+ int i;
+ u32 nblocks;
+
+ if (hd->bctx.count > 0)
+ {
+ padlen = 32 - hd->bctx.count;
+ memset (hd->bctx.buf + hd->bctx.count, 0, padlen);
+ hd->bctx.count += padlen;
+ _gcry_md_block_write (hd, NULL, 0); /* flush */;
+ }
+
+ if (hd->bctx.count != 0)
+ return; /* Something went wrong */
+
+ memset (l, 0, 32);
+
+ nblocks = hd->bctx.nblocks;
+ if (padlen)
+ {
+ nblocks --;
+ l[0] = 256 - padlen * 8;
+ }
+
+ for (i = 1; i < 32 && nblocks != 0; i++)
+ {
+ l[i] = nblocks % 256;
+ nblocks /= 256;
+ }
+
+ do_hash_step (&hd->hd, hd->h, l);
+ do_hash_step (&hd->hd, hd->h, hd->sigma);
+}
+
+static byte *
+gost3411_read (void *context)
+{
+ GOSTR3411_CONTEXT *hd = context;
+
+ return hd->h;
+}
+gcry_md_spec_t _gcry_digest_spec_gost3411_94 =
+ {
+ "GOST_R_34.11-94", NULL, 0, NULL, 32,
+ gost3411_init, _gcry_md_block_write, gost3411_final, gost3411_read,
+ sizeof (GOSTR3411_CONTEXT)
+ };
diff --git a/cipher/md.c b/cipher/md.c
index 46567a1..348fa7a 100644
--- a/cipher/md.c
+++ b/cipher/md.c
@@ -56,6 +56,10 @@ static struct digest_table_entry
{ &_gcry_digest_spec_crc24_rfc2440,
&dummy_extra_spec, GCRY_MD_CRC24_RFC2440, 1 },
#endif
+#ifdef USE_GOST_R_3411_94
+ { &_gcry_digest_spec_gost3411_94,
+ &dummy_extra_spec, GCRY_MD_GOSTR3411_94 },
+#endif
#if USE_MD4
{ &_gcry_digest_spec_md4,
&dummy_extra_spec, GCRY_MD_MD4 },
diff --git a/configure.ac b/configure.ac
index 21ca88d..0abfa79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -192,7 +192,7 @@ available_pubkey_ciphers="dsa elgamal rsa ecc"
enabled_pubkey_ciphers=""
# Definitions for message digests.
-available_digests="crc md4 md5 rmd160 sha1 sha256"
+available_digests="crc gostr3411-94 md4 md5 rmd160 sha1 sha256"
available_digests_64="sha512 tiger whirlpool"
enabled_digests=""
@@ -1510,6 +1510,16 @@ if test "$found" = "1" ; then
AC_DEFINE(USE_CRC, 1, [Defined if this module should be included])
fi
+LIST_MEMBER(gostr3411-94, $enabled_digests)
+if test "$found" = "1" ; then
+ # GOST R 34.11-94 internally uses GOST 28147-89
+ LIST_MEMBER(gost28147, $enabled_ciphers)
+ if test "$found" = "1" ; then
+ GCRYPT_DIGESTS="$GCRYPT_DIGESTS gostr3411-94.lo"
+ AC_DEFINE(USE_GOST_R_3411_94, 1, [Defined if this module should be included])
+ fi
+fi
+
LIST_MEMBER(md4, $enabled_digests)
if test "$found" = "1" ; then
GCRYPT_DIGESTS="$GCRYPT_DIGESTS md4.lo"
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index d5b5955..3d16713 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -2942,6 +2942,10 @@ cryptographic sense.
This is the Whirlpool algorithm which yields a message digest of 64
bytes.
+ at item GCRY_MD_GOSTR3411_94
+This is the hash algorithm described in GOST R 34.11-94. It yields
+message digest of 32 bytes.
+
@end table
@c end table of hash algorithms
diff --git a/src/cipher.h b/src/cipher.h
index 9e725bc..1787a5e 100644
--- a/src/cipher.h
+++ b/src/cipher.h
@@ -208,6 +208,7 @@ extern cipher_extra_spec_t _gcry_cipher_extraspec_salsa20;
extern gcry_md_spec_t _gcry_digest_spec_crc32;
extern gcry_md_spec_t _gcry_digest_spec_crc32_rfc1510;
extern gcry_md_spec_t _gcry_digest_spec_crc24_rfc2440;
+extern gcry_md_spec_t _gcry_digest_spec_gost3411_94;
extern gcry_md_spec_t _gcry_digest_spec_md4;
extern gcry_md_spec_t _gcry_digest_spec_md5;
extern gcry_md_spec_t _gcry_digest_spec_rmd160;
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 8be0bf0..88286c6 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1065,7 +1065,8 @@ enum gcry_md_algos
GCRY_MD_CRC24_RFC2440 = 304,
GCRY_MD_WHIRLPOOL = 305,
GCRY_MD_TIGER1 = 306, /* TIGER fixed. */
- GCRY_MD_TIGER2 = 307 /* TIGER2 variant. */
+ GCRY_MD_TIGER2 = 307, /* TIGER2 variant. */
+ GCRY_MD_GOSTR3411_94 = 308, /* GOST R 34.11-94. */
};
/* Flags used with the open function. */
diff --git a/tests/basic.c b/tests/basic.c
index 9c210df..fc2e494 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -2532,6 +2532,24 @@ check_digests (void)
"\x29\x05\x7F\xD8\x6B\x20\xBF\xD6\x2D\xEC\xA0\xF1\xCC\xEA\x4A\xF5"
"\x1F\xC1\x54\x90\xED\xDC\x47\xAF\x32\xBB\x2B\x66\xC3\x4F\xF9\xAD"
"\x8C\x60\x08\xAD\x67\x7F\x77\x12\x69\x53\xB2\x26\xE4\xED\x8B\x01" },
+#ifdef USE_GOST_R_3411_94
+ { GCRY_MD_GOSTR3411_94,
+ "This is message, length=32 bytes",
+ "\xB1\xC4\x66\xD3\x75\x19\xB8\x2E\x83\x19\x81\x9F\xF3\x25\x95\xE0"
+ "\x47\xA2\x8C\xB6\xF8\x3E\xFF\x1C\x69\x16\xA8\x15\xA6\x37\xFF\xFA" },
+ { GCRY_MD_GOSTR3411_94,
+ "Suppose the original message has length = 50 bytes",
+ "\x47\x1A\xBA\x57\xA6\x0A\x77\x0D\x3A\x76\x13\x06\x35\xC1\xFB\xEA"
+ "\x4E\xF1\x4D\xE5\x1F\x78\xB4\xAE\x57\xDD\x89\x3B\x62\xF5\x52\x08" },
+ { GCRY_MD_GOSTR3411_94,
+ "",
+ "\xCE\x85\xB9\x9C\xC4\x67\x52\xFF\xFE\xE3\x5C\xAB\x9A\x7B\x02\x78"
+ "\xAB\xB4\xC2\xD2\x05\x5C\xFF\x68\x5A\xF4\x91\x2C\x49\x49\x0F\x8D" },
+ { GCRY_MD_GOSTR3411_94,
+ "!",
+ "\x5C\x00\xCC\xC2\x73\x4C\xDD\x33\x32\xD3\xD4\x74\x95\x76\xE3\xC1"
+ "\xA7\xDB\xAF\x0E\x7E\xA7\x4E\x9F\xA6\x02\x41\x3C\x90\xA1\x29\xFA" },
+#endif
{ 0 },
};
int i;
--
1.8.4.rc3
More information about the Gcrypt-devel
mailing list