[PATCH 2/3] gost28147: add support for CryptoPro key meshing per RFC 4357

dbaryshkov at gmail.com dbaryshkov at gmail.com
Sun Feb 9 19:29:43 CET 2020


From: Dmitry Baryshkov <dbaryshkov at gmail.com>

* cipher/gost28147.c (gost_do_set_sbox, cryptopro_key_meshing,
  CryptoProMeshingKey, gost_encrypt_block_mesh): New.
  (_gcry_cipher_spec_gost28147_mesh): New cipher with keymeshing,
  (_gcry_cipher_spec_gost28147): Remove OIDs for this cipher should not
  be selected using these OIDs (they are for CFB with keymeshing).

* cipher/cipher.c (cipher_list, cipher_list_algo301): add
  _gcry_cipher_spec_gost28147_mesh.

* src/gcrypt.h.in (GCRY_CIPHER_GOST28147_MESH): New cipher with
  keymeshing.

* doc/gcrypt.texi (GCRY_CIPHER_GOST28147_MESH): Add definition.

* tests/basic.c (check_gost28147_cipher, check_gost28147_cipher_basic):
  Run basic tests on GCRY_CIPHER_GOST28147_MESH.

--

Add actual cipher implementing CryptoPro KeyMeshing. This has been
requested by AltLinux team to properly support CFB-encrypted CMS files.

Signed-off-by: Dmitry Baryshkov <dbaryshkov at gmail.com>
---
 cipher/cipher.c    |  8 +++-
 cipher/gost.h      |  2 +
 cipher/gost28147.c | 92 ++++++++++++++++++++++++++++++++++++++++++++--
 doc/gcrypt.texi    |  7 ++++
 src/cipher.h       |  1 +
 src/gcrypt.h.in    |  3 +-
 tests/basic.c      | 15 ++++++--
 7 files changed, 118 insertions(+), 10 deletions(-)

diff --git a/cipher/cipher.c b/cipher/cipher.c
index 09b8d829932f..edcb421abe88 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -83,6 +83,7 @@ static gcry_cipher_spec_t * const cipher_list[] =
 #endif
 #if USE_GOST28147
      &_gcry_cipher_spec_gost28147,
+     &_gcry_cipher_spec_gost28147_mesh,
 #endif
 #if USE_CHACHA20
      &_gcry_cipher_spec_chacha20,
@@ -193,7 +194,12 @@ static gcry_cipher_spec_t * const cipher_list_algo301[] =
     NULL,
 #endif
 #if USE_CHACHA20
-    &_gcry_cipher_spec_chacha20
+    &_gcry_cipher_spec_chacha20,
+#else
+    NULL,
+#endif
+#if USE_GOST28147
+    &_gcry_cipher_spec_gost28147_mesh,
 #else
     NULL,
 #endif
diff --git a/cipher/gost.h b/cipher/gost.h
index 04c2f85e57d2..53a40505038b 100644
--- a/cipher/gost.h
+++ b/cipher/gost.h
@@ -23,6 +23,8 @@
 typedef struct {
   u32 key[8];
   const u32 *sbox;
+  unsigned int mesh_counter;
+  unsigned int mesh_limit;
 } GOST28147_context;
 
 /* This is a simple interface that will be used by GOST R 34.11-94 */
diff --git a/cipher/gost28147.c b/cipher/gost28147.c
index f30ca16a4d02..00d729020799 100644
--- a/cipher/gost28147.c
+++ b/cipher/gost28147.c
@@ -38,6 +38,13 @@
 #include "gost.h"
 #include "gost-sb.h"
 
+static void
+gost_do_set_sbox (GOST28147_context *ctx, unsigned int index)
+{
+  ctx->sbox = gost_oid_map[index].sbox;
+  ctx->mesh_limit = gost_oid_map[index].keymeshing ? 1024 : 0;
+}
+
 static gcry_err_code_t
 gost_setkey (void *c, const byte *key, unsigned keylen,
              gcry_cipher_hd_t hd)
@@ -51,12 +58,15 @@ gost_setkey (void *c, const byte *key, unsigned keylen,
     return GPG_ERR_INV_KEYLEN;
 
   if (!ctx->sbox)
-    ctx->sbox = sbox_test_3411;
+    gost_do_set_sbox (ctx, 0);
 
   for (i = 0; i < 8; i++)
     {
       ctx->key[i] = buf_get_le32(&key[4*i]);
     }
+
+  ctx->mesh_counter = 0;
+
   return GPG_ERR_NO_ERROR;
 }
 
@@ -178,7 +188,7 @@ gost_set_sbox (GOST28147_context *ctx, const char *oid)
     {
       if (!strcmp(gost_oid_map[i].oid, oid))
         {
-          ctx->sbox = gost_oid_map[i].sbox;
+          gost_do_set_sbox (ctx, i);
           return 0;
         }
     }
@@ -207,8 +217,67 @@ gost_set_extra_info (void *c, int what, const void *buffer, size_t buflen)
   return ec;
 }
 
-static gcry_cipher_oid_spec_t oids_gost28147[] =
+static const byte CryptoProKeyMeshingKey[] = {
+    0x69, 0x00, 0x72, 0x22, 0x64, 0xC9, 0x04, 0x23,
+    0x8D, 0x3A, 0xDB, 0x96, 0x46, 0xE9, 0x2A, 0xC4,
+    0x18, 0xFE, 0xAC, 0x94, 0x00, 0xED, 0x07, 0x12,
+    0xC0, 0x86, 0xDC, 0xC2, 0xEF, 0x4C, 0xA9, 0x2B
+};
+
+/* Implements key meshing algorithm by modifing ctx and returning new IV.
+   Thanks to Dmitry Belyavskiy. */
+static void
+cryptopro_key_meshing (GOST28147_context *ctx)
+{
+    unsigned char newkey[32];
+    unsigned int i;
+
+    /* "Decrypt" the static keymeshing key */
+    for (i = 0; i < 4; i++)
+      {
+	gost_decrypt_block (ctx, newkey + i*8, CryptoProKeyMeshingKey + i*8);
+      }
+
+    /* Set new key */
+    for (i = 0; i < 8; i++)
+      {
+	ctx->key[i] = buf_get_le32(&newkey[4*i]);
+      }
+
+    ctx->mesh_counter = 0;
+}
+
+static unsigned int
+gost_encrypt_block_mesh (void *c, byte *outbuf, const byte *inbuf)
+{
+  GOST28147_context *ctx = c;
+  u32 n1, n2;
+  unsigned int burn;
+
+  n1 = buf_get_le32 (inbuf);
+  n2 = buf_get_le32 (inbuf+4);
+
+  if (ctx->mesh_limit && (ctx->mesh_counter == ctx->mesh_limit))
+    {
+      cryptopro_key_meshing (ctx);
+      /* Yes, encrypt twice: once for KeyMeshing procedure per RFC 4357,
+       * once for block encryption */
+      _gost_encrypt_data(ctx->sbox, ctx->key, &n1, &n2, n1, n2);
+    }
+
+  burn = _gost_encrypt_data(ctx->sbox, ctx->key, &n1, &n2, n1, n2);
+
+  ctx->mesh_counter += 8;
+
+  buf_put_le32 (outbuf+0, n1);
+  buf_put_le32 (outbuf+4, n2);
+
+  return /* burn_stack */ burn + 6*sizeof(void*) /* func call */;
+}
+
+static gcry_cipher_oid_spec_t oids_gost28147_mesh[] =
   {
+    { "1.2.643.2.2.21", GCRY_CIPHER_MODE_CFB },
     /* { "1.2.643.2.2.31.0", GCRY_CIPHER_MODE_CNTGOST }, */
     { "1.2.643.2.2.31.1", GCRY_CIPHER_MODE_CFB },
     { "1.2.643.2.2.31.2", GCRY_CIPHER_MODE_CFB },
@@ -220,10 +289,25 @@ static gcry_cipher_oid_spec_t oids_gost28147[] =
 gcry_cipher_spec_t _gcry_cipher_spec_gost28147 =
   {
     GCRY_CIPHER_GOST28147, {0, 0},
-    "GOST28147", NULL, oids_gost28147, 8, 256,
+    "GOST28147", NULL, NULL, 8, 256,
     sizeof (GOST28147_context),
     gost_setkey,
     gost_encrypt_block,
     gost_decrypt_block,
     NULL, NULL, NULL, gost_set_extra_info,
   };
+
+/* Meshing is used only for CFB, so no need to have separate
+ * gost_decrypt_block_mesh.
+ * Moreover key meshing is specified as encrypting the block (IV). Decrypting
+ * it afterwards would be meaningless. */
+gcry_cipher_spec_t _gcry_cipher_spec_gost28147_mesh =
+  {
+    GCRY_CIPHER_GOST28147_MESH, {0, 0},
+    "GOST28147_MESH", NULL, oids_gost28147_mesh, 8, 256,
+    sizeof (GOST28147_context),
+    gost_setkey,
+    gost_encrypt_block_mesh,
+    gost_decrypt_block,
+    NULL, NULL, NULL, gost_set_extra_info,
+  };
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 091704de16c6..8cd6e48013b3 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -1630,6 +1630,13 @@ This is the Salsa20/12 - reduced round version of Salsa20 stream cipher.
 The GOST 28147-89 cipher, defined in the respective GOST standard.
 Translation of this GOST into English is provided in the RFC-5830.
 
+ at item GCRY_CIPHER_GOST28147_MESH
+ at cindex GOST 28147-89 CryptoPro keymeshing
+The GOST 28147-89 cipher, defined in the respective GOST standard.
+Translation of this GOST into English is provided in the RFC-5830.
+This cipher will use CryptoPro keymeshing as defined in RFC 4357
+if it has to be used for the selected parameter set.
+
 @item GCRY_CIPHER_CHACHA20
 @cindex ChaCha20
 This is the ChaCha20 stream cipher.
diff --git a/src/cipher.h b/src/cipher.h
index 1fe50890f879..20ccb8c51846 100644
--- a/src/cipher.h
+++ b/src/cipher.h
@@ -300,6 +300,7 @@ extern gcry_cipher_spec_t _gcry_cipher_spec_idea;
 extern gcry_cipher_spec_t _gcry_cipher_spec_salsa20;
 extern gcry_cipher_spec_t _gcry_cipher_spec_salsa20r12;
 extern gcry_cipher_spec_t _gcry_cipher_spec_gost28147;
+extern gcry_cipher_spec_t _gcry_cipher_spec_gost28147_mesh;
 extern gcry_cipher_spec_t _gcry_cipher_spec_chacha20;
 
 /* Declarations for the digest specifications.  */
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 46f92a91f6bb..375a40e2acaa 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -945,7 +945,8 @@ enum gcry_cipher_algos
     GCRY_CIPHER_SALSA20     = 313,
     GCRY_CIPHER_SALSA20R12  = 314,
     GCRY_CIPHER_GOST28147   = 315,
-    GCRY_CIPHER_CHACHA20    = 316
+    GCRY_CIPHER_CHACHA20    = 316,
+    GCRY_CIPHER_GOST28147_MESH   = 317 /* GOST 28247 with optional CryptoPro keymeshing */
   };
 
 /* The Rijndael algorithm is basically AES, so provide some macros. */
diff --git a/tests/basic.c b/tests/basic.c
index 4e3589eea2f4..6dc2fe40a6a4 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -7126,7 +7126,7 @@ check_xts_cipher (void)
 
 
 static void
-check_gost28147_cipher (void)
+check_gost28147_cipher_basic (enum gcry_cipher_algos algo)
 {
 #if USE_GOST28147
   static const struct {
@@ -7203,7 +7203,7 @@ check_gost28147_cipher (void)
 
   if (verbose)
     fprintf (stderr, "  Starting GOST28147 cipher checks.\n");
-  keylen = gcry_cipher_get_algo_keylen(GCRY_CIPHER_GOST28147);
+  keylen = gcry_cipher_get_algo_keylen(algo);
   if (!keylen)
     {
       fail ("gost28147, gcry_cipher_get_algo_keylen failed\n");
@@ -7212,10 +7212,10 @@ check_gost28147_cipher (void)
 
   for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
     {
-      err = gcry_cipher_open (&hde, GCRY_CIPHER_GOST28147,
+      err = gcry_cipher_open (&hde, algo,
                               GCRY_CIPHER_MODE_ECB, 0);
       if (!err)
-        err = gcry_cipher_open (&hdd, GCRY_CIPHER_GOST28147,
+        err = gcry_cipher_open (&hdd, algo,
                                 GCRY_CIPHER_MODE_ECB, 0);
       if (err)
         {
@@ -7292,6 +7292,12 @@ check_gost28147_cipher (void)
 #endif
 }
 
+static void
+check_gost28147_cipher (void)
+{
+	check_gost28147_cipher_basic (GCRY_CIPHER_GOST28147);
+	check_gost28147_cipher_basic (GCRY_CIPHER_GOST28147_MESH);
+}
 
 static void
 check_stream_cipher (void)
@@ -9281,6 +9287,7 @@ check_ciphers (void)
 #endif
 #if USE_GOST28147
     GCRY_CIPHER_GOST28147,
+    GCRY_CIPHER_GOST28147_MESH,
 #endif
     0
   };
-- 
2.24.1




More information about the Gcrypt-devel mailing list