Merge tag 'next-pr-pull-request' of https://gitlab.com/berrange/qemu into staging

Merge crypto and other misc fixes / features

 * Increase minimum gnutls to 3.7.5
 * Increase minimum libgcrypt to 1.9.4
 * Increase minimum nettle to 3.7.3
 * Drop obsolete in-tree XTS impl
 * Fix memory leak when loading certificates
 * Remove/reduce duplication when loading certifcates
 * Fix possible crash when certificates are unloaded
   while an active TLS connection is using when in a
   TLS handshake operation
 * Deprecate use of dh-params.pem file
 * Document how to create certificates with Post-Quantum
   Cryptography compliant algorithms.
 * Support loading multiple certificate identities to
   allow support for Post-Quantum crypto in parallel
   with traditional RSA/ECC
 * Add "-run-with exit-with-parent=on" parameter
 * Flush pending errors when seeing ENOBUFS with
   a zero-copy send attempt
 * Fix data buffer parameters in hash & IO channel APIs
   to use 'void *'

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmkIr/8ACgkQvobrtBUQ
# T9+2RhAAhEak/krdlTJw8OlJonUop7G5mlLU2TEoX0duRORcFhScsdSwb2pyc/wM
# tnwfWXsnsKFItJx1y3STkOICtdNqizGoU3+c7wl4anQBurydu+XTs4ESBtVJtMYr
# 1lTYvp0HFyKvaXwDWKE+ztltlJiog51tHPDLUIBCnyJysLVqxCHMHmkbG46IPBZo
# A2XXxp3j/VBPmhls0JHpbAD4iVE3PChdK7zhyeGe/rld9+0JA12EPCvZ5Uokdj41
# aYP/okvnVH1atucoygPdDE3P5GYBKaSXZUWqzfkKhU7FgaF2863Td7ff1ip+WyWN
# FFPNEU1hVg+T5hfsZVQmmIFDdSJWqoZaZM/WJVYdrRY4dKUCPnJ9OINbbnhuWz5E
# JFmZOPibRZKQ44XcHX49JRfJEBvoq1z9OT1r7HkEP4D9/O7V/riIunbAESMk0sgi
# 0/fatvdhNKMN6YBQM3mtN3yNOcfRSWFtSy9XS9zDjdpEKT7ui2t9FC0ZNSP0FRkS
# aTY31FyacjHwU3zaoh6NoqqpxV9wwHrgsJwNbA/IztjmX/jvGG0Gb/sXVEqM59tR
# e3VWTmlmZ1T8OLImh1hG4t+nY+XzI64QpVX8H9RCGm21o28DyTcOnTFK4OyIfWe5
# ttnNfEJN8WCVCsA8tcM8yAbZ/0qXrYfiZSO7hq79wE7LvyholAQ=
# =9ESG
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 03 Nov 2025 02:37:03 PM CET
# gpg:                using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF
# gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [unknown]
# gpg:                 aka "Daniel P. Berrange <berrange@redhat.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E  8E3F BE86 EBB4 1510 4FDF

* tag 'next-pr-pull-request' of https://gitlab.com/berrange/qemu: (32 commits)
  docs: creation of x509 certs compliant with post-quantum crypto
  crypto: support upto 5 parallel certificate identities
  crypto: expand logic to cope with multiple certificate identities
  crypto: avoid loading the identity certs twice
  crypto: avoid loading the CA certs twice
  crypto: deprecate use of external dh-params.pem file
  crypto: make TLS credentials structs private
  crypto: fix lifecycle handling of gnutls credentials objects
  crypto: introduce a wrapper around gnutls credentials
  crypto: introduce method for reloading TLS creds
  crypto: reduce duplication in handling TLS priority strings
  crypto: remove duplication loading x509 CA cert
  crypto: shorten the endpoint == server check in TLS creds
  crypto: move release of DH parameters into TLS creds parent
  crypto: remove needless indirection via parent_obj field
  crypto: use g_autofree when loading x509 credentials
  crypto: move check for TLS creds 'dir' property
  crypto: remove redundant access() checks before loading certs
  crypto: replace stat() with access() for credential checks
  crypto: add missing free of certs array
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson
2025-11-04 15:17:31 +01:00
42 changed files with 1216 additions and 1533 deletions
-8
View File
@@ -23,10 +23,6 @@
#include <gnutls/crypto.h>
#if GNUTLS_VERSION_NUMBER >= 0x030608
#define QEMU_GNUTLS_XTS
#endif
bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
QCryptoCipherMode mode)
{
@@ -44,7 +40,6 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
default:
return false;
}
#ifdef QEMU_GNUTLS_XTS
case QCRYPTO_CIPHER_MODE_XTS:
switch (alg) {
case QCRYPTO_CIPHER_ALGO_AES_128:
@@ -53,7 +48,6 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
default:
return false;
}
#endif
default:
return false;
}
@@ -241,7 +235,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
int err;
switch (mode) {
#ifdef QEMU_GNUTLS_XTS
case QCRYPTO_CIPHER_MODE_XTS:
switch (alg) {
case QCRYPTO_CIPHER_ALGO_AES_128:
@@ -254,7 +247,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
break;
}
break;
#endif
case QCRYPTO_CIPHER_MODE_ECB:
case QCRYPTO_CIPHER_MODE_CBC:
-44
View File
@@ -18,10 +18,6 @@
*
*/
#ifdef CONFIG_QEMU_PRIVATE_XTS
#include "crypto/xts.h"
#endif
#include <nettle/nettle-types.h>
#include <nettle/aes.h>
#include <nettle/des.h>
@@ -30,9 +26,7 @@
#include <nettle/serpent.h>
#include <nettle/twofish.h>
#include <nettle/ctr.h>
#ifndef CONFIG_QEMU_PRIVATE_XTS
#include <nettle/xts.h>
#endif
#ifdef CONFIG_CRYPTO_SM4
#include <nettle/sm4.h>
#endif
@@ -154,43 +148,6 @@ static const struct QCryptoCipherDriver NAME##_driver_ctr = { \
};
#ifdef CONFIG_QEMU_PRIVATE_XTS
#define DEFINE__XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \
static void NAME##_xts_wrape(const void *ctx, size_t length, \
uint8_t *dst, const uint8_t *src) \
{ \
ENCRYPT((const void *)ctx, length, dst, src); \
} \
static void NAME##_xts_wrapd(const void *ctx, size_t length, \
uint8_t *dst, const uint8_t *src) \
{ \
DECRYPT((const void *)ctx, length, dst, src); \
} \
static int NAME##_encrypt_xts(QCryptoCipher *cipher, const void *in, \
void *out, size_t len, Error **errp) \
{ \
TYPE *ctx = container_of(cipher, TYPE, base); \
if (!qcrypto_length_check(len, BLEN, errp)) { \
return -1; \
} \
xts_encrypt(&ctx->key, &ctx->key_xts, \
NAME##_xts_wrape, NAME##_xts_wrapd, \
ctx->iv, len, out, in); \
return 0; \
} \
static int NAME##_decrypt_xts(QCryptoCipher *cipher, const void *in, \
void *out, size_t len, Error **errp) \
{ \
TYPE *ctx = container_of(cipher, TYPE, base); \
if (!qcrypto_length_check(len, BLEN, errp)) { \
return -1; \
} \
xts_decrypt(&ctx->key, &ctx->key_xts, \
NAME##_xts_wrape, NAME##_xts_wrapd, \
ctx->iv, len, out, in); \
return 0; \
}
#else
#define DEFINE__XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \
static int NAME##_encrypt_xts(QCryptoCipher *cipher, const void *in, \
void *out, size_t len, Error **errp) \
@@ -214,7 +171,6 @@ static int NAME##_decrypt_xts(QCryptoCipher *cipher, const void *in, \
ctx->iv, len, out, in); \
return 0; \
}
#endif
#define DEFINE_XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \
QEMU_BUILD_BUG_ON(BLEN != XTS_BLOCK_SIZE); \
+1 -1
View File
@@ -142,7 +142,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgo alg,
#include "cipher-gcrypt.c.inc"
#elif defined CONFIG_NETTLE
#include "cipher-nettle.c.inc"
#elif defined CONFIG_GNUTLS_CRYPTO
#elif defined CONFIG_GNUTLS
#include "cipher-gnutls.c.inc"
#else
#include "cipher-stub.c.inc"
+8 -8
View File
@@ -67,13 +67,13 @@ int qcrypto_hash_bytesv(QCryptoHashAlgo alg,
int qcrypto_hash_bytes(QCryptoHashAlgo alg,
const char *buf,
const void *buf,
size_t len,
uint8_t **result,
size_t *resultlen,
Error **errp)
{
struct iovec iov = { .iov_base = (char *)buf,
struct iovec iov = { .iov_base = (void *)buf,
.iov_len = len };
return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp);
}
@@ -89,11 +89,11 @@ int qcrypto_hash_updatev(QCryptoHash *hash,
}
int qcrypto_hash_update(QCryptoHash *hash,
const char *buf,
const void *buf,
size_t len,
Error **errp)
{
struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };
struct iovec iov = { .iov_base = (void *)buf, .iov_len = len };
return qcrypto_hash_updatev(hash, &iov, 1, errp);
}
@@ -206,12 +206,12 @@ int qcrypto_hash_digestv(QCryptoHashAlgo alg,
}
int qcrypto_hash_digest(QCryptoHashAlgo alg,
const char *buf,
const void *buf,
size_t len,
char **digest,
Error **errp)
{
struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };
struct iovec iov = { .iov_base = (void *)buf, .iov_len = len };
return qcrypto_hash_digestv(alg, &iov, 1, digest, errp);
}
@@ -237,12 +237,12 @@ int qcrypto_hash_base64v(QCryptoHashAlgo alg,
}
int qcrypto_hash_base64(QCryptoHashAlgo alg,
const char *buf,
const void *buf,
size_t len,
char **base64,
Error **errp)
{
struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };
struct iovec iov = { .iov_base = (void *)buf, .iov_len = len };
return qcrypto_hash_base64v(alg, &iov, 1, base64, errp);
}
+4 -4
View File
@@ -28,14 +28,14 @@ int qcrypto_hmac_bytesv(QCryptoHmac *hmac,
}
int qcrypto_hmac_bytes(QCryptoHmac *hmac,
const char *buf,
const void *buf,
size_t len,
uint8_t **result,
size_t *resultlen,
Error **errp)
{
struct iovec iov = {
.iov_base = (char *)buf,
.iov_base = (void *)buf,
.iov_len = len
};
@@ -70,13 +70,13 @@ int qcrypto_hmac_digestv(QCryptoHmac *hmac,
}
int qcrypto_hmac_digest(QCryptoHmac *hmac,
const char *buf,
const void *buf,
size_t len,
char **digest,
Error **errp)
{
struct iovec iov = {
.iov_base = (char *)buf,
.iov_base = (void *)buf,
.iov_len = len
};
+5 -5
View File
@@ -25,7 +25,10 @@ crypto_ss.add(files(
))
if gnutls.found()
crypto_ss.add(files('x509-utils.c'))
crypto_ss.add(files(
'tlscredsbox.c',
'x509-utils.c',
))
endif
if nettle.found()
@@ -33,12 +36,9 @@ if nettle.found()
if hogweed.found()
crypto_ss.add(gmp, hogweed)
endif
if xts == 'private'
crypto_ss.add(files('xts.c'))
endif
elif gcrypt.found()
crypto_ss.add(gcrypt, files('hash-gcrypt.c', 'hmac-gcrypt.c', 'pbkdf-gcrypt.c'))
elif gnutls_crypto.found()
elif gnutls.found()
crypto_ss.add(gnutls, files('hash-gnutls.c', 'hmac-gnutls.c', 'pbkdf-gnutls.c'))
else
crypto_ss.add(files('hash-glib.c', 'hmac-glib.c', 'pbkdf-stub.c'))
+51 -28
View File
@@ -22,6 +22,7 @@
#include "qapi/error.h"
#include "qapi-types-crypto.h"
#include "qemu/module.h"
#include "qemu/error-report.h"
#include "tlscredspriv.h"
#include "trace.h"
@@ -38,22 +39,7 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : "<generated>");
if (filename == NULL) {
ret = gnutls_dh_params_init(dh_params);
if (ret < 0) {
error_setg(errp, "Unable to initialize DH parameters: %s",
gnutls_strerror(ret));
return -1;
}
ret = gnutls_dh_params_generate2(*dh_params, DH_BITS);
if (ret < 0) {
gnutls_dh_params_deinit(*dh_params);
*dh_params = NULL;
error_setg(errp, "Unable to generate DH parameters: %s",
gnutls_strerror(ret));
return -1;
}
} else {
if (filename != NULL) {
GError *gerr = NULL;
gchar *contents;
gsize len;
@@ -67,6 +53,10 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
g_error_free(gerr);
return -1;
}
warn_report_once("Use of an external DH parameters file '%s' is "
"deprecated and will be removed in a future release",
filename);
data.data = (unsigned char *)contents;
data.size = len;
ret = gnutls_dh_params_init(dh_params);
@@ -87,12 +77,22 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
filename, gnutls_strerror(ret));
return -1;
}
} else {
*dh_params = NULL;
}
return 0;
}
char *
qcrypto_tls_creds_build_path(QCryptoTLSCreds *creds,
const char *filename)
{
return g_strdup_printf("%s/%s", creds->dir, filename);
}
int
qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
const char *filename,
@@ -100,21 +100,11 @@ qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
char **cred,
Error **errp)
{
struct stat sb;
int ret = -1;
if (!creds->dir) {
if (required) {
error_setg(errp, "Missing 'dir' property value");
return -1;
} else {
return 0;
}
}
*cred = qcrypto_tls_creds_build_path(creds, filename);
*cred = g_strdup_printf("%s/%s", creds->dir, filename);
if (stat(*cred, &sb) < 0) {
if (access(*cred, R_OK) < 0) {
if (errno == ENOENT && !required) {
ret = 0;
} else {
@@ -256,6 +246,9 @@ qcrypto_tls_creds_finalize(Object *obj)
{
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
#ifdef CONFIG_GNUTLS
qcrypto_tls_creds_box_unref(creds->box);
#endif
g_free(creds->dir);
g_free(creds->priority);
}
@@ -272,6 +265,36 @@ bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds,
return true;
}
char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds)
{
QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_GET_CLASS(creds);
const char *priorityBase =
creds->priority ? creds->priority : CONFIG_TLS_PRIORITY;
if (tcc->prioritySuffix) {
return g_strdup_printf("%s:%s", priorityBase, tcc->prioritySuffix);
} else {
return g_strdup(priorityBase);
}
}
bool qcrypto_tls_creds_reload(QCryptoTLSCreds *creds,
Error **errp)
{
QCryptoTLSCredsClass *credscls = QCRYPTO_TLS_CREDS_GET_CLASS(creds);
if (credscls->reload) {
return credscls->reload(creds, errp);
}
error_setg(errp, "%s does not support reloading credentials",
object_get_typename(OBJECT(creds)));
return false;
}
static const TypeInfo qcrypto_tls_creds_info = {
.parent = TYPE_OBJECT,
.name = TYPE_QCRYPTO_TLS_CREDS,
+19 -45
View File
@@ -27,15 +27,19 @@
#include "trace.h"
struct QCryptoTLSCredsAnon {
QCryptoTLSCreds parent_obj;
};
#ifdef CONFIG_GNUTLS
#include <gnutls/gnutls.h>
static int
qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
Error **errp)
{
g_autoptr(QCryptoTLSCredsBox) box = NULL;
g_autofree char *dhparams = NULL;
int ret;
@@ -43,13 +47,16 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_ANON);
if (creds->parent_obj.dir &&
qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_DH_PARAMS,
false, &dhparams, errp) < 0) {
return -1;
}
ret = gnutls_anon_allocate_server_credentials(&creds->data.server);
ret = gnutls_anon_allocate_server_credentials(&box->data.anonserver);
if (ret < 0) {
error_setg(errp, "Cannot allocate credentials: %s",
gnutls_strerror(ret));
@@ -57,46 +64,28 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
}
if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
&creds->parent_obj.dh_params,
errp) < 0) {
&box->dh_params, errp) < 0) {
return -1;
}
gnutls_anon_set_server_dh_params(creds->data.server,
creds->parent_obj.dh_params);
if (box->dh_params) {
gnutls_anon_set_server_dh_params(box->data.anonserver,
box->dh_params);
}
} else {
ret = gnutls_anon_allocate_client_credentials(&creds->data.client);
ret = gnutls_anon_allocate_client_credentials(&box->data.anonclient);
if (ret < 0) {
error_setg(errp, "Cannot allocate credentials: %s",
gnutls_strerror(ret));
return -1;
}
}
creds->parent_obj.box = g_steal_pointer(&box);
return 0;
}
static void
qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds)
{
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
if (creds->data.client) {
gnutls_anon_free_client_credentials(creds->data.client);
creds->data.client = NULL;
}
} else {
if (creds->data.server) {
gnutls_anon_free_server_credentials(creds->data.server);
creds->data.server = NULL;
}
}
if (creds->parent_obj.dh_params) {
gnutls_dh_params_deinit(creds->parent_obj.dh_params);
creds->parent_obj.dh_params = NULL;
}
}
#else /* ! CONFIG_GNUTLS */
@@ -108,13 +97,6 @@ qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED,
}
static void
qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED)
{
/* nada */
}
#endif /* ! CONFIG_GNUTLS */
@@ -127,21 +109,14 @@ qcrypto_tls_creds_anon_complete(UserCreatable *uc, Error **errp)
}
static void
qcrypto_tls_creds_anon_finalize(Object *obj)
{
QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
qcrypto_tls_creds_anon_unload(creds);
}
static void
qcrypto_tls_creds_anon_class_init(ObjectClass *oc, const void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_CLASS(oc);
ucc->complete = qcrypto_tls_creds_anon_complete;
tcc->prioritySuffix = "+ANON-DH";
}
@@ -149,7 +124,6 @@ static const TypeInfo qcrypto_tls_creds_anon_info = {
.parent = TYPE_QCRYPTO_TLS_CREDS,
.name = TYPE_QCRYPTO_TLS_CREDS_ANON,
.instance_size = sizeof(QCryptoTLSCredsAnon),
.instance_finalize = qcrypto_tls_creds_anon_finalize,
.class_size = sizeof(QCryptoTLSCredsAnonClass),
.class_init = qcrypto_tls_creds_anon_class_init,
.interfaces = (const InterfaceInfo[]) {
+101
View File
@@ -0,0 +1,101 @@
/*
* 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);
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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 */
+5 -33
View File
@@ -22,6 +22,7 @@
#define QCRYPTO_TLSCREDSPRIV_H
#include "crypto/tlscreds.h"
#include "crypto/tlscredsbox.h"
#ifdef CONFIG_GNUTLS
#include <gnutls/gnutls.h>
@@ -31,45 +32,16 @@ struct QCryptoTLSCreds {
Object parent_obj;
char *dir;
QCryptoTLSCredsEndpoint endpoint;
#ifdef CONFIG_GNUTLS
gnutls_dh_params_t dh_params;
#endif
bool verifyPeer;
char *priority;
};
struct QCryptoTLSCredsAnon {
QCryptoTLSCreds parent_obj;
#ifdef CONFIG_GNUTLS
union {
gnutls_anon_server_credentials_t server;
gnutls_anon_client_credentials_t client;
} data;
#endif
};
struct QCryptoTLSCredsPSK {
QCryptoTLSCreds parent_obj;
char *username;
#ifdef CONFIG_GNUTLS
union {
gnutls_psk_server_credentials_t server;
gnutls_psk_client_credentials_t client;
} data;
#endif
};
struct QCryptoTLSCredsX509 {
QCryptoTLSCreds parent_obj;
#ifdef CONFIG_GNUTLS
gnutls_certificate_credentials_t data;
#endif
bool sanityCheck;
char *passwordid;
QCryptoTLSCredsBox *box;
};
#ifdef CONFIG_GNUTLS
char *qcrypto_tls_creds_build_path(QCryptoTLSCreds *creds,
const char *filename);
int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
const char *filename,
bool required,
+28 -36
View File
@@ -27,6 +27,11 @@
#include "trace.h"
struct QCryptoTLSCredsPSK {
QCryptoTLSCreds parent_obj;
char *username;
};
#ifdef CONFIG_GNUTLS
#include <gnutls/gnutls.h>
@@ -71,6 +76,7 @@ static int
qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
Error **errp)
{
g_autoptr(QCryptoTLSCredsBox) box = NULL;
g_autofree char *pskfile = NULL;
g_autofree char *dhparams = NULL;
const char *username;
@@ -81,7 +87,14 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
trace_qcrypto_tls_creds_psk_load(creds,
creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
if (!creds->parent_obj.dir) {
error_setg(errp, "Missing 'dir' property value");
goto cleanup;
}
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_PSK);
if (creds->username) {
error_setg(errp, "username should not be set when endpoint=server");
goto cleanup;
@@ -96,7 +109,7 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
goto cleanup;
}
ret = gnutls_psk_allocate_server_credentials(&creds->data.server);
ret = gnutls_psk_allocate_server_credentials(&box->data.pskserver);
if (ret < 0) {
error_setg(errp, "Cannot allocate credentials: %s",
gnutls_strerror(ret));
@@ -104,20 +117,25 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
}
if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
&creds->parent_obj.dh_params,
&box->dh_params,
errp) < 0) {
goto cleanup;
}
ret = gnutls_psk_set_server_credentials_file(creds->data.server, pskfile);
ret = gnutls_psk_set_server_credentials_file(box->data.pskserver,
pskfile);
if (ret < 0) {
error_setg(errp, "Cannot set PSK server credentials: %s",
gnutls_strerror(ret));
goto cleanup;
}
gnutls_psk_set_server_dh_params(creds->data.server,
creds->parent_obj.dh_params);
if (box->dh_params) {
gnutls_psk_set_server_dh_params(box->data.pskserver,
box->dh_params);
}
} else {
box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_PSK);
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_PSKFILE,
true, &pskfile, errp) < 0) {
@@ -133,14 +151,14 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
goto cleanup;
}
ret = gnutls_psk_allocate_client_credentials(&creds->data.client);
ret = gnutls_psk_allocate_client_credentials(&box->data.pskclient);
if (ret < 0) {
error_setg(errp, "Cannot allocate credentials: %s",
gnutls_strerror(ret));
goto cleanup;
}
ret = gnutls_psk_set_client_credentials(creds->data.client,
ret = gnutls_psk_set_client_credentials(box->data.pskclient,
username, &key, GNUTLS_PSK_KEY_HEX);
if (ret < 0) {
error_setg(errp, "Cannot set PSK client credentials: %s",
@@ -148,6 +166,7 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
goto cleanup;
}
}
creds->parent_obj.box = g_steal_pointer(&box);
rv = 0;
cleanup:
@@ -155,27 +174,6 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
return rv;
}
static void
qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds)
{
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
if (creds->data.client) {
gnutls_psk_free_client_credentials(creds->data.client);
creds->data.client = NULL;
}
} else {
if (creds->data.server) {
gnutls_psk_free_server_credentials(creds->data.server);
creds->data.server = NULL;
}
}
if (creds->parent_obj.dh_params) {
gnutls_dh_params_deinit(creds->parent_obj.dh_params);
creds->parent_obj.dh_params = NULL;
}
}
#else /* ! CONFIG_GNUTLS */
@@ -187,13 +185,6 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED,
}
static void
qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED)
{
/* nada */
}
#endif /* ! CONFIG_GNUTLS */
@@ -211,7 +202,6 @@ qcrypto_tls_creds_psk_finalize(Object *obj)
{
QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
qcrypto_tls_creds_psk_unload(creds);
g_free(creds->username);
}
@@ -239,8 +229,10 @@ static void
qcrypto_tls_creds_psk_class_init(ObjectClass *oc, const void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_CLASS(oc);
ucc->complete = qcrypto_tls_creds_psk_complete;
tcc->prioritySuffix = "+ECDHE-PSK:+DHE-PSK:+PSK";
object_class_property_add_str(oc, "username",
qcrypto_tls_creds_psk_prop_get_username,
+397 -206
View File
File diff suppressed because it is too large Load Diff
+36 -107
View File
@@ -38,6 +38,7 @@
struct QCryptoTLSSession {
QCryptoTLSCreds *creds;
QCryptoTLSCredsBox *credsbox;
gnutls_session_t handle;
char *hostname;
char *authzid;
@@ -78,6 +79,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session)
g_free(session->hostname);
g_free(session->peername);
g_free(session->authzid);
qcrypto_tls_creds_box_unref(session->credsbox);
object_unref(OBJECT(session->creds));
qemu_mutex_destroy(&session->lock);
g_free(session);
@@ -155,9 +157,6 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
}
}
#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH"
#define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK"
QCryptoTLSSession *
qcrypto_tls_session_new(QCryptoTLSCreds *creds,
const char *hostname,
@@ -167,6 +166,7 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
{
QCryptoTLSSession *session;
int ret;
g_autofree char *prio = NULL;
session = g_new0(QCryptoTLSSession, 1);
trace_qcrypto_tls_session_new(
@@ -200,113 +200,41 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
goto error;
}
if (object_dynamic_cast(OBJECT(creds),
TYPE_QCRYPTO_TLS_CREDS_ANON)) {
QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds);
char *prio;
if (creds->priority != NULL) {
prio = g_strdup_printf("%s:%s",
creds->priority,
TLS_PRIORITY_ADDITIONAL_ANON);
} else {
prio = g_strdup(CONFIG_TLS_PRIORITY ":"
TLS_PRIORITY_ADDITIONAL_ANON);
}
ret = gnutls_priority_set_direct(session->handle, prio, NULL);
if (ret < 0) {
error_setg(errp, "Unable to set TLS session priority %s: %s",
prio, gnutls_strerror(ret));
g_free(prio);
goto error;
}
g_free(prio);
if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
ret = gnutls_credentials_set(session->handle,
GNUTLS_CRD_ANON,
acreds->data.server);
} else {
ret = gnutls_credentials_set(session->handle,
GNUTLS_CRD_ANON,
acreds->data.client);
}
if (ret < 0) {
error_setg(errp, "Cannot set session credentials: %s",
gnutls_strerror(ret));
goto error;
}
} else if (object_dynamic_cast(OBJECT(creds),
TYPE_QCRYPTO_TLS_CREDS_PSK)) {
QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds);
char *prio;
if (creds->priority != NULL) {
prio = g_strdup_printf("%s:%s",
creds->priority,
TLS_PRIORITY_ADDITIONAL_PSK);
} else {
prio = g_strdup(CONFIG_TLS_PRIORITY ":"
TLS_PRIORITY_ADDITIONAL_PSK);
}
ret = gnutls_priority_set_direct(session->handle, prio, NULL);
if (ret < 0) {
error_setg(errp, "Unable to set TLS session priority %s: %s",
prio, gnutls_strerror(ret));
g_free(prio);
goto error;
}
g_free(prio);
if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
ret = gnutls_credentials_set(session->handle,
GNUTLS_CRD_PSK,
pcreds->data.server);
} else {
ret = gnutls_credentials_set(session->handle,
GNUTLS_CRD_PSK,
pcreds->data.client);
}
if (ret < 0) {
error_setg(errp, "Cannot set session credentials: %s",
gnutls_strerror(ret));
goto error;
}
} else if (object_dynamic_cast(OBJECT(creds),
TYPE_QCRYPTO_TLS_CREDS_X509)) {
QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds);
const char *prio = creds->priority;
if (!prio) {
prio = CONFIG_TLS_PRIORITY;
}
ret = gnutls_priority_set_direct(session->handle, prio, NULL);
if (ret < 0) {
error_setg(errp, "Cannot set default TLS session priority %s: %s",
prio, gnutls_strerror(ret));
goto error;
}
ret = gnutls_credentials_set(session->handle,
GNUTLS_CRD_CERTIFICATE,
tcreds->data);
if (ret < 0) {
error_setg(errp, "Cannot set session credentials: %s",
gnutls_strerror(ret));
goto error;
}
if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
/* This requests, but does not enforce a client cert.
* The cert checking code later does enforcement */
gnutls_certificate_server_set_request(session->handle,
GNUTLS_CERT_REQUEST);
}
} else {
error_setg(errp, "Unsupported TLS credentials type %s",
object_get_typename(OBJECT(creds)));
prio = qcrypto_tls_creds_get_priority(creds);
ret = gnutls_priority_set_direct(session->handle, prio, NULL);
if (ret < 0) {
error_setg(errp, "Unable to set TLS session priority %s: %s",
prio, gnutls_strerror(ret));
goto error;
}
ret = gnutls_credentials_set(session->handle,
creds->box->type,
creds->box->data.any);
if (ret < 0) {
error_setg(errp, "Cannot set session credentials: %s",
gnutls_strerror(ret));
goto error;
}
/*
* creds->box->data.any must be kept alive for as long
* as the gnutls_session_t is alive, so acquire a ref
*/
qcrypto_tls_creds_box_ref(creds->box);
session->credsbox = creds->box;
if (object_dynamic_cast(OBJECT(creds),
TYPE_QCRYPTO_TLS_CREDS_X509) &&
creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
/*
* This requests, but does not enforce a client cert.
* The cert checking code later does enforcement
*/
gnutls_certificate_server_set_request(session->handle,
GNUTLS_CERT_REQUEST);
}
gnutls_transport_set_ptr(session->handle, session);
gnutls_transport_set_push_function(session->handle,
qcrypto_tls_session_push);
@@ -417,6 +345,7 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
goto error;
}
session->peername = (char *)g_steal_pointer(&dname.data);
trace_qcrypto_tls_session_check_x509_dn(session, session->peername);
if (session->authzid) {
bool allow;
+1
View File
@@ -21,6 +21,7 @@ qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds
# tlssession.c
qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d"
qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s"
qcrypto_tls_session_check_x509_dn(void *session, const char *dname) "TLS session check x509 distinguished name session=%p dname=%s"
qcrypto_tls_session_parameters(void *session, int threadSafety, int protocol, int cipher) "TLS session parameters session=%p threadSafety=%d protocol=%d cipher=%d"
qcrypto_tls_session_bug1717_workaround(void *session) "TLS session bug1717 workaround session=%p"
-250
View File
@@ -1,250 +0,0 @@
/*
* QEMU Crypto XTS cipher mode
*
* Copyright (c) 2015-2016 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* This code is originally derived from public domain / WTFPL code in
* LibTomCrypt crytographic library http://libtom.org. The XTS code
* was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com)
* to the LibTom Projects
*
*/
#include "qemu/osdep.h"
#include "qemu/bswap.h"
#include "crypto/xts.h"
typedef union {
uint8_t b[XTS_BLOCK_SIZE];
uint64_t u[2];
} xts_uint128;
static inline void xts_uint128_xor(xts_uint128 *D,
const xts_uint128 *S1,
const xts_uint128 *S2)
{
D->u[0] = S1->u[0] ^ S2->u[0];
D->u[1] = S1->u[1] ^ S2->u[1];
}
static inline void xts_uint128_cpu_to_les(xts_uint128 *v)
{
cpu_to_le64s(&v->u[0]);
cpu_to_le64s(&v->u[1]);
}
static inline void xts_uint128_le_to_cpus(xts_uint128 *v)
{
le64_to_cpus(&v->u[0]);
le64_to_cpus(&v->u[1]);
}
static void xts_mult_x(xts_uint128 *I)
{
uint64_t tt;
xts_uint128_le_to_cpus(I);
tt = I->u[0] >> 63;
I->u[0] <<= 1;
if (I->u[1] >> 63) {
I->u[0] ^= 0x87;
}
I->u[1] <<= 1;
I->u[1] |= tt;
xts_uint128_cpu_to_les(I);
}
/**
* xts_tweak_encdec:
* @param ctxt: the cipher context
* @param func: the cipher function
* @src: buffer providing the input text of XTS_BLOCK_SIZE bytes
* @dst: buffer to output the output text of XTS_BLOCK_SIZE bytes
* @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
*
* Encrypt/decrypt data with a tweak
*/
static inline void xts_tweak_encdec(const void *ctx,
xts_cipher_func *func,
const xts_uint128 *src,
xts_uint128 *dst,
xts_uint128 *iv)
{
/* tweak encrypt block i */
xts_uint128_xor(dst, src, iv);
func(ctx, XTS_BLOCK_SIZE, dst->b, dst->b);
xts_uint128_xor(dst, dst, iv);
/* LFSR the tweak */
xts_mult_x(iv);
}
void xts_decrypt(const void *datactx,
const void *tweakctx,
xts_cipher_func *encfunc,
xts_cipher_func *decfunc,
uint8_t *iv,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
xts_uint128 PP, CC, T;
unsigned long i, m, mo, lim;
/* get number of blocks */
m = length >> 4;
mo = length & 15;
/* must have at least one full block */
g_assert(m != 0);
if (mo == 0) {
lim = m;
} else {
lim = m - 1;
}
/* encrypt the iv */
encfunc(tweakctx, XTS_BLOCK_SIZE, T.b, iv);
if (QEMU_PTR_IS_ALIGNED(src, sizeof(uint64_t)) &&
QEMU_PTR_IS_ALIGNED(dst, sizeof(uint64_t))) {
xts_uint128 *S = (xts_uint128 *)src;
xts_uint128 *D = (xts_uint128 *)dst;
for (i = 0; i < lim; i++, S++, D++) {
xts_tweak_encdec(datactx, decfunc, S, D, &T);
}
} else {
xts_uint128 D;
for (i = 0; i < lim; i++) {
memcpy(&D, src, XTS_BLOCK_SIZE);
xts_tweak_encdec(datactx, decfunc, &D, &D, &T);
memcpy(dst, &D, XTS_BLOCK_SIZE);
src += XTS_BLOCK_SIZE;
dst += XTS_BLOCK_SIZE;
}
}
/* if length is not a multiple of XTS_BLOCK_SIZE then */
if (mo > 0) {
xts_uint128 S, D;
memcpy(&CC, &T, XTS_BLOCK_SIZE);
xts_mult_x(&CC);
/* PP = tweak decrypt block m-1 */
memcpy(&S, src, XTS_BLOCK_SIZE);
xts_tweak_encdec(datactx, decfunc, &S, &PP, &CC);
/* Pm = first length % XTS_BLOCK_SIZE bytes of PP */
for (i = 0; i < mo; i++) {
CC.b[i] = src[XTS_BLOCK_SIZE + i];
dst[XTS_BLOCK_SIZE + i] = PP.b[i];
}
for (; i < XTS_BLOCK_SIZE; i++) {
CC.b[i] = PP.b[i];
}
/* Pm-1 = Tweak uncrypt CC */
xts_tweak_encdec(datactx, decfunc, &CC, &D, &T);
memcpy(dst, &D, XTS_BLOCK_SIZE);
}
/* Decrypt the iv back */
decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T.b);
}
void xts_encrypt(const void *datactx,
const void *tweakctx,
xts_cipher_func *encfunc,
xts_cipher_func *decfunc,
uint8_t *iv,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
xts_uint128 PP, CC, T;
unsigned long i, m, mo, lim;
/* get number of blocks */
m = length >> 4;
mo = length & 15;
/* must have at least one full block */
g_assert(m != 0);
if (mo == 0) {
lim = m;
} else {
lim = m - 1;
}
/* encrypt the iv */
encfunc(tweakctx, XTS_BLOCK_SIZE, T.b, iv);
if (QEMU_PTR_IS_ALIGNED(src, sizeof(uint64_t)) &&
QEMU_PTR_IS_ALIGNED(dst, sizeof(uint64_t))) {
xts_uint128 *S = (xts_uint128 *)src;
xts_uint128 *D = (xts_uint128 *)dst;
for (i = 0; i < lim; i++, S++, D++) {
xts_tweak_encdec(datactx, encfunc, S, D, &T);
}
} else {
xts_uint128 D;
for (i = 0; i < lim; i++) {
memcpy(&D, src, XTS_BLOCK_SIZE);
xts_tweak_encdec(datactx, encfunc, &D, &D, &T);
memcpy(dst, &D, XTS_BLOCK_SIZE);
dst += XTS_BLOCK_SIZE;
src += XTS_BLOCK_SIZE;
}
}
/* if length is not a multiple of XTS_BLOCK_SIZE then */
if (mo > 0) {
xts_uint128 S, D;
/* CC = tweak encrypt block m-1 */
memcpy(&S, src, XTS_BLOCK_SIZE);
xts_tweak_encdec(datactx, encfunc, &S, &CC, &T);
/* Cm = first length % XTS_BLOCK_SIZE bytes of CC */
for (i = 0; i < mo; i++) {
PP.b[i] = src[XTS_BLOCK_SIZE + i];
dst[XTS_BLOCK_SIZE + i] = CC.b[i];
}
for (; i < XTS_BLOCK_SIZE; i++) {
PP.b[i] = CC.b[i];
}
/* Cm-1 = Tweak encrypt PP */
xts_tweak_encdec(datactx, encfunc, &PP, &D, &T);
memcpy(dst, &D, XTS_BLOCK_SIZE);
}
/* Decrypt the iv back */
decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T.b);
}
+9
View File
@@ -385,6 +385,15 @@ Options are:
- move backing file to NVDIMM storage and keep ``pmem=on``
(to have NVDIMM with persistence guaranties).
Using an external DH (Diffie-Hellman) parameters file (since 10.2)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Loading of external Diffie-Hellman parameters from a 'dh-params.pem'
file is deprecated and will be removed with no replacement in a
future release. Where no 'dh-params.pem' file is provided, the DH
parameters will be automatically negotiated in accordance with
RFC7919.
Device options
--------------
+127 -7
View File
@@ -36,8 +36,58 @@ server and exposing it directly to remote browser clients. In such a
case it might be useful to use a commercial CA to avoid needing to
install custom CA certs in the web browsers.
The recommendation is for the server to keep its certificates in either
``/etc/pki/qemu`` or for unprivileged users in ``$HOME/.pki/qemu``.
.. _tls_cert_file_naming:
Certificate file naming
~~~~~~~~~~~~~~~~~~~~~~~
In a simple setup, where all QEMU instances on a machine share the
same TLS configuration, it is suggested that QEMU certificates be
kept in either ``/etc/pki/qemu`` or, for unprivileged users, in
``$HOME/.pki/qemu``. Where different QEMU subsystems require
different certificate configurations, sub-dirs of these locations
may be chosen.
The default file names that QEMU will traditionally load are:
* ``ca-cert.pem`` - mandatory; for both client and server configurations
* ``ca-crl.pem`` - optional; for server configurations only
* ``server-cert.pem`` - mandatory; for server configurations only
* ``server-key.pem`` - mandatory; for server configurations only
* ``client-cert.pem`` - optional; for client configurations only
* ``client-key.pem`` - optional; for client configurations only
* ``dh-params.pem`` - optional; for server configurations only
Since QEMU 10.2.0, there is support for loading upto four additional
identities:
* ``server-cert-[IDX].pem`` - optional; for server configurations only
* ``server-key-[IDX].pem`` - optional; for server configurations only
* ``client-cert-[IDX].pem`` - optional; for client configurations only
* ``client-key-[IDX].pem`` - optional; for client configurations only
where ``-[IDX]`` is one of the digits 0-3. Loading will terminate at
the first absent index. The index based certificate files may be used
as a replacement for, or in addition to, the traditional non-index
based certificate files. The traditional certificate files will be
loaded first, if present, then the index based certificates. Where
multiple certificates are compatible with a TLS session, the first
loaded certificate will preferred. IOW file naming can influence
which certificates are used for a session.
The use of multiple sets of certificates is intended to allow an
incremental transition to certificates using different crytographic
algorithms. This allows a newly deployed QEMU to introduce use of
stronger cryptographic algorithms that will be preferred when talking
to other newly deployed QEMU instances, while retaining compatbility
with certificates issued to a historically deployed QEMU. This is
notably useful to support live migration from an old QEMU deployed
on older operating system releases, which may support fewer crypto
algorithm choices than the current OS.
The certificate creation commands below will be illustrated using
the traditional naming scheme, but their args can be substituted
to use the indexed naming in the obvious manner.
.. _tls_005fgenerate_005fca:
@@ -251,11 +301,13 @@ When specifying the object, the ``dir`` parameters specifies which
directory contains the credential files. This directory is expected to
contain files with the names mentioned previously, ``ca-cert.pem``,
``server-key.pem``, ``server-cert.pem``, ``client-key.pem`` and
``client-cert.pem`` as appropriate. It is also possible to include a set
of pre-generated Diffie-Hellman (DH) parameters in a file
``dh-params.pem``, which can be created using the
``certtool --generate-dh-params`` command. If omitted, QEMU will
dynamically generate DH parameters when loading the credentials.
``client-cert.pem`` as appropriate.
While it is possible to include a set of pre-generated Diffie-Hellman
(DH) parameters in a file ``dh-params.pem``, this facility is now
deprecated and will be removed in a future release. When omitted the
DH parameters will be automatically negotiated in accordance with
RFC7919.
The ``endpoint`` parameter indicates whether the credentials will be
used for a network client or server, and determines which PEM files are
@@ -293,6 +345,74 @@ example with VNC:
.. _tls_005fpsk:
TLS certificates for Post-Quantum Cryptography
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given a new enough gnutls release, suitably integrated & configured with the
operating system crypto policies, QEMU is able to support post-quantum
crytography on TLS enabled services, either exclusively or in a hybrid mode.
In exclusive mode, only a single set of certificates need to be configured
for QEMU, with PQC compliant algorithms. Such a QEMU configuration will only
be able to interoperate with other services (including other QEMU's) that
also have PQC enabled. This can result in compatibility concerns during the
period of transition over to PQC compliant algorithms.
In hybrid mode, multiple sets of certificates need to be configured for QEMU,
at least one set with traditional (non-PQC compliant) algorithms, and at least
one other set with modern (PQC compliant) algorithms. At time of the TLS
handshake, the GNUTLS algorithm priorities should ensure that PQC compliant
algorithms are negotiated if both sides of the connection support PQC. If one
side lacks PQC, the TLS handshake should fallback to the non-PQC algorithms.
This can assist with interoperability during the transition to PQC, but has a
potential weakness wrt downgrade attacks forcing use of non-PQC algorithms.
Exclusive PQC mode should be preferred where both peers in the TLS connections
are known to support PQC.
Key generation parameters
^^^^^^^^^^^^^^^^^^^^^^^^^
To create certificates with PQC compliant algorithms, the ``--key-type``
argument must be passed to ``certtool`` when creating private keys. No
extra arguments are required for the other ``certtool`` commands, as
their behaviour will be determined by the private key type.
The typical PQC compliant algorithms to use are ``ML-DSA-44``, ``ML-DSA-65``
and ``ML-DSA-87``, with ``ML-DSA-65`` being a suitable default choice in
the absence of explicit requirements.
Taking the example earlier, for creating a key for a client certificate,
to use ``ML-DSA-65`` the command line would be modified to look like::
# certtool --generate-privkey --key-type=mldsa65 > client-hostNNN-key.pem
The equivalent modification applies to the creation of the private keys
used for server certs, or root/intermediate CA certs.
For hybrid mode, the additional indexed certificate naming must be used.
If multiple configured certificates are compatible with the mutually
supported crypto algorithms between the client and server, then the
first matching certificate will be used.
IOW, to ensure that PQC certificates are preferred, they must use a
non-index based filename, or use an index that is smaller than any
non-PQC certificates. ie, ``server-cert.pem`` for PQC and ``server-cert-0.pem``
for non-PQC, or ``server-cert-0.pem`` for PQC and ``server-cert-1.pem`` for
non-PQC.
Force disabling PQC via crypto priority
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the OS configuration for system crypto algorithm priorities has
enabled PQC, this can (optionally) be overriden in QEMU configuration
disable use of PQC using the ``priority`` parameter to the ``tls-creds-x509``
object::
NO_MLDSA="-SIGN-ML-DSA-65:-SIGN-ML-DSA-44:-SIGN-ML-DSA-87"
NO_MLKEM="-GROUP-X25519-MLKEM768:-GROUP-SECP256R1-MLKEM768:-GROUP-SECP384R1-MLKEM1024"
# qemu-nbd --object tls-creds-x509,id=tls0,endpoint=server,dir=....,priority=@SYSTEM:$NO_MLDSA:$NO_MLKEM
TLS Pre-Shared Keys (PSK)
~~~~~~~~~~~~~~~~~~~~~~~~~
+4 -4
View File
@@ -122,7 +122,7 @@ int qcrypto_hash_bytesv(QCryptoHashAlgo alg,
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_bytes(QCryptoHashAlgo alg,
const char *buf,
const void *buf,
size_t len,
uint8_t **result,
size_t *resultlen,
@@ -180,7 +180,7 @@ int qcrypto_hash_updatev(QCryptoHash *hash,
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_update(QCryptoHash *hash,
const char *buf,
const void *buf,
size_t len,
Error **errp);
@@ -289,7 +289,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoHash, qcrypto_hash_free)
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_digest(QCryptoHashAlgo alg,
const char *buf,
const void *buf,
size_t len,
char **digest,
Error **errp);
@@ -335,7 +335,7 @@ int qcrypto_hash_base64v(QCryptoHashAlgo alg,
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_base64(QCryptoHashAlgo alg,
const char *buf,
const void *buf,
size_t len,
char **base64,
Error **errp);
+2 -2
View File
@@ -139,7 +139,7 @@ int qcrypto_hmac_bytesv(QCryptoHmac *hmac,
* 0 on success, -1 on error
*/
int qcrypto_hmac_bytes(QCryptoHmac *hmac,
const char *buf,
const void *buf,
size_t len,
uint8_t **result,
size_t *resultlen,
@@ -187,7 +187,7 @@ int qcrypto_hmac_digestv(QCryptoHmac *hmac,
* Returns: 0 on success, -1 on error
*/
int qcrypto_hmac_digest(QCryptoHmac *hmac,
const char *buf,
const void *buf,
size_t len,
char **digest,
Error **errp);

Some files were not shown because too many files have changed in this diff Show More