All of lore.kernel.org
 help / color / mirror / Atom feed
From: Scott Mayhew <smayhew@redhat.com>
To: chuck.lever@oracle.com
Cc: kernel-tls-handshake@lists.linux.dev
Subject: [PATCH 2/5] tlshd: Server-side dual certificate support
Date: Thu, 28 Aug 2025 18:23:45 -0400	[thread overview]
Message-ID: <20250828222348.601924-3-smayhew@redhat.com> (raw)
In-Reply-To: <20250828222348.601924-1-smayhew@redhat.com>

Add two new config options, "x509.pq.certificate" and
"x509.pq.private_key" to configure tlshd to use an ML-DSA certificate.
If the cert callback determines that the client supports ML-DSA, it will
select this certificate.  Otherwise, it will fall back to the
traditional certficate (i.e. the certificate configured via
"x509.certificate" and "x509.private_key").

Link: https://github.com/oracle/ktls-utils/issues/113
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
 configure.ac             | 12 ++++++++
 src/tlshd/config.c       | 12 +++++---
 src/tlshd/server.c       | 59 ++++++++++++++++++++++++++++++++++++++--
 src/tlshd/tlshd.conf     |  2 ++
 src/tlshd/tlshd.conf.man | 13 +++++++++
 src/tlshd/tlshd.h        |  6 ++--
 6 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/configure.ac b/configure.ac
index a6d9d09..0dd23f2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,18 @@ AC_CHECK_LIB([gnutls], [gnutls_get_system_config_file],
 AC_CHECK_LIB([gnutls], [gnutls_psk_allocate_client_credentials2],
              [AC_DEFINE([HAVE_GNUTLS_PSK_ALLOCATE_CREDENTIALS2], [1],
                         [Define to 1 if you have the gnutls_psk_allocate_client_credentials2 function.])])
+
+AC_MSG_CHECKING(for ML-DSA support in gnutls)
+AC_COMPILE_IFELSE(
+	[AC_LANG_PROGRAM([[ #include <gnutls/gnutls.h> ]],
+		[[ (void) GNUTLS_SIGN_MLDSA65; ]])],
+	[ have_mldsa=yes ],
+	[ have_mldsa=no ])
+AC_MSG_RESULT([$have_mldsa])
+if test "x$have_mldsa" = xyes ; then
+	AC_DEFINE([HAVE_GNUTLS_MLDSA], [1], [Define to 1 if gnutls supports ML-DSA])
+fi
+
 AC_SUBST([AM_CPPFLAGS])
 
 AC_CONFIG_FILES([Makefile src/Makefile src/tlshd/Makefile systemd/Makefile])
diff --git a/src/tlshd/config.c b/src/tlshd/config.c
index 4c54d37..20634dd 100644
--- a/src/tlshd/config.c
+++ b/src/tlshd/config.c
@@ -403,6 +403,7 @@ bool tlshd_config_get_server_crl(char **result)
 
 /**
  * tlshd_config_get_server_certs - Get certs for ServerHello from .conf
+ * @key: IN: the key field name from .conf
  * @certs: OUT: in-memory certificates
  * @certs_len: IN: maximum number of certs to get, OUT: number of certs found
  *
@@ -410,7 +411,8 @@ bool tlshd_config_get_server_crl(char **result)
  *   %true: certificate retrieved successfully
  *   %false: certificate not retrieved
  */
-bool tlshd_config_get_server_certs(gnutls_pcert_st *certs,
+bool tlshd_config_get_server_certs(const gchar *key,
+				   gnutls_pcert_st *certs,
 				   unsigned int *certs_len)
 {
 	gnutls_datum_t data;
@@ -418,7 +420,7 @@ bool tlshd_config_get_server_certs(gnutls_pcert_st *certs,
 	int ret;
 
 	pathname = g_key_file_get_string(tlshd_configuration, "authenticate.server",
-					"x509.certificate", NULL);
+					key, NULL);
 	if (!pathname)
 		return false;
 
@@ -446,20 +448,22 @@ bool tlshd_config_get_server_certs(gnutls_pcert_st *certs,
 
 /**
  * tlshd_config_get_server_privkey - Get private key for ServerHello from .conf
+ * @key: IN: the key field name from .conf
  * @privkey: OUT: in-memory private key
  *
  * Return values:
  *   %true: private key retrieved successfully
  *   %false: private key not retrieved
  */
-bool tlshd_config_get_server_privkey(gnutls_privkey_t *privkey)
+bool tlshd_config_get_server_privkey(const gchar *key,
+				     gnutls_privkey_t *privkey)
 {
 	gnutls_datum_t data;
 	gchar *pathname;
 	int ret;
 
 	pathname = g_key_file_get_string(tlshd_configuration, "authenticate.server",
-					"x509.private_key", NULL);
+					key, NULL);
 	if (!pathname)
 		return false;
 
diff --git a/src/tlshd/server.c b/src/tlshd/server.c
index 6b4535d..96b1b88 100644
--- a/src/tlshd/server.c
+++ b/src/tlshd/server.c
@@ -46,13 +46,25 @@ static gnutls_privkey_t tlshd_server_privkey;
 static unsigned int tlshd_server_certs_len = TLSHD_MAX_CERTS;
 static gnutls_pcert_st tlshd_server_certs[TLSHD_MAX_CERTS];
 
+#ifdef HAVE_GNUTLS_MLDSA
+static gnutls_privkey_t tlshd_server_pq_privkey;
+static unsigned int tlshd_server_pq_certs_len = TLSHD_MAX_CERTS;
+static gnutls_pcert_st tlshd_server_pq_certs[TLSHD_MAX_CERTS];
+#endif /* HAVE_GNUTLS_MLDSA */
+
 static bool tlshd_x509_server_get_certs(struct tlshd_handshake_parms *parms)
 {
 	if (parms->x509_cert != TLS_NO_CERT)
 		return tlshd_keyring_get_certs(parms->x509_cert,
 					       tlshd_server_certs,
 					       &tlshd_server_certs_len);
-	return tlshd_config_get_server_certs(tlshd_server_certs,
+#ifdef HAVE_GNUTLS_MLDSA
+	tlshd_config_get_server_certs("x509.pq.certificate",
+				      tlshd_server_pq_certs,
+				      &tlshd_server_pq_certs_len);
+#endif /* HAVE_GNUTLS_MLDSA */
+	return tlshd_config_get_server_certs("x509.certificate",
+					     tlshd_server_certs,
 					     &tlshd_server_certs_len);
 }
 
@@ -62,6 +74,11 @@ static void tlshd_x509_server_put_certs(void)
 
 	for (i = 0; i < tlshd_server_certs_len; i++)
 		gnutls_pcert_deinit(&tlshd_server_certs[i]);
+
+#ifdef HAVE_GNUTLS_MLDSA
+	for (i = 0; i < tlshd_server_pq_certs_len; i++)
+		gnutls_pcert_deinit(&tlshd_server_pq_certs[i]);
+#endif /* HAVE_GNUTLS_MLDSA */
 }
 
 static bool tlshd_x509_server_get_privkey(struct tlshd_handshake_parms *parms)
@@ -69,12 +86,18 @@ static bool tlshd_x509_server_get_privkey(struct tlshd_handshake_parms *parms)
 	if (parms->x509_privkey != TLS_NO_PRIVKEY)
 		return tlshd_keyring_get_privkey(parms->x509_privkey,
 						 &tlshd_server_privkey);
-	return tlshd_config_get_server_privkey(&tlshd_server_privkey);
+#ifdef HAVE_GNUTLS_MLDSA
+	tlshd_config_get_server_privkey("x509.pq.private_key", &tlshd_server_pq_privkey);
+#endif /* HAVE_GNUTLS_MLDSA */
+	return tlshd_config_get_server_privkey("x509.private_key", &tlshd_server_privkey);
 }
 
 static void tlshd_x509_server_put_privkey(void)
 {
 	gnutls_privkey_deinit(tlshd_server_privkey);
+#ifdef HAVE_GNUTLS_MLDSA
+	gnutls_privkey_deinit(tlshd_server_pq_privkey);
+#endif /* HAVE_GNUTLS_MLDSA */
 }
 
 static void tlshd_x509_log_issuers(const gnutls_datum_t *req_ca_rdn, int nreqs)
@@ -120,6 +143,11 @@ tlshd_x509_retrieve_key_cb(gnutls_session_t session,
 			   gnutls_privkey_t *privkey)
 {
 	gnutls_certificate_type_t type;
+#ifdef HAVE_GNUTLS_MLDSA
+	gnutls_sign_algorithm_t client_alg;
+	bool use_pq_cert = false;
+	int i, ret;
+#endif /* HAVE_GNUTLS_MLDSA */
 
 	tlshd_x509_log_issuers(req_ca_rdn, nreqs);
 
@@ -127,9 +155,36 @@ tlshd_x509_retrieve_key_cb(gnutls_session_t session,
 	if (type != GNUTLS_CRT_X509)
 		return -1;
 
+#ifdef HAVE_GNUTLS_MLDSA
+	for (i = 0; ; i++) {
+		ret = gnutls_sign_algorithm_get_requested(session, i, &client_alg);
+		if (ret != GNUTLS_E_SUCCESS)
+			break;
+		if (client_alg == GNUTLS_SIGN_MLDSA44
+				|| client_alg == GNUTLS_SIGN_MLDSA65
+				|| client_alg == GNUTLS_SIGN_MLDSA87) {
+			tlshd_log_debug("%s: Client supports ML-DSA", __func__);
+			use_pq_cert = true;
+			break;
+		}
+	}
+
+	if (use_pq_cert == true && tlshd_server_pq_certs_len > 0) {
+		tlshd_log_debug("%s: Selecting x509.pq.certificate from conf file", __func__);
+		*pcert_length = tlshd_server_pq_certs_len;
+		*pcert = tlshd_server_pq_certs;
+		*privkey = tlshd_server_pq_privkey;
+	} else {
+		tlshd_log_debug("%s: Selecting x509.certificate from conf file", __func__);
+		*pcert_length = tlshd_server_certs_len;
+		*pcert = tlshd_server_certs;
+		*privkey = tlshd_server_privkey;
+	}
+#else
 	*pcert_length = tlshd_server_certs_len;
 	*pcert = tlshd_server_certs;
 	*privkey = tlshd_server_privkey;
+#endif /* HAVE_GNUTLS_MLDSA */
 	return 0;
 }
 
diff --git a/src/tlshd/tlshd.conf b/src/tlshd/tlshd.conf
index 620bd17..5419146 100644
--- a/src/tlshd/tlshd.conf
+++ b/src/tlshd/tlshd.conf
@@ -39,3 +39,5 @@ nl=0
 #x509.crl= <pathname>
 #x509.certificate= <pathname>
 #x509.private_key= <pathname>
+#x509.pq.certificate= <pathname>
+#x509.pq.private_key= <pathname>
diff --git a/src/tlshd/tlshd.conf.man b/src/tlshd/tlshd.conf.man
index 914261e..ed545e4 100644
--- a/src/tlshd/tlshd.conf.man
+++ b/src/tlshd/tlshd.conf.man
@@ -125,6 +125,19 @@ a handshake request when no other certificate is available.
 .B x509.private_key
 This option specifies the pathname of a file containing
 a PEM-encoded private key associated with the above certificate.
+.TP
+.B x509.pq.certificate
+This option specifies the pathname of a file containing
+a PEM-encoded x.509 certificate that is to be presented during
+a handshake request if the peer supports post-quantum cryptography.
+If the peer does not support post-quantum cryptography, the
+certificate configured in the
+.I x509.certificate
+option will be presented instead.
+.TP
+.B x509.pq.private_key
+This option specifies the pathname of a file containing
+a PEM-encoded private key associated with the above certificate.
 .SH SEE ALSO
 .BR tlshd (8)
 .SH AUTHOR
diff --git a/src/tlshd/tlshd.h b/src/tlshd/tlshd.h
index a0dd47e..d9b68ed 100644
--- a/src/tlshd/tlshd.h
+++ b/src/tlshd/tlshd.h
@@ -59,9 +59,11 @@ bool tlshd_config_get_client_certs(gnutls_pcert_st *certs,
 bool tlshd_config_get_client_privkey(gnutls_privkey_t *privkey);
 bool tlshd_config_get_server_truststore(char **bundle);
 bool tlshd_config_get_server_crl(char **result);
-bool tlshd_config_get_server_certs(gnutls_pcert_st *certs,
+bool tlshd_config_get_server_certs(const gchar *key,
+				   gnutls_pcert_st *certs,
 				   unsigned int *certs_len);
-bool tlshd_config_get_server_privkey(gnutls_privkey_t *privkey);
+bool tlshd_config_get_server_privkey(const gchar *key,
+				     gnutls_privkey_t *privkey);
 
 /* handshake.c */
 extern void tlshd_start_tls_handshake(gnutls_session_t session,
-- 
2.50.1


  parent reply	other threads:[~2025-08-28 22:23 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-28 22:23 [PATCH 0/5] tlshd: Allow the use of post-quantum cryptography Scott Mayhew
2025-08-28 22:23 ` [PATCH 1/5] tlshd: Fix priority string to allow PQC Scott Mayhew
2025-08-29 16:47   ` Chuck Lever
2025-08-28 22:23 ` Scott Mayhew [this message]
2025-08-29 16:59   ` [PATCH 2/5] tlshd: Server-side dual certificate support Chuck Lever
2025-09-03 13:28     ` Scott Mayhew
2025-09-03 14:17       ` Chuck Lever
2025-09-03 15:03       ` Chuck Lever
2025-08-28 22:23 ` [PATCH 3/5] tlshd: Make sure x509.pq.certificate is using a PQ public-key alg Scott Mayhew
2025-08-28 22:23 ` [PATCH 4/5] tlshd: Make sure the client supports the PQ pk alg used by the server cert Scott Mayhew
2025-08-28 22:23 ` [PATCH 5/5] tlshd: Client-side dual certificate support Scott Mayhew

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250828222348.601924-3-smayhew@redhat.com \
    --to=smayhew@redhat.com \
    --cc=chuck.lever@oracle.com \
    --cc=kernel-tls-handshake@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.