From 5e4c2eb07e3be5bbabe71c315b36702e0004ca25 Mon Sep 17 00:00:00 2001
From: Filip Janus <fjanus@redhat.com>
Date: Wed, 22 Oct 2025 22:31:25 +0200
Subject: [PATCH 1/2] Add regression test for ML-DSA channel binding support

Add a test case to verify that SCRAM channel binding works correctly
with ML-DSA-65 (post-quantum) server certificates.

This test is similar to the existing RSA-PSS test and verifies:
- ML-DSA-65 certificates can be loaded
- Channel binding works with post-quantum signature algorithms
- SCRAM-SHA-256 authentication succeeds
- No 'could not find digest for NID UNDEF' error occurs

The test uses a self-signed ML-DSA-65 certificate generated via
sslfiles.mk
---
 configure                               | 285 ++++++++++++------------
 configure.ac                            |   5 +
 meson.build                             |   9 +
 src/include/pg_config.h.in              |   3 +
 src/test/ssl/README                     |   3 +-
 src/test/ssl/conf/server-mldsa65.config |  15 ++
 src/test/ssl/ssl/server-mldsa65.crt     | 118 ++++++++++
 src/test/ssl/ssl/server-mldsa65.key     |  88 ++++++++
 src/test/ssl/sslfiles.mk                |  14 +-
 src/test/ssl/t/002_scram.pl             |  18 ++
 10 files changed, 417 insertions(+), 141 deletions(-)
 create mode 100644 src/test/ssl/conf/server-mldsa65.config
 create mode 100644 src/test/ssl/ssl/server-mldsa65.crt
 create mode 100644 src/test/ssl/ssl/server-mldsa65.key

diff --git a/configure b/configure
index 22cd866147b..690cbd04ac5 100755
--- a/configure
+++ b/configure
@@ -2128,6 +2128,56 @@ $as_echo "$ac_res" >&6; }
 
 } # ac_fn_c_check_func
 
+# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
+# ---------------------------------------------
+# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+# accordingly.
+ac_fn_c_check_decl ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  # Initialize each $ac_[]_AC_LANG_ABBREV[]_decl_warn_flag once.
+      as_decl_name=`echo $2|sed 's/ *(.*//'`
+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_werror_flag=$ac_c_werror_flag
+  ac_c_werror_flag="$ac_c_decl_warn_flag$ac_c_werror_flag"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+#ifndef $as_decl_name
+#ifdef __cplusplus
+  (void) $as_decl_use;
+#else
+  (void) $as_decl_name;
+#endif
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  ac_c_werror_flag=$ac_save_werror_flag
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_decl
+
 # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
 # ----------------------------------------------------
 # Tries to find if the field MEMBER exists in type AGGR, after including
@@ -2421,56 +2471,6 @@ rm -f conftest.val
   as_fn_set_status $ac_retval
 
 } # ac_fn_c_compute_int
-
-# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
-# ---------------------------------------------
-# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
-# accordingly.
-ac_fn_c_check_decl ()
-{
-  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  # Initialize each $ac_[]_AC_LANG_ABBREV[]_decl_warn_flag once.
-      as_decl_name=`echo $2|sed 's/ *(.*//'`
-  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
-$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
-if eval \${$3+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_save_werror_flag=$ac_c_werror_flag
-  ac_c_werror_flag="$ac_c_decl_warn_flag$ac_c_werror_flag"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$4
-int
-main ()
-{
-#ifndef $as_decl_name
-#ifdef __cplusplus
-  (void) $as_decl_use;
-#else
-  (void) $as_decl_name;
-#endif
-#endif
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  eval "$3=yes"
-else
-  eval "$3=no"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-  ac_c_werror_flag=$ac_save_werror_flag
-fi
-eval ac_res=\$$3
-	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
-
-} # ac_fn_c_check_decl
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
@@ -12969,6 +12969,103 @@ _ACEOF
 fi
 done
 
+  # Check for ML-DSA support (OpenSSL 3.5+)
+  # The Clang compiler raises a warning for an undeclared identifier that matches
+# a compiler builtin function.  All extant Clang versions are affected, as of
+# Clang 3.6.0.  Test a builtin known to every version.  This problem affects the
+# C and Objective C languages, but Clang does report an error under C++ and
+# Objective C++.
+#
+# Passing -fno-builtin to the compiler would suppress this problem.  That
+# strategy would have the advantage of being insensitive to stray warnings, but
+# it would make tests less realistic.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how $CC reports undeclared, standard C functions" >&5
+$as_echo_n "checking how $CC reports undeclared, standard C functions... " >&6; }
+if ${ac_cv_c_decl_report+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+(void) strchr;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  if test -s conftest.err; then :
+      # For AC_CHECK_DECL to react to warnings, the compiler must be silent on
+    # valid AC_CHECK_DECL input.  No library function is consistently available
+    # on freestanding implementations, so test against a dummy declaration.
+    # Include always-available headers on the off chance that they somehow
+    # elicit warnings.
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <float.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+extern void ac_decl (int, char *);
+int
+main ()
+{
+#ifdef __cplusplus
+  (void) ac_decl ((int) 0, (char *) 0);
+  (void) ac_decl;
+#else
+  (void) ac_decl;
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  if test -s conftest.err; then :
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot detect from compiler exit status or warnings
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  ac_cv_c_decl_report=warning
+fi
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compile a simple declaration test
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "compiler does not report undeclared identifiers
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+else
+  ac_cv_c_decl_report=error
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_decl_report" >&5
+$as_echo "$ac_cv_c_decl_report" >&6; }
+
+case $ac_cv_c_decl_report in
+  warning) ac_c_decl_warn_flag=yes ;;
+  *) ac_c_decl_warn_flag= ;;
+esac
+
+ac_fn_c_check_decl "$LINENO" "NID_ML_DSA_65" "ac_cv_have_decl_NID_ML_DSA_65" "#include <openssl/obj_mac.h>
+"
+if test "x$ac_cv_have_decl_NID_ML_DSA_65" = xyes; then :
+
+$as_echo "#define HAVE_ML_DSA_SUPPORT 1" >>confdefs.h
+
+fi
+
 
 $as_echo "#define USE_OPENSSL 1" >>confdefs.h
 
@@ -15809,94 +15906,6 @@ fi
 # posix_fadvise() is a no-op on Solaris, so don't incur function overhead
 # by calling it, 2009-04-02
 # http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/posix_fadvise.c
-# The Clang compiler raises a warning for an undeclared identifier that matches
-# a compiler builtin function.  All extant Clang versions are affected, as of
-# Clang 3.6.0.  Test a builtin known to every version.  This problem affects the
-# C and Objective C languages, but Clang does report an error under C++ and
-# Objective C++.
-#
-# Passing -fno-builtin to the compiler would suppress this problem.  That
-# strategy would have the advantage of being insensitive to stray warnings, but
-# it would make tests less realistic.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how $CC reports undeclared, standard C functions" >&5
-$as_echo_n "checking how $CC reports undeclared, standard C functions... " >&6; }
-if ${ac_cv_c_decl_report+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main ()
-{
-(void) strchr;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  if test -s conftest.err; then :
-      # For AC_CHECK_DECL to react to warnings, the compiler must be silent on
-    # valid AC_CHECK_DECL input.  No library function is consistently available
-    # on freestanding implementations, so test against a dummy declaration.
-    # Include always-available headers on the off chance that they somehow
-    # elicit warnings.
-    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <float.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stddef.h>
-extern void ac_decl (int, char *);
-int
-main ()
-{
-#ifdef __cplusplus
-  (void) ac_decl ((int) 0, (char *) 0);
-  (void) ac_decl;
-#else
-  (void) ac_decl;
-#endif
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  if test -s conftest.err; then :
-  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot detect from compiler exit status or warnings
-See \`config.log' for more details" "$LINENO" 5; }
-else
-  ac_cv_c_decl_report=warning
-fi
-else
-  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot compile a simple declaration test
-See \`config.log' for more details" "$LINENO" 5; }
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
-  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "compiler does not report undeclared identifiers
-See \`config.log' for more details" "$LINENO" 5; }
-fi
-else
-  ac_cv_c_decl_report=error
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_decl_report" >&5
-$as_echo "$ac_cv_c_decl_report" >&6; }
-
-case $ac_cv_c_decl_report in
-  warning) ac_c_decl_warn_flag=yes ;;
-  *) ac_c_decl_warn_flag= ;;
-esac
-
 if test "$PORTNAME" != "solaris"; then :
 
 for ac_func in posix_fadvise
diff --git a/configure.ac b/configure.ac
index e44943aa6fe..2ae50ffdccc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1394,6 +1394,11 @@ if test "$with_ssl" = openssl ; then
   AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
   AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback])
+  # Check for ML-DSA support (OpenSSL 3.5+)
+  AC_CHECK_DECL([NID_ML_DSA_65],
+    [AC_DEFINE([HAVE_ML_DSA_SUPPORT], 1, [Define if OpenSSL supports ML-DSA])],
+    [],
+    [#include <openssl/obj_mac.h>])
   AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
 elif test "$with_ssl" != no ; then
   AC_MSG_ERROR([--with-ssl must specify openssl])
diff --git a/meson.build b/meson.build
index 395416a6060..d2730917b43 100644
--- a/meson.build
+++ b/meson.build
@@ -1575,6 +1575,15 @@ if sslopt in ['auto', 'openssl']
                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
       cdata.set('OPENSSL_API_COMPAT', '0x10101000L',
                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
+      
+      # Check for ML-DSA support (OpenSSL 3.5+)
+      if cc.has_header_symbol('openssl/obj_mac.h', 'NID_ML_DSA_65',
+                              args: test_c_args,
+                              dependencies: ssl_int)
+        cdata.set('HAVE_ML_DSA_SUPPORT', 1,
+                  description: 'Define if OpenSSL supports ML-DSA')
+      endif
+      
       ssl_library = 'openssl'
     else
       ssl = not_found_dep
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c4dc5d72bdb..a2e3412bf41 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -301,6 +301,9 @@
 /* Define to 1 if you have the `mkdtemp' function. */
 #undef HAVE_MKDTEMP
 
+/* Define if OpenSSL supports ML-DSA */
+#undef HAVE_ML_DSA_SUPPORT
+
 /* Define to 1 if you have the <ossp/uuid.h> header file. */
 #undef HAVE_OSSP_UUID_H
 
diff --git a/src/test/ssl/README b/src/test/ssl/README
index 2101a466d22..668db5cda2e 100644
--- a/src/test/ssl/README
+++ b/src/test/ssl/README
@@ -92,7 +92,8 @@ ssl/ subdirectory. The Makefile also contains a rule, "make sslfiles", to
 recreate them if you need to make changes. "make sslfiles-clean" is required
 in order to recreate the full set of keypairs and certificates. To rebuild
 separate files, touch (or remove) the files in question and run "make sslfiles".
-This step requires at least OpenSSL 1.1.1.
+This step requires at least OpenSSL 1.1.1. For generating the ML-DSA-65
+certificate, OpenSSL 3.5 or later is required.
 
 Note
 ====
diff --git a/src/test/ssl/conf/server-mldsa65.config b/src/test/ssl/conf/server-mldsa65.config
new file mode 100644
index 00000000000..acda640e58c
--- /dev/null
+++ b/src/test/ssl/conf/server-mldsa65.config
@@ -0,0 +1,15 @@
+# An OpenSSL format CSR config file for creating a server certificate.
+#
+# This is identical to server-cn-only certificate, but we specify
+# ML-DSA-65 as the algorithm on the command line.
+
+[ req ]
+distinguished_name     = req_distinguished_name
+prompt                 = no
+
+[ req_distinguished_name ]
+CN = common-name.pg-ssltest.test
+OU = PostgreSQL test suite
+
+# No Subject Alternative Names
+
diff --git a/src/test/ssl/ssl/server-mldsa65.crt b/src/test/ssl/ssl/server-mldsa65.crt
new file mode 100644
index 00000000000..929fa5d5251
--- /dev/null
+++ b/src/test/ssl/ssl/server-mldsa65.crt
@@ -0,0 +1,118 @@
+-----BEGIN CERTIFICATE-----
+MIIVtDCCCLGgAwIBAgIUIH4CjpeOAhzglphXw/lR8sSkzvswCwYJYIZIAWUDBAMS
+MEYxJDAiBgNVBAMMG2NvbW1vbi1uYW1lLnBnLXNzbHRlc3QudGVzdDEeMBwGA1UE
+CwwVUG9zdGdyZVNRTCB0ZXN0IHN1aXRlMB4XDTI1MTAyMjIwMDczMFoXDTI1MTEy
+MTIwMDczMFowRjEkMCIGA1UEAwwbY29tbW9uLW5hbWUucGctc3NsdGVzdC50ZXN0
+MR4wHAYDVQQLDBVQb3N0Z3JlU1FMIHRlc3Qgc3VpdGUwggeyMAsGCWCGSAFlAwQD
+EgOCB6EA/6siV1bdS9QVks5BoKdWSaoc5gs5Q59EHKsU7CpG10Y5sUuOAN4FOScv
+EBueVG85/GIThoK3QG2IBmtbpyKXso+fwypGDw559q35HgQKk6Uj0PaQdxQ7rUJc
+z5L1eBY5FbOf545RDaXa/04WnT5f9N5gLXvxdxrrqV757c25+UFc+pdHFAZB6KrL
+aAUmarQd+AwTlwHbJunsST6M5aPMx2l4xB1bucCW9jbDNH2WYjW5FsNYx358kk1f
+ikTVfJOhDTomqxOCyRAiVFkp+9cJfRmdWQgdYKGGwEwrB5oiRsDiLX3fEFszx0Jn
+Wq55AwFMBSb5ukL3t8SSDzLlytG8J05Enrju2eVT4fLqfJXLpJQ76Dlkw3BBMOZ3
+a0afk/BAieG23bZpYSYEZzcE7rksmCIccD4bcJZYwavjXHD3QmGp7mVnPNh/8fAb
+h9mL8hpNMmASCM/ig/t5FW47P/vDibZB4sgbYlCe/1Q2c6NBD63w2+RBo6JAGcOf
+Q8ZAlE1BqjbM7QIFoN8y9icSI3MaxJdiwpMnxrqjId0n2oLvBSxavYhIuWD2Khrn
+/gQjxG2t0rwMeiMWpeeK89w80BQPn03oUw7inMHbWJrkxYKKy0FPdYh+b5eeq+Zq
+ek6N27Qo07vpMuuiAnGqh1yjR6bQNHA/rvKBy6XRNRdUWtXmAKHlylL7iMpA9s5i
+29/9gN6UTpBjSVum8vgOckJp9I9viI7XhzeSE3MIlByzclIHQEzoVPX2mieJgMks
+ANtoN9N633XRSyS87an6TTLkFPkQZGkiRvn07SOjsTg7v4lx3OBBHIoMCF5GlQ8P
+jlHss5JAiaIyb8DQN2Ragft32UBpFo4cq1fU0lX6dZsToB85e5ymyKL/iYosKnRu
+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
+-----END CERTIFICATE-----
diff --git a/src/test/ssl/ssl/server-mldsa65.key b/src/test/ssl/ssl/server-mldsa65.key
new file mode 100644
index 00000000000..abb7b8023c6
--- /dev/null
+++ b/src/test/ssl/ssl/server-mldsa65.key
@@ -0,0 +1,88 @@
+-----BEGIN PRIVATE KEY-----
+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
+EXQoCki1MArL4VWcyBax3Nhw
+-----END PRIVATE KEY-----
diff --git a/src/test/ssl/sslfiles.mk b/src/test/ssl/sslfiles.mk
index 23aaad0c766..b7e07ba5e44 100644
--- a/src/test/ssl/sslfiles.mk
+++ b/src/test/ssl/sslfiles.mk
@@ -40,14 +40,16 @@ CLIENTS := client client-dn client-revoked client_ext client-long \
 # To add a new non-standard certificate, add it to SPECIAL_CERTS and then add
 # a recipe for creating it to the "Special-case certificates" section below.
 #
-SPECIAL_CERTS := ssl/server-rsapss.crt
+SPECIAL_CERTS := ssl/server-rsapss.crt \
+	ssl/server-mldsa65.crt
 
 # Likewise for non-standard keys
 SPECIAL_KEYS := ssl/server-password.key \
 	ssl/client-der.key \
 	ssl/client-encrypted-pem.key \
 	ssl/client-encrypted-der.key \
-	ssl/server-rsapss.key
+	ssl/server-rsapss.key \
+	ssl/server-mldsa65.key
 
 #
 # These files are just concatenations of other files. You can add new ones to
@@ -101,6 +103,10 @@ ssl/root_ca.crt: ssl/root_ca.key conf/root_ca.config
 ssl/server-rsapss.crt: ssl/server-rsapss.key conf/server-rsapss.config
 	$(OPENSSL) req -new -x509 -config conf/server-rsapss.config -key $< -out $@
 
+# Certificate using ML-DSA-65 algorithm. Also self-signed.
+ssl/server-mldsa65.crt: ssl/server-mldsa65.key conf/server-mldsa65.config
+	$(OPENSSL) req -new -x509 -config conf/server-mldsa65.config -key $< -out $@
+
 #
 # Special-case keys
 #
@@ -115,6 +121,10 @@ ssl/server-password.key: ssl/server-cn-only.key
 ssl/server-rsapss.key:
 	$(OPENSSL) genpkey -algorithm rsa-pss -out $@
 
+# Key that uses the ML-DSA-65 algorithm
+ssl/server-mldsa65.key:
+	$(OPENSSL) genpkey -algorithm ML-DSA-65 -out $@
+
 # DER-encoded version of client.key
 ssl/client-der.key: ssl/client.key
 	$(OPENSSL) rsa -in $< -outform DER -out $@
diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl
index 60b60b28657..a543f407bb7 100644
--- a/src/test/ssl/t/002_scram.pl
+++ b/src/test/ssl/t/002_scram.pl
@@ -171,4 +171,22 @@ if ($supports_rsapss_certs)
 			qr/connection authenticated: identity="ssltestuser" method=scram-sha-256/
 		]);
 }
+
+# Now test with a server certificate that uses the ML-DSA-65 algorithm.
+# This tests post-quantum cryptography support for channel binding.
+# Requires OpenSSL 3.5+.
+my $supports_mldsa_certs =
+  check_pg_config("#define HAVE_ML_DSA_SUPPORT 1");
+
+if ($supports_mldsa_certs)
+{
+	switch_server_cert($node, certfile => 'server-mldsa65');
+	$node->connect_ok(
+		"$common_connstr user=ssltestuser channel_binding=require",
+		"SCRAM with SSL and channel_binding=require, server certificate uses 'ML-DSA-65'",
+		log_like => [
+			qr/connection authenticated: identity="ssltestuser" method=scram-sha-256/
+		]);
+}
+
 done_testing();
-- 
2.39.5 (Apple Git-154)

