[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