Hi,

Here is a patch to add OpenSSL support for SHA-3 using the EVP API.
There are two things I am not 100% happy with, but I do not see a way
around.

First, we must call EVP_MD_CTX_create to malloc an EVP_MD_CTX. This is
because an EVP_MD_CTX field cannot be inside of a structure; it is a
typedef to an incomplete type.

Second, I used xalloc-die as a conditional dependency. This is because
the EVP functions are documented as returning 0 on failure. In practice,
I can only see this being the case for EVP_MD_CTX_create, but that
should be rare (e.g. OOM). I would rather not change the prototypes to
be different than the other digests in Gnulib, so there is no way to
return errors back to the caller. This shouldn't matter for Coreutils,
but calling abort in libraries is not great, in my opinion. Using
xalloc_die is only slightly more friendly.

Here are some basic benchmarks for reference, using no other arguments to
./configure besides --with-openssl={yes|no}:

    Gnulib:
    $ ./gltests/bench-sha3-512 1000000000 5
    real  30.514154
    user  30.374
    sys    0.001
    
    OpenSSL:
    $ ./gltests/bench-sha3-512 1000000000 5
    real  19.172793
    user  19.080
    sys    0.000

Side note, SHA-3 seems really slow compared to SHA-2 from what I can
tell from this little test:

    Gnulib:
    $ ./gltests/bench-sha512 1000000000 5
    real  10.915839
    user  10.858
    sys    0.000
    
    $ ./gltests/bench-sha512 1000000000 5
    real   5.702266
    user   5.670
    sys    0.001

I guess that is because it isn't used enough to make Intel or AMD bother
to create a special instruction set for it.

Will leave unpushed for now to allow others to review.

Collin

>From 1cbaff1c43fecf206b1d7ab56509d48be0d35d2c Mon Sep 17 00:00:00 2001
Message-ID: <1cbaff1c43fecf206b1d7ab56509d48be0d35d2c.1756794420.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Mon, 1 Sep 2025 22:57:22 -0700
Subject: [PATCH] crypto/sha3-buffer: Add support for OpenSSL.

* lib/sha3.c (DEFINE_SHA3_INIT_CTX, sha3_read_ctx, sha3_finish_ctx)
(DEFINE_SHA3_BUFFER, sha3_process_bytes, sha3_process_block)
[HAVE_OPENSSL_SHA3]: Define these functions/macros using the OpenSSL EVP
API.
* lib/sha3.h [HAVE_OPENSSL_SHA3]: Include <openssl/evp.h>.
(struct sha3_ctx) [HAVE_OPENSSL_SHA3]: Only store a pointer to an
EVP_MD_CTX in the structure.
* m4/gl-openssl.m4 (gl_CRYPTO_CHECK): If the argument is SHA3 check for
EVP_sha3_224.
* m4/sha3.m4 (gl_SHA3): New file, based on m4/sha512.m4.
* modules/crypto/sha3-buffer (Files): Add m4/gl-openssl.m4 and
m4/sha3.m4.
(configure.ac): Invoke gl_SHA3. Remove gl_BIGENDIAN.
* modules/crypto/sha3-buffer-tests (Makefile.am): Link to @LIB_CRYPTO@.
---
 ChangeLog                        | 18 ++++++++
 lib/sha3.c                       | 72 ++++++++++++++++++++++++++++++++
 lib/sha3.h                       | 12 ++++++
 m4/gl-openssl.m4                 | 17 +++++---
 m4/sha3.m4                       | 16 +++++++
 modules/crypto/sha3-buffer       |  8 +++-
 modules/crypto/sha3-buffer-tests |  8 ++++
 7 files changed, 145 insertions(+), 6 deletions(-)
 create mode 100644 m4/sha3.m4

diff --git a/ChangeLog b/ChangeLog
index f6f365a118..1a0621d390 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2025-09-01  Collin Funk  <[email protected]>
+
+	crypto/sha3-buffer: Add support for OpenSSL.
+	* lib/sha3.c (DEFINE_SHA3_INIT_CTX, sha3_read_ctx, sha3_finish_ctx)
+	(DEFINE_SHA3_BUFFER, sha3_process_bytes, sha3_process_block)
+	[HAVE_OPENSSL_SHA3]: Define these functions/macros using the OpenSSL EVP
+	API.
+	* lib/sha3.h [HAVE_OPENSSL_SHA3]: Include <openssl/evp.h>.
+	(struct sha3_ctx) [HAVE_OPENSSL_SHA3]: Only store a pointer to an
+	EVP_MD_CTX in the structure.
+	* m4/gl-openssl.m4 (gl_CRYPTO_CHECK): If the argument is SHA3 check for
+	EVP_sha3_224.
+	* m4/sha3.m4 (gl_SHA3): New file, based on m4/sha512.m4.
+	* modules/crypto/sha3-buffer (Files): Add m4/gl-openssl.m4 and
+	m4/sha3.m4.
+	(configure.ac): Invoke gl_SHA3. Remove gl_BIGENDIAN.
+	* modules/crypto/sha3-buffer-tests (Makefile.am): Link to @LIB_CRYPTO@.
+
 2025-09-01  Bruno Haible  <[email protected]>
 
 	uchar-h: Fix compilation error of <cuchar> in C++ mode on macOS 15.
diff --git a/lib/sha3.c b/lib/sha3.c
index f4e56ab3a1..9e1beef2f3 100644
--- a/lib/sha3.c
+++ b/lib/sha3.c
@@ -32,6 +32,8 @@
 # define SWAP(n) (n)
 #endif
 
+#if ! HAVE_OPENSSL_SHA3
+
 static const u64 rc[] = {
   u64init (0x00000000, 0x00000001), u64init (0x00000000, 0x00008082),
   u64init (0x80000000, 0x0000808A), u64init (0x80000000, 0x80008000),
@@ -314,3 +316,73 @@ sha3_process_block (const void *buffer, size_t len, struct sha3_ctx *ctx)
         }
     }
 }
+
+#else /* OpenSSL implementation.  */
+
+/* EVP functions shouldn't fail.  But print a generic message if they do.  */
+extern _Noreturn void xalloc_die (void);
+
+#define DEFINE_SHA3_INIT_CTX(SIZE)                                      \
+  void                                                                  \
+  sha3_##SIZE##_init_ctx (struct sha3_ctx *ctx)                         \
+  {                                                                     \
+    int rc;                                                             \
+    ctx->evp_ctx = EVP_MD_CTX_create ();                                \
+    if (ctx->evp_ctx == NULL)                                           \
+      xalloc_die ();                                                    \
+    rc = EVP_DigestInit_ex (ctx->evp_ctx, EVP_sha3_##SIZE (), NULL);    \
+    if (rc == 0)                                                        \
+      xalloc_die ();                                                    \
+  }
+
+DEFINE_SHA3_INIT_CTX (224)
+DEFINE_SHA3_INIT_CTX (256)
+DEFINE_SHA3_INIT_CTX (384)
+DEFINE_SHA3_INIT_CTX (512)
+
+void *
+sha3_read_ctx (const struct sha3_ctx *ctx, void *resbuf)
+{
+  /* Assume any unprocessed bytes in ctx are not to be ignored.  */
+  return sha3_finish_ctx ((struct sha3_ctx *) ctx, resbuf);
+}
+
+void *
+sha3_finish_ctx (struct sha3_ctx *ctx, void *resbuf)
+{
+  int result = EVP_DigestFinal_ex (ctx->evp_ctx, resbuf, NULL);
+  if (result == 0)
+    xalloc_die ();
+  return 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)
+{
+  int result = EVP_DigestUpdate (ctx->evp_ctx, buffer, len);
+  if (result == 0)
+    xalloc_die ();
+}
+
+void
+sha3_process_block (const void *buffer, size_t len, struct sha3_ctx *ctx)
+{
+  sha3_process_bytes (buffer, len, ctx);
+}
+
+#endif
diff --git a/lib/sha3.h b/lib/sha3.h
index 8e0a7a7fdf..7de1216fda 100644
--- a/lib/sha3.h
+++ b/lib/sha3.h
@@ -28,6 +28,12 @@
 extern "C" {
 # endif
 
+/* OpenSSL does not have the Init, Update, Final API for SHA-3.  We must use
+   the EVP API.  */
+# if HAVE_OPENSSL_SHA3
+#  include <openssl/evp.h>
+# endif
+
 /* Digest sizes in bytes.  */
 enum { SHA3_224_DIGEST_SIZE = 224 / 8 };
 enum { SHA3_256_DIGEST_SIZE = 256 / 8 };
@@ -43,11 +49,17 @@ enum { SHA3_512_BLOCK_SIZE = 576 / 8 };
 /* Structure to save state of computation between the single steps.  */
 struct sha3_ctx
 {
+# if HAVE_OPENSSL_SHA3
+  /* This is an incomplete type, so we can only place a pointer in the
+     struct.  */
+  EVP_MD_CTX *evp_ctx;
+# else
   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.  */
+# endif
 };
 
 /* Initialize structure containing state of computation.  */
diff --git a/m4/gl-openssl.m4 b/m4/gl-openssl.m4
index 3cfea50fe5..cc700ec2ea 100644
--- a/m4/gl-openssl.m4
+++ b/m4/gl-openssl.m4
@@ -1,5 +1,5 @@
 # gl-openssl.m4
-# serial 7
+# serial 8
 dnl Copyright (C) 2013-2025 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -58,12 +58,19 @@ AC_DEFUN([gl_CRYPTO_CHECK]
     fi
     if test "x$with_openssl" != xauto-gpl-compat ||
        test "x$gl_cv_openssl_gpl_compat" = xyes; then
-      AC_CHECK_LIB([crypto], [$1],
-        [AC_CHECK_HEADERS(
-           m4_if([$1], [MD5], [openssl/md5.h], [openssl/sha.h]),
+      m4_if([$1], [SHA3],
+        [AC_CHECK_LIB([crypto], [EVP_sha3_224],
            [LIB_CRYPTO=-lcrypto
+            HAVE_OPENSSL_SHA3=1
+            AC_SUBST([HAVE_OPENSSL_SHA3])
             AC_DEFINE([HAVE_OPENSSL_$1], [1],
-              [Define to 1 if libcrypto is used for $1.])])])
+              [Define to 1 if libcrypto is used for $1.])])],
+        [AC_CHECK_LIB([crypto], [$1],
+           [AC_CHECK_HEADERS(
+              m4_if([$1], [MD5], [openssl/md5.h], [openssl/sha.h]),
+              [LIB_CRYPTO=-lcrypto
+               AC_DEFINE([HAVE_OPENSSL_$1], [1],
+                 [Define to 1 if libcrypto is used for $1.])])])])
     fi
     if test "x$LIB_CRYPTO" = x; then
       message='openssl development library not found for $1.
diff --git a/m4/sha3.m4 b/m4/sha3.m4
new file mode 100644
index 0000000000..773f420ff9
--- /dev/null
+++ b/m4/sha3.m4
@@ -0,0 +1,16 @@
+# sha3.m4
+# serial 1
+dnl Copyright (C) 2025 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl This file is offered as-is, without any warranty.
+
+AC_DEFUN([gl_SHA3],
+[
+  dnl Prerequisites of lib/sha3.c.
+  AC_REQUIRE([gl_BIGENDIAN])
+
+  dnl Determine HAVE_OPENSSL_SHA3 and LIB_CRYPTO
+  gl_CRYPTO_CHECK([SHA3])
+])
diff --git a/modules/crypto/sha3-buffer b/modules/crypto/sha3-buffer
index 375e7ba6a2..afe3c5f855 100644
--- a/modules/crypto/sha3-buffer
+++ b/modules/crypto/sha3-buffer
@@ -4,16 +4,19 @@ Compute SHA-3 checksums.
 Files:
 lib/sha3.h
 lib/sha3.c
+m4/gl-openssl.m4
+m4/sha3.m4
 
 Depends-on:
 byteswap
 c99
 stdint-h
 u64
+xalloc-die           [$HAVE_OPENSSL_SHA3]
 
 configure.ac:
 AC_REQUIRE([AC_C_RESTRICT])
-AC_REQUIRE([gl_BIGENDIAN])
+gl_SHA3
 
 Makefile.am:
 lib_SOURCES += sha3.c
@@ -21,6 +24,9 @@ lib_SOURCES += sha3.c
 Include:
 "sha3.h"
 
+Link:
+$(LIB_CRYPTO)
+
 License:
 LGPLv2+
 
diff --git a/modules/crypto/sha3-buffer-tests b/modules/crypto/sha3-buffer-tests
index 0f9e57318a..ebb293381b 100644
--- a/modules/crypto/sha3-buffer-tests
+++ b/modules/crypto/sha3-buffer-tests
@@ -24,7 +24,15 @@ check_PROGRAMS += test-sha3-224-buffer test-sha3-256-buffer
 check_PROGRAMS += test-sha3-384-buffer test-sha3-512-buffer
 noinst_PROGRAMS += bench-sha3-224 bench-sha3-256
 noinst_PROGRAMS += bench-sha3-384 bench-sha3-512
+test_sha3_224_buffer_LDADD = $(LDADD) @LIB_CRYPTO@
+test_sha3_256_buffer_LDADD = $(LDADD) @LIB_CRYPTO@
+test_sha3_384_buffer_LDADD = $(LDADD) @LIB_CRYPTO@
+test_sha3_512_buffer_LDADD = $(LDADD) @LIB_CRYPTO@
 bench_sha3_224_CPPFLAGS = $(AM_CPPFLAGS) -DNDEBUG
+bench_sha3_224_LDADD = $(LDADD) @LIB_CRYPTO@
 bench_sha3_256_CPPFLAGS = $(AM_CPPFLAGS) -DNDEBUG
+bench_sha3_256_LDADD = $(LDADD) @LIB_CRYPTO@
 bench_sha3_384_CPPFLAGS = $(AM_CPPFLAGS) -DNDEBUG
+bench_sha3_384_LDADD = $(LDADD) @LIB_CRYPTO@
 bench_sha3_512_CPPFLAGS = $(AM_CPPFLAGS) -DNDEBUG
+bench_sha3_512_LDADD = $(LDADD) @LIB_CRYPTO@
-- 
2.51.0

Reply via email to