[PATCH] keccak: add md_read support for SHAKE algorithms

Jussi Kivilinna jussi.kivilinna at iki.fi
Sun Jun 25 20:46:23 CEST 2023


* cipher/hash-common.c (_gcry_hash_selftest_check_one): Adjust
for SHAKE algorithms now returning non-zero for digest length.
* cipher/keccak.c (KECCAK_CONTEXT_S): Add 'shake_in_extract_mode'
and 'shake_in_read_mode' flags.
(keccak_init): Initialize new context fields; set output length
for SHAKE algorithms.
(keccak_extract): Rename to ...
(do_keccak_extract): this and add return value.
(keccak_extract): New with 'shake_in_???_mode' checks & setup.
(keccak_shake_read): New.
(_gcry_sha3_hash_buffers): Adjust for 'spec->mdlen' not being
zero for SHAKE algorithms, instead check 'suffix' for type.
(_gcry_digest_spec_shake128): Set mdlen to 32 bytes; Set read
function.
(_gcry_digest_spec_shake256): Set mdlen to 64 bytes; Set read
function.
* cipher/md.c (md_extract): Pass return value from algo extract
function.
(_gcry_md_hash_buffers_extract): Adjust for 'spec->mdlen' not
being zero for SHAKE algorithms.
* src/cipher-proto.h (gcry_md_extract_t): Change return type
from 'void' to 'gpg_err_code_t'.
* tests/basic.c (check_one_md, check_one_md_multi): Adjust
for 'gcry_md_get_algo_dlen()' not being zero for SHAKE
algorithms.
(check_digests): Add md_read interface test-vectors for SHAKE128
and SHAKE256.
--

GnuPG-bug-id: 6539
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 cipher/hash-common.c | 10 +++---
 cipher/keccak.c      | 76 +++++++++++++++++++++++++++++++++++++-------
 cipher/md.c          | 18 ++++++-----
 src/cipher-proto.h   |  3 +-
 tests/basic.c        | 44 +++++++++++++++----------
 5 files changed, 108 insertions(+), 43 deletions(-)

diff --git a/cipher/hash-common.c b/cipher/hash-common.c
index ed2d7cac..deff40d7 100644
--- a/cipher/hash-common.c
+++ b/cipher/hash-common.c
@@ -51,12 +51,10 @@ _gcry_hash_selftest_check_one (int algo,
   gcry_md_hd_t hd;
   unsigned char *digest;
   char aaa[1000];
-  int xof = 0;
+  int expect_xof = 0;
 
-  if (_gcry_md_get_algo_dlen (algo) == 0)
-    xof = 1;
-  else if (_gcry_md_get_algo_dlen (algo) != expectlen)
-    return "digest size does not match expected size";
+  if (_gcry_md_get_algo_dlen (algo) != expectlen)
+    expect_xof = 1;
 
   err = _gcry_md_open (&hd, algo, 0);
   if (err)
@@ -85,7 +83,7 @@ _gcry_hash_selftest_check_one (int algo,
 
   if (!result)
     {
-      if (!xof)
+      if (!expect_xof)
 	{
 	  digest = _gcry_md_read (hd, algo);
 
diff --git a/cipher/keccak.c b/cipher/keccak.c
index 22c40302..9883af4d 100644
--- a/cipher/keccak.c
+++ b/cipher/keccak.c
@@ -143,7 +143,9 @@ typedef struct KECCAK_CONTEXT_S
   unsigned int outlen;
   unsigned int blocksize;
   unsigned int count;
-  unsigned int suffix;
+  unsigned int suffix:8;
+  unsigned int shake_in_extract_mode:1;
+  unsigned int shake_in_read_mode:1;
   const keccak_ops_t *ops;
 #ifdef USE_S390X_CRYPTO
   unsigned int kimd_func;
@@ -966,6 +968,8 @@ keccak_init (int algo, void *context, unsigned int flags)
   memset (hd, 0, sizeof *hd);
 
   ctx->count = 0;
+  ctx->shake_in_extract_mode = 0;
+  ctx->shake_in_read_mode = 0;
 
   /* Select generic implementation. */
 #ifdef USE_64BIT
@@ -1024,12 +1028,12 @@ keccak_init (int algo, void *context, unsigned int flags)
     case GCRY_MD_SHAKE128:
       ctx->suffix = SHAKE_DELIMITED_SUFFIX;
       ctx->blocksize = 1344 / 8;
-      ctx->outlen = 0;
+      ctx->outlen = 256 / 8;
       break;
     case GCRY_MD_SHAKE256:
       ctx->suffix = SHAKE_DELIMITED_SUFFIX;
       ctx->blocksize = 1088 / 8;
-      ctx->outlen = 0;
+      ctx->outlen = 512 / 8;
       break;
     default:
       BUG();
@@ -1181,8 +1185,8 @@ keccak_read (void *context)
 }
 
 
-static void
-keccak_extract (void *context, void *out, size_t outlen)
+static gcry_err_code_t
+do_keccak_extract (void *context, void *out, size_t outlen)
 {
   KECCAK_CONTEXT *ctx = context;
   KECCAK_STATE *hd = &ctx->state;
@@ -1199,7 +1203,7 @@ keccak_extract (void *context, void *out, size_t outlen)
   if (ctx->kimd_func)
     {
       keccak_extract_s390x (context, out, outlen);
-      return;
+      return 0;
     }
 #endif
 
@@ -1304,6 +1308,52 @@ keccak_extract (void *context, void *out, size_t outlen)
 
   if (burn)
     _gcry_burn_stack (burn);
+
+  return 0;
+}
+
+
+static gcry_err_code_t
+keccak_extract (void *context, void *out, size_t outlen)
+{
+  KECCAK_CONTEXT *ctx = context;
+
+  if (ctx->shake_in_read_mode)
+    return GPG_ERR_INV_STATE;
+  if (!ctx->shake_in_extract_mode)
+    ctx->shake_in_extract_mode = 1;
+
+  return do_keccak_extract (context, out, outlen);
+}
+
+
+static byte *
+keccak_shake_read (void *context)
+{
+  KECCAK_CONTEXT *ctx = (KECCAK_CONTEXT *) context;
+  KECCAK_STATE *hd = &ctx->state;
+
+  if (ctx->shake_in_extract_mode)
+    {
+      /* Already in extract mode. */
+      return NULL;
+    }
+
+  if (!ctx->shake_in_read_mode)
+    {
+      byte tmpbuf[64];
+
+      gcry_assert(sizeof(tmpbuf) >= ctx->outlen);
+
+      ctx->shake_in_read_mode = 1;
+
+      do_keccak_extract (context, tmpbuf, ctx->outlen);
+      buf_cpy (&hd->u, tmpbuf, ctx->outlen);
+
+      wipememory(tmpbuf, sizeof(tmpbuf));
+    }
+
+  return (byte *)&hd->u;
 }
 
 
@@ -1318,10 +1368,10 @@ _gcry_sha3_hash_buffers (void *outbuf, size_t nbytes, const gcry_buffer_t *iov,
   for (;iovcnt > 0; iov++, iovcnt--)
     keccak_write (&hd, (const char*)iov[0].data + iov[0].off, iov[0].len);
   keccak_final (&hd);
-  if (spec->mdlen > 0)
+  if (hd.suffix == SHA3_DELIMITED_SUFFIX)
     memcpy (outbuf, keccak_read (&hd), spec->mdlen);
   else
-    keccak_extract (&hd, outbuf, nbytes);
+    do_keccak_extract (&hd, outbuf, nbytes);
 }
 
 
@@ -1630,8 +1680,9 @@ const gcry_md_spec_t _gcry_digest_spec_sha3_512 =
 const gcry_md_spec_t _gcry_digest_spec_shake128 =
   {
     GCRY_MD_SHAKE128, {0, 1},
-    "SHAKE128", shake128_asn, DIM (shake128_asn), oid_spec_shake128, 0,
-    shake128_init, keccak_write, keccak_final, NULL, keccak_extract,
+    "SHAKE128", shake128_asn, DIM (shake128_asn), oid_spec_shake128, 32,
+    shake128_init, keccak_write, keccak_final, keccak_shake_read,
+    keccak_extract,
     _gcry_shake128_hash_buffers,
     sizeof (KECCAK_CONTEXT),
     run_selftests
@@ -1639,8 +1690,9 @@ const gcry_md_spec_t _gcry_digest_spec_shake128 =
 const gcry_md_spec_t _gcry_digest_spec_shake256 =
   {
     GCRY_MD_SHAKE256, {0, 1},
-    "SHAKE256", shake256_asn, DIM (shake256_asn), oid_spec_shake256, 0,
-    shake256_init, keccak_write, keccak_final, NULL, keccak_extract,
+    "SHAKE256", shake256_asn, DIM (shake256_asn), oid_spec_shake256, 64,
+    shake256_init, keccak_write, keccak_final, keccak_shake_read,
+    keccak_extract,
     _gcry_shake256_hash_buffers,
     sizeof (KECCAK_CONTEXT),
     run_selftests
diff --git a/cipher/md.c b/cipher/md.c
index 40a862f6..a128dd82 100644
--- a/cipher/md.c
+++ b/cipher/md.c
@@ -1125,8 +1125,8 @@ md_extract(gcry_md_hd_t a, int algo, void *out, size_t outlen)
 	{
 	  if (r->next)
 	    log_debug ("more than one algorithm in md_extract(0)\n");
-	  r->spec->extract (r->context, out, outlen);
-	  return 0;
+
+	  return r->spec->extract (r->context, out, outlen);
 	}
     }
   else
@@ -1134,8 +1134,7 @@ md_extract(gcry_md_hd_t a, int algo, void *out, size_t outlen)
       for (r = a->ctx->list; r; r = r->next)
 	if (r->spec->algo == algo && r->spec->extract)
 	  {
-	    r->spec->extract (r->context, out, outlen);
-	    return 0;
+	    return r->spec->extract (r->context, out, outlen);
 	  }
     }
 
@@ -1248,6 +1247,7 @@ _gcry_md_hash_buffers_extract (int algo, unsigned int flags, void *digest,
 			       int iovcnt)
 {
   const gcry_md_spec_t *spec;
+  int is_xof;
   int hmac;
 
   if (!iov || iovcnt < 0)
@@ -1266,11 +1266,13 @@ _gcry_md_hash_buffers_extract (int algo, unsigned int flags, void *digest,
       return GPG_ERR_DIGEST_ALGO;
     }
 
-  if (spec->mdlen > 0 && digestlen != -1 && digestlen != spec->mdlen)
-    return GPG_ERR_DIGEST_ALGO;
-  if (spec->mdlen == 0 && digestlen == -1)
+  is_xof = spec->extract != NULL;
+  if (!is_xof && digestlen != -1 && digestlen != spec->mdlen)
     return GPG_ERR_DIGEST_ALGO;
 
+  if (digestlen == -1)
+    digestlen = spec->mdlen;
+
   if (!hmac && spec->hash_buffers)
     {
       if (spec->flags.disabled || (!spec->flags.fips && fips_mode ()))
@@ -1304,7 +1306,7 @@ _gcry_md_hash_buffers_extract (int algo, unsigned int flags, void *digest,
       for (;iovcnt; iov++, iovcnt--)
         md_write (h, (const char*)iov[0].data + iov[0].off, iov[0].len);
       md_final (h);
-      if (spec->mdlen > 0)
+      if (digestlen == spec->mdlen)
 	memcpy (digest, md_read (h, algo), spec->mdlen);
       else if (digestlen > 0)
 	md_extract (h, algo, digest, digestlen);
diff --git a/src/cipher-proto.h b/src/cipher-proto.h
index 3cf6f74c..2d03ed7d 100644
--- a/src/cipher-proto.h
+++ b/src/cipher-proto.h
@@ -216,7 +216,8 @@ typedef void (*gcry_md_final_t) (void *c);
 typedef unsigned char *(*gcry_md_read_t) (void *c);
 
 /* Type for the md_extract function.  */
-typedef void (*gcry_md_extract_t) (void *c, void *outbuf, size_t nbytes);
+typedef gpg_err_code_t (*gcry_md_extract_t) (void *c, void *outbuf,
+					     size_t nbytes);
 
 /* Type for the md_hash_buffers function. */
 typedef void (*gcry_md_hash_buffers_t) (void *outbuf, size_t nbytes,
diff --git a/tests/basic.c b/tests/basic.c
index 5d5ceac9..a405c82c 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -13604,18 +13604,16 @@ check_one_md (int algo, const char *data, int len, const char *expect, int elen,
     }
 
   mdlen = gcry_md_get_algo_dlen (algo);
-  if (mdlen < 1 || mdlen > 500)
+  if (elen != 0 && mdlen != elen
+      && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
     {
-      if (mdlen == 0 && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
-        {
-          xof = 1;
-        }
-      else
-        {
-	  gcry_md_close (hd);
-          fail ("algo %d, gcry_md_get_algo_dlen failed: %d\n", algo, mdlen);
-          return;
-        }
+      xof = 1;
+    }
+  else if (mdlen < 1 || mdlen > 500)
+    {
+      gcry_md_close (hd);
+      fail ("algo %d, gcry_md_get_algo_dlen failed: %d\n", algo, mdlen);
+      return;
     }
 
   if (key && klen)
@@ -13942,7 +13940,8 @@ check_one_md (int algo, const char *data, int len, const char *expect, int elen,
 
 
 static void
-check_one_md_multi (int algo, const char *data, int len, const char *expect)
+check_one_md_multi (int algo, const char *data, int len, const char *expect,
+		    int elen)
 {
   gpg_error_t err;
   gcry_buffer_t iov[3];
@@ -13954,14 +13953,15 @@ check_one_md_multi (int algo, const char *data, int len, const char *expect)
   mdlen = gcry_md_get_algo_dlen (algo);
   if (mdlen < 1 || mdlen > 64)
     {
-      if (mdlen == 0 && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
-        return;
-
       fail ("check_one_md_multi: algo %d, gcry_md_get_algo_dlen failed: %d\n",
             algo, mdlen);
       return;
     }
 
+  if (elen != 0 && elen != mdlen
+      && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
+    return;
+
   if (*data == '!' && !data[1])
     return;  /* We can't do that here.  */
   if (*data == '?' && !data[1])
@@ -14716,6 +14716,11 @@ check_digests (void)
 	"\x43\xE4\x1B\x45\xA6\x53\xF2\xA5\xC4\x49\x2C\x1A\xDD\x54\x45\x12"
 	"\xDD\xA2\x52\x98\x33\x46\x2B\x71\xA4\x1A\x45\xBE\x97\x29\x0B\x6F",
 	0, 512, },
+      { GCRY_MD_SHAKE128,
+	"",
+	"\x7F\x9C\x2B\xA4\xE8\x8F\x82\x7D\x61\x60\x45\x50\x76\x05\x85\x3E"
+	"\xD7\x3B\x80\x93\xF6\xEF\xBC\x88\xEB\x1A\x6E\xAC\xFA\x66\xEF\x26",
+	0, 0, /* test md_read interface */ },
       { GCRY_MD_SHAKE128,
 	"\x5A\xAB\x62\x75\x6D\x30\x7A\x66\x9D\x14\x6A\xBA\x98\x8D\x90\x74"
 	"\xC5\xA1\x59\xB3\xDE\x85\x15\x1A\x81\x9B\x11\x7C\xA1\xFF\x65\x97"
@@ -14834,6 +14839,13 @@ check_digests (void)
 	"\xAB\x0B\xAE\x31\x63\x39\x89\x43\x04\xE3\x58\x77\xB0\xC2\x8A\x9B"
 	"\x1F\xD1\x66\xC7\x96\xB9\xCC\x25\x8A\x06\x4A\x8F\x57\xE2\x7F\x2A",
 	0, 512, },
+      { GCRY_MD_SHAKE256,
+	"",
+	"\x46\xB9\xDD\x2B\x0B\xA8\x8D\x13\x23\x3B\x3F\xEB\x74\x3E\xEB\x24"
+	"\x3F\xCD\x52\xEA\x62\xB8\x1B\x82\xB5\x0C\x27\x64\x6E\xD5\x76\x2F"
+	"\xD7\x5D\xC4\xDD\xD8\xC0\xF2\x00\xCB\x05\x01\x9D\x67\xB5\x92\xF6"
+	"\xFC\x82\x1C\x49\x47\x9A\xB4\x86\x40\x29\x2E\xAC\xB3\xB7\xC4\xBE",
+	0, 0, /* test md_read interface */ },
       { GCRY_MD_SHAKE256,
 	"\xB3\x2D\x95\xB0\xB9\xAA\xD2\xA8\x81\x6D\xE6\xD0\x6D\x1F\x86\x00"
 	"\x85\x05\xBD\x8C\x14\x12\x4F\x6E\x9A\x16\x3B\x5A\x2A\xDE\x55\xF8"
@@ -15416,7 +15428,7 @@ check_digests (void)
       check_one_md_multi (algos[i].md, algos[i].data,
 			  algos[i].datalen > 0 ? algos[i].datalen
 					       : strlen (algos[i].data),
-			  algos[i].expect);
+			  algos[i].expect, algos[i].expectlen);
     }
 
   /* Check the Whirlpool bug emulation.  */
-- 
2.39.2




More information about the Gcrypt-devel mailing list