public inbox for kernel-tls-handshake@lists.linux.dev
 help / color / mirror / Atom feed
* [PATCH v2] tlshd: Add runtime check for ML-DSA support in gnutls
@ 2026-04-15 18:13 Chuck Lever
  0 siblings, 0 replies; only message in thread
From: Chuck Lever @ 2026-04-15 18:13 UTC (permalink / raw)
  To: kernel-tls-handshake; +Cc: Chuck Lever

From: Chuck Lever <chuck.lever@oracle.com>

Fedora's TEST-PQ crypto-policy submodule adds ML-DSA to the
high-level sign list but does not add the corresponding entries
to the gnutls backend configuration.  Because the gnutls
backend operates in allowlist mode, ML-DSA signature algorithms
are absent from the session's priority list, and
cert_select_sign_algorithm rejects every cipher suite during
handshakes that present an ML-DSA certificate.

The symptom is that tlshd's server-side retrieve_key callback
selects the ML-DSA certificate (because the client advertises
ML-DSA in its ClientHello), gnutls then fails
cert_select_sign_algorithm for every cipher suite, and the
handshake fails with "No supported cipher suites have been
found. (-87)".  The RSA fallback never runs because gnutls
retries the callback for each cipher suite and gets the same
unusable ML-DSA certificate each time.

After the x.509 priority cache is built at startup, check
whether the ML-DSA sign algorithm corresponding to the
certificate's PK algorithm appears in
gnutls_priority_sign_list().  When absent,
tlshd_cert_check_pk_alg() returns false with a notice
directing the administrator to the crypto-policies
configuration, and the RSA certificate is used instead.

Administrators can resolve the issue by ensuring ML-DSA
signature algorithms and SHAKE-256 are present in
/etc/crypto-policies/back-ends/gnutls.config.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 src/tlshd/config.c | 48 +++++++++++++++++++++++++++++++---------------
 src/tlshd/ktls.c   | 18 +++++++++++++++++
 src/tlshd/tlshd.h  |  1 +
 3 files changed, 52 insertions(+), 15 deletions(-)

Changes since v1:
- ML-DSA sig algorithms aren't listed in the usual places

diff --git a/src/tlshd/config.c b/src/tlshd/config.c
index ce48a4d17fc2..efb8bb227c9e 100644
--- a/src/tlshd/config.c
+++ b/src/tlshd/config.c
@@ -379,11 +379,27 @@ bool tlshd_config_get_crl(int peer_type, char **result)
 }
 
 #ifdef HAVE_GNUTLS_MLDSA
+static gnutls_sign_algorithm_t tlshd_pk_to_sign(gnutls_pk_algorithm_t pk)
+{
+	switch (pk) {
+	case GNUTLS_PK_MLDSA44:
+		return GNUTLS_SIGN_MLDSA44;
+	case GNUTLS_PK_MLDSA65:
+		return GNUTLS_SIGN_MLDSA65;
+	case GNUTLS_PK_MLDSA87:
+		return GNUTLS_SIGN_MLDSA87;
+	default:
+		return GNUTLS_SIGN_UNKNOWN;
+	}
+}
+
 static bool tlshd_cert_check_pk_alg(gnutls_datum_t *data,
 				    gnutls_pk_algorithm_t *pkalg)
 {
+	gnutls_sign_algorithm_t sign_alg;
 	gnutls_x509_crt_t cert;
 	gnutls_pk_algorithm_t pk_alg;
+	bool result = false;
 	int ret;
 
 	ret = gnutls_x509_crt_init(&cert);
@@ -391,27 +407,29 @@ static bool tlshd_cert_check_pk_alg(gnutls_datum_t *data,
 		return false;
 
 	ret = gnutls_x509_crt_import(cert, data, GNUTLS_X509_FMT_PEM);
-	if (ret < 0) {
-		gnutls_x509_crt_deinit(cert);
-		return false;
-	}
+	if (ret < 0)
+		goto out;
 
 	pk_alg = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
 	tlshd_log_debug("%s: certificate pk algorithm %s", __func__,
 			gnutls_pk_algorithm_get_name(pk_alg));
-	switch (pk_alg) {
-	case GNUTLS_PK_MLDSA44:
-	case GNUTLS_PK_MLDSA65:
-	case GNUTLS_PK_MLDSA87:
-		*pkalg = pk_alg;
-		break;
-	default:
-		gnutls_x509_crt_deinit(cert);
-		return false;
-	}
 
+	sign_alg = tlshd_pk_to_sign(pk_alg);
+	if (sign_alg == GNUTLS_SIGN_UNKNOWN)
+		goto out;
+	if (!tlshd_gnutls_priority_have_sign(sign_alg)) {
+		tlshd_log_notice("%s: %s is not in the GnuTLS priority list; "
+				 "check crypto-policies configuration",
+				 __func__,
+				 gnutls_sign_get_name(sign_alg));
+		goto out;
+	}
+	*pkalg = pk_alg;
+	result = true;
+
+out:
 	gnutls_x509_crt_deinit(cert);
-	return true;
+	return result;
 }
 #else
 static bool tlshd_cert_check_pk_alg(__attribute__ ((unused)) gnutls_datum_t *data,
diff --git a/src/tlshd/ktls.c b/src/tlshd/ktls.c
index e3e0ec94e6e7..311847df2377 100644
--- a/src/tlshd/ktls.c
+++ b/src/tlshd/ktls.c
@@ -673,6 +673,24 @@ int tlshd_gnutls_priority_set(gnutls_session_t session,
 	return gnutls_priority_set(session, priority);
 }
 
+/**
+ * @brief Check whether a sign algorithm is in the x.509 priority list
+ * @param[in]     sign  GnuTLS sign algorithm constant
+ *
+ * @retval true if the algorithm is present; false otherwise
+ */
+bool tlshd_gnutls_priority_have_sign(unsigned int sign)
+{
+	const unsigned int *list;
+	int i, n;
+
+	n = gnutls_priority_sign_list(tlshd_gnutls_priority_x509, &list);
+	for (i = 0; i < n; i++)
+		if (list[i] == sign)
+			return true;
+	return false;
+}
+
 /**
  * @brief Free GnuTLS priority caches
  */
diff --git a/src/tlshd/tlshd.h b/src/tlshd/tlshd.h
index d00cfdfb9e59..02ccab299e1b 100644
--- a/src/tlshd/tlshd.h
+++ b/src/tlshd/tlshd.h
@@ -103,6 +103,7 @@ extern int tlshd_gnutls_priority_init(void);
 extern int tlshd_gnutls_priority_set(gnutls_session_t session,
 				     const struct tlshd_handshake_parms *parms,
 				     unsigned int psk_len);
+extern bool tlshd_gnutls_priority_have_sign(unsigned int sign);
 extern void tlshd_gnutls_priority_deinit(void);
 
 /* log.c */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-04-15 18:14 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-15 18:13 [PATCH v2] tlshd: Add runtime check for ML-DSA support in gnutls Chuck Lever

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox