[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