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.
-- 
>From 883774a0f5b2770bbc1c5c84bea2a403d6d46605 Mon Sep 17 00:00:00 2001
From: NIIBE Yutaka <gni...@fsij.org>
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 <gni...@fsij.org>
---
 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

_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
https://lists.gnupg.org/mailman/listinfo/gcrypt-devel

Reply via email to