]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
crypto/sha3-buffer: New module.
authorCollin Funk <collin.funk1@gmail.com>
Sun, 31 Aug 2025 18:43:37 +0000 (11:43 -0700)
committerCollin Funk <collin.funk1@gmail.com>
Sun, 31 Aug 2025 18:43:37 +0000 (11:43 -0700)
* lib/u64.h (u64getlo, u64not): New functions.
* lib/sha3.c: New file, based on lib/sha512.c.
* lib/sha3.h: New file, based on lib/sha512.h.
* modules/crypto/sha3-buffer: New file.

ChangeLog
lib/sha3.c [new file with mode: 0644]
lib/sha3.h [new file with mode: 0644]
lib/u64.h
modules/crypto/sha3-buffer [new file with mode: 0644]

index 70dc1936260e4acd821ff910abb4a42a2d60920a..24093d2d63adaadfdc4a061fab446ff7328d61d6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-08-31  Collin Funk  <collin.funk1@gmail.com>
+
+       crypto/sha3-buffer: New module.
+       * lib/u64.h (u64getlo, u64not): New functions.
+       * lib/sha3.c: New file, based on lib/sha512.c.
+       * lib/sha3.h: New file, based on lib/sha512.h.
+       * modules/crypto/sha3-buffer: New file.
+
 2025-08-31  Paul Eggert  <eggert@cs.ucla.edu>
 
        u64: avoid theoretical problem with >64-bit int
diff --git a/lib/sha3.c b/lib/sha3.c
new file mode 100644 (file)
index 0000000..f4e56ab
--- /dev/null
@@ -0,0 +1,316 @@
+/* sha3.c - Functions to calculate SHA-3 hashes as specified by FIPS-202.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Collin Funk <collin.funk1@gmail.com>, 2025.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sha3.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <byteswap.h>
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) u64bswap (n)
+#else
+# define SWAP(n) (n)
+#endif
+
+static const u64 rc[] = {
+  u64init (0x00000000, 0x00000001), u64init (0x00000000, 0x00008082),
+  u64init (0x80000000, 0x0000808A), u64init (0x80000000, 0x80008000),
+  u64init (0x00000000, 0x0000808B), u64init (0x00000000, 0x80000001),
+  u64init (0x80000000, 0x80008081), u64init (0x80000000, 0x00008009),
+  u64init (0x00000000, 0x0000008A), u64init (0x00000000, 0x00000088),
+  u64init (0x00000000, 0x80008009), u64init (0x00000000, 0x8000000A),
+  u64init (0x00000000, 0x8000808B), u64init (0x80000000, 0x0000008B),
+  u64init (0x80000000, 0x00008089), u64init (0x80000000, 0x00008003),
+  u64init (0x80000000, 0x00008002), u64init (0x80000000, 0x00000080),
+  u64init (0x00000000, 0x0000800A), u64init (0x80000000, 0x8000000A),
+  u64init (0x80000000, 0x80008081), u64init (0x80000000, 0x00008080),
+  u64init (0x00000000, 0x80000001), u64init (0x80000000, 0x80008008)
+};
+
+#define DEFINE_SHA3_INIT_CTX(SIZE)                                      \
+  void                                                                  \
+  sha3_##SIZE##_init_ctx (struct sha3_ctx *ctx)                         \
+  {                                                                     \
+    memset (&ctx->state, '\0', sizeof ctx->state);                      \
+    ctx->buflen = 0;                                                    \
+    ctx->digestlen = SHA3_##SIZE##_DIGEST_SIZE;                         \
+    ctx->blocklen = SHA3_##SIZE##_BLOCK_SIZE;                           \
+  }
+
+DEFINE_SHA3_INIT_CTX (224)
+DEFINE_SHA3_INIT_CTX (256)
+DEFINE_SHA3_INIT_CTX (384)
+DEFINE_SHA3_INIT_CTX (512)
+
+/* Copy the value from V into the memory location pointed to by *CP,
+   If your architecture allows unaligned access, this is equivalent to
+   * (__typeof__ (v) *) cp = v  */
+static void
+set_uint64 (char *cp, u64 v)
+{
+  memcpy (cp, &v, sizeof v);
+}
+
+void *
+sha3_read_ctx (const struct sha3_ctx *ctx, void *resbuf)
+{
+  char *r = resbuf;
+  int i;
+  size_t words = ctx->digestlen / sizeof *ctx->state;
+  size_t bytes = ctx->digestlen % sizeof *ctx->state;
+
+  for (i = 0; i < words; ++i, r += sizeof *ctx->state)
+    set_uint64 (r, SWAP (ctx->state[i]));
+  if (bytes)
+    {
+      u64 word = ctx->state[i];
+      do
+        {
+          *r++ = u64getlo (word) & 0xFF;
+          word = u64shr (word, 8);
+        }
+      while (--bytes);
+    }
+  return resbuf;
+}
+
+static void
+sha3_conclude_ctx (struct sha3_ctx *ctx)
+{
+  ctx->buffer[ctx->buflen++] = 0x06;
+  memset (ctx->buffer + ctx->buflen, '\0', ctx->blocklen - ctx->buflen);
+  ctx->buffer[ctx->blocklen - 1] |= 0x80;
+  sha3_process_block (ctx->buffer, ctx->blocklen, ctx);
+}
+
+void *
+sha3_finish_ctx (struct sha3_ctx *ctx, void *resbuf)
+{
+  sha3_conclude_ctx (ctx);
+  return sha3_read_ctx (ctx, resbuf);
+}
+
+#define DEFINE_SHA3_BUFFER(SIZE)                                        \
+  void *                                                                \
+  sha3_##SIZE##_buffer (const char *buffer, size_t len, void *resblock) \
+  {                                                                     \
+    struct sha3_ctx ctx;                                                \
+    sha3_##SIZE##_init_ctx (&ctx);                                      \
+    sha3_process_bytes (buffer, len, &ctx);                             \
+    return sha3_finish_ctx (&ctx, resblock);                            \
+  }
+
+DEFINE_SHA3_BUFFER (224)
+DEFINE_SHA3_BUFFER (256)
+DEFINE_SHA3_BUFFER (384)
+DEFINE_SHA3_BUFFER (512)
+
+void
+sha3_process_bytes (const void *buffer, size_t len, struct sha3_ctx *ctx)
+{
+  if (0 < ctx->buflen)
+    {
+      size_t left = ctx->blocklen - ctx->buflen;
+      if (len < left)
+        {
+          /* Not enough to fill a full block.  */
+          memcpy (ctx->buffer + ctx->buflen, buffer, len);
+          ctx->buflen += len;
+          return;
+        }
+      /* Process the block that already had bytes buffered.  */
+      memcpy (ctx->buffer + ctx->buflen, buffer, left);
+      buffer = (char *) buffer + left;
+      len -= left;
+      sha3_process_block (ctx->buffer, ctx->blocklen, ctx);
+    }
+  /* Process as many complete blocks as possible.  */
+  if (0 < len)
+    {
+      size_t full_blocks = (len / ctx->blocklen) * ctx->blocklen;
+      sha3_process_block (buffer, full_blocks, ctx);
+      buffer = (char *) buffer + full_blocks;
+      len -= full_blocks;
+      memcpy (ctx->buffer, buffer, len);
+      ctx->buflen = len;
+    }
+}
+
+void
+sha3_process_block (const void *buffer, size_t len, struct sha3_ctx *ctx)
+{
+  u64 *a = ctx->state;
+  const u64 *words = buffer;
+  size_t nwords = len / sizeof *words;
+  const u64 *endp = words + nwords;
+  u64 c[5];
+  u64 d[5];
+  u64 t1;
+  u64 t2;
+
+  while (words < endp)
+    {
+      for (size_t i = 0; i < ctx->blocklen / sizeof *ctx->state; ++i, ++words)
+        ctx->state[i] = u64xor (ctx->state[i], SWAP (*words));
+      for (int i = 0; i < 24; ++i)
+        {
+          /* Theta step 1.  */
+          c[0] = u64xor (u64xor (u64xor (u64xor (a[0], a[5]), a[10]),
+                                 a[15]), a[20]);
+          c[1] = u64xor (u64xor (u64xor (u64xor (a[1], a[6]), a[11]),
+                                 a[16]), a[21]);
+          c[2] = u64xor (u64xor (u64xor (u64xor (a[2], a[7]), a[12]),
+                                 a[17]), a[22]);
+          c[3] = u64xor (u64xor (u64xor (u64xor (a[3], a[8]), a[13]),
+                                 a[18]), a[23]);
+          c[4] = u64xor (u64xor (u64xor (u64xor (a[4], a[9]), a[14]),
+                                 a[19]), a[24]);
+
+          /* Theta step 2.  */
+          d[0] = u64xor (c[4], u64rol (c[1], 1));
+          d[1] = u64xor (c[0], u64rol (c[2], 1));
+          d[2] = u64xor (c[1], u64rol (c[3], 1));
+          d[3] = u64xor (c[2], u64rol (c[4], 1));
+          d[4] = u64xor (c[3], u64rol (c[0], 1));
+
+          /* Theta step 3.  */
+          a[0] = u64xor (a[0], d[0]);
+          a[5] = u64xor (a[5], d[0]);
+          a[10] = u64xor (a[10], d[0]);
+          a[15] = u64xor (a[15], d[0]);
+          a[20] = u64xor (a[20], d[0]);
+          a[1] = u64xor (a[1], d[1]);
+          a[6] = u64xor (a[6], d[1]);
+          a[11] = u64xor (a[11], d[1]);
+          a[16] = u64xor (a[16], d[1]);
+          a[21] = u64xor (a[21], d[1]);
+          a[2] = u64xor (a[2], d[2]);
+          a[7] = u64xor (a[7], d[2]);
+          a[12] = u64xor (a[12], d[2]);
+          a[17] = u64xor (a[17], d[2]);
+          a[22] = u64xor (a[22], d[2]);
+          a[3] = u64xor (a[3], d[3]);
+          a[8] = u64xor (a[8], d[3]);
+          a[13] = u64xor (a[13], d[3]);
+          a[18] = u64xor (a[18], d[3]);
+          a[23] = u64xor (a[23], d[3]);
+          a[4] = u64xor (a[4], d[4]);
+          a[9] = u64xor (a[9], d[4]);
+          a[14] = u64xor (a[14], d[4]);
+          a[19] = u64xor (a[19], d[4]);
+          a[24] = u64xor (a[24], d[4]);
+
+          /* Rho and Pi.  */
+          t1 = a[1];
+          t2 = u64rol (t1, 1);
+          t1 = a[10];
+          a[10] = t2;
+          t2 = u64rol (t1, 3);
+          t1 = a[7];
+          a[7] = t2;
+          t2 = u64rol (t1, 6);
+          t1 = a[11];
+          a[11] = t2;
+          t2 = u64rol (t1, 10);
+          t1 = a[17];
+          a[17] = t2;
+          t2 = u64rol (t1, 15);
+          t1 = a[18];
+          a[18] = t2;
+          t2 = u64rol (t1, 21);
+          t1 = a[3];
+          a[3] = t2;
+          t2 = u64rol (t1, 28);
+          t1 = a[5];
+          a[5] = t2;
+          t2 = u64rol (t1, 36);
+          t1 = a[16];
+          a[16] = t2;
+          t2 = u64rol (t1, 45);
+          t1 = a[8];
+          a[8] = t2;
+          t2 = u64rol (t1, 55);
+          t1 = a[21];
+          a[21] = t2;
+          t2 = u64rol (t1, 2);
+          t1 = a[24];
+          a[24] = t2;
+          t2 = u64rol (t1, 14);
+          t1 = a[4];
+          a[4] = t2;
+          t2 = u64rol (t1, 27);
+          t1 = a[15];
+          a[15] = t2;
+          t2 = u64rol (t1, 41);
+          t1 = a[23];
+          a[23] = t2;
+          t2 = u64rol (t1, 56);
+          t1 = a[19];
+          a[19] = t2;
+          t2 = u64rol (t1, 8);
+          t1 = a[13];
+          a[13] = t2;
+          t2 = u64rol (t1, 25);
+          t1 = a[12];
+          a[12] = t2;
+          t2 = u64rol (t1, 43);
+          t1 = a[2];
+          a[2] = t2;
+          t2 = u64rol (t1, 62);
+          t1 = a[20];
+          a[20] = t2;
+          t2 = u64rol (t1, 18);
+          t1 = a[14];
+          a[14] = t2;
+          t2 = u64rol (t1, 39);
+          t1 = a[22];
+          a[22] = t2;
+          t2 = u64rol (t1, 61);
+          t1 = a[9];
+          a[9] = t2;
+          t2 = u64rol (t1, 20);
+          t1 = a[6];
+          a[6] = t2;
+          t2 = u64rol (t1, 44);
+          t1 = a[1];
+          a[1] = t2;
+
+          /* Chi.  */
+          for (int j = 0; j < 25; j += 5)
+            {
+              t1 = a[j];
+              t2 = a[j + 1];
+              a[j] = u64xor (a[j], u64and (u64not (a[j + 1]), a[j + 2]));
+              a[j + 1] = u64xor (a[j + 1], u64and (u64not (a[j + 2]),
+                                                   a[j + 3]));
+              a[j + 2] = u64xor (a[j + 2], u64and (u64not (a[j + 3]),
+                                                   a[j + 4]));
+              a[j + 3] = u64xor (a[j + 3], u64and (u64not (a[j + 4]), t1));
+              a[j + 4] = u64xor (a[j + 4], u64and (u64not (t1), t2));
+            }
+
+          /* Iota.  */
+          a[0] = u64xor (a[0], rc[i]);
+        }
+    }
+}
diff --git a/lib/sha3.h b/lib/sha3.h
new file mode 100644 (file)
index 0000000..c299c6f
--- /dev/null
@@ -0,0 +1,101 @@
+/* sha3.h - Functions to calculate SHA-3 hashes as specified by FIPS-202.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Collin Funk <collin.funk1@gmail.com>, 2025.  */
+
+#ifndef SHA3_H
+# define SHA3_H 1
+
+# include <stdio.h>
+# include <stdint.h>
+
+# include "u64.h"
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/* Digest sizes in bytes.  */
+enum { SHA3_224_DIGEST_SIZE = 224 / 8 };
+enum { SHA3_256_DIGEST_SIZE = 256 / 8 };
+enum { SHA3_384_DIGEST_SIZE = 384 / 8 };
+enum { SHA3_512_DIGEST_SIZE = 512 / 8 };
+
+/* Block sizes in bytes.  */
+enum { SHA3_224_BLOCK_SIZE = 1152 / 8 };
+enum { SHA3_256_BLOCK_SIZE = 1088 / 8 };
+enum { SHA3_384_BLOCK_SIZE = 832 / 8 };
+enum { SHA3_512_BLOCK_SIZE = 576 / 8 };
+
+/* Structure to save state of computation between the single steps.  */
+struct sha3_ctx
+{
+  u64 state[25];
+  uint8_t buffer[144]; /* Up to BLOCKLEN in use.  */
+  size_t buflen;       /* ≥ 0, ≤ BLOCKLEN  */
+  size_t digestlen;    /* One of SHA3_{224,256,384,512}_DIGEST_SIZE.  */
+  size_t blocklen;     /* One of SHA3_{224,256,384,512}_BLOCK_SIZE.  */
+};
+
+/* Initialize structure containing state of computation.  */
+extern void sha3_224_init_ctx (struct sha3_ctx *ctx);
+extern void sha3_256_init_ctx (struct sha3_ctx *ctx);
+extern void sha3_384_init_ctx (struct sha3_ctx *ctx);
+extern void sha3_512_init_ctx (struct sha3_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is necessary that LEN is a multiple of the BLOCKLEN member of CTX!!!  */
+extern void sha3_process_block (const void *buffer, size_t len,
+                                struct sha3_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of the BLOCKLEN member of CTX.  */
+extern void sha3_process_bytes (const void *buffer, size_t len,
+                                struct sha3_ctx *ctx);
+
+/* Process the remaining bytes in the buffer and put result from CTX in RESBUF.
+   The result is always in little endian byte order, so that a byte-wise output
+   yields to the wanted ASCII representation of the message digest.  */
+extern void *sha3_finish_ctx (struct sha3_ctx *ctx, void *restrict resbuf);
+
+/* Put result from CTX in RESBUF.  The result is always in little endian byte
+   order, so that a byte-wise output yields to the wanted ASCII representation
+   of the message digest.  */
+extern void *sha3_read_ctx (const struct sha3_ctx *ctx,
+                            void *restrict resbuf);
+
+/* Compute a SHA-3 message digest for LEN bytes beginning at BUFFER.
+   The result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+extern void *sha3_224_buffer (const char *buffer, size_t len,
+                              void *restrict resblock);
+extern void *sha3_256_buffer (const char *buffer, size_t len,
+                              void *restrict resblock);
+extern void *sha3_384_buffer (const char *buffer, size_t len,
+                              void *restrict resblock);
+extern void *sha3_512_buffer (const char *buffer, size_t len,
+                              void *restrict resblock);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
index 01135057c2ef21693a4dee82e4f8b400ad4ec21f..a344bc7bd5a65188008d1bfb4d0197c4ccd8680e 100644 (file)
--- a/lib/u64.h
+++ b/lib/u64.h
@@ -46,7 +46,9 @@ typedef uint64_t u64;
 # define u64hilo(hi, lo) ((u64) (((u64) (hi) << 32) + (lo)))
 # define u64init(hi, lo) u64hilo (hi, lo)
 # define u64lo(x) ((u64) (x))
+# define u64getlo(x) ((uint32_t) ((x) & UINT32_MAX))
 # define u64size(x) u64lo (x)
+# define u64not(x) (~(x))
 # define u64lt(x, y) ((x) < (y))
 # define u64and(x, y) ((x) & (y))
 # define u64or(x, y) ((x) | (y))
@@ -95,6 +97,13 @@ u64lo (unsigned int lo)
   return r;
 }
 
+/* Return the low 32 bits of the u64 value X.  */
+_GL_U64_INLINE unsigned int
+u64getlo (u64 x)
+{
+  return x.lo & _GL_U64_MASK32;
+}
+
 /* Return a u64 value representing SIZE, where 0 <= SIZE < 2**64.  */
 _GL_U64_INLINE u64
 u64size (size_t size)
@@ -105,6 +114,16 @@ u64size (size_t size)
   return r;
 }
 
+/* Return the bitwise NOT of X.  */
+_GL_U64_INLINE u64
+u64not (u64 x)
+{
+  u64 r;
+  r.hi = ~x.hi;
+  r.lo = ~x.lo;
+  return r;
+}
+
 /* Return X < Y.  */
 _GL_U64_INLINE bool
 u64lt (u64 x, u64 y)
diff --git a/modules/crypto/sha3-buffer b/modules/crypto/sha3-buffer
new file mode 100644 (file)
index 0000000..375e7ba
--- /dev/null
@@ -0,0 +1,28 @@
+Description:
+Compute SHA-3 checksums.
+
+Files:
+lib/sha3.h
+lib/sha3.c
+
+Depends-on:
+byteswap
+c99
+stdint-h
+u64
+
+configure.ac:
+AC_REQUIRE([AC_C_RESTRICT])
+AC_REQUIRE([gl_BIGENDIAN])
+
+Makefile.am:
+lib_SOURCES += sha3.c
+
+Include:
+"sha3.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all