On Wed, Aug 05, 2026 at 01:09:13PM -0700, Mark Atwood wrote:
> cryptohash_openssl.c initializes the EVP_MD_CTX with the implicit static 
> digest
> objects (EVP_sha256() and friends), which do not deterministically dispatch
> through a loaded provider.  The patch fetches the digest by name, caches it in
> the context, and frees it on teardown, so hashing is served by the active
> provider.  The digest type is fixed for the lifetime of the context, so the
> fetch is done once.  The implicit path is kept for older OpenSSL and for
> LibreSSL, guarded by OPENSSL_VERSION_NUMBER >= 0x30000000L.
> 
> The fetch uses the default library context and a NULL property query, so no
> dependency is added and no particular provider is required.

I was looking at this patch, and the switch to EVP_MD_fetch() makes
sense, based on this:
https://docs.openssl.org/3.0/man7/migration_guide/#programming

With this quote:
"If a library context is needed then all EVP_* digest functions that
return a const EVP_MD * such as EVP_sha256() should be replaced with a
call to EVP_MD_fetch(3). See "ALGORITHM FETCHING" in crypto(7)."

The code your agent has generated (because it's AI-generated) is not
really in line with the usual PostgreSQL format.  The doc addition
does not bring much: the stance about MD5 and FIPS was already true:
we blocked MD5 computations in cryptohash_openssl.c before this
change, it's still the case after this change.

I have tested the attached with 3.5 (version installed on my system),
and some custom 3.0 and 1.1.1 (not interesting, legacy API) builds,
and that seems to work fine.
--
Michael
From 22d19ff0a54f953caee1c748d9eab96b7c1c299a Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email protected]>
Date: Fri, 7 Aug 2026 16:36:08 +0900
Subject: [PATCH v2] Fetch digests explicitly for cryptohash with OpenSSL 3.0
 and later

cryptohash_openssl.c initialized the EVP_MD_CTX with the implicit static
digest objects (EVP_sha256() and friends), which do not deterministically
dispatch through a loaded provider.

On OpenSSL 3.0 and newer, we now fetch the digest by name with
EVP_MD_fetch(), cache it in the context, and free it on teardown, so
hashing is served by the active provider.  The digest type is fixed for
the lifetime of the context, so the fetch is done once.  The implicit
path is kept for older OpenSSL and for LibreSSL.  OpenSSL 3.0 recommends
to switch from the older APIs to EVP_MD_fetch().
---
 src/common/cryptohash_openssl.c | 46 +++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c
index 51b7e0409333..8cab239b4a18 100644
--- a/src/common/cryptohash_openssl.c
+++ b/src/common/cryptohash_openssl.c
@@ -67,6 +67,9 @@ struct pg_cryptohash_ctx
        const char *errreason;
 
        EVP_MD_CTX *evpctx;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       EVP_MD     *algo;
+#endif
 
 #ifndef FRONTEND
        ResourceOwner resowner;
@@ -178,10 +181,49 @@ int
 pg_cryptohash_init(pg_cryptohash_ctx *ctx)
 {
        int                     status = 0;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       const char *name = NULL;
+#endif
 
        if (ctx == NULL)
                return -1;
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       /*
+        * Fetch the digest implementation so that it is served by the loaded
+        * provider.
+        */
+       switch (ctx->type)
+       {
+               case PG_MD5:
+                       name = "MD5";
+                       break;
+               case PG_SHA1:
+                       name = "SHA1";
+                       break;
+               case PG_SHA224:
+                       name = "SHA224";
+                       break;
+               case PG_SHA256:
+                       name = "SHA256";
+                       break;
+               case PG_SHA384:
+                       name = "SHA384";
+                       break;
+               case PG_SHA512:
+                       name = "SHA512";
+                       break;
+       }
+
+       /*
+        * Call EVP_MD_fetch() only once for each context, as provider lookups
+        * can be expensive.
+        */
+       if (ctx->algo == NULL)
+               ctx->algo = EVP_MD_fetch(NULL, name, NULL);
+       if (ctx->algo != NULL)
+               status = EVP_DigestInit_ex(ctx->evpctx, ctx->algo, NULL);
+#else
        switch (ctx->type)
        {
                case PG_MD5:
@@ -203,6 +245,7 @@ pg_cryptohash_init(pg_cryptohash_ctx *ctx)
                        status = EVP_DigestInit_ex(ctx->evpctx, EVP_sha512(), 
NULL);
                        break;
        }
+#endif
 
        /* OpenSSL internals return 1 on success, 0 on failure */
        if (status <= 0)
@@ -329,6 +372,9 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx)
                return;
 
        EVP_MD_CTX_destroy(ctx->evpctx);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       EVP_MD_free(ctx->algo);
+#endif
 
 #ifndef FRONTEND
        if (ctx->resowner)
-- 
2.55.0

Attachment: signature.asc
Description: PGP signature

Reply via email to