[PATCH 2/2] gost28147: implement special MAC mode called imitovstavka (IMIT)

dbaryshkov at gmail.com dbaryshkov at gmail.com
Sat Mar 21 20:33:51 CET 2020


From: Dmitry Baryshkov <dbaryshkov at gmail.com>

 * src/gcrypt.h.in (GCRY_MAC_GOST28147_IMIT): New.
 * cipher/gost28147.c (gost_imit_open, gost_imit_close,
gost_imit_setkey, gost_imit_setiv, gost_imit_reset, _gost_imit_block,
gost_imit_block, gost_imit_write, gost_imit_finish, gost_imit_read,
gost_imit_verify, gost_imit_get_maclen, gost_imit_get_keylen,
gost_imit_set_extra_info): New functions implementing GOST 28147-89 MAC
(imitovstavka, IMIT) mode.
 * cipher/gost28147.c (gost_imit_ops,
_gcry_mac_type_spec_gost28147_imit): declare GOST 28147-89 IMIT handler.
 * cipher/mac-internal.h (gcry_mac_handle): add fields to support GOST
28147-89 IMIT mode.
 * cipher/mac.c (mac_list): add _gcry_mac_type_spec_gost28147_imit,
   (spec_from_algo): handle GCRY_MAC_GOST28147_IMIT.
 * tests/basic.c (check_mac): add GOST28147-89 IMIT test vector.

--
GOST 28147-89 (see RFC 5830 Section 8) defines MAC construction using
the same base transformation. Implement support for it: required to read
some CMS files produced using GOST algorithms, see RFC 4490.

Signed-off-by: Dmitry Baryshkov <dbaryshkov at gmail.com>
---
 cipher/gost28147.c    | 242 +++++++++++++++++++++++++++++++++++++++++-
 cipher/mac-internal.h |  11 ++
 cipher/mac.c          |   5 +
 src/gcrypt.h.in       |   1 +
 tests/basic.c         |   7 ++
 5 files changed, 264 insertions(+), 2 deletions(-)

diff --git a/cipher/gost28147.c b/cipher/gost28147.c
index 00d729020799..24385915031c 100644
--- a/cipher/gost28147.c
+++ b/cipher/gost28147.c
@@ -23,16 +23,17 @@
  * - OFB-like mode with additional transformation on keystream
  *   RFC 5830 names this 'counter encryption' mode
  *   Original GOST text uses the term 'gammirovanie'
- * - MAC mode
+ * - MAC mode ('imitovstavka')
  *
  * This implementation handles ECB and CFB modes via usual libgcrypt handling.
- * OFB-like and MAC modes are unsupported.
+ * OFB-like modes are unsupported.
  */
 
 #include <config.h>
 #include "types.h"
 #include "g10lib.h"
 #include "cipher.h"
+#include "mac-internal.h"
 #include "bufhelp.h"
 
 #include "gost.h"
@@ -311,3 +312,240 @@ gcry_cipher_spec_t _gcry_cipher_spec_gost28147_mesh =
     gost_decrypt_block,
     NULL, NULL, NULL, gost_set_extra_info,
   };
+
+static gcry_err_code_t
+gost_imit_open (gcry_mac_hd_t h)
+{
+  memset(&h->u.imit, 0, sizeof(h->u.imit));
+  return 0;
+}
+
+static void
+gost_imit_close (gcry_mac_hd_t h)
+{
+  (void) h;
+}
+
+static gcry_err_code_t
+gost_imit_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
+{
+  int i;
+
+  if (keylen != 256 / 8)
+    return GPG_ERR_INV_KEYLEN;
+
+  if (!h->u.imit.ctx.sbox)
+    h->u.imit.ctx.sbox = sbox_CryptoPro_A;
+
+  for (i = 0; i < 8; i++)
+    {
+      h->u.imit.ctx.key[i] = buf_get_le32(&key[4*i]);
+    }
+
+  return 0;
+}
+
+static gcry_err_code_t
+gost_imit_setiv (gcry_mac_hd_t h,
+		 const unsigned char *iv,
+		 size_t ivlen)
+{
+  if (ivlen != 8)
+    return GPG_ERR_INV_LENGTH;
+
+  h->u.imit.n1 = buf_get_le32 (iv + 0);
+  h->u.imit.n2 = buf_get_le32 (iv + 4);
+
+  return 0;
+}
+
+static gcry_err_code_t
+gost_imit_reset (gcry_mac_hd_t h)
+{
+  h->u.imit.n1 = h->u.imit.n2 = 0;
+  h->u.imit.unused = 0;
+  return 0;
+}
+
+static unsigned int
+_gost_imit_block (const u32 *sbox, const u32 *key, u32 *o1, u32 *o2, u32 n1, u32 n2)
+{
+  n1 ^= *o1;
+  n2 ^= *o2;
+
+  n2 ^= gost_val (key[0], n1, sbox); n1 ^= gost_val (key[1], n2, sbox);
+  n2 ^= gost_val (key[2], n1, sbox); n1 ^= gost_val (key[3], n2, sbox);
+  n2 ^= gost_val (key[4], n1, sbox); n1 ^= gost_val (key[5], n2, sbox);
+  n2 ^= gost_val (key[6], n1, sbox); n1 ^= gost_val (key[7], n2, sbox);
+
+  n2 ^= gost_val (key[0], n1, sbox); n1 ^= gost_val (key[1], n2, sbox);
+  n2 ^= gost_val (key[2], n1, sbox); n1 ^= gost_val (key[3], n2, sbox);
+  n2 ^= gost_val (key[4], n1, sbox); n1 ^= gost_val (key[5], n2, sbox);
+  n2 ^= gost_val (key[6], n1, sbox); n1 ^= gost_val (key[7], n2, sbox);
+
+  *o1 = n1;
+  *o2 = n2;
+
+  return /* burn_stack */ 4*sizeof(void*) /* func call */ +
+                          3*sizeof(void*) /* stack */ +
+                          4*sizeof(void*) /* gost_val call */;
+}
+
+static inline unsigned int
+gost_imit_block (GOST28147_context *ctx, u32 *n1, u32 *n2, const unsigned char *buf)
+{
+  if (ctx->mesh_limit && (ctx->mesh_counter == ctx->mesh_limit))
+    cryptopro_key_meshing (ctx);
+
+  return _gost_imit_block (ctx->sbox, ctx->key,
+			   n1, n2,
+			   buf_get_le32 (buf+0),
+			   buf_get_le32 (buf+4));
+}
+
+static gcry_err_code_t
+gost_imit_write (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
+{
+  const int blocksize = 8;
+  unsigned int burn = 0;
+  if (!buflen || !buf)
+    return GPG_ERR_NO_ERROR;
+
+  if (h->u.imit.unused)
+    {
+      for (; buflen && h->u.imit.unused < blocksize; buflen --)
+        h->u.imit.lastiv[h->u.imit.unused++] = *buf++;
+
+      if (h->u.imit.unused < blocksize)
+        return GPG_ERR_NO_ERROR;
+
+      h->u.imit.count ++;
+      burn = gost_imit_block (&h->u.imit.ctx,
+			      &h->u.imit.n1, &h->u.imit.n2,
+			      h->u.imit.lastiv);
+
+      h->u.imit.unused = 0;
+    }
+
+  while (buflen >= blocksize)
+    {
+      h->u.imit.count ++;
+      burn = gost_imit_block (&h->u.imit.ctx,
+			      &h->u.imit.n1, &h->u.imit.n2,
+			      buf);
+      buf += blocksize;
+      buflen -= blocksize;
+    }
+
+  for (; buflen; buflen--)
+    h->u.imit.lastiv[h->u.imit.unused++] = *buf++;
+
+  _gcry_burn_stack (burn);
+
+  return GPG_ERR_NO_ERROR;
+}
+
+static void
+gost_imit_finish (gcry_mac_hd_t h)
+{
+  static const unsigned char zero[8] = {0};
+
+  /* Fill till full block */
+  if (h->u.imit.unused)
+    gost_imit_write(h, zero, 8 - h->u.imit.unused);
+
+  if (h->u.imit.count == 1)
+    gost_imit_write(h, zero, 8);
+}
+
+static gcry_err_code_t
+gost_imit_read (gcry_mac_hd_t h, unsigned char *outbuf, size_t * outlen)
+{
+  unsigned int dlen = 8;
+  unsigned char digest[8];
+
+  gost_imit_finish (h);
+
+  buf_put_le32 (digest+0, h->u.imit.n1);
+  buf_put_le32 (digest+4, h->u.imit.n2);
+
+  if (*outlen <= dlen)
+    buf_cpy (outbuf, digest, *outlen);
+  else
+    {
+      buf_cpy (outbuf, digest, dlen);
+      *outlen = dlen;
+    }
+  return 0;
+}
+
+static gcry_err_code_t
+gost_imit_verify (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
+{
+  unsigned char tbuf[8];
+
+  gost_imit_finish (h);
+
+  buf_put_le32 (tbuf+0, h->u.imit.n1);
+  buf_put_le32 (tbuf+4, h->u.imit.n2);
+
+  return buf_eq_const(tbuf, buf, buflen) ?
+             GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
+}
+
+static unsigned int
+gost_imit_get_maclen (int algo)
+{
+  (void) algo;
+  return 4; /* or 8 */
+}
+
+
+static unsigned int
+gost_imit_get_keylen (int algo)
+{
+  (void) algo;
+  return 256 / 8;
+}
+
+static gpg_err_code_t
+gost_imit_set_extra_info (gcry_mac_hd_t hd, int what, const void *buffer, size_t buflen)
+{
+  gpg_err_code_t ec = 0;
+
+  (void)buffer;
+  (void)buflen;
+
+  switch (what)
+    {
+    case GCRYCTL_SET_SBOX:
+      ec = gost_set_sbox (&hd->u.imit.ctx, buffer);
+      break;
+
+    default:
+      ec = GPG_ERR_INV_OP;
+      break;
+    }
+  return ec;
+}
+
+
+static gcry_mac_spec_ops_t gost_imit_ops = {
+  gost_imit_open,
+  gost_imit_close,
+  gost_imit_setkey,
+  gost_imit_setiv,
+  gost_imit_reset,
+  gost_imit_write,
+  gost_imit_read,
+  gost_imit_verify,
+  gost_imit_get_maclen,
+  gost_imit_get_keylen,
+  gost_imit_set_extra_info,
+};
+
+gcry_mac_spec_t _gcry_mac_type_spec_gost28147_imit =
+  {
+    GCRY_MAC_GOST28147_IMIT, {0, 0}, "GOST28147_IMIT",
+    &gost_imit_ops
+  };
diff --git a/cipher/mac-internal.h b/cipher/mac-internal.h
index 15b4dfc2c5de..646ec1cfa220 100644
--- a/cipher/mac-internal.h
+++ b/cipher/mac-internal.h
@@ -95,6 +95,7 @@ typedef struct gcry_mac_spec
   const gcry_mac_spec_ops_t *ops;
 } gcry_mac_spec_t;
 
+#include "gost.h"
 
 /* The handle structure.  */
 struct gcry_mac_handle
@@ -120,6 +121,13 @@ struct gcry_mac_handle
     struct {
       struct poly1305mac_context_s *ctx;
     } poly1305mac;
+    struct {
+      GOST28147_context ctx;
+      u32 n1, n2;
+      unsigned int unused;
+      unsigned int count;
+      unsigned char lastiv[8]; /* IMIT blocksize */
+    } imit;
   } u;
 };
 
@@ -219,6 +227,9 @@ extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_idea;
 #if USE_GOST28147
 extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_gost28147;
 #endif
+#if USE_GOST28147
+extern gcry_mac_spec_t _gcry_mac_type_spec_gost28147_imit;
+#endif
 
 /*
  * The GMAC algorithm specifications (mac-gmac.c).
diff --git a/cipher/mac.c b/cipher/mac.c
index 1bc1aa985c93..0abc0d33bfbc 100644
--- a/cipher/mac.c
+++ b/cipher/mac.c
@@ -127,6 +127,7 @@ static gcry_mac_spec_t * const mac_list[] = {
 #endif
 #if USE_GOST28147
   &_gcry_mac_type_spec_cmac_gost28147,
+  &_gcry_mac_type_spec_gost28147_imit,
 #endif
   &_gcry_mac_type_spec_poly1305mac,
   NULL,
@@ -403,6 +404,10 @@ spec_from_algo (int algo)
     spec = mac_list_algo401[algo - 401];
   else if (algo >= 501 && algo < 501 + DIM(mac_list_algo501))
     spec = mac_list_algo501[algo - 501];
+#ifdef USE_GOST28147
+  else if (algo == GCRY_MAC_GOST28147_IMIT)
+    spec = &_gcry_mac_type_spec_gost28147_imit;
+#endif
 
   if (spec)
     gcry_assert (spec->algo == algo);
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 375a40e2acaa..bc96ba40aeea 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1440,6 +1440,7 @@ typedef struct gcry_mac_handle *gcry_mac_hd_t;
 enum gcry_mac_algos
   {
     GCRY_MAC_NONE               = 0,
+    GCRY_MAC_GOST28147_IMIT     = 1,
 
     GCRY_MAC_HMAC_SHA256        = 101,
     GCRY_MAC_HMAC_SHA224        = 102,
diff --git a/tests/basic.c b/tests/basic.c
index 3a7d667602df..2dee1beef043 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -12862,6 +12862,13 @@ check_mac (void)
         "\x9d\xeb\xb0\xcd\x24\x90\xd3\x9b\x47\x78\x37\x0a\x81\xf2\x83\x2a",
         "\x61\xee\x09\x21\x8d\x29\xb0\xaa\xed\x7e\x15\x4a\x2c\x55\x09\xcc",
         0, 32 },
+      { GCRY_MAC_GOST28147_IMIT,
+        "\xb5\xa1\xf0\xe3\xce\x2f\x02\x1d\x67\x61\x94\x34\x5c\x41\xe3\x6e",
+	"\x9d\x05\xb7\x9e\x90\xca\xd0\x0a\x2c\xda\xd2\x2e\xf4\xe8\x6f\x5c"
+	"\xf5\xdc\x37\x68\x19\x85\xb3\xbf\xaa\x18\xc1\xc3\x05\x0a\x91\xa2",
+        "\xf8\x1f\x08\xa3",
+        NULL,
+        16, 32 },
       { 0 },
     };
   int i;
-- 
2.25.1




More information about the Gcrypt-devel mailing list