Hello Niels,

Niels Möller <ni...@lysator.liu.se> writes:

> Daiki Ueno <u...@gnu.org> writes:
>
>>> * One could perhaps use index == 0 instead of index == block_size for
>>>   the case that there is no buffered data. But the current convention
>>>   does make your "if (length <= left)" nice and simple.
>>
>> I agree that the current convention is a bit awkward, so in the attached
>> patch I changed to use index == 0 as the indicator where buffering is
>> needed.  That actually makes the code simpler as we can defer buffering
>> until when the data is read.  One drawback though is that it causes
>> additional memcpy in a corner case where the _shake_output is used to
>> retrieve data smaller than the block size.
>
> I wonder if that will still be simpler if one also moves the
> sha3_permute calls?
>
> I have merged your previous version to a branch
> add-sha3_256_shake_output, and ci looks green. So perhaps best to merge
> that to master, and iterate from there?

Sorry for the delay, and thank you for merging it to master.  I've come
up with the attached 3 patches on top of it, which basically do:

- Apply my changes in the previous post to count index from zero, not
  the end of the block

- Rename sha3_256_shake_output to sha3_shake256_output and add
  sha3_shake256_init/update as well, as you suggested in the previous
  conversation.  That would help us implement SHAKE128 without exposing
  SHA3-128 digest functions and I find it easier to read when used in
  the ML-KEM implementation.

- Generalize _shake_output function independent of the underlying SHA-3
  algorithm.

>>> * It looks a bit backwards to me that each iteration *first* copies data
>>>   to the digest, and *then* calls sha3_permute. In case no more data is
>>>   to be output, that sha3_permute call is wasted. It would be more
>>>   natural to me to not call sha3_permute until we know the output is
>>>   needed. But to fix that and still keep things nice for the first
>>>   output block, I think one would need to reorganize _nettle_sha3_pad to
>>>   not imply a call to sha3_permute (via sha3_absorb). So that's better
>>>   done in a separate change.
>>
>> Right, I can do that after the current patch is settled.
>
> I've done a bit of hacking locally. What I did was to take out the
> xoring parts of sha3_absorb into it's own function sha3_xor_block, and
> let sha3_pad_shake use that, without any call to sha3_permute. And then
> call sha3_permute as output is needed.

Cool!

>>> * I'm still tempted to use ctx->index = ~index rather than ctx->index =
>>>   index | INDEX_HIGH_BIT. But maybe that would just be too obfuscated.
>>
>> I'm actually not sure how this works.  For example, if unsigned int is
>> 32-bit and index is 3, wouldn't ~index turn to 0xfffffffc, while index |
>> INDEX_HIGH_BIT is 0x80000003?
>
> It would be a different representation, with the very minor advantage
> that the INDEX_HIGH_BIT value isn't needed (in source code, or handled
> at runtime). Like
>
>   index = ctx->index;
>
>   if (index < sizeof(ctx->block)) 
>     { ... first call to shake_output, pad and initialize...  }
>   else
>     index = ~index;
>
>   assert (index <= sizeof(ctx->block));
>
>   ... output processing ...
>
>   ctx->index = ~index;

I see.  I rewote it using this pattern.

>>> In next step, to also support shake128, we should generalize your code
>>> using an internal function _sha3_shake_output taking block and block
>>> size as arguments.
>>
>> Yes.
>
> I've tried that in my local hack, I think it's rather straight-forward.
> (I might be able to post corresponding patch later). What's unclear is
> how much to share between _shake and shake_output. One could define
> _shake as _shake_output + _init. The drawback I see is that (i) we would
> allow _shake_output followed by _shake, which isn't proper api usage,
> and (ii) _shake needs a lot less logic since it should always start by
> padding, and it doesn't need to buffer any data, so it seems a bit wrong
> to have it call shake_output that does this unneeded extra work.

In the attached patch, _shake is now re-implemented using _shake_output
+ _init, and also marked as deprecated in favor of the new incremental
interface.

Regards,
-- 
Daiki Ueno
>From aab41e6fe2dd4461c59af63a84b45e19ec0eb9e7 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <du...@redhat.com>
Date: Sat, 23 Mar 2024 17:44:33 +0900
Subject: [PATCH 1/3] shake256: Simplify the implementation

Signed-off-by: Daiki Ueno <du...@redhat.com>
---
 shake256.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/shake256.c b/shake256.c
index 9c418395..11d34d5e 100644
--- a/shake256.c
+++ b/shake256.c
@@ -75,23 +75,30 @@ sha3_256_shake_output (struct sha3_256_ctx *ctx,
 
   /* We use the leftmost bit as a flag to indicate SHAKE is initialized.  */
   if (ctx->index & INDEX_HIGH_BIT)
-    index = ctx->index & ~INDEX_HIGH_BIT;
+    index = ~ctx->index;
   else
     {
       /* This is the first call of _shake_output.  */
       _sha3_pad_shake (&ctx->state, sizeof (ctx->block), ctx->block, ctx->index);
       /* Point at the end of block to trigger fill in of the buffer.  */
-      index = sizeof (ctx->block);
+      index = 0;
     }
 
-  assert (index <= sizeof (ctx->block));
+  assert (index < sizeof (ctx->block));
+
+  if (index == 0)
+    {
+      /* Fill in the buffer.  */
+      _nettle_write_le64 (sizeof (ctx->block), ctx->block, ctx->state.a);
+      sha3_permute (&ctx->state);
+    }
 
   /* Write remaining data from the buffer.  */
   left = sizeof (ctx->block) - index;
   if (length <= left)
     {
       memcpy (digest, ctx->block + index, length);
-      ctx->index = (index + length) | INDEX_HIGH_BIT;
+      ctx->index = ~((index + length) % sizeof (ctx->block));
       return;
     }
   else
@@ -102,22 +109,20 @@ sha3_256_shake_output (struct sha3_256_ctx *ctx,
     }
 
   /* Write full blocks.  */
-  while (length > sizeof (ctx->block))
+  while (length >= sizeof (ctx->block))
     {
       _nettle_write_le64 (sizeof (ctx->block), digest, ctx->state.a);
+      sha3_permute (&ctx->state);
       length -= sizeof (ctx->block);
       digest += sizeof (ctx->block);
-      sha3_permute (&ctx->state);
     }
 
   if (length > 0)
     {
-      /* Fill in the buffer for next call.  */
+      /* Fill in the buffer.  */
       _nettle_write_le64 (sizeof (ctx->block), ctx->block, ctx->state.a);
       sha3_permute (&ctx->state);
       memcpy (digest, ctx->block, length);
-      ctx->index = length | INDEX_HIGH_BIT;
     }
-  else
-    ctx->index = sizeof (ctx->block) | INDEX_HIGH_BIT;
+  ctx->index = ~length;
 }
-- 
2.44.0

>From 1dc19a0714d3e58b9fbbe1838137002254ba87c1 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <du...@redhat.com>
Date: Sat, 23 Mar 2024 17:58:21 +0900
Subject: [PATCH 2/3] shake256: Rename sha3_256_shake_output to
 sha3_shake256_output

This adds a new family of functions to deal with SHAKE256.  The
original sha3_256_shake is deprecated in favor of this, and
re-implemented using the new functions.

Signed-off-by: Daiki Ueno <du...@redhat.com>
---
 sha3.h                    | 36 ++++++++++++++++++++++++++---------
 shake256.c                | 40 +++++++++++++++++++++++----------------
 testsuite/shake256-test.c | 10 +++++-----
 3 files changed, 56 insertions(+), 30 deletions(-)

diff --git a/sha3.h b/sha3.h
index 4b7e186c..87cbb879 100644
--- a/sha3.h
+++ b/sha3.h
@@ -49,7 +49,9 @@ extern "C" {
 #define sha3_256_update nettle_sha3_256_update
 #define sha3_256_digest nettle_sha3_256_digest
 #define sha3_256_shake nettle_sha3_256_shake
-#define sha3_256_shake_output nettle_sha3_256_shake_output
+#define sha3_shake256_init nettle_sha3_shake256_init
+#define sha3_shake256_update nettle_sha3_shake256_update
+#define sha3_shake256_output nettle_sha3_shake256_output
 #define sha3_384_init nettle_sha3_384_init
 #define sha3_384_update nettle_sha3_384_update
 #define sha3_384_digest nettle_sha3_384_digest
@@ -91,6 +93,8 @@ sha3_permute (struct sha3_state *state);
 #define SHA3_512_DIGEST_SIZE 64
 #define SHA3_512_BLOCK_SIZE 72
 
+#define SHA3_SHAKE256_BLOCK_SIZE SHA3_256_BLOCK_SIZE
+
 /* For backwards compatibility */
 #define SHA3_224_DATA_SIZE SHA3_224_BLOCK_SIZE
 #define SHA3_256_DATA_SIZE SHA3_256_BLOCK_SIZE
@@ -137,17 +141,31 @@ sha3_256_digest(struct sha3_256_ctx *ctx,
 		size_t length,
 		uint8_t *digest);
 
-/* Alternative digest function implementing shake256, with arbitrary
-   digest size */
+/* This function is deprecated; consider migrating to sha3_shake256_*
+   interface.  */
 void
-sha3_256_shake(struct sha3_256_ctx *ctx,
-	       size_t length,
-	       uint8_t *digest);
+sha3_256_shake (struct sha3_256_ctx *ctx,
+		size_t length,
+		uint8_t *digest);
+
+/* Alternative interface implementing SHAKE256, with arbitrary digest size.  */
+struct sha3_shake256_ctx
+{
+  struct sha3_state state;
+  unsigned index;
+  uint8_t block[SHA3_SHAKE256_BLOCK_SIZE];
+};
+
+void
+sha3_shake256_init (struct sha3_shake256_ctx *ctx);
+
+void
+sha3_shake256_update (struct sha3_shake256_ctx *ctx,
+		      size_t length,
+		      const uint8_t *data);
 
-/* Unlike sha3_256_shake, this function can be called multiple times
-   to retrieve output from shake256 in an incremental manner */
 void
-sha3_256_shake_output(struct sha3_256_ctx *ctx,
+sha3_shake256_output (struct sha3_shake256_ctx *ctx,
 		      size_t length,
 		      uint8_t *digest);
 
diff --git a/shake256.c b/shake256.c
index 11d34d5e..879c0935 100644
--- a/shake256.c
+++ b/shake256.c
@@ -49,27 +49,25 @@
 #define INDEX_HIGH_BIT (~((UINT_MAX) >> 1))
 
 void
-sha3_256_shake (struct sha3_256_ctx *ctx,
-		size_t length,
-		uint8_t *dst)
+sha3_shake256_init (struct sha3_shake256_ctx *ctx)
 {
-  _sha3_pad_shake (&ctx->state, SHA3_256_BLOCK_SIZE, ctx->block, ctx->index);
-  while (length > SHA3_256_BLOCK_SIZE)
-    {
-      _nettle_write_le64 (SHA3_256_BLOCK_SIZE, dst, ctx->state.a);
-      length -= SHA3_256_BLOCK_SIZE;
-      dst += SHA3_256_BLOCK_SIZE;
-      sha3_permute (&ctx->state);
-    }
-  _nettle_write_le64 (length, dst, ctx->state.a);
+  memset (ctx, 0, offsetof (struct sha3_shake256_ctx, block));
+}
 
-  sha3_256_init (ctx);
+void
+sha3_shake256_update (struct sha3_shake256_ctx *ctx,
+		      size_t length,
+		      const uint8_t *data)
+{
+  ctx->index = _nettle_sha3_update (&ctx->state,
+				    SHA3_256_BLOCK_SIZE, ctx->block,
+				    ctx->index, length, data);
 }
 
 void
-sha3_256_shake_output (struct sha3_256_ctx *ctx,
-		       size_t length,
-		       uint8_t *digest)
+sha3_shake256_output (struct sha3_shake256_ctx *ctx,
+		      size_t length,
+		      uint8_t *digest)
 {
   unsigned index, left;
 
@@ -126,3 +124,13 @@ sha3_256_shake_output (struct sha3_256_ctx *ctx,
     }
   ctx->index = ~length;
 }
+
+void
+sha3_256_shake (struct sha3_256_ctx *ctx,
+		size_t length,
+		uint8_t *dst)
+{
+  struct sha3_shake256_ctx *shake = (struct sha3_shake256_ctx *) ctx;
+  sha3_shake256_output (shake, length, dst);
+  sha3_shake256_init (shake);
+}
diff --git a/testsuite/shake256-test.c b/testsuite/shake256-test.c
index 016060e8..38326466 100644
--- a/testsuite/shake256-test.c
+++ b/testsuite/shake256-test.c
@@ -39,7 +39,7 @@
 const struct nettle_hash nettle_shake256 =
   {
    "shake256",
-   sizeof(struct sha3_256_ctx),
+   sizeof(struct sha3_shake256_ctx),
    0,
    SHA3_256_BLOCK_SIZE,
    (nettle_hash_init_func *) sha3_256_init,
@@ -52,18 +52,18 @@ test_incremental (const struct tstring *msg,
 		  const struct tstring *digest,
 		  size_t interval)
 {
-  struct sha3_256_ctx ctx;
+  struct sha3_shake256_ctx ctx;
   size_t offset = 0;
   uint8_t *buffer = xalloc (digest->length);
 
-  sha3_256_init (&ctx);
-  sha3_256_update (&ctx, msg->length, msg->data);
+  sha3_shake256_init (&ctx);
+  sha3_shake256_update (&ctx, msg->length, msg->data);
 
   while (offset < digest->length)
     {
       size_t to_read = MIN(digest->length - offset, interval);
 
-      sha3_256_shake_output (&ctx, to_read, buffer + offset);
+      sha3_shake256_output (&ctx, to_read, buffer + offset);
 
       offset += to_read;
     }
-- 
2.44.0

>From d33ed2d88d41aae20ad2733e19f8bf41a06693a1 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <du...@redhat.com>
Date: Sun, 24 Mar 2024 09:14:40 +0900
Subject: [PATCH 3/3] sha3-shake: Generalize sha3_shake*_output

Signed-off-by: Daiki Ueno <du...@redhat.com>
---
 Makefile.in     |   2 +-
 sha3-internal.h |   7 +++
 sha3-shake.c    | 111 ++++++++++++++++++++++++++++++++++++++++++++++++
 shake256.c      |  63 ++-------------------------
 4 files changed, 122 insertions(+), 61 deletions(-)
 create mode 100644 sha3-shake.c

diff --git a/Makefile.in b/Makefile.in
index f027e762..6953a528 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -151,7 +151,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c aes-decrypt-table.c \
 		 sha3.c sha3-permute.c \
 		 sha3-224.c sha3-224-meta.c sha3-256.c sha3-256-meta.c \
 		 sha3-384.c sha3-384-meta.c sha3-512.c sha3-512-meta.c \
-		 shake256.c \
+		 sha3-shake.c shake256.c \
 		 sm3.c sm3-meta.c \
 		 serpent-set-key.c serpent-encrypt.c serpent-decrypt.c \
 		 serpent-meta.c \
diff --git a/sha3-internal.h b/sha3-internal.h
index 9b01231a..f1e5c55b 100644
--- a/sha3-internal.h
+++ b/sha3-internal.h
@@ -56,5 +56,12 @@ _nettle_sha3_pad (struct sha3_state *state,
 #define _sha3_pad_shake(state, block_size, block, pos) \
   _nettle_sha3_pad (state, block_size, block, pos, SHA3_SHAKE_MAGIC)
 
+unsigned
+_nettle_sha3_shake_output (struct sha3_state *state,
+			   size_t block_size,
+			   uint8_t *block,
+			   unsigned index,
+			   size_t length,
+			   uint8_t *digest);
 
 #endif
diff --git a/sha3-shake.c b/sha3-shake.c
new file mode 100644
index 00000000..eb6df3bb
--- /dev/null
+++ b/sha3-shake.c
@@ -0,0 +1,111 @@
+/* sha3-shake.c
+
+   Support function for SHA3 SHAKE XOF.
+
+   Copyright (C) 2024 Red Hat, Inc.
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "sha3.h"
+#include "sha3-internal.h"
+
+#include "nettle-write.h"
+
+#define INDEX_HIGH_BIT (~((UINT_MAX) >> 1))
+
+unsigned
+_nettle_sha3_shake_output (struct sha3_state *state,
+			   size_t block_size,
+			   uint8_t *block,
+			   unsigned index,
+			   size_t length,
+			   uint8_t *digest)
+{
+  unsigned left;
+
+  /* We use the leftmost bit as a flag to indicate SHAKE is initialized.  */
+  if (index & INDEX_HIGH_BIT)
+    index = ~index;
+  else
+    {
+      /* This is the first call of _shake_output.  */
+      _sha3_pad_shake (state, block_size, block, index);
+      /* Point at the end of block to trigger fill in of the buffer.  */
+      index = 0;
+    }
+
+  assert (index < block_size);
+
+  if (index == 0)
+    {
+      /* Fill in the buffer.  */
+      _nettle_write_le64 (block_size, block, state->a);
+      sha3_permute (state);
+    }
+
+  /* Write remaining data from the buffer.  */
+  left = block_size - index;
+  if (length <= left)
+    {
+      memcpy (digest, block + index, length);
+      return ~((index + length) % block_size);
+    }
+  else
+    {
+      memcpy (digest, block + index, left);
+      length -= left;
+      digest += left;
+    }
+
+  /* Write full blocks.  */
+  while (length >= block_size)
+    {
+      _nettle_write_le64 (block_size, digest, state->a);
+      sha3_permute (state);
+      length -= block_size;
+      digest += block_size;
+    }
+
+  if (length > 0)
+    {
+      /* Fill in the buffer.  */
+      _nettle_write_le64 (block_size, block, state->a);
+      sha3_permute (state);
+      memcpy (digest, block, length);
+    }
+  return ~length;
+}
diff --git a/shake256.c b/shake256.c
index 879c0935..9656b4ce 100644
--- a/shake256.c
+++ b/shake256.c
@@ -36,18 +36,12 @@
 # include "config.h"
 #endif
 
-#include <assert.h>
-#include <limits.h>
 #include <stddef.h>
 #include <string.h>
 
 #include "sha3.h"
 #include "sha3-internal.h"
 
-#include "nettle-write.h"
-
-#define INDEX_HIGH_BIT (~((UINT_MAX) >> 1))
-
 void
 sha3_shake256_init (struct sha3_shake256_ctx *ctx)
 {
@@ -69,60 +63,9 @@ sha3_shake256_output (struct sha3_shake256_ctx *ctx,
 		      size_t length,
 		      uint8_t *digest)
 {
-  unsigned index, left;
-
-  /* We use the leftmost bit as a flag to indicate SHAKE is initialized.  */
-  if (ctx->index & INDEX_HIGH_BIT)
-    index = ~ctx->index;
-  else
-    {
-      /* This is the first call of _shake_output.  */
-      _sha3_pad_shake (&ctx->state, sizeof (ctx->block), ctx->block, ctx->index);
-      /* Point at the end of block to trigger fill in of the buffer.  */
-      index = 0;
-    }
-
-  assert (index < sizeof (ctx->block));
-
-  if (index == 0)
-    {
-      /* Fill in the buffer.  */
-      _nettle_write_le64 (sizeof (ctx->block), ctx->block, ctx->state.a);
-      sha3_permute (&ctx->state);
-    }
-
-  /* Write remaining data from the buffer.  */
-  left = sizeof (ctx->block) - index;
-  if (length <= left)
-    {
-      memcpy (digest, ctx->block + index, length);
-      ctx->index = ~((index + length) % sizeof (ctx->block));
-      return;
-    }
-  else
-    {
-      memcpy (digest, ctx->block + index, left);
-      length -= left;
-      digest += left;
-    }
-
-  /* Write full blocks.  */
-  while (length >= sizeof (ctx->block))
-    {
-      _nettle_write_le64 (sizeof (ctx->block), digest, ctx->state.a);
-      sha3_permute (&ctx->state);
-      length -= sizeof (ctx->block);
-      digest += sizeof (ctx->block);
-    }
-
-  if (length > 0)
-    {
-      /* Fill in the buffer.  */
-      _nettle_write_le64 (sizeof (ctx->block), ctx->block, ctx->state.a);
-      sha3_permute (&ctx->state);
-      memcpy (digest, ctx->block, length);
-    }
-  ctx->index = ~length;
+  ctx->index = _nettle_sha3_shake_output (&ctx->state,
+					  SHA3_256_BLOCK_SIZE, ctx->block,
+					  ctx->index, length, digest);
 }
 
 void
-- 
2.44.0

_______________________________________________
nettle-bugs mailing list -- nettle-bugs@lists.lysator.liu.se
To unsubscribe send an email to nettle-bugs-le...@lists.lysator.liu.se

Reply via email to