From: Scott Mayhew <smayhew@redhat.com>
To: chuck.lever@oracle.com
Cc: kernel-tls-handshake@lists.linux.dev
Subject: [PATCH 5/5] tlshd: Client-side dual certificate support
Date: Thu, 28 Aug 2025 18:23:48 -0400 [thread overview]
Message-ID: <20250828222348.601924-6-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", this time to the "[authenticate.client]" stanza
of tlshd.conf. This is for client-side handling of the server's
certificate request when the client is mounting with "xprtsec=mtls".
This commit also makes sure the client-side x509.pq.certificate is using
a post-quantum public-key algorithm, and we make sure that the server
supports that algorithm before returning that cert in the cert callback
(unlike the server-side cert callback, the pk_algos list is populated,
so this check is more straightforward than on the server-side).
Link: https://github.com/oracle/ktls-utils/issues/113
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
src/tlshd/client.c | 66 +++++++++++++++++++++++++--
src/tlshd/config.c | 104 ++++++++++++++++++++++++++-----------------
src/tlshd/tlshd.conf | 2 +
src/tlshd/tlshd.h | 9 ++--
4 files changed, 132 insertions(+), 49 deletions(-)
diff --git a/src/tlshd/client.c b/src/tlshd/client.c
index 72cdd60..d74ea69 100644
--- a/src/tlshd/client.c
+++ b/src/tlshd/client.c
@@ -138,12 +138,24 @@ static gnutls_privkey_t tlshd_privkey;
static unsigned int tlshd_certs_len = TLSHD_MAX_CERTS;
static gnutls_pcert_st tlshd_certs[TLSHD_MAX_CERTS];
+#ifdef HAVE_GNUTLS_MLDSA
+static gnutls_privkey_t tlshd_pq_privkey;
+static unsigned int tlshd_pq_certs_len = TLSHD_MAX_CERTS;
+static gnutls_pcert_st tlshd_pq_certs[TLSHD_MAX_CERTS];
+static gnutls_pk_algorithm_t tlshd_pq_pkalg = GNUTLS_PK_UNKNOWN;
+#endif /* HAVE_GNUTLS_MLDSA */
+
static bool tlshd_x509_client_get_certs(struct tlshd_handshake_parms *parms)
{
if (parms->x509_cert != TLS_NO_CERT)
return tlshd_keyring_get_certs(parms->x509_cert, tlshd_certs,
&tlshd_certs_len);
- return tlshd_config_get_client_certs(tlshd_certs, &tlshd_certs_len);
+#ifdef HAVE_GNUTLS_MLDSA
+ tlshd_config_get_client_certs("x509.pq.certificate", tlshd_pq_certs,
+ &tlshd_pq_certs_len, &tlshd_pq_pkalg);
+#endif /* HAVE_GNUTLS_MLDSA */
+ return tlshd_config_get_client_certs("x509.certificate", tlshd_certs,
+ &tlshd_certs_len, NULL);
}
static void tlshd_x509_client_put_certs(void)
@@ -152,6 +164,11 @@ static void tlshd_x509_client_put_certs(void)
for (i = 0; i < tlshd_certs_len; i++)
gnutls_pcert_deinit(&tlshd_certs[i]);
+
+#ifdef HAVE_GNUTLS_MLDSA
+ for (i = 0; i < tlshd_pq_certs_len; i++)
+ gnutls_pcert_deinit(&tlshd_pq_certs[i]);
+#endif /* HAVE_GNUTLS_MLDSA */
}
static bool tlshd_x509_client_get_privkey(struct tlshd_handshake_parms *parms)
@@ -159,12 +176,18 @@ static bool tlshd_x509_client_get_privkey(struct tlshd_handshake_parms *parms)
if (parms->x509_privkey != TLS_NO_PRIVKEY)
return tlshd_keyring_get_privkey(parms->x509_privkey,
&tlshd_privkey);
- return tlshd_config_get_client_privkey(&tlshd_privkey);
+#ifdef HAVE_GNUTLS_MLDSA
+ tlshd_config_get_client_privkey("x509.pq.private_key", &tlshd_pq_privkey);
+#endif /* HAVE_GNUTLS_MLDSA */
+ return tlshd_config_get_client_privkey("x509.private_key", &tlshd_privkey);
}
static void tlshd_x509_client_put_privkey(void)
{
gnutls_privkey_deinit(tlshd_privkey);
+#ifdef HAVE_GNUTLS_MLDSA
+ gnutls_privkey_deinit(tlshd_pq_privkey);
+#endif /* HAVE_GNUTLS_MLDSA */
}
static void tlshd_x509_log_issuers(const gnutls_datum_t *req_ca_rdn, int nreqs)
@@ -203,13 +226,21 @@ static void tlshd_x509_log_issuers(const gnutls_datum_t *req_ca_rdn, int nreqs)
static int
tlshd_x509_retrieve_key_cb(gnutls_session_t session,
const gnutls_datum_t *req_ca_rdn, int nreqs,
- __attribute__ ((unused)) const gnutls_pk_algorithm_t *pk_algos,
- __attribute__ ((unused)) int pk_algos_length,
+ const gnutls_pk_algorithm_t *pk_algos,
+ int pk_algos_length,
gnutls_pcert_st **pcert,
unsigned int *pcert_length,
gnutls_privkey_t *privkey)
{
+#ifndef HAVE_GNUTLS_MLDSA
+ (void)pk_algos;
+ (void)pk_algos_length;
+#endif /* HAVE_GNUTLS_MLDSA */
gnutls_certificate_type_t type;
+#ifdef HAVE_GNUTLS_MLDSA
+ bool use_pq_cert = false;
+ int i;
+#endif /* HAVE_GNUTLS_MLDSA */
tlshd_x509_log_issuers(req_ca_rdn, nreqs);
@@ -217,9 +248,36 @@ tlshd_x509_retrieve_key_cb(gnutls_session_t session,
if (type != GNUTLS_CRT_X509)
return -1;
+#ifdef HAVE_GNUTLS_MLDSA
+ if (tlshd_pq_pkalg != GNUTLS_PK_UNKNOWN) {
+ for (i = 0; i < pk_algos_length; i++) {
+ if (pk_algos[i] == tlshd_pq_pkalg) {
+ use_pq_cert = true;
+ break;
+ }
+ }
+ if (use_pq_cert == true) {
+ tlshd_log_debug("%s: Server supports %s", __func__,
+ gnutls_pk_algorithm_get_name(pk_algos[i]));
+ }
+ }
+
+ if (use_pq_cert == true) {
+ tlshd_log_debug("%s: Selecting x509.pq.certificate from conf file", __func__);
+ *pcert_length = tlshd_pq_certs_len;
+ *pcert = tlshd_pq_certs;
+ *privkey = tlshd_pq_privkey;
+ } else {
+ tlshd_log_debug("%s: Selecting x509.certificate from conf file", __func__);
+ *pcert_length = tlshd_certs_len;
+ *pcert = tlshd_certs;
+ *privkey = tlshd_privkey;
+ }
+#else
*pcert_length = tlshd_certs_len;
*pcert = tlshd_certs;
*privkey = tlshd_privkey;
+#endif /* HAVE_GNUTLS_MLDSA */
return 0;
}
diff --git a/src/tlshd/config.c b/src/tlshd/config.c
index 735d24a..7af345b 100644
--- a/src/tlshd/config.c
+++ b/src/tlshd/config.c
@@ -248,24 +248,68 @@ bool tlshd_config_get_client_crl(char **result)
return true;
}
+#ifdef HAVE_GNUTLS_MLDSA
+static bool tlshd_cert_check_pk_alg(gnutls_datum_t *data,
+ gnutls_pk_algorithm_t *pkalg)
+{
+ gnutls_x509_crt_t cert;
+ gnutls_pk_algorithm_t pk_alg;
+ int ret;
+
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0)
+ return false;
+
+ ret = gnutls_x509_crt_import(cert, data, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(cert);
+ return false;
+ }
+
+ 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;
+ }
+
+ gnutls_x509_crt_deinit(cert);
+ return true;
+}
+#endif /* HAVE_GNUTLS_MLDSA */
+
/**
* tlshd_config_get_client_certs - Get certs for ClientHello 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
+ * @pkgalg: OUT: the PQ public-key alg that was used in the cert
*
* Return values:
* %true: certificate retrieved successfully
* %false: certificate not retrieved
*/
-bool tlshd_config_get_client_certs(gnutls_pcert_st *certs,
- unsigned int *certs_len)
+bool tlshd_config_get_client_certs(const gchar *key,
+ gnutls_pcert_st *certs,
+ unsigned int *certs_len,
+ gnutls_pk_algorithm_t *pkalg)
{
+#ifndef HAVE_GNUTLS_MLSDA
+ (void)pkalg;
+#endif /* HAVE_GNUTLS_MLDSA */
gnutls_datum_t data;
gchar *pathname;
int ret;
pathname = g_key_file_get_string(tlshd_configuration, "authenticate.client",
- "x509.certificate", NULL);
+ key, NULL);
if (!pathname)
return false;
@@ -275,6 +319,17 @@ bool tlshd_config_get_client_certs(gnutls_pcert_st *certs,
return false;
}
+#ifdef HAVE_GNUTLS_MLDSA
+ if (pkalg && !tlshd_cert_check_pk_alg(&data, pkalg)) {
+ tlshd_log_debug("%s: %s certificate not using a PQ public-key algorithm",
+ __func__, key);
+ free(data.data);
+ g_free(pathname);
+ *certs_len = 0;
+ return false;
+ }
+#endif /* HAVE_GNUTLS_MLDSA */
+
/* Config file supports only PEM-encoded certificates */
ret = gnutls_pcert_list_import_x509_raw(certs, certs_len, &data,
GNUTLS_X509_FMT_PEM, 0);
@@ -293,20 +348,22 @@ bool tlshd_config_get_client_certs(gnutls_pcert_st *certs,
/**
* tlshd_config_get_client_privkey - Get private key for ClientHello 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_client_privkey(gnutls_privkey_t *privkey)
+bool tlshd_config_get_client_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.client",
- "x509.private_key", NULL);
+ key, NULL);
if (!pathname)
return false;
@@ -401,43 +458,6 @@ bool tlshd_config_get_server_crl(char **result)
return true;
}
-#ifdef HAVE_GNUTLS_MLDSA
-static bool tlshd_cert_check_pk_alg(gnutls_datum_t *data,
- gnutls_pk_algorithm_t *pkalg)
-{
- gnutls_x509_crt_t cert;
- gnutls_pk_algorithm_t pk_alg;
- int ret;
-
- ret = gnutls_x509_crt_init(&cert);
- if (ret < 0)
- return false;
-
- ret = gnutls_x509_crt_import(cert, data, GNUTLS_X509_FMT_PEM);
- if (ret < 0) {
- gnutls_x509_crt_deinit(cert);
- return false;
- }
-
- 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;
- }
-
- gnutls_x509_crt_deinit(cert);
- return true;
-}
-#endif /* HAVE_GNUTLS_MLDSA */
-
/**
* tlshd_config_get_server_certs - Get certs for ServerHello from .conf
* @key: IN: the key field name from .conf
diff --git a/src/tlshd/tlshd.conf b/src/tlshd/tlshd.conf
index 5419146..1d4220e 100644
--- a/src/tlshd/tlshd.conf
+++ b/src/tlshd/tlshd.conf
@@ -33,6 +33,8 @@ nl=0
#x509.crl= <pathname>
#x509.certificate= <pathname>
#x509.private_key= <pathname>
+#x509.pq.certificate= <pathname>
+#x509.pq.private_key= <pathname>
[authenticate.server]
#x509.truststore= <pathname>
diff --git a/src/tlshd/tlshd.h b/src/tlshd/tlshd.h
index ca84d8f..a95ef70 100644
--- a/src/tlshd/tlshd.h
+++ b/src/tlshd/tlshd.h
@@ -54,9 +54,12 @@ bool tlshd_config_init(const gchar *pathname);
void tlshd_config_shutdown(void);
bool tlshd_config_get_client_truststore(char **bundle);
bool tlshd_config_get_client_crl(char **result);
-bool tlshd_config_get_client_certs(gnutls_pcert_st *certs,
- unsigned int *certs_len);
-bool tlshd_config_get_client_privkey(gnutls_privkey_t *privkey);
+bool tlshd_config_get_client_certs(const gchar *key,
+ gnutls_pcert_st *certs,
+ unsigned int *certs_len,
+ gnutls_pk_algorithm_t *pkalg);
+bool tlshd_config_get_client_privkey(const gchar *key,
+ 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(const gchar *key,
--
2.50.1
prev parent reply other threads:[~2025-08-28 22:24 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 ` [PATCH 2/5] tlshd: Server-side dual certificate support Scott Mayhew
2025-08-29 16:59 ` 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 ` Scott Mayhew [this message]
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-6-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.