From: Collin Funk Date: Sun, 31 Aug 2025 18:43:37 +0000 (-0700) Subject: crypto/sha3-buffer: New module. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4cdf7734fbdb040557f253ceb885c8aa1dbf225b;p=thirdparty%2Fgnulib.git 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. --- diff --git a/ChangeLog b/ChangeLog index 70dc193626..24093d2d63 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2025-08-31 Collin Funk + + 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 u64: avoid theoretical problem with >64-bit int diff --git a/lib/sha3.c b/lib/sha3.c new file mode 100644 index 0000000000..f4e56ab3a1 --- /dev/null +++ b/lib/sha3.c @@ -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 . */ + +/* Written by Collin Funk , 2025. */ + +#include + +/* Specification. */ +#include "sha3.h" + +#include +#include +#include + +#include +#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 index 0000000000..c299c6fab7 --- /dev/null +++ b/lib/sha3.h @@ -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 . */ + +/* Written by Collin Funk , 2025. */ + +#ifndef SHA3_H +# define SHA3_H 1 + +# include +# include + +# 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 diff --git a/lib/u64.h b/lib/u64.h index 01135057c2..a344bc7bd5 100644 --- 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 index 0000000000..375e7ba6a2 --- /dev/null +++ b/modules/crypto/sha3-buffer @@ -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