Adding cSHAKE digest

NIIBE Yutaka <[email protected]>
Newsgroups gmane.comp.encryption.gpg.libgcrypt.devel
Message-ID <[email protected]>
Hello,

In the task T6637, adding cSHAKE and KMAC is proposed.  I read the
patch, while it works somehow, it is not easy to merge it directly.

Thus, I do implement cSHAKE part, with minimum change.  Attached is my
try.

I plan to take the test vectors for cSHAKE from the patch in T6637 and
add them.
--

_______________________________________________
Gcrypt-devel mailing list
[email protected]
https://lists.gnupg.org/mailman/listinfo/gcrypt-devel
0001-md-Add-cSHAKE-digest-algorithm-and-the-implementatio.patch (text/x-diff, 17 KB)
>From 883774a0f5b2770bbc1c5c84bea2a403d6d46605 Mon Sep 17 00:00:00 2001
From: NIIBE Yutaka <[email protected]>
Date: Tue, 26 Mar 2024 13:39:55 +0900
Subject: [PATCH] md: Add cSHAKE digest algorithm and the implementation.

* src/gcrypt.h.in (gcry_ctl_cmds): Add GCRYCTL_MD_CUSTOMIZE.
(gcry_md_algos): Add GCRY_MD_CSHAKE128 and GCRY_MD_CSHAKE256.
(struct gcry_cshake_customization): New.
* cipher/keccak.c (CSHAKE_DELIMITED_SUFFIX): New.
(keccak_init): Support GCRY_MD_CSHAKE128 and GCRY_MD_CSHAKE256.
(selftests_keccak): Likewise.
(cshake_input_n, cshake_input_s, _gcry_cshake_customize): New.
(cshake128_init, cshake256_init, cshake_hash_buffers): New.
(_gcry_cshake128_hash_buffers, _gcry_cshake256_hash_buffers): New.
(_gcry_digest_spec_cshake128, _gcry_digest_spec_cshake256): New.
* cipher/md.c (digest_list): Add cSHAKE md_specs.
(digest_list_algo301): Likewise.
(md_customize): New.
(_gcry_md_ctl): Support GCRYCTL_MD_CUSTOMIZE.
* src/cipher.h (_gcry_cshake_customize): New.
(_gcry_digest_spec_cshake128, _gcry_digest_spec_cshake256): New.
* src/fips.c (_gcry_fips_indicator_md): Support GCRY_MD_CSHAKE128 and
GCRY_MD_CSHAKE256.
* tests/basic.c (check_one_md): Support GCRY_MD_CSHAKE128 and
GCRY_MD_CSHAKE256 as xof.
(check_one_md_multi): Exclude GCRY_MD_CSHAKE128 and GCRY_MD_CSHAKE256
as xof.
* tests/bench-slope.c (hash_bench, kdf_bench): Exclude
GCRY_MD_CSHAKE128 and GCRY_MD_CSHAKE256.
* tests/benchmark.c (md_bench): Exclude GCRY_MD_CSHAKE128 and
GCRY_MD_CSHAKE256.

--

Signed-off-by: NIIBE Yutaka <[email protected]>
---
 cipher/keccak.c     | 168 +++++++++++++++++++++++++++++++++++++++++++-
 cipher/md.c         |  63 ++++++++++++++++-
 doc/gcrypt.texi     |  15 +++-
 src/cipher.h        |   6 ++
 src/fips.c          |   2 +
 src/gcrypt.h.in     |  15 +++-
 tests/basic.c       |   6 +-
 tests/bench-slope.c |  11 ++-
 tests/benchmark.c   |   2 +
 9 files changed, 279 insertions(+), 9 deletions(-)

diff --git a/cipher/keccak.c b/cipher/keccak.c
index cc055a1c..aaf83a62 100644
--- a/cipher/keccak.c
+++ b/cipher/keccak.c
@@ -112,7 +112,7 @@
 
 #define SHA3_DELIMITED_SUFFIX 0x06
 #define SHAKE_DELIMITED_SUFFIX 0x1F
-
+#define CSHAKE_DELIMITED_SUFFIX 0x04
 
 typedef struct
 {
@@ -1025,11 +1025,13 @@ keccak_init (int algo, void *context, unsigned int flags)
       ctx->blocksize = 576 / 8;
       ctx->outlen = 512 / 8;
       break;
+    case GCRY_MD_CSHAKE128:
     case GCRY_MD_SHAKE128:
       ctx->suffix = SHAKE_DELIMITED_SUFFIX;
       ctx->blocksize = 1344 / 8;
       ctx->outlen = 256 / 8;
       break;
+    case GCRY_MD_CSHAKE256:
     case GCRY_MD_SHAKE256:
       ctx->suffix = SHAKE_DELIMITED_SUFFIX;
       ctx->blocksize = 1088 / 8;
@@ -1059,9 +1061,11 @@ keccak_init (int algo, void *context, unsigned int flags)
 	case GCRY_MD_SHA3_512:
 	  kimd_func = KMID_FUNCTION_SHA3_512;
 	  break;
+	case GCRY_MD_CSHAKE128:
 	case GCRY_MD_SHAKE128:
 	  kimd_func = KMID_FUNCTION_SHAKE128;
 	  break;
+	case GCRY_MD_CSHAKE256:
 	case GCRY_MD_SHAKE256:
 	  kimd_func = KMID_FUNCTION_SHAKE256;
 	  break;
@@ -1423,6 +1427,146 @@ _gcry_shake256_hash_buffers (void *outbuf, size_t nbytes,
 			   &_gcry_digest_spec_shake256);
 }
 
+
+static unsigned int
+cshake_input_n (KECCAK_CONTEXT *ctx, const void *n, unsigned int n_len)
+{
+  unsigned char buf[3];
+
+  buf[0] = 1;
+  buf[1] = ctx->blocksize;
+  keccak_write (ctx, buf, 2);
+
+  /* Here, N_LEN must be less than 255 */
+  if (n_len < 32)
+    {
+      buf[0] = 1;
+      buf[1] = n_len * 8;
+    }
+  else
+    {
+      buf[0] = 2;
+      buf[1] = (n_len * 8) >> 8;
+      buf[2] = (n_len * 8) & 0xff;
+    }
+
+  keccak_write (ctx, buf, buf[0] + 1);
+  keccak_write (ctx, n, n_len);
+  return 2 + buf[0] + 1 + n_len;
+}
+
+static void
+cshake_input_s (KECCAK_CONTEXT *ctx, const void *s, unsigned int s_len,
+                unsigned int len_written)
+{
+  unsigned char buf[168];
+  unsigned int padlen;
+
+  /* Here, S_LEN must be less than 255 */
+  if (s_len < 32)
+    {
+      buf[0] = 1;
+      buf[1] = s_len * 8;
+    }
+  else
+    {
+      buf[0] = 2;
+      buf[1] = (s_len * 8) >> 8;
+      buf[2] = (s_len * 8) & 0xff;
+    }
+
+  keccak_write (ctx, buf, buf[0] + 1);
+  keccak_write (ctx, s, s_len);
+
+  len_written += buf[0] + 1 + s_len;
+  padlen = ctx->blocksize - (len_written % ctx->blocksize);
+  memset (buf, 0, padlen);
+  keccak_write (ctx, buf, padlen);
+}
+
+gpg_err_code_t
+_gcry_cshake_customize (void *context, struct gcry_cshake_customization *p)
+{
+  KECCAK_CONTEXT *ctx = (KECCAK_CONTEXT *) context;
+  unsigned int len_written;
+
+  if (p->n_len >= 255 || p->s_len >= 255)
+    return GPG_ERR_TOO_LARGE;
+
+  if (p->n_len == 0 && p->s_len == 0)
+    /* No customization */
+    return 0;
+
+  len_written = cshake_input_n (ctx, p->n, p->n_len);
+  cshake_input_s (ctx, p->s, p->s_len, len_written);
+  ctx->suffix = CSHAKE_DELIMITED_SUFFIX;
+  return 0;
+}
+
+
+static void
+cshake128_init (void *context, unsigned int flags)
+{
+  keccak_init (GCRY_MD_CSHAKE128, context, flags);
+}
+
+static void
+cshake256_init (void *context, unsigned int flags)
+{
+  keccak_init (GCRY_MD_CSHAKE256, context, flags);
+}
+
+static void
+cshake_hash_buffers (const gcry_md_spec_t *spec, void *outbuf, size_t nbytes,
+                     const gcry_buffer_t *iov, int iovcnt)
+{
+  KECCAK_CONTEXT ctx;
+
+  spec->init (&ctx, 0);
+
+  if (iovcnt < 2)
+    ; /* No customization, do same as SHAKE does.  */
+  else
+    {
+      if (iov[0].len != 0 || iov[1].len != 0)
+        {
+          const void *n = (unsigned char *)iov[0].data + iov[0].off;
+          size_t n_len = iov[0].len;
+          const void *s = (unsigned char *)iov[1].data + iov[1].off;
+          size_t s_len = iov[1].len;
+          size_t len;
+
+          len = cshake_input_n (&ctx, n, n_len);
+          cshake_input_s (&ctx, s, s_len, len);
+          ctx.suffix = CSHAKE_DELIMITED_SUFFIX;
+        }
+      iovcnt -= 2;
+      iov += 2;
+    }
+
+  for (;iovcnt > 0; iov++, iovcnt--)
+    keccak_write (&ctx, (const char*)iov[0].data + iov[0].off, iov[0].len);
+  keccak_final (&ctx);
+  do_keccak_extract (&ctx, outbuf, nbytes);
+}
+
+static void
+_gcry_cshake128_hash_buffers (void *outbuf, size_t nbytes,
+                              const gcry_buffer_t *iov, int iovcnt)
+{
+  const gcry_md_spec_t *spec = &_gcry_digest_spec_shake128;
+
+  cshake_hash_buffers (spec, outbuf, nbytes, iov, iovcnt);
+}
+
+static void
+_gcry_cshake256_hash_buffers (void *outbuf, size_t nbytes,
+                              const gcry_buffer_t *iov, int iovcnt)
+{
+  const gcry_md_spec_t *spec = &_gcry_digest_spec_shake256;
+
+  cshake_hash_buffers (spec, outbuf, nbytes, iov, iovcnt);
+}
 
 /*
      Self-test section.
@@ -1505,6 +1649,7 @@ selftests_keccak (int algo, int extended, selftest_report_func_t report)
       hash_len = 64;
       break;
 
+    case GCRY_MD_CSHAKE128:
     case GCRY_MD_SHAKE128:
       short_hash =
 	"\x58\x81\x09\x2d\xd8\x18\xbf\x5c\xf8\xa3\xdd\xb7\x93\xfb\xcb\xa7"
@@ -1518,6 +1663,7 @@ selftests_keccak (int algo, int extended, selftest_report_func_t report)
       hash_len = 32;
       break;
 
+    case GCRY_MD_CSHAKE256:
     case GCRY_MD_SHAKE256:
       short_hash =
 	"\x48\x33\x66\x60\x13\x60\xa8\x77\x1c\x68\x63\x08\x0c\xc4\x11\x4d"
@@ -1577,7 +1723,9 @@ run_selftests (int algo, int extended, selftest_report_func_t report)
     case GCRY_MD_SHA3_256:
     case GCRY_MD_SHA3_384:
     case GCRY_MD_SHA3_512:
+    case GCRY_MD_CSHAKE128:
     case GCRY_MD_SHAKE128:
+    case GCRY_MD_CSHAKE256:
     case GCRY_MD_SHAKE256:
       ec = selftests_keccak (algo, extended, report);
       break;
@@ -1736,3 +1884,21 @@ const gcry_md_spec_t _gcry_digest_spec_shake256 =
     sizeof (KECCAK_CONTEXT),
     run_selftests
   };
+const gcry_md_spec_t _gcry_digest_spec_cshake128 =
+  {
+    GCRY_MD_CSHAKE128, {0, 1},
+    "CSHAKE128", NULL, 0, NULL, 32,
+    cshake128_init, keccak_write, keccak_final, keccak_shake_read,
+    keccak_extract, _gcry_cshake128_hash_buffers,
+    sizeof (KECCAK_CONTEXT),
+    run_selftests
+  };
+const gcry_md_spec_t _gcry_digest_spec_cshake256 =
+  {
+    GCRY_MD_CSHAKE256, {0, 1},
+    "CSHAKE256", NULL, 0, NULL, 64,
+    cshake256_init, keccak_write, keccak_final, keccak_shake_read,
+    keccak_extract, _gcry_cshake256_hash_buffers,
+    sizeof (KECCAK_CONTEXT),
+    run_selftests
+  };
diff --git a/cipher/md.c b/cipher/md.c
index ee910689..e50bbc2f 100644
--- a/cipher/md.c
+++ b/cipher/md.c
@@ -58,6 +58,8 @@ static const gcry_md_spec_t * const digest_list[] =
      &_gcry_digest_spec_sha3_512,
      &_gcry_digest_spec_shake128,
      &_gcry_digest_spec_shake256,
+     &_gcry_digest_spec_cshake128,
+     &_gcry_digest_spec_cshake256,
 #endif
 #if USE_GOST_R_3411_94
      &_gcry_digest_spec_gost3411_94,
@@ -240,7 +242,14 @@ static const gcry_md_spec_t * const digest_list_algo301[] =
 #endif
 #if USE_SHA512
     &_gcry_digest_spec_sha512_256,
-    &_gcry_digest_spec_sha512_224
+    &_gcry_digest_spec_sha512_224,
+#else
+    NULL,
+    NULL,
+#endif
+#if USE_SHA3
+    &_gcry_digest_spec_cshake128,
+    &_gcry_digest_spec_cshake256,
 #else
     NULL,
     NULL
@@ -996,6 +1005,55 @@ prepare_macpads (gcry_md_hd_t a, const unsigned char *key, size_t keylen)
 }
 
 
+static gcry_err_code_t
+md_customize (gcry_md_hd_t h, void *buffer, size_t buflen)
+{
+  gcry_err_code_t rc = 0;
+  GcryDigestEntry *r;
+  int algo_had_customize = 0;
+
+  if (!h->ctx->list)
+    return GPG_ERR_DIGEST_ALGO; /* Might happen if no algo is enabled.  */
+
+  for (r = h->ctx->list; r; r = r->next)
+    {
+      switch (r->spec->algo)
+        {
+        case GCRY_MD_CSHAKE128:
+        case GCRY_MD_CSHAKE256:
+          algo_had_customize = 1;
+          if (buflen != sizeof (struct gcry_cshake_customization))
+            rc = GPG_ERR_INV_ARG;
+          else
+            rc = _gcry_cshake_customize (r->context, buffer);
+          break;
+        default:
+          rc = GPG_ERR_DIGEST_ALGO;
+          break;
+        }
+
+      if (rc)
+        break;
+    }
+
+  if (rc && !algo_had_customize)
+    {
+      /* None of algorithms had customize implementation, so contexts were not
+       * modified. Just return error. */
+      return rc;
+    }
+  else if (rc && algo_had_customize)
+    {
+      /* Some of the contexts have been modified, but got error. Reset
+       * all contexts. */
+      _gcry_md_reset (h);
+      return rc;
+    }
+
+  return 0;
+}
+
+
 gcry_err_code_t
 _gcry_md_ctl (gcry_md_hd_t hd, int cmd, void *buffer, size_t buflen)
 {
@@ -1014,6 +1072,9 @@ _gcry_md_ctl (gcry_md_hd_t hd, int cmd, void *buffer, size_t buflen)
     case GCRYCTL_STOP_DUMP:
       md_stop_debug ( hd );
       break;
+    case GCRYCTL_MD_CUSTOMIZE:
+      rc = md_customize (hd, buffer, buflen);
+      break;
     default:
       rc = GPG_ERR_INV_OP;
     }
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 92491f4a..c10c0b47 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -3606,7 +3606,7 @@ are also supported.
 @c begin table of hash algorithms
 @cindex SHA-1
 @cindex SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256
-@cindex SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256
+@cindex SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256, cSHAKE128, cSHAKE256
 @cindex RIPE-MD-160
 @cindex MD2, MD4, MD5
 @cindex TIGER, TIGER1, TIGER2
@@ -3716,6 +3716,19 @@ This is the SHAKE256 extendable-output function (XOF) algorithm with 256 bit
 security strength.
 See FIPS 202 for the specification.
 
+@item GCRY_MD_CSHAKE128
+This is the cSHAKE128 extendable-output function (XOF) algorithm with
+128 bit security strength defined in NIST SP 800-185.  cSHAKE takes
+two optional additional inputs N and S, which can be set by using
+@code{gcry_md_ctl} with the control commands @code{GCRYCTL_MD_CUSTOMISE},
+and the argument @code{struct gcry_cshake_customization}.  The lengths
+of N or S is limited to 255 bytes.
+
+@item GCRY_MD_CSHAKE256
+This is the cSHAKE256 extendable-output function (XOF) algorithm with 256 bit
+security strength defined in NIST SP 800-185.  Regarding the usage of the
+optional additional inputs N and S, see the above description of cSHAKE128.
+
 @item GCRY_MD_CRC32
 This is the ISO 3309 and ITU-T V.42 cyclic redundancy check.  It yields
 an output of 4 bytes.  Note that this is not a hash algorithm in the
diff --git a/src/cipher.h b/src/cipher.h
index ae6337c2..0a2551fe 100644
--- a/src/cipher.h
+++ b/src/cipher.h
@@ -142,6 +142,10 @@ void _gcry_register_pk_ecc_progress (gcry_handler_progress_t cbc,
 void _gcry_register_primegen_progress (gcry_handler_progress_t cb,
                                        void *cb_data);
 
+/*-- keccak.c --*/
+gpg_err_code_t _gcry_cshake_customize (void *context,
+                                       struct gcry_cshake_customization *p);
+
 /*-- pubkey.c --*/
 
 /* Declarations for the cipher specifications.  */
@@ -200,6 +204,8 @@ extern const gcry_md_spec_t _gcry_digest_spec_sha3_512;
 extern const gcry_md_spec_t _gcry_digest_spec_sha3_384;
 extern const gcry_md_spec_t _gcry_digest_spec_shake128;
 extern const gcry_md_spec_t _gcry_digest_spec_shake256;
+extern const gcry_md_spec_t _gcry_digest_spec_cshake128;
+extern const gcry_md_spec_t _gcry_digest_spec_cshake256;
 extern const gcry_md_spec_t _gcry_digest_spec_tiger;
 extern const gcry_md_spec_t _gcry_digest_spec_tiger1;
 extern const gcry_md_spec_t _gcry_digest_spec_tiger2;
diff --git a/src/fips.c b/src/fips.c
index 574776ac..cf91baa8 100644
--- a/src/fips.c
+++ b/src/fips.c
@@ -422,6 +422,8 @@ _gcry_fips_indicator_md (va_list arg_ptr)
     case GCRY_MD_SHA3_512:
     case GCRY_MD_SHAKE128:
     case GCRY_MD_SHAKE256:
+    case GCRY_MD_CSHAKE128:
+    case GCRY_MD_CSHAKE256:
       return GPG_ERR_NO_ERROR;
     default:
       return GPG_ERR_NOT_SUPPORTED;
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 441e8d78..a9d6b87f 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -333,7 +333,8 @@ enum gcry_ctl_cmds
     GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION = 84,
     GCRYCTL_FIPS_SERVICE_INDICATOR_MAC = 85,
     GCRYCTL_FIPS_SERVICE_INDICATOR_MD = 86,
-    GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS = 87
+    GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS = 87,
+    GCRYCTL_MD_CUSTOMIZE = 88
   };
 
 /* Perform various operations defined by CMD. */
@@ -1305,7 +1306,9 @@ enum gcry_md_algos
     GCRY_MD_BLAKE2S_128   = 325,
     GCRY_MD_SM3           = 326,
     GCRY_MD_SHA512_256    = 327,
-    GCRY_MD_SHA512_224    = 328
+    GCRY_MD_SHA512_224    = 328,
+    GCRY_MD_CSHAKE128     = 329,
+    GCRY_MD_CSHAKE256     = 330
   };
 
 /* Flags used with the open function.  */
@@ -1461,6 +1464,14 @@ void gcry_md_debug (gcry_md_hd_t hd, const char *suffix);
 #define gcry_md_get_asnoid(a,b,n) \
             gcry_md_algo_info((a), GCRYCTL_GET_ASNOID, (b), (n))
 
+struct gcry_cshake_customization
+{
+  const void *n;
+  unsigned int n_len;
+  const void *s;
+  unsigned int s_len;
+};
+
 
 
 /**********************************************
diff --git a/tests/basic.c b/tests/basic.c
index 8c4e50f4..ebe6f6d0 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -13605,7 +13605,8 @@ check_one_md (int algo, const char *data, int len, const char *expect, int elen,
 
   mdlen = gcry_md_get_algo_dlen (algo);
   if (elen != 0 && mdlen != elen
-      && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
+      && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256
+          || algo == GCRY_MD_CSHAKE128 || algo == GCRY_MD_CSHAKE256))
     {
       xof = 1;
     }
@@ -13959,7 +13960,8 @@ check_one_md_multi (int algo, const char *data, int len, const char *expect,
     }
 
   if (elen != 0 && elen != mdlen
-      && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
+      && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256
+          || algo == GCRY_MD_CSHAKE128 || algo == GCRY_MD_CSHAKE256))
     return;
 
   if (*data == '!' && !data[1])
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index 6b3a6ccd..4b14541b 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -2000,7 +2000,9 @@ hash_bench (char **argv, int argc)
   else
     {
       for (i = 1; i < 400; i++)
-	if (!gcry_md_test_algo (i))
+        if (i == GCRY_MD_CSHAKE128 || i == GCRY_MD_CSHAKE256)
+          ; /* Skip the bench. */
+        else if (!gcry_md_test_algo (i))
 	  _hash_bench (i);
     }
 
@@ -2295,6 +2297,9 @@ kdf_bench (char **argv, int argc)
 	{
 	  for (j = 1; j < 400; j++)
 	    {
+              if (i == GCRY_MD_CSHAKE128 || i == GCRY_MD_CSHAKE256)
+                continue; /* Skip the bench. */
+
 	      if (gcry_md_test_algo (j))
 		continue;
 
@@ -2309,7 +2314,9 @@ kdf_bench (char **argv, int argc)
   else
     {
       for (i = 1; i < 400; i++)
-	if (!gcry_md_test_algo (i))
+        if (i == GCRY_MD_CSHAKE128 || i == GCRY_MD_CSHAKE256)
+          ; /* Skip the bench. */
+	else if (!gcry_md_test_algo (i))
 	  kdf_bench_one (GCRY_KDF_PBKDF2, i);
     }
 
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 57c2428a..48025bbc 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -489,6 +489,8 @@ md_bench ( const char *algoname )
       for (i=1; i < 400; i++)
         if (in_fips_mode && i == GCRY_MD_MD5)
           ; /* Don't use MD5 in fips mode.  */
+        else if (i == GCRY_MD_CSHAKE128 || i == GCRY_MD_CSHAKE256)
+          ; /* Skip. */
         else if ( !gcry_md_test_algo (i) )
           md_bench (gcry_md_algo_name (i));
       return;
-- 
2.39.2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.