* lib/sha3.c (sha3_process_bytes):
Update ctx->buflen to 0 if all bytes were processed.
* tests/test-sha3-224-buffer.c:
* tests/test-sha3-256-buffer.c:
* tests/test-sha3-384-buffer.c:
* tests/test-sha3-512-buffer.c:
(mismatch): New function.
(check): Check for the bug.
---
 ChangeLog                    | 12 ++++++++++
 lib/sha3.c                   |  2 +-
 tests/test-sha3-224-buffer.c | 45 +++++++++++++++++++++++++++---------
 tests/test-sha3-256-buffer.c | 45 +++++++++++++++++++++++++++---------
 tests/test-sha3-384-buffer.c | 45 +++++++++++++++++++++++++++---------
 tests/test-sha3-512-buffer.c | 45 +++++++++++++++++++++++++++---------
 6 files changed, 149 insertions(+), 45 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 8eac7b6430..599452f58d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2026-02-22  Paul Eggert  <[email protected]>
+
+       crypto/sha3: fix partial-buffer bug
+       * lib/sha3.c (sha3_process_bytes):
+       Update ctx->buflen to 0 if all bytes were processed.
+       * tests/test-sha3-224-buffer.c:
+       * tests/test-sha3-256-buffer.c:
+       * tests/test-sha3-384-buffer.c:
+       * tests/test-sha3-512-buffer.c:
+       (mismatch): New function.
+       (check): Check for the bug.
+
 2026-02-22  Bruno Haible  <[email protected]>
 
        strnul-bench-tests: New module.
diff --git a/lib/sha3.c b/lib/sha3.c
index 5ee23dc48e..352cea514a 100644
--- a/lib/sha3.c
+++ b/lib/sha3.c
@@ -163,8 +163,8 @@ sha3_process_bytes (const void *buffer, size_t len, struct 
sha3_ctx *ctx)
       buf += full_blocks;
       len -= full_blocks;
       memcpy (ctx->buffer, buf, len);
-      ctx->buflen = len;
     }
+  ctx->buflen = len;
   return true;
 }
 
diff --git a/tests/test-sha3-224-buffer.c b/tests/test-sha3-224-buffer.c
index 4df1312527..3b6dd22bf8 100644
--- a/tests/test-sha3-224-buffer.c
+++ b/tests/test-sha3-224-buffer.c
@@ -44,23 +44,46 @@ static const struct test_case test_cases[] = {
     "\x85\x07\x0a\x51\xc1\x4c\xbf\x66\x5c\xbc" },
 };
 
+static int
+mismatch (char const expect[SHA3_224_DIGEST_SIZE],
+          char const buf[SHA3_224_DIGEST_SIZE])
+{
+  if (memcmp (expect, buf, SHA3_224_DIGEST_SIZE) == 0)
+    return 0;
+  printf ("expected:\n");
+  for (size_t i = 0; i < SHA3_224_DIGEST_SIZE; i++)
+    printf ("%02x ", expect[i] & 0xFFu);
+  printf ("\ncomputed:\n");
+  for (size_t i = 0; i < SHA3_224_DIGEST_SIZE; i++)
+    printf ("%02x ", buf[i] & 0xFFu);
+  printf ("\n");
+  return 1;
+}
+
 static int
 check (char const *message, size_t len, char const *expect)
 {
+  int failed = 0;
+
   char buf[SHA3_224_DIGEST_SIZE];
-  if (memcmp (sha3_224_buffer (message, len, buf),
-              expect, SHA3_224_DIGEST_SIZE) != 0)
+  failed |= mismatch (expect, sha3_224_buffer (message, len, buf));
+
+  while (SHA3_224_BLOCK_SIZE <= len)
     {
-      printf ("expected:\n");
-      for (size_t i = 0; i < SHA3_224_DIGEST_SIZE; i++)
-        printf ("%02x ", expect[i] & 0xFFu);
-      printf ("\ncomputed:\n");
-      for (size_t i = 0; i < SHA3_224_DIGEST_SIZE; i++)
-        printf ("%02x ", buf[i] & 0xFFu);
-      printf ("\n");
-      return 1;
+      struct sha3_ctx ctx;
+      sha3_224_init_ctx (&ctx);
+      int part = SHA3_224_BLOCK_SIZE / 3;
+      sha3_process_bytes (message, part, &ctx);
+      sha3_process_bytes (message + part, SHA3_224_BLOCK_SIZE - part, &ctx);
+      char buf2[SHA3_224_DIGEST_SIZE];
+      sha3_finish_ctx (&ctx, buf2);
+      failed |= mismatch (sha3_224_buffer (message, SHA3_224_BLOCK_SIZE, buf),
+                          buf2);
+      message += SHA3_224_BLOCK_SIZE;
+      len -= SHA3_224_BLOCK_SIZE;
     }
-  return 0;
+
+  return failed;
 }
 
 int
diff --git a/tests/test-sha3-256-buffer.c b/tests/test-sha3-256-buffer.c
index 2f34c94fc3..ee97c95c21 100644
--- a/tests/test-sha3-256-buffer.c
+++ b/tests/test-sha3-256-buffer.c
@@ -45,23 +45,46 @@ static const struct test_case test_cases[] = {
     "\xa3\x2d\xc3\x6c\xb3\x25\x4e\x81\x2b\xe2\x7a\xad\x1d\x18" },
 };
 
+static int
+mismatch (char const expect[SHA3_256_DIGEST_SIZE],
+          char const buf[SHA3_256_DIGEST_SIZE])
+{
+  if (memcmp (expect, buf, SHA3_256_DIGEST_SIZE) == 0)
+    return 0;
+  printf ("expected:\n");
+  for (size_t i = 0; i < SHA3_256_DIGEST_SIZE; i++)
+    printf ("%02x ", expect[i] & 0xFFu);
+  printf ("\ncomputed:\n");
+  for (size_t i = 0; i < SHA3_256_DIGEST_SIZE; i++)
+    printf ("%02x ", buf[i] & 0xFFu);
+  printf ("\n");
+  return 1;
+}
+
 static int
 check (char const *message, size_t len, char const *expect)
 {
+  int failed = 0;
+
   char buf[SHA3_256_DIGEST_SIZE];
-  if (memcmp (sha3_256_buffer (message, len, buf),
-              expect, SHA3_256_DIGEST_SIZE) != 0)
+  failed |= mismatch (expect, sha3_256_buffer (message, len, buf));
+
+  while (SHA3_256_BLOCK_SIZE <= len)
     {
-      printf ("expected:\n");
-      for (size_t i = 0; i < SHA3_256_DIGEST_SIZE; i++)
-        printf ("%02x ", expect[i] & 0xFFu);
-      printf ("\ncomputed:\n");
-      for (size_t i = 0; i < SHA3_256_DIGEST_SIZE; i++)
-        printf ("%02x ", buf[i] & 0xFFu);
-      printf ("\n");
-      return 1;
+      struct sha3_ctx ctx;
+      sha3_256_init_ctx (&ctx);
+      int part = SHA3_256_BLOCK_SIZE / 3;
+      sha3_process_bytes (message, part, &ctx);
+      sha3_process_bytes (message + part, SHA3_256_BLOCK_SIZE - part, &ctx);
+      char buf2[SHA3_256_DIGEST_SIZE];
+      sha3_finish_ctx (&ctx, buf2);
+      failed |= mismatch (sha3_256_buffer (message, SHA3_256_BLOCK_SIZE, buf),
+                          buf2);
+      message += SHA3_256_BLOCK_SIZE;
+      len -= SHA3_256_BLOCK_SIZE;
     }
-  return 0;
+
+  return failed;
 }
 
 int
diff --git a/tests/test-sha3-384-buffer.c b/tests/test-sha3-384-buffer.c
index 6523052fe8..7452b138f9 100644
--- a/tests/test-sha3-384-buffer.c
+++ b/tests/test-sha3-384-buffer.c
@@ -49,23 +49,46 @@ static const struct test_case test_cases[] = {
     "\xb4\x3b\x38\x52\xb3\x37\x21\x61\x79\xaa\x7f\xc7" }
 };
 
+static int
+mismatch (char const expect[SHA3_384_DIGEST_SIZE],
+          char const buf[SHA3_384_DIGEST_SIZE])
+{
+  if (memcmp (expect, buf, SHA3_384_DIGEST_SIZE) == 0)
+    return 0;
+  printf ("expected:\n");
+  for (size_t i = 0; i < SHA3_384_DIGEST_SIZE; i++)
+    printf ("%02x ", expect[i] & 0xFFu);
+  printf ("\ncomputed:\n");
+  for (size_t i = 0; i < SHA3_384_DIGEST_SIZE; i++)
+    printf ("%02x ", buf[i] & 0xFFu);
+  printf ("\n");
+  return 1;
+}
+
 static int
 check (char const *message, size_t len, char const *expect)
 {
+  int failed = 0;
+
   char buf[SHA3_384_DIGEST_SIZE];
-  if (memcmp (sha3_384_buffer (message, len, buf),
-              expect, SHA3_384_DIGEST_SIZE) != 0)
+  failed |= mismatch (expect, sha3_384_buffer (message, len, buf));
+
+  while (SHA3_384_BLOCK_SIZE <= len)
     {
-      printf ("expected:\n");
-      for (size_t i = 0; i < SHA3_384_DIGEST_SIZE; i++)
-        printf ("%02x ", expect[i] & 0xFFu);
-      printf ("\ncomputed:\n");
-      for (size_t i = 0; i < SHA3_384_DIGEST_SIZE; i++)
-        printf ("%02x ", buf[i] & 0xFFu);
-      printf ("\n");
-      return 1;
+      struct sha3_ctx ctx;
+      sha3_384_init_ctx (&ctx);
+      int part = SHA3_384_BLOCK_SIZE / 3;
+      sha3_process_bytes (message, part, &ctx);
+      sha3_process_bytes (message + part, SHA3_384_BLOCK_SIZE - part, &ctx);
+      char buf2[SHA3_384_DIGEST_SIZE];
+      sha3_finish_ctx (&ctx, buf2);
+      failed |= mismatch (sha3_384_buffer (message, SHA3_384_BLOCK_SIZE, buf),
+                          buf2);
+      message += SHA3_384_BLOCK_SIZE;
+      len -= SHA3_384_BLOCK_SIZE;
     }
-  return 0;
+
+  return failed;
 }
 
 int
diff --git a/tests/test-sha3-512-buffer.c b/tests/test-sha3-512-buffer.c
index 5897b89fe6..303f75d61e 100644
--- a/tests/test-sha3-512-buffer.c
+++ b/tests/test-sha3-512-buffer.c
@@ -53,23 +53,46 @@ static const struct test_case test_cases[] = {
     "\xf3\x82\x27\x0c\xb4\x55\xf2\x1d\xd1\x85" }
 };
 
+static int
+mismatch (char const expect[SHA3_512_DIGEST_SIZE],
+          char const buf[SHA3_512_DIGEST_SIZE])
+{
+  if (memcmp (expect, buf, SHA3_512_DIGEST_SIZE) == 0)
+    return 0;
+  printf ("expected:\n");
+  for (size_t i = 0; i < SHA3_512_DIGEST_SIZE; i++)
+    printf ("%02x ", expect[i] & 0xFFu);
+  printf ("\ncomputed:\n");
+  for (size_t i = 0; i < SHA3_512_DIGEST_SIZE; i++)
+    printf ("%02x ", buf[i] & 0xFFu);
+  printf ("\n");
+  return 1;
+}
+
 static int
 check (char const *message, size_t len, char const *expect)
 {
+  int failed = 0;
+
   char buf[SHA3_512_DIGEST_SIZE];
-  if (memcmp (sha3_512_buffer (message, len, buf),
-              expect, SHA3_512_DIGEST_SIZE) != 0)
+  failed |= mismatch (expect, sha3_512_buffer (message, len, buf));
+
+  while (SHA3_512_BLOCK_SIZE <= len)
     {
-      printf ("expected:\n");
-      for (size_t i = 0; i < SHA3_512_DIGEST_SIZE; i++)
-        printf ("%02x ", expect[i] & 0xFFu);
-      printf ("\ncomputed:\n");
-      for (size_t i = 0; i < SHA3_512_DIGEST_SIZE; i++)
-        printf ("%02x ", buf[i] & 0xFFu);
-      printf ("\n");
-      return 1;
+      struct sha3_ctx ctx;
+      sha3_512_init_ctx (&ctx);
+      int part = SHA3_512_BLOCK_SIZE / 3;
+      sha3_process_bytes (message, part, &ctx);
+      sha3_process_bytes (message + part, SHA3_512_BLOCK_SIZE - part, &ctx);
+      char buf2[SHA3_512_DIGEST_SIZE];
+      sha3_finish_ctx (&ctx, buf2);
+      failed |= mismatch (sha3_512_buffer (message, SHA3_512_BLOCK_SIZE, buf),
+                          buf2);
+      message += SHA3_512_BLOCK_SIZE;
+      len -= SHA3_512_BLOCK_SIZE;
     }
-  return 0;
+
+  return failed;
 }
 
 int
-- 
2.51.0


Reply via email to