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-----

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
commit e9c692eabb
42 changed files with 1216 additions and 1533 deletions

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:

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); \

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"

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);
}

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
};

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'))

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,

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
crypto/tlscredsbox.c Normal file
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
crypto/tlscredsbox.h Normal file
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 */

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,

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,

View file

@ -28,12 +28,81 @@
#include "trace.h"
struct QCryptoTLSCredsX509 {
QCryptoTLSCreds parent_obj;
bool sanityCheck;
char *passwordid;
};
#ifdef CONFIG_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
typedef struct QCryptoTLSCredsX509IdentFiles QCryptoTLSCredsX509IdentFiles;
struct QCryptoTLSCredsX509IdentFiles {
char *certpath;
char *keypath;
gnutls_x509_crt_t *certs;
unsigned int ncerts;
gnutls_x509_privkey_t key;
};
typedef struct QCryptoTLSCredsX509Files QCryptoTLSCredsX509Files;
struct QCryptoTLSCredsX509Files {
char *cacertpath;
gnutls_x509_crt_t *cacerts;
unsigned int ncacerts;
QCryptoTLSCredsX509IdentFiles **identities;
size_t nidentities;
};
static QCryptoTLSCredsX509Files *
qcrypto_tls_creds_x509_files_new(void)
{
return g_new0(QCryptoTLSCredsX509Files, 1);
}
static void
qcrypto_tls_creds_x509_ident_files_free(QCryptoTLSCredsX509IdentFiles *files)
{
size_t i;
for (i = 0; i < files->ncerts; i++) {
gnutls_x509_crt_deinit(files->certs[i]);
}
gnutls_x509_privkey_deinit(files->key);
g_free(files->certs);
g_free(files->certpath);
g_free(files->keypath);
g_free(files);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509IdentFiles,
qcrypto_tls_creds_x509_ident_files_free);
static void
qcrypto_tls_creds_x509_files_free(QCryptoTLSCredsX509Files *files)
{
size_t i;
for (i = 0; i < files->ncacerts; i++) {
gnutls_x509_crt_deinit(files->cacerts[i]);
}
g_free(files->cacerts);
g_free(files->cacertpath);
for (i = 0; i < files->nidentities; i++) {
qcrypto_tls_creds_x509_ident_files_free(files->identities[i]);
}
g_free(files->identities);
g_free(files);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSCredsX509Files,
qcrypto_tls_creds_x509_files_free);
static int
qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
const char *certFile,
@ -309,13 +378,10 @@ qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds,
static int
qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsX509Files *files,
gnutls_x509_crt_t *certs,
unsigned int ncerts,
gnutls_x509_crt_t *cacerts,
unsigned int ncacerts,
const char *cacertFile,
bool isServer,
bool isCA,
Error **errp)
{
gnutls_x509_crt_t cert_to_check = certs[ncerts - 1];
@ -355,13 +421,13 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
* reached the root of trust.
*/
return qcrypto_tls_creds_check_cert(
creds, cert_to_check, cacertFile,
isServer, isCA, errp);
creds, cert_to_check, files->cacertpath,
isServer, true, errp);
}
for (int i = 0; i < ncacerts; i++) {
for (int i = 0; i < files->ncacerts; i++) {
if (gnutls_x509_crt_check_issuer(cert_to_check,
cacerts[i])) {
cert_issuer = cacerts[i];
files->cacerts[i])) {
cert_issuer = files->cacerts[i];
break;
}
}
@ -369,8 +435,8 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
break;
}
if (qcrypto_tls_creds_check_cert(creds, cert_issuer, cacertFile,
isServer, isCA, errp) < 0) {
if (qcrypto_tls_creds_check_cert(creds, cert_issuer, files->cacertpath,
isServer, true, errp) < 0) {
return -1;
}
@ -389,19 +455,17 @@ qcrypto_tls_creds_check_authority_chain(QCryptoTLSCredsX509 *creds,
}
static int
qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t *certs,
qcrypto_tls_creds_check_cert_pair(QCryptoTLSCredsX509Files *files,
gnutls_x509_crt_t *certs,
size_t ncerts,
const char *certFile,
gnutls_x509_crt_t *cacerts,
size_t ncacerts,
const char *cacertFile,
bool isServer,
Error **errp)
{
unsigned int status;
if (gnutls_x509_crt_list_verify(certs, ncerts,
cacerts, ncacerts,
files->cacerts, files->ncacerts,
NULL, 0,
0, &status) < 0) {
error_setg(errp, isServer ?
@ -409,7 +473,7 @@ qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t *certs,
"CA certificate %s" :
"Unable to verify client certificate %s against "
"CA certificate %s",
certFile, cacertFile);
certFile, files->cacertpath);
return -1;
}
@ -434,7 +498,7 @@ qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t *certs,
error_setg(errp,
"Our own certificate %s failed validation against %s: %s",
certFile, cacertFile, reason);
certFile, files->cacertpath, reason);
return -1;
}
@ -447,14 +511,13 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509 *creds,
const char *certFile,
gnutls_x509_crt_t **certs,
unsigned int *ncerts,
bool isServer,
bool isCA,
Error **errp)
{
gnutls_datum_t data;
g_autofree char *buf = NULL;
gsize buflen;
GError *gerr = NULL;
int ret;
*ncerts = 0;
trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile);
@ -469,13 +532,94 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509 *creds,
data.data = (unsigned char *)buf;
data.size = strlen(buf);
if (gnutls_x509_crt_list_import2(certs, ncerts, &data,
GNUTLS_X509_FMT_PEM, 0) < 0) {
error_setg(errp,
isCA ? "Unable to import CA certificate list %s" :
(isServer ? "Unable to import server certificate %s" :
"Unable to import client certificate %s"),
certFile);
ret = gnutls_x509_crt_list_import2(certs, ncerts, &data,
GNUTLS_X509_FMT_PEM, 0);
if (ret < 0) {
error_setg(errp, "Unable to import certificate %s: %s",
certFile, gnutls_strerror(ret));
return -1;
}
return 0;
}
static int
qcrypto_tls_creds_load_privkey(QCryptoTLSCredsX509 *creds,
const char *keyFile,
gnutls_x509_privkey_t *key,
Error **errp)
{
gnutls_datum_t data;
g_autofree char *buf = NULL;
g_autofree char *password = NULL;
gsize buflen;
GError *gerr = NULL;
int ret;
ret = gnutls_x509_privkey_init(key);
if (ret < 0) {
error_setg(errp, "Unable to initialize private key: %s",
gnutls_strerror(ret));
return -1;
}
if (!g_file_get_contents(keyFile, &buf, &buflen, &gerr)) {
error_setg(errp, "Cannot load private key %s: %s",
keyFile, gerr->message);
g_error_free(gerr);
return -1;
}
data.data = (unsigned char *)buf;
data.size = strlen(buf);
if (creds->passwordid) {
password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
errp);
if (!password) {
return -1;
}
}
if (gnutls_x509_privkey_import2(*key, &data,
GNUTLS_X509_FMT_PEM,
password, 0) < 0) {
error_setg(errp, "Unable to import private key %s", keyFile);
return -1;
}
return 0;
}
static int
qcrypto_tls_creds_x509_sanity_check_identity(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsX509Files *files,
QCryptoTLSCredsX509IdentFiles *ifiles,
bool isServer,
Error **errp)
{
size_t i;
for (i = 0; i < ifiles->ncerts; i++) {
if (qcrypto_tls_creds_check_cert(creds,
ifiles->certs[i], ifiles->certpath,
isServer, i != 0, errp) < 0) {
return -1;
}
}
if (ifiles->ncerts &&
qcrypto_tls_creds_check_authority_chain(creds, files,
ifiles->certs, ifiles->ncerts,
isServer, errp) < 0) {
return -1;
}
if (ifiles->ncerts &&
qcrypto_tls_creds_check_cert_pair(files, ifiles->certs, ifiles->ncerts,
ifiles->certpath, isServer, errp) < 0) {
return -1;
}
@ -485,78 +629,191 @@ qcrypto_tls_creds_load_cert_list(QCryptoTLSCredsX509 *creds,
static int
qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsX509Files *files,
bool isServer,
const char *cacertFile,
const char *certFile,
Error **errp)
{
gnutls_x509_crt_t *certs = NULL;
unsigned int ncerts = 0;
gnutls_x509_crt_t *cacerts = NULL;
unsigned int ncacerts = 0;
size_t i;
int ret = -1;
if (certFile &&
access(certFile, R_OK) == 0) {
if (qcrypto_tls_creds_load_cert_list(creds,
certFile,
&certs,
&ncerts,
isServer,
false,
errp) < 0) {
goto cleanup;
for (i = 0; i < files->nidentities; i++) {
if (qcrypto_tls_creds_x509_sanity_check_identity(creds,
files,
files->identities[i],
isServer,
errp) < 0) {
return -1;
}
}
if (access(cacertFile, R_OK) == 0) {
if (qcrypto_tls_creds_load_cert_list(creds,
cacertFile,
&cacerts,
&ncacerts,
isServer,
true,
errp) < 0) {
goto cleanup;
return 0;
}
static int
qcrypto_tls_creds_x509_load_ca(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsBox *box,
QCryptoTLSCredsX509Files *files,
bool isServer,
Error **errp)
{
int ret;
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_X509_CA_CERT,
true, &files->cacertpath, errp) < 0) {
return -1;
}
if (qcrypto_tls_creds_load_cert_list(creds,
files->cacertpath,
&files->cacerts,
&files->ncacerts,
errp) < 0) {
return -1;
}
ret = gnutls_certificate_set_x509_trust(box->data.cert,
files->cacerts, files->ncacerts);
if (ret < 0) {
error_setg(errp, "Cannot set CA certificate '%s': %s",
files->cacertpath, gnutls_strerror(ret));
return -1;
}
return 0;
}
static QCryptoTLSCredsX509IdentFiles *
qcrypto_tls_creds_x509_load_identity(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsBox *box,
const char *certbase,
const char *keybase,
Error **errp)
{
g_autoptr(QCryptoTLSCredsX509IdentFiles) files =
g_new0(QCryptoTLSCredsX509IdentFiles, 1);
int ret;
if (qcrypto_tls_creds_get_path(&creds->parent_obj, certbase,
false, &files->certpath, errp) < 0 ||
qcrypto_tls_creds_get_path(&creds->parent_obj, keybase,
false, &files->keypath, errp) < 0) {
return NULL;
}
if (!files->certpath &&
!files->keypath) {
return NULL;
}
if (files->certpath && !files->keypath) {
g_autofree char *keypath =
qcrypto_tls_creds_build_path(&creds->parent_obj, keybase);
error_setg(errp, "Cert '%s' without corresponding key '%s'",
files->certpath, keypath);
return NULL;
}
if (!files->certpath && files->keypath) {
g_autofree char *certpath =
qcrypto_tls_creds_build_path(&creds->parent_obj, certbase);
error_setg(errp, "Key '%s' without corresponding cert '%s'",
files->keypath, certpath);
return NULL;
}
if (qcrypto_tls_creds_load_cert_list(creds,
files->certpath,
&files->certs,
&files->ncerts,
errp) < 0) {
return NULL;
}
if (qcrypto_tls_creds_load_privkey(creds,
files->keypath,
&files->key,
errp) < 0) {
return NULL;
}
ret = gnutls_certificate_set_x509_key(box->data.cert,
files->certs,
files->ncerts,
files->key);
if (ret < 0) {
error_setg(errp, "Cannot set certificate '%s' & key '%s': %s",
files->certpath, files->keypath, gnutls_strerror(ret));
return NULL;
}
return g_steal_pointer(&files);
}
static int
qcrypto_tls_creds_x509_load_identities(QCryptoTLSCredsX509 *creds,
QCryptoTLSCredsBox *box,
QCryptoTLSCredsX509Files *files,
bool isServer,
Error **errp)
{
ERRP_GUARD();
QCryptoTLSCredsX509IdentFiles *ifiles;
size_t i;
ifiles = qcrypto_tls_creds_x509_load_identity(
creds, box,
isServer ?
QCRYPTO_TLS_CREDS_X509_SERVER_CERT :
QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
isServer ?
QCRYPTO_TLS_CREDS_X509_SERVER_KEY :
QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
errp);
if (!ifiles && *errp) {
return -1;
}
if (ifiles) {
files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
files->identities,
files->nidentities + 1);
files->identities[files->nidentities++] = ifiles;
}
for (i = 0; i < QCRYPTO_TLS_CREDS_X509_IDENTITY_MAX; i++) {
g_autofree char *cert = g_strdup_printf(
isServer ?
QCRYPTO_TLS_CREDS_X509_SERVER_CERT_N :
QCRYPTO_TLS_CREDS_X509_CLIENT_CERT_N, i);
g_autofree char *key = g_strdup_printf(
isServer ?
QCRYPTO_TLS_CREDS_X509_SERVER_KEY_N :
QCRYPTO_TLS_CREDS_X509_CLIENT_KEY_N, i);
ifiles = qcrypto_tls_creds_x509_load_identity(creds, box,
cert, key, errp);
if (!ifiles && *errp) {
return -1;
}
}
for (i = 0; i < ncerts; i++) {
if (qcrypto_tls_creds_check_cert(creds,
certs[i], certFile,
isServer, i != 0, errp) < 0) {
goto cleanup;
if (!ifiles) {
break;
}
files->identities = g_renew(QCryptoTLSCredsX509IdentFiles *,
files->identities,
files->nidentities + 1);
files->identities[files->nidentities++] = ifiles;
}
if (ncerts &&
qcrypto_tls_creds_check_authority_chain(creds,
certs, ncerts,
cacerts, ncacerts,
cacertFile, isServer,
true, errp) < 0) {
goto cleanup;
if (files->nidentities == 0 && isServer) {
g_autofree char *certpath = qcrypto_tls_creds_build_path(
&creds->parent_obj, QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
g_autofree char *keypath = qcrypto_tls_creds_build_path(
&creds->parent_obj, QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
error_setg(errp, "Missing server cert '%s' & key '%s'",
certpath, keypath);
return -1;
}
if (ncerts && ncacerts &&
qcrypto_tls_creds_check_cert_pair(certs, ncerts, certFile,
cacerts, ncacerts, cacertFile,
isServer, errp) < 0) {
goto cleanup;
}
ret = 0;
cleanup:
for (i = 0; i < ncerts; i++) {
gnutls_x509_crt_deinit(certs[i]);
}
for (i = 0; i < ncacerts; i++) {
gnutls_x509_crt_deinit(cacerts[i]);
}
g_free(cacerts);
return ret;
return 0;
}
@ -564,134 +821,85 @@ static int
qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
Error **errp)
{
char *cacert = NULL, *cacrl = NULL, *cert = NULL,
*key = NULL, *dhparams = NULL;
g_autoptr(QCryptoTLSCredsBox) box = NULL;
g_autoptr(QCryptoTLSCredsX509Files) files = NULL;
g_autofree char *cacrl = NULL;
g_autofree char *dhparams = NULL;
bool isServer = (creds->parent_obj.endpoint ==
QCRYPTO_TLS_CREDS_ENDPOINT_SERVER);
int ret;
int rv = -1;
trace_qcrypto_tls_creds_x509_load(creds,
creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
if (!creds->parent_obj.dir) {
error_setg(errp, "Missing 'dir' property value");
return -1;
}
trace_qcrypto_tls_creds_x509_load(creds, creds->parent_obj.dir);
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_CERTIFICATE);
} else {
box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_CERTIFICATE);
}
ret = gnutls_certificate_allocate_credentials(&box->data.cert);
if (ret < 0) {
error_setg(errp, "Cannot allocate credentials: '%s'",
gnutls_strerror(ret));
return -1;
}
files = qcrypto_tls_creds_x509_files_new();
if (qcrypto_tls_creds_x509_load_ca(creds, box, files, isServer, errp) < 0) {
return -1;
}
if (qcrypto_tls_creds_x509_load_identities(creds, box, files,
isServer, errp) < 0) {
return -1;
}
if (isServer) {
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_X509_CA_CERT,
true, &cacert, errp) < 0 ||
qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_X509_CA_CRL,
false, &cacrl, errp) < 0 ||
qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
true, &cert, errp) < 0 ||
qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
true, &key, errp) < 0 ||
qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_DH_PARAMS,
false, &dhparams, errp) < 0) {
goto cleanup;
}
} else {
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_X509_CA_CERT,
true, &cacert, errp) < 0 ||
qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
false, &cert, errp) < 0 ||
qcrypto_tls_creds_get_path(&creds->parent_obj,
QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
false, &key, errp) < 0) {
goto cleanup;
return -1;
}
}
if (creds->sanityCheck &&
qcrypto_tls_creds_x509_sanity_check(creds,
creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
cacert, cert, errp) < 0) {
goto cleanup;
}
ret = gnutls_certificate_allocate_credentials(&creds->data);
if (ret < 0) {
error_setg(errp, "Cannot allocate credentials: '%s'",
gnutls_strerror(ret));
goto cleanup;
}
ret = gnutls_certificate_set_x509_trust_file(creds->data,
cacert,
GNUTLS_X509_FMT_PEM);
if (ret < 0) {
error_setg(errp, "Cannot load CA certificate '%s': %s",
cacert, gnutls_strerror(ret));
goto cleanup;
}
if (cert != NULL && key != NULL) {
char *password = NULL;
if (creds->passwordid) {
password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
errp);
if (!password) {
goto cleanup;
}
}
ret = gnutls_certificate_set_x509_key_file2(creds->data,
cert, key,
GNUTLS_X509_FMT_PEM,
password,
0);
g_free(password);
if (ret < 0) {
error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
cert, key, gnutls_strerror(ret));
goto cleanup;
}
qcrypto_tls_creds_x509_sanity_check(creds, files, isServer, errp) < 0) {
return -1;
}
if (cacrl != NULL) {
ret = gnutls_certificate_set_x509_crl_file(creds->data,
ret = gnutls_certificate_set_x509_crl_file(box->data.cert,
cacrl,
GNUTLS_X509_FMT_PEM);
if (ret < 0) {
error_setg(errp, "Cannot load CRL '%s': %s",
cacrl, gnutls_strerror(ret));
goto cleanup;
return -1;
}
}
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
if (isServer) {
if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
&creds->parent_obj.dh_params,
&box->dh_params,
errp) < 0) {
goto cleanup;
return -1;
}
if (box->dh_params) {
gnutls_certificate_set_dh_params(box->data.cert, box->dh_params);
}
gnutls_certificate_set_dh_params(creds->data,
creds->parent_obj.dh_params);
}
creds->parent_obj.box = g_steal_pointer(&box);
rv = 0;
cleanup:
g_free(cacert);
g_free(cacrl);
g_free(cert);
g_free(key);
g_free(dhparams);
return rv;
}
static void
qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds)
{
if (creds->data) {
gnutls_certificate_free_credentials(creds->data);
creds->data = NULL;
}
if (creds->parent_obj.dh_params) {
gnutls_dh_params_deinit(creds->parent_obj.dh_params);
creds->parent_obj.dh_params = NULL;
}
return 0;
}
@ -706,13 +914,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED,
}
static void
qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED)
{
/* nada */
}
#endif /* ! CONFIG_GNUTLS */
@ -775,26 +976,17 @@ qcrypto_tls_creds_x509_reload(QCryptoTLSCreds *creds, Error **errp)
{
QCryptoTLSCredsX509 *x509_creds = QCRYPTO_TLS_CREDS_X509(creds);
Error *local_err = NULL;
gnutls_certificate_credentials_t creds_data = x509_creds->data;
gnutls_dh_params_t creds_dh_params = x509_creds->parent_obj.dh_params;
QCryptoTLSCredsBox *creds_box = creds->box;
x509_creds->data = NULL;
x509_creds->parent_obj.dh_params = NULL;
creds->box = NULL;
qcrypto_tls_creds_x509_load(x509_creds, &local_err);
if (local_err) {
qcrypto_tls_creds_x509_unload(x509_creds);
x509_creds->data = creds_data;
x509_creds->parent_obj.dh_params = creds_dh_params;
creds->box = creds_box;
error_propagate(errp, local_err);
return false;
}
if (creds_data) {
gnutls_certificate_free_credentials(creds_data);
}
if (creds_dh_params) {
gnutls_dh_params_deinit(creds_dh_params);
}
qcrypto_tls_creds_box_unref(creds_box);
return true;
}
@ -827,7 +1019,6 @@ qcrypto_tls_creds_x509_finalize(Object *obj)
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
g_free(creds->passwordid);
qcrypto_tls_creds_x509_unload(creds);
}

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;

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"

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);
}

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
--------------

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)
~~~~~~~~~~~~~~~~~~~~~~~~~

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);

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);

View file

@ -47,6 +47,7 @@ typedef bool (*CryptoTLSCredsReload)(QCryptoTLSCreds *, Error **);
struct QCryptoTLSCredsClass {
ObjectClass parent_class;
CryptoTLSCredsReload reload;
const char *prioritySuffix;
};
/**
@ -64,4 +65,29 @@ bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds,
QCryptoTLSCredsEndpoint endpoint,
Error **errp);
/**
* qcrypto_tls_creds_get_priority:
* @creds: pointer to a TLS credentials object
*
* Get the TLS credentials priority string. The caller
* must free the returned string when no longer required.
*
* Returns: a non-NULL priority string
*/
char *qcrypto_tls_creds_get_priority(QCryptoTLSCreds *creds);
/**
* qcrypto_tls_creds_reload:
* @creds: pointer to a TLS credentials object
* @errp: pointer to a NULL-initialized error object
*
* Request a reload of the TLS credentials, if supported
*
* Returns: true on success, false on error or if not supported
*/
bool qcrypto_tls_creds_reload(QCryptoTLSCreds *creds,
Error **errp);
#endif /* QCRYPTO_TLSCREDS_H */

View file

@ -37,7 +37,13 @@ typedef struct QCryptoTLSCredsX509Class QCryptoTLSCredsX509Class;
#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT "server-cert.pem"
#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY "client-key.pem"
#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT "client-cert.pem"
#define QCRYPTO_TLS_CREDS_X509_SERVER_KEY_N "server-key-%zu.pem"
#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT_N "server-cert-%zu.pem"
#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY_N "client-key-%zu.pem"
#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT_N "client-cert-%zu.pem"
/* Max number of additional cert/key pairs (ie _N constants) */
#define QCRYPTO_TLS_CREDS_X509_IDENTITY_MAX 4
/**
* QCryptoTLSCredsX509:

View file

@ -199,11 +199,11 @@ int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess,
* These must return QCRYPTO_TLS_SESSION_ERR_BLOCK if the I/O
* would block, but on other errors, must fill 'errp'
*/
typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf,
typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const void *buf,
size_t len,
void *opaque,
Error **errp);
typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf,
typedef ssize_t (*QCryptoTLSSessionReadFunc)(void *buf,
size_t len,
void *opaque,
Error **errp);

View file

@ -1,82 +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
*
*/
#ifndef QCRYPTO_XTS_H
#define QCRYPTO_XTS_H
#define XTS_BLOCK_SIZE 16
typedef void xts_cipher_func(const void *ctx,
size_t length,
uint8_t *dst,
const uint8_t *src);
/**
* xts_decrypt:
* @datactx: the cipher context for data decryption
* @tweakctx: the cipher context for tweak decryption
* @encfunc: the cipher function for encryption
* @decfunc: the cipher function for decryption
* @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
* @length: the length of @dst and @src
* @dst: buffer to hold the decrypted plaintext
* @src: buffer providing the ciphertext
*
* Decrypts @src into @dst
*/
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_decrypt:
* @datactx: the cipher context for data encryption
* @tweakctx: the cipher context for tweak encryption
* @encfunc: the cipher function for encryption
* @decfunc: the cipher function for decryption
* @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
* @length: the length of @dst and @src
* @dst: buffer to hold the encrypted ciphertext
* @src: buffer providing the plaintext
*
* Decrypts @src into @dst
*/
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);
#endif /* QCRYPTO_XTS_H */

View file

@ -49,6 +49,12 @@ struct QIOChannelSocket {
socklen_t remoteAddrLen;
ssize_t zero_copy_queued;
ssize_t zero_copy_sent;
bool blocking;
/**
* This flag indicates whether any new data was successfully sent with
* zerocopy since the last qio_channel_socket_flush() call.
*/
bool new_zero_copy_sent_success;
};

View file

@ -437,7 +437,7 @@ ssize_t qio_channel_writev(QIOChannel *ioc,
* a single memory region.
*/
ssize_t qio_channel_read(QIOChannel *ioc,
char *buf,
void *buf,
size_t buflen,
Error **errp);
@ -453,7 +453,7 @@ ssize_t qio_channel_read(QIOChannel *ioc,
* single memory region.
*/
ssize_t qio_channel_write(QIOChannel *ioc,
const char *buf,
const void *buf,
size_t buflen,
Error **errp);
@ -475,7 +475,7 @@ ssize_t qio_channel_write(QIOChannel *ioc,
* without data, or -1 on error
*/
int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc,
char *buf,
void *buf,
size_t buflen,
Error **errp);
@ -495,7 +495,7 @@ int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc,
* Returns: 0 if all bytes were read, or -1 on error
*/
int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc,
char *buf,
void *buf,
size_t buflen,
Error **errp);
@ -514,7 +514,7 @@ int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc,
* Returns: 0 if all bytes were written, or -1 on error
*/
int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc,
const char *buf,
const void *buf,
size_t buflen,
Error **errp);
@ -595,7 +595,7 @@ ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov,
* flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method.
*
*/
ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen,
ssize_t qio_channel_pwrite(QIOChannel *ioc, void *buf, size_t buflen,
off_t offset, Error **errp);
/**
@ -631,7 +631,7 @@ ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov,
* flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method.
*
*/
ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen,
ssize_t qio_channel_pread(QIOChannel *ioc, void *buf, size_t buflen,
off_t offset, Error **errp);
/**

View file

@ -0,0 +1,57 @@
/*
* SPDX-License-Identifier: BSD-3-Clause
* Originally derived from nbdkit common/utils/exit-with-parent.h
* Copyright Red Hat
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Red Hat nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef NBDKIT_EXIT_WITH_PARENT_H
#define NBDKIT_EXIT_WITH_PARENT_H
/* Test if the feature is available on the platform. */
static inline bool can_exit_with_parent(void)
{
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
return true;
#else
return false;
#endif
}
/*
* --exit-with-parent: kill the current process if the parent exits.
* This may return -1 on error.
*
* Note this will abort on platforms where can_exit_with_parent()
* returned false.
*/
extern int set_exit_with_parent(void);
#endif /* NBDKIT_EXIT_WITH_PARENT_H */

View file

@ -37,6 +37,12 @@
#define SOCKET_MAX_FDS 16
#ifdef QEMU_MSG_ZEROCOPY
static int qio_channel_socket_flush_internal(QIOChannel *ioc,
bool block,
Error **errp);
#endif
SocketAddress *
qio_channel_socket_get_local_address(QIOChannelSocket *ioc,
Error **errp)
@ -65,6 +71,8 @@ qio_channel_socket_new(void)
sioc->fd = -1;
sioc->zero_copy_queued = 0;
sioc->zero_copy_sent = 0;
sioc->blocking = false;
sioc->new_zero_copy_sent_success = false;
ioc = QIO_CHANNEL(sioc);
qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
@ -617,6 +625,10 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc,
size_t fdsize = sizeof(int) * nfds;
struct cmsghdr *cmsg;
int sflags = 0;
#ifdef QEMU_MSG_ZEROCOPY
bool blocking = sioc->blocking;
bool zerocopy_flushed_once = false;
#endif
memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS));
@ -661,13 +673,30 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc,
return QIO_CHANNEL_ERR_BLOCK;
case EINTR:
goto retry;
#ifdef QEMU_MSG_ZEROCOPY
case ENOBUFS:
if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) {
error_setg_errno(errp, errno,
"Process can't lock enough memory for using MSG_ZEROCOPY");
return -1;
/**
* Socket error queueing may exhaust the OPTMEM limit. Try
* flushing the error queue once.
*/
if (!zerocopy_flushed_once) {
ret = qio_channel_socket_flush_internal(ioc, blocking,
errp);
if (ret < 0) {
return -1;
}
zerocopy_flushed_once = true;
goto retry;
} else {
error_setg_errno(errp, errno,
"Process can't lock enough memory for "
"using MSG_ZEROCOPY");
return -1;
}
}
break;
#endif
}
error_setg_errno(errp, errno,
@ -776,8 +805,9 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc,
#ifdef QEMU_MSG_ZEROCOPY
static int qio_channel_socket_flush(QIOChannel *ioc,
Error **errp)
static int qio_channel_socket_flush_internal(QIOChannel *ioc,
bool block,
Error **errp)
{
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
struct msghdr msg = {};
@ -785,7 +815,6 @@ static int qio_channel_socket_flush(QIOChannel *ioc,
struct cmsghdr *cm;
char control[CMSG_SPACE(sizeof(*serr))];
int received;
int ret;
if (sioc->zero_copy_queued == sioc->zero_copy_sent) {
return 0;
@ -795,16 +824,25 @@ static int qio_channel_socket_flush(QIOChannel *ioc,
msg.msg_controllen = sizeof(control);
memset(control, 0, sizeof(control));
ret = 1;
while (sioc->zero_copy_sent < sioc->zero_copy_queued) {
received = recvmsg(sioc->fd, &msg, MSG_ERRQUEUE);
if (received < 0) {
switch (errno) {
case EAGAIN:
/* Nothing on errqueue, wait until something is available */
qio_channel_wait(ioc, G_IO_ERR);
continue;
if (block) {
/*
* Nothing on errqueue, wait until something is
* available.
*
* Use G_IO_ERR instead of G_IO_IN since MSG_ERRQUEUE reads
* are signaled via POLLERR, not POLLIN, as the kernel
* sets POLLERR when zero-copy notificatons appear on the
* socket error queue.
*/
qio_channel_wait(ioc, G_IO_ERR);
continue;
}
return 0;
case EINTR:
continue;
default:
@ -842,13 +880,32 @@ static int qio_channel_socket_flush(QIOChannel *ioc,
/* No errors, count successfully finished sendmsg()*/
sioc->zero_copy_sent += serr->ee_data - serr->ee_info + 1;
/* If any sendmsg() succeeded using zero copy, return 0 at the end */
/* If any sendmsg() succeeded using zero copy, mark zerocopy success */
if (serr->ee_code != SO_EE_CODE_ZEROCOPY_COPIED) {
ret = 0;
sioc->new_zero_copy_sent_success = true;
}
}
return ret;
return 0;
}
static int qio_channel_socket_flush(QIOChannel *ioc,
Error **errp)
{
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
int ret;
ret = qio_channel_socket_flush_internal(ioc, true, errp);
if (ret < 0) {
return ret;
}
if (sioc->new_zero_copy_sent_success) {
sioc->new_zero_copy_sent_success = false;
return 0;
}
return 1;
}
#endif /* QEMU_MSG_ZEROCOPY */
@ -859,6 +916,7 @@ qio_channel_socket_set_blocking(QIOChannel *ioc,
Error **errp)
{
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
sioc->blocking = enabled;
if (!qemu_set_blocking(sioc->fd, enabled, errp)) {
return -1;

View file

@ -26,7 +26,7 @@
#include "qemu/atomic.h"
static ssize_t qio_channel_tls_write_handler(const char *buf,
static ssize_t qio_channel_tls_write_handler(const void *buf,
size_t len,
void *opaque,
Error **errp)
@ -43,7 +43,7 @@ static ssize_t qio_channel_tls_write_handler(const char *buf,
return ret;
}
static ssize_t qio_channel_tls_read_handler(char *buf,
static ssize_t qio_channel_tls_read_handler(void *buf,
size_t len,
void *opaque,
Error **errp)

View file

@ -310,7 +310,7 @@ ssize_t qio_channel_writev(QIOChannel *ioc,
ssize_t qio_channel_read(QIOChannel *ioc,
char *buf,
void *buf,
size_t buflen,
Error **errp)
{
@ -320,7 +320,7 @@ ssize_t qio_channel_read(QIOChannel *ioc,
ssize_t qio_channel_write(QIOChannel *ioc,
const char *buf,
const void *buf,
size_t buflen,
Error **errp)
{
@ -330,7 +330,7 @@ ssize_t qio_channel_write(QIOChannel *ioc,
int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc,
char *buf,
void *buf,
size_t buflen,
Error **errp)
{
@ -340,7 +340,7 @@ int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc,
int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc,
char *buf,
void *buf,
size_t buflen,
Error **errp)
{
@ -350,7 +350,7 @@ int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc,
int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc,
const char *buf,
const void *buf,
size_t buflen,
Error **errp)
{
@ -475,7 +475,7 @@ ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov,
return klass->io_pwritev(ioc, iov, niov, offset, errp);
}
ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen,
ssize_t qio_channel_pwrite(QIOChannel *ioc, void *buf, size_t buflen,
off_t offset, Error **errp)
{
struct iovec iov = {
@ -504,7 +504,7 @@ ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov,
return klass->io_preadv(ioc, iov, niov, offset, errp);
}
ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen,
ssize_t qio_channel_pread(QIOChannel *ioc, void *buf, size_t buflen,
off_t offset, Error **errp)
{
struct iovec iov = {

View file

@ -1823,33 +1823,11 @@ if not get_option('libcbor').auto() or have_system
endif
gnutls = not_found
gnutls_crypto = not_found
gnutls_bug1717_workaround = false
if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system)
# For general TLS support our min gnutls matches
# that implied by our platform support matrix
#
# For the crypto backends, we look for a newer
# gnutls:
#
# Version 3.6.8 is needed to get XTS
# Version 3.6.13 is needed to get PBKDF
# Version 3.6.14 is needed to get HW accelerated XTS
#
# If newer enough gnutls isn't available, we can
# still use a different crypto backend to satisfy
# the platform support requirements
gnutls_crypto = dependency('gnutls', version: '>=3.6.14',
method: 'pkg-config',
required: false)
if gnutls_crypto.found()
gnutls = gnutls_crypto
else
# Our min version if all we need is TLS
gnutls = dependency('gnutls', version: '>=3.5.18',
method: 'pkg-config',
required: get_option('gnutls'))
endif
gnutls = dependency('gnutls', version: '>=3.7.5',
method: 'pkg-config',
required: get_option('gnutls'))
#if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled()
# XXX: when bug 1717 is resolved, add logic to probe for
@ -1868,20 +1846,14 @@ nettle = not_found
hogweed = not_found
crypto_sm4 = not_found
crypto_sm3 = not_found
xts = 'none'
if get_option('nettle').enabled() and get_option('gcrypt').enabled()
error('Only one of gcrypt & nettle can be enabled')
endif
# Explicit nettle/gcrypt request, so ignore gnutls for crypto
if get_option('nettle').enabled() or get_option('gcrypt').enabled()
gnutls_crypto = not_found
endif
if not gnutls_crypto.found()
if not gnutls.found()
if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled()
gcrypt = dependency('libgcrypt', version: '>=1.8',
gcrypt = dependency('libgcrypt', version: '>=1.9.4',
required: get_option('gcrypt'))
# Debian has removed -lgpg-error from libgcrypt-config
# as it "spreads unnecessary dependencies" which in
@ -1893,35 +1865,12 @@ if not gnutls_crypto.found()
version: gcrypt.version())
endif
crypto_sm4 = gcrypt
# SM4 ALG is available in libgcrypt >= 1.9
if gcrypt.found() and not cc.links('''
#include <gcrypt.h>
int main(void) {
gcry_cipher_hd_t handler;
gcry_cipher_open(&handler, GCRY_CIPHER_SM4, GCRY_CIPHER_MODE_ECB, 0);
return 0;
}''', dependencies: gcrypt)
crypto_sm4 = not_found
endif
crypto_sm3 = gcrypt
# SM3 ALG is available in libgcrypt >= 1.9
if gcrypt.found() and not cc.links('''
#include <gcrypt.h>
int main(void) {
gcry_md_hd_t handler;
gcry_md_open(&handler, GCRY_MD_SM3, 0);
return 0;
}''', dependencies: gcrypt)
crypto_sm3 = not_found
endif
endif
if (not get_option('nettle').auto() or have_system) and not gcrypt.found()
nettle = dependency('nettle', version: '>=3.4',
nettle = dependency('nettle', version: '>=3.7.3',
method: 'pkg-config',
required: get_option('nettle'))
if nettle.found() and not cc.has_header('nettle/xts.h', dependencies: nettle)
xts = 'private'
endif
crypto_sm4 = nettle
# SM4 ALG is available in nettle >= 3.9
if nettle.found() and not cc.links('''
@ -2606,7 +2555,6 @@ config_host_data.set('CONFIG_XKBCOMMON', xkbcommon.found())
config_host_data.set('CONFIG_KEYUTILS', keyutils.found())
config_host_data.set('CONFIG_GETTID', has_gettid)
config_host_data.set('CONFIG_GNUTLS', gnutls.found())
config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found())
config_host_data.set('CONFIG_GNUTLS_BUG1717_WORKAROUND', gnutls_bug1717_workaround)
config_host_data.set('CONFIG_TASN1', tasn1.found())
config_host_data.set('CONFIG_GCRYPT', gcrypt.found())
@ -2614,7 +2562,6 @@ config_host_data.set('CONFIG_NETTLE', nettle.found())
config_host_data.set('CONFIG_CRYPTO_SM4', crypto_sm4.found())
config_host_data.set('CONFIG_CRYPTO_SM3', crypto_sm3.found())
config_host_data.set('CONFIG_HOGWEED', hogweed.found())
config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private')
config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
config_host_data.set('CONFIG_ZSTD', zstd.found())
config_host_data.set('CONFIG_QPL', qpl.found())
@ -4906,14 +4853,10 @@ summary_info = {}
summary_info += {'TLS priority': get_option('tls_priority')}
summary_info += {'GNUTLS support': gnutls}
if gnutls.found()
summary_info += {' GNUTLS crypto': gnutls_crypto.found()}
summary_info += {' GNUTLS bug 1717 workaround': gnutls_bug1717_workaround }
endif
summary_info += {'libgcrypt': gcrypt}
summary_info += {'nettle': nettle}
if nettle.found()
summary_info += {' XTS': xts != 'private'}
endif
summary_info += {'SM4 ALG support': crypto_sm4}
summary_info += {'SM3 ALG support': crypto_sm3}
summary_info += {'AF_ALG support': have_afalg}

View file

@ -5467,15 +5467,18 @@ DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL)
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
DEF("run-with", HAS_ARG, QEMU_OPTION_run_with,
"-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]\n"
"-run-with [async-teardown=on|off][,chroot=dir]\n" \
" [,exit-with-parent=on|off][,user=username|uid:gid]\n"
" Set miscellaneous QEMU process lifecycle options:\n"
" async-teardown=on enables asynchronous teardown (Linux only)\n"
" exit-with-parent=on causes QEMU to exit if the parent\n"
" process of QEMU exits (Linux, FreeBSD, macOS only)\n"
" chroot=dir chroot to dir just before starting the VM\n"
" user=username switch to the specified user before starting the VM\n"
" user=uid:gid ditto, but use specified user-ID and group-ID instead\n",
QEMU_ARCH_ALL)
SRST
``-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]``
``-run-with [async-teardown=on|off][,chroot=dir][,exit-with-parent=on|off][,user=username|uid:gid]``
Set QEMU process lifecycle options.
``async-teardown=on`` enables asynchronous teardown. A new process called
@ -5493,6 +5496,12 @@ SRST
immediately before starting the guest execution. This is especially useful
in combination with ``user=...``.
``exit-with-parent=on`` causes QEMU to exit if the parent process of
QEMU exits. This can be used when QEMU runs a captive appliance,
where the lifetime of the appliance is scoped to the parent process.
In case the parent process crashes, QEMU is still cleaned up.
This only works on Linux, FreeBSD and macOS platforms.
``user=username`` or ``user=uid:gid`` can be used to drop root privileges
before starting guest execution. QEMU will use the ``setuid`` and ``setgid``
system calls to switch to the specified identity. Note that the

140
system/exit-with-parent.c Normal file
View file

@ -0,0 +1,140 @@
/*
* SPDX-License-Identifier: BSD-3-Clause
* Originally derived from nbdkit common/utils/exit-with-parent.c
* Copyright Red Hat
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Red Hat nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Implement the --exit-with-parent feature on operating systems which
* support it.
*/
#include "qemu/osdep.h"
#include "qemu/exit-with-parent.h"
#if defined(__linux__)
#include <sys/prctl.h>
/*
* Send SIGTERM to self when the parent exits. This will cause
* qemu_system_killed() to be called.
*
* PR_SET_PDEATHSIG has been defined since Linux 2.1.57.
*/
int
set_exit_with_parent(void)
{
return prctl(PR_SET_PDEATHSIG, SIGTERM);
}
#elif defined(__FreeBSD__)
#include <sys/procctl.h>
/*
* Send SIGTERM to self when the parent exits. This will cause
* qemu_system_killed() to be called.
*
* PROC_PDEATHSIG_CTL has been defined since FreeBSD 11.2.
*/
int
set_exit_with_parent(void)
{
const int sig = SIGTERM;
return procctl(P_PID, 0, PROC_PDEATHSIG_CTL, (void *) &sig);
}
#elif defined(__APPLE__)
/* For macOS. */
#include "qemu/thread.h"
#include "qemu/error-report.h"
#include "system/runstate.h"
#include <sys/event.h>
static void *
exit_with_parent_loop(void *vp)
{
const pid_t ppid = getppid();
int fd;
struct kevent kev, res[1];
int r;
/* Register the kevent to wait for ppid to exit. */
fd = kqueue();
if (fd == -1) {
error_report("exit_with_parent_loop: kqueue: %m");
return NULL;
}
EV_SET(&kev, ppid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_EXIT, 0, NULL);
if (kevent(fd, &kev, 1, NULL, 0, NULL) == -1) {
error_report("exit_with_parent_loop: kevent: %m");
close(fd);
return NULL;
}
/* Wait for the kevent to happen. */
r = kevent(fd, 0, 0, res, 1, NULL);
if (r == 1 && res[0].ident == ppid) {
/* Behave like Linux and FreeBSD above, as if SIGTERM was sent */
qemu_system_killed(SIGTERM, ppid);
}
return NULL;
}
int
set_exit_with_parent(void)
{
QemuThread exit_with_parent_thread;
/*
* We have to block waiting for kevent, so that requires that we
* start a background thread.
*/
qemu_thread_create(&exit_with_parent_thread,
"exit-parent",
exit_with_parent_loop, NULL,
QEMU_THREAD_DETACHED);
return 0;
}
#else /* any platform that doesn't support this function */
int
set_exit_with_parent(void)
{
g_assert_not_reached();
}
#endif

View file

@ -15,6 +15,7 @@ system_ss.add(files(
'datadir.c',
'dirtylimit.c',
'dma-helpers.c',
'exit-with-parent.c',
'globals.c',
'ioport.c',
'ram-block-attributes.c',

View file

@ -53,6 +53,7 @@
#include "qemu/sockets.h"
#include "qemu/accel.h"
#include "qemu/async-teardown.h"
#include "qemu/exit-with-parent.h"
#include "hw/usb.h"
#include "hw/isa/isa.h"
#include "hw/scsi/scsi.h"
@ -783,6 +784,10 @@ static QemuOptsList qemu_run_with_opts = {
.name = "chroot",
.type = QEMU_OPT_STRING,
},
{
.name = "exit-with-parent",
.type = QEMU_OPT_BOOL,
},
{
.name = "user",
.type = QEMU_OPT_STRING,
@ -3691,6 +3696,14 @@ void qemu_init(int argc, char **argv)
if (str) {
os_set_chroot(str);
}
if (qemu_opt_get_bool(opts, "exit-with-parent", false)) {
if (!can_exit_with_parent()) {
error_report("exit-with-parent is not available"
" on this platform");
exit(1);
}
set_exit_with_parent();
}
str = qemu_opt_get(opts, "user");
if (str) {
if (!os_set_runas(str)) {

View file

@ -33,6 +33,7 @@
#include "qemu/accel.h"
#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/exit-with-parent.h"
#include "qemu/sockets.h"
#include "qobject/qdict.h"
#include "qobject/qjson.h"
@ -433,24 +434,6 @@ static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args,
#ifndef _WIN32
pid = fork();
if (pid == 0) {
#ifdef __linux__
/*
* Although we register a ABRT handler to kill off QEMU
* when g_assert() triggers, we want an extra safety
* net. The QEMU process might be non-functional and
* thus not have responded to SIGTERM. The test script
* might also have crashed with SEGV, in which case the
* cleanup handlers won't ever run.
*
* This PR_SET_PDEATHSIG setup will ensure any remaining
* QEMU will get terminated with SIGKILL in these cases.
*/
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
#endif /* __linux__ */
#ifdef __FreeBSD__
int sig = SIGKILL;
procctl(P_PID, getpid(), PROC_PDEATHSIG_CTL, &sig);
#endif /* __FreeBSD__ */
execlp("/bin/sh", "sh", "-c", command->str, NULL);
exit(1);
}
@ -482,12 +465,15 @@ gchar *qtest_qemu_args(const char *extra_args)
"-display none "
"-audio none "
"%s"
"%s"
" -accel qtest",
tracearg,
socket_path,
getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL,
qmp_socket_path,
can_exit_with_parent() ?
"-run-with exit-with-parent=on " : "",
extra_args ?: "");
return args;

View file

@ -110,9 +110,6 @@ if have_block
if pam.found()
tests += {'test-authz-pam': [authz]}
endif
if xts == 'private'
tests += {'test-crypto-xts': [crypto, io]}
endif
if host_os != 'windows'
tests += {
'test-image-locking': [testblock],

View file

@ -31,8 +31,7 @@
#endif
#if (defined(_WIN32) || defined RUSAGE_THREAD) && \
(defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT) || \
defined(CONFIG_GNUTLS_CRYPTO))
(defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT))
#define TEST_LUKS
#else
#undef TEST_LUKS

View file

@ -95,16 +95,16 @@ static void test_tls_creds(const void *opaque)
if (access(data->crt, R_OK) == 0) {
g_assert(link(data->crt,
CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0);
g_assert(link(KEYFILE,
CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
}
g_assert(link(KEYFILE,
CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
} else {
if (access(data->crt, R_OK) == 0) {
g_assert(link(data->crt,
CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0);
g_assert(link(KEYFILE,
CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
}
g_assert(link(KEYFILE,
CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
}
creds = test_tls_creds_create(

View file

@ -36,7 +36,7 @@
#define KEYFILE WORKDIR "key-ctx.pem"
static ssize_t
testWrite(const char *buf, size_t len, void *opaque, Error **errp)
testWrite(const void *buf, size_t len, void *opaque, Error **errp)
{
int *fd = opaque;
int ret;
@ -54,7 +54,7 @@ testWrite(const char *buf, size_t len, void *opaque, Error **errp)
}
static ssize_t
testRead(char *buf, size_t len, void *opaque, Error **errp)
testRead(void *buf, size_t len, void *opaque, Error **errp)
{
int *fd = opaque;
int ret;

View file

@ -1,529 +0,0 @@
/*
* QEMU Crypto XTS cipher mode
*
* Copyright (c) 2015-2018 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 "crypto/init.h"
#include "crypto/xts.h"
#include "crypto/aes.h"
typedef struct {
const char *path;
int keylen;
unsigned char key1[32];
unsigned char key2[32];
uint64_t seqnum;
unsigned long PTLEN;
unsigned char PTX[512], CTX[512];
} QCryptoXTSTestData;
static const QCryptoXTSTestData test_data[] = {
/* #1 32 byte key, 32 byte PTX */
{
"/crypto/xts/t-1-key-32-ptx-32",
32,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
0,
32,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x91, 0x7c, 0xf6, 0x9e, 0xbd, 0x68, 0xb2, 0xec,
0x9b, 0x9f, 0xe9, 0xa3, 0xea, 0xdd, 0xa6, 0x92,
0xcd, 0x43, 0xd2, 0xf5, 0x95, 0x98, 0xed, 0x85,
0x8c, 0x02, 0xc2, 0x65, 0x2f, 0xbf, 0x92, 0x2e },
},
/* #2, 32 byte key, 32 byte PTX */
{
"/crypto/xts/t-2-key-32-ptx-32",
32,
{ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 },
{ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 },
0x3333333333LL,
32,
{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 },
{ 0xc4, 0x54, 0x18, 0x5e, 0x6a, 0x16, 0x93, 0x6e,
0x39, 0x33, 0x40, 0x38, 0xac, 0xef, 0x83, 0x8b,
0xfb, 0x18, 0x6f, 0xff, 0x74, 0x80, 0xad, 0xc4,
0x28, 0x93, 0x82, 0xec, 0xd6, 0xd3, 0x94, 0xf0 },
},
/* #5 from xts.7, 32 byte key, 32 byte PTX */
{
"/crypto/xts/t-5-key-32-ptx-32",
32,
{ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 },
{ 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8,
0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 },
0x123456789aLL,
32,
{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 },
{ 0xb0, 0x1f, 0x86, 0xf8, 0xed, 0xc1, 0x86, 0x37,
0x06, 0xfa, 0x8a, 0x42, 0x53, 0xe3, 0x4f, 0x28,
0xaf, 0x31, 0x9d, 0xe3, 0x83, 0x34, 0x87, 0x0f,
0x4d, 0xd1, 0xf9, 0x4c, 0xbe, 0x98, 0x32, 0xf1 },
},
/* #4, 32 byte key, 512 byte PTX */
{
"/crypto/xts/t-4-key-32-ptx-512",
32,
{ 0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45,
0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26 },
{ 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93,
0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95 },
0,
512,
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
},
{
0x27, 0xa7, 0x47, 0x9b, 0xef, 0xa1, 0xd4, 0x76,
0x48, 0x9f, 0x30, 0x8c, 0xd4, 0xcf, 0xa6, 0xe2,
0xa9, 0x6e, 0x4b, 0xbe, 0x32, 0x08, 0xff, 0x25,
0x28, 0x7d, 0xd3, 0x81, 0x96, 0x16, 0xe8, 0x9c,
0xc7, 0x8c, 0xf7, 0xf5, 0xe5, 0x43, 0x44, 0x5f,
0x83, 0x33, 0xd8, 0xfa, 0x7f, 0x56, 0x00, 0x00,
0x05, 0x27, 0x9f, 0xa5, 0xd8, 0xb5, 0xe4, 0xad,
0x40, 0xe7, 0x36, 0xdd, 0xb4, 0xd3, 0x54, 0x12,
0x32, 0x80, 0x63, 0xfd, 0x2a, 0xab, 0x53, 0xe5,
0xea, 0x1e, 0x0a, 0x9f, 0x33, 0x25, 0x00, 0xa5,
0xdf, 0x94, 0x87, 0xd0, 0x7a, 0x5c, 0x92, 0xcc,
0x51, 0x2c, 0x88, 0x66, 0xc7, 0xe8, 0x60, 0xce,
0x93, 0xfd, 0xf1, 0x66, 0xa2, 0x49, 0x12, 0xb4,
0x22, 0x97, 0x61, 0x46, 0xae, 0x20, 0xce, 0x84,
0x6b, 0xb7, 0xdc, 0x9b, 0xa9, 0x4a, 0x76, 0x7a,
0xae, 0xf2, 0x0c, 0x0d, 0x61, 0xad, 0x02, 0x65,
0x5e, 0xa9, 0x2d, 0xc4, 0xc4, 0xe4, 0x1a, 0x89,
0x52, 0xc6, 0x51, 0xd3, 0x31, 0x74, 0xbe, 0x51,
0xa1, 0x0c, 0x42, 0x11, 0x10, 0xe6, 0xd8, 0x15,
0x88, 0xed, 0xe8, 0x21, 0x03, 0xa2, 0x52, 0xd8,
0xa7, 0x50, 0xe8, 0x76, 0x8d, 0xef, 0xff, 0xed,
0x91, 0x22, 0x81, 0x0a, 0xae, 0xb9, 0x9f, 0x91,
0x72, 0xaf, 0x82, 0xb6, 0x04, 0xdc, 0x4b, 0x8e,
0x51, 0xbc, 0xb0, 0x82, 0x35, 0xa6, 0xf4, 0x34,
0x13, 0x32, 0xe4, 0xca, 0x60, 0x48, 0x2a, 0x4b,
0xa1, 0xa0, 0x3b, 0x3e, 0x65, 0x00, 0x8f, 0xc5,
0xda, 0x76, 0xb7, 0x0b, 0xf1, 0x69, 0x0d, 0xb4,
0xea, 0xe2, 0x9c, 0x5f, 0x1b, 0xad, 0xd0, 0x3c,
0x5c, 0xcf, 0x2a, 0x55, 0xd7, 0x05, 0xdd, 0xcd,
0x86, 0xd4, 0x49, 0x51, 0x1c, 0xeb, 0x7e, 0xc3,
0x0b, 0xf1, 0x2b, 0x1f, 0xa3, 0x5b, 0x91, 0x3f,
0x9f, 0x74, 0x7a, 0x8a, 0xfd, 0x1b, 0x13, 0x0e,
0x94, 0xbf, 0xf9, 0x4e, 0xff, 0xd0, 0x1a, 0x91,
0x73, 0x5c, 0xa1, 0x72, 0x6a, 0xcd, 0x0b, 0x19,
0x7c, 0x4e, 0x5b, 0x03, 0x39, 0x36, 0x97, 0xe1,
0x26, 0x82, 0x6f, 0xb6, 0xbb, 0xde, 0x8e, 0xcc,
0x1e, 0x08, 0x29, 0x85, 0x16, 0xe2, 0xc9, 0xed,
0x03, 0xff, 0x3c, 0x1b, 0x78, 0x60, 0xf6, 0xde,
0x76, 0xd4, 0xce, 0xcd, 0x94, 0xc8, 0x11, 0x98,
0x55, 0xef, 0x52, 0x97, 0xca, 0x67, 0xe9, 0xf3,
0xe7, 0xff, 0x72, 0xb1, 0xe9, 0x97, 0x85, 0xca,
0x0a, 0x7e, 0x77, 0x20, 0xc5, 0xb3, 0x6d, 0xc6,
0xd7, 0x2c, 0xac, 0x95, 0x74, 0xc8, 0xcb, 0xbc,
0x2f, 0x80, 0x1e, 0x23, 0xe5, 0x6f, 0xd3, 0x44,
0xb0, 0x7f, 0x22, 0x15, 0x4b, 0xeb, 0xa0, 0xf0,
0x8c, 0xe8, 0x89, 0x1e, 0x64, 0x3e, 0xd9, 0x95,
0xc9, 0x4d, 0x9a, 0x69, 0xc9, 0xf1, 0xb5, 0xf4,
0x99, 0x02, 0x7a, 0x78, 0x57, 0x2a, 0xee, 0xbd,
0x74, 0xd2, 0x0c, 0xc3, 0x98, 0x81, 0xc2, 0x13,
0xee, 0x77, 0x0b, 0x10, 0x10, 0xe4, 0xbe, 0xa7,
0x18, 0x84, 0x69, 0x77, 0xae, 0x11, 0x9f, 0x7a,
0x02, 0x3a, 0xb5, 0x8c, 0xca, 0x0a, 0xd7, 0x52,
0xaf, 0xe6, 0x56, 0xbb, 0x3c, 0x17, 0x25, 0x6a,
0x9f, 0x6e, 0x9b, 0xf1, 0x9f, 0xdd, 0x5a, 0x38,
0xfc, 0x82, 0xbb, 0xe8, 0x72, 0xc5, 0x53, 0x9e,
0xdb, 0x60, 0x9e, 0xf4, 0xf7, 0x9c, 0x20, 0x3e,
0xbb, 0x14, 0x0f, 0x2e, 0x58, 0x3c, 0xb2, 0xad,
0x15, 0xb4, 0xaa, 0x5b, 0x65, 0x50, 0x16, 0xa8,
0x44, 0x92, 0x77, 0xdb, 0xd4, 0x77, 0xef, 0x2c,
0x8d, 0x6c, 0x01, 0x7d, 0xb7, 0x38, 0xb1, 0x8d,
0xeb, 0x4a, 0x42, 0x7d, 0x19, 0x23, 0xce, 0x3f,
0xf2, 0x62, 0x73, 0x57, 0x79, 0xa4, 0x18, 0xf2,
0x0a, 0x28, 0x2d, 0xf9, 0x20, 0x14, 0x7b, 0xea,
0xbe, 0x42, 0x1e, 0xe5, 0x31, 0x9d, 0x05, 0x68,
}
},
/* #7, 32 byte key, 17 byte PTX */
{
"/crypto/xts/t-7-key-32-ptx-17",
32,
{ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 },
{ 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8,
0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 },
0x123456789aLL,
17,
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 },
{ 0x6c, 0x16, 0x25, 0xdb, 0x46, 0x71, 0x52, 0x2d,
0x3d, 0x75, 0x99, 0x60, 0x1d, 0xe7, 0xca, 0x09, 0xed },
},
/* #15, 32 byte key, 25 byte PTX */
{
"/crypto/xts/t-15-key-32-ptx-25",
32,
{ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 },
{ 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8,
0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 },
0x123456789aLL,
25,
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 },
{ 0x8f, 0x4d, 0xcb, 0xad, 0x55, 0x55, 0x8d, 0x7b,
0x4e, 0x01, 0xd9, 0x37, 0x9c, 0xd4, 0xea, 0x22,
0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, 0x73 },
},
/* #21, 32 byte key, 31 byte PTX */
{
"/crypto/xts/t-21-key-32-ptx-31",
32,
{ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 },
{ 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8,
0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 },
0x123456789aLL,
31,
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e },
{ 0xd0, 0x5b, 0xc0, 0x90, 0xa8, 0xe0, 0x4f, 0x1b,
0x3d, 0x3e, 0xcd, 0xd5, 0xba, 0xec, 0x0f, 0xd4,
0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a,
0x73, 0x06, 0xe6, 0x4b, 0xe5, 0xdd, 0x82 },
},
};
#define STORE64L(x, y) \
do { \
(y)[7] = (unsigned char)(((x) >> 56) & 255); \
(y)[6] = (unsigned char)(((x) >> 48) & 255); \
(y)[5] = (unsigned char)(((x) >> 40) & 255); \
(y)[4] = (unsigned char)(((x) >> 32) & 255); \
(y)[3] = (unsigned char)(((x) >> 24) & 255); \
(y)[2] = (unsigned char)(((x) >> 16) & 255); \
(y)[1] = (unsigned char)(((x) >> 8) & 255); \
(y)[0] = (unsigned char)((x) & 255); \
} while (0)
struct TestAES {
AES_KEY enc;
AES_KEY dec;
};
static void test_xts_aes_encrypt(const void *ctx,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
const struct TestAES *aesctx = ctx;
AES_encrypt(src, dst, &aesctx->enc);
}
static void test_xts_aes_decrypt(const void *ctx,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
const struct TestAES *aesctx = ctx;
AES_decrypt(src, dst, &aesctx->dec);
}
static void test_xts(const void *opaque)
{
const QCryptoXTSTestData *data = opaque;
uint8_t out[512], Torg[16], T[16];
uint64_t seq;
struct TestAES aesdata;
struct TestAES aestweak;
AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc);
AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec);
AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc);
AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec);
seq = data->seqnum;
STORE64L(seq, Torg);
memset(Torg + 8, 0, 8);
memcpy(T, Torg, sizeof(T));
xts_encrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, data->PTLEN, out, data->PTX);
g_assert(memcmp(out, data->CTX, data->PTLEN) == 0);
memcpy(T, Torg, sizeof(T));
xts_decrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, data->PTLEN, out, data->CTX);
g_assert(memcmp(out, data->PTX, data->PTLEN) == 0);
}
static void test_xts_split(const void *opaque)
{
const QCryptoXTSTestData *data = opaque;
uint8_t out[512], Torg[16], T[16];
uint64_t seq;
unsigned long len = data->PTLEN / 2;
struct TestAES aesdata;
struct TestAES aestweak;
AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc);
AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec);
AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc);
AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec);
seq = data->seqnum;
STORE64L(seq, Torg);
memset(Torg + 8, 0, 8);
memcpy(T, Torg, sizeof(T));
xts_encrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, len, out, data->PTX);
xts_encrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, len, &out[len], &data->PTX[len]);
g_assert(memcmp(out, data->CTX, data->PTLEN) == 0);
memcpy(T, Torg, sizeof(T));
xts_decrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, len, out, data->CTX);
xts_decrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, len, &out[len], &data->CTX[len]);
g_assert(memcmp(out, data->PTX, data->PTLEN) == 0);
}
static void test_xts_unaligned(const void *opaque)
{
#define BAD_ALIGN 3
const QCryptoXTSTestData *data = opaque;
uint8_t in[512 + BAD_ALIGN], out[512 + BAD_ALIGN];
uint8_t Torg[16], T[16 + BAD_ALIGN];
uint64_t seq;
struct TestAES aesdata;
struct TestAES aestweak;
AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc);
AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec);
AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc);
AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec);
seq = data->seqnum;
STORE64L(seq, Torg);
memset(Torg + 8, 0, 8);
/* IV not aligned */
memcpy(T + BAD_ALIGN, Torg, 16);
memcpy(in, data->PTX, data->PTLEN);
xts_encrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T + BAD_ALIGN, data->PTLEN, out, in);
g_assert(memcmp(out, data->CTX, data->PTLEN) == 0);
/* plain text not aligned */
memcpy(T, Torg, 16);
memcpy(in + BAD_ALIGN, data->PTX, data->PTLEN);
xts_encrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, data->PTLEN, out, in + BAD_ALIGN);
g_assert(memcmp(out, data->CTX, data->PTLEN) == 0);
/* cipher text not aligned */
memcpy(T, Torg, 16);
memcpy(in, data->PTX, data->PTLEN);
xts_encrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, data->PTLEN, out + BAD_ALIGN, in);
g_assert(memcmp(out + BAD_ALIGN, data->CTX, data->PTLEN) == 0);
/* IV not aligned */
memcpy(T + BAD_ALIGN, Torg, 16);
memcpy(in, data->CTX, data->PTLEN);
xts_decrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T + BAD_ALIGN, data->PTLEN, out, in);
g_assert(memcmp(out, data->PTX, data->PTLEN) == 0);
/* cipher text not aligned */
memcpy(T, Torg, 16);
memcpy(in + BAD_ALIGN, data->CTX, data->PTLEN);
xts_decrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, data->PTLEN, out, in + BAD_ALIGN);
g_assert(memcmp(out, data->PTX, data->PTLEN) == 0);
/* plain text not aligned */
memcpy(T, Torg, 16);
memcpy(in, data->CTX, data->PTLEN);
xts_decrypt(&aesdata, &aestweak,
test_xts_aes_encrypt,
test_xts_aes_decrypt,
T, data->PTLEN, out + BAD_ALIGN, in);
g_assert(memcmp(out + BAD_ALIGN, data->PTX, data->PTLEN) == 0);
}
int main(int argc, char **argv)
{
size_t i;
g_test_init(&argc, &argv, NULL);
g_assert(qcrypto_init(NULL) == 0);
for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
gchar *path = g_strdup_printf("%s/basic", test_data[i].path);
g_test_add_data_func(path, &test_data[i], test_xts);
g_free(path);
/* skip the cases where the length is smaller than 2*blocklen
* or the length is not a multiple of 32
*/
if ((test_data[i].PTLEN >= 32) && !(test_data[i].PTLEN % 32)) {
path = g_strdup_printf("%s/split", test_data[i].path);
g_test_add_data_func(path, &test_data[i], test_xts_split);
g_free(path);
}
path = g_strdup_printf("%s/unaligned", test_data[i].path);
g_test_add_data_func(path, &test_data[i], test_xts_unaligned);
g_free(path);
}
return g_test_run();
}

View file

@ -578,7 +578,6 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
bool vnc_display_reload_certs(const char *id, Error **errp)
{
VncDisplay *vd = vnc_display_find(id);
QCryptoTLSCredsClass *creds = NULL;
if (!vd) {
error_setg(errp, "Can not find vnc display");
@ -590,13 +589,7 @@ bool vnc_display_reload_certs(const char *id, Error **errp)
return false;
}
creds = QCRYPTO_TLS_CREDS_GET_CLASS(OBJECT(vd->tlscreds));
if (creds->reload == NULL) {
error_setg(errp, "%s doesn't support to reload TLS credential",
object_get_typename(OBJECT(vd->tlscreds)));
return false;
}
if (!creds->reload(vd->tlscreds, errp)) {
if (!qcrypto_tls_creds_reload(vd->tlscreds, errp)) {
return false;
}