--- /dev/null
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QEMU crypto TLS credential support
+ *
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/tlscredsbox.h"
+#include "qemu/atomic.h"
+
+
+static QCryptoTLSCredsBox *
+qcrypto_tls_creds_box_new_impl(int type, bool server)
+{
+ QCryptoTLSCredsBox *credsbox = g_new0(QCryptoTLSCredsBox, 1);
+ credsbox->ref = 1;
+ credsbox->server = server;
+ credsbox->type = type;
+ return credsbox;
+}
+
+
+QCryptoTLSCredsBox *
+qcrypto_tls_creds_box_new_server(int type)
+{
+ return qcrypto_tls_creds_box_new_impl(type, true);
+}
+
+
+QCryptoTLSCredsBox *
+qcrypto_tls_creds_box_new_client(int type)
+{
+ return qcrypto_tls_creds_box_new_impl(type, false);
+}
+
+static void qcrypto_tls_creds_box_free(QCryptoTLSCredsBox *credsbox)
+{
+ switch (credsbox->type) {
+ case GNUTLS_CRD_CERTIFICATE:
+ if (credsbox->data.cert) {
+ gnutls_certificate_free_credentials(credsbox->data.cert);
+ }
+ break;
+ case GNUTLS_CRD_PSK:
+ if (credsbox->server) {
+ if (credsbox->data.pskserver) {
+ gnutls_psk_free_server_credentials(credsbox->data.pskserver);
+ }
+ } else {
+ if (credsbox->data.pskclient) {
+ gnutls_psk_free_client_credentials(credsbox->data.pskclient);
+ }
+ }
+ break;
+ case GNUTLS_CRD_ANON:
+ if (credsbox->server) {
+ if (credsbox->data.anonserver) {
+ gnutls_anon_free_server_credentials(credsbox->data.anonserver);
+ }
+ } else {
+ if (credsbox->data.anonclient) {
+ gnutls_anon_free_client_credentials(credsbox->data.anonclient);
+ }
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (credsbox->dh_params) {
+ gnutls_dh_params_deinit(credsbox->dh_params);
+ }
+
+ g_free(credsbox);
+}
+
+
+void qcrypto_tls_creds_box_ref(QCryptoTLSCredsBox *credsbox)
+{
+ uint32_t ref = qatomic_fetch_inc(&credsbox->ref);
+ /* Assert waaay before the integer overflows */
+ g_assert(ref < INT_MAX);
+}
+
+
+void qcrypto_tls_creds_box_unref(QCryptoTLSCredsBox *credsbox)
+{
+ if (!credsbox) {
+ return;
+ }
+
+ g_assert(credsbox->ref > 0);
+
+ if (qatomic_fetch_dec(&credsbox->ref) == 1) {
+ qcrypto_tls_creds_box_free(credsbox);
+ }
+
+}
+
--- /dev/null
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QEMU crypto TLS credential support
+ *
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#ifndef QCRYPTO_TLSCREDS_BOX_H
+#define QCRYPTO_TLSCREDS_BOX_H
+
+#include "qom/object.h"
+
+#ifdef CONFIG_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
+typedef struct QCryptoTLSCredsBox QCryptoTLSCredsBox;
+
+struct QCryptoTLSCredsBox {
+ uint32_t ref;
+ bool server;
+ int type;
+ union {
+ void *any;
+#ifdef CONFIG_GNUTLS
+ /*
+ * All of these gnutls_XXXX_credentials_t types are
+ * pointers, hence matching the 'any' field above
+ */
+ gnutls_anon_server_credentials_t anonserver;
+ gnutls_anon_client_credentials_t anonclient;
+ gnutls_psk_server_credentials_t pskserver;
+ gnutls_psk_client_credentials_t pskclient;
+ gnutls_certificate_credentials_t cert;
+#endif
+ } data;
+#ifdef CONFIG_GNUTLS
+ gnutls_dh_params_t dh_params;
+#endif
+};
+
+QCryptoTLSCredsBox *qcrypto_tls_creds_box_new_server(int type);
+QCryptoTLSCredsBox *qcrypto_tls_creds_box_new_client(int type);
+void qcrypto_tls_creds_box_ref(QCryptoTLSCredsBox *credsbox);
+void qcrypto_tls_creds_box_unref(QCryptoTLSCredsBox *credsbox);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsBox, qcrypto_tls_creds_box_unref);
+
+#endif /* QCRYPTO_TLSCREDS_BOX_H */