Add basic implementation of GOST R 34.11-94 message digest
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Wed, 18 Sep 2013 12:21:13 +0000 (14:21 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 18 Sep 2013 12:32:20 +0000 (14:32 +0200)
* 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@gmail.com>
Stack burn value in gost3411_init added by wk.

cipher/Makefile.am
cipher/gostr3411-94.c [new file with mode: 0644]
cipher/md.c
configure.ac
doc/gcrypt.texi
src/cipher.h
src/gcrypt.h.in
tests/basic.c

index f3bd71c..f0aaebe 100644 (file)
@@ -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 (file)
index 0000000..dcf5fd2
--- /dev/null
@@ -0,0 +1,273 @@
+/* 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;
+  /* FIXME: Fix this arbitrary value for the stack_burn size.  -wk */
+  hd->bctx.stack_burn = 200;
+  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)
+  };
index 44dc6dc..0d6a7c2 100644 (file)
@@ -57,6 +57,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 },
index a1f7dfa..f700080 100644 (file)
@@ -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=""
 
@@ -1538,6 +1538,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"
index 580ddf8..f40ab66 100644 (file)
@@ -2964,6 +2964,10 @@ cryptographic sense.
 This is the Whirlpool algorithm which yields a message digest of 64
 bytes.
 
+@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
 
index 34fc2e8..74d59ba 100644 (file)
@@ -212,6 +212,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;
index b87dd0e..0bf79fc 100644 (file)
@@ -1090,7 +1090,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.  */
index 527c8ae..07d38fa 100644 (file)
@@ -2816,7 +2816,25 @@ 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" },
-      {        0 },
+#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;