crypto: add support for gcrypt's native XTS impl

Libgcrypt 1.8.0 added support for the XTS mode. Use this because long
term we wish to delete QEMU's XTS impl to avoid carrying private crypto
algorithm impls.

As an added benefit, using this improves performance from 531 MB/sec to
670 MB/sec, since we are avoiding several layers of function call
indirection.

This is even more noticable with the gcrypt builds in Fedora or RHEL-8
which have a non-upstream patch for FIPS mode which does mutex locking.
This is catastrophic for encryption performance with small block sizes,
meaning this patch improves encryption from 240 MB/sec to 670 MB/sec.

Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2019-10-14 17:28:27 +01:00
parent b4296d7f88
commit e0576942e0
4 changed files with 89 additions and 36 deletions

22
configure vendored
View File

@ -474,6 +474,8 @@ gnutls=""
nettle=""
gcrypt=""
gcrypt_hmac="no"
gcrypt_xts="no"
qemu_private_xts="yes"
auth_pam=""
vte=""
virglrenderer=""
@ -2911,6 +2913,18 @@ EOF
if compile_prog "$gcrypt_cflags" "$gcrypt_libs" ; then
gcrypt_hmac=yes
fi
cat > $TMPC << EOF
#include <gcrypt.h>
int main(void) {
gcry_cipher_hd_t handle;
gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_XTS, 0);
return 0;
}
EOF
if compile_prog "$gcrypt_cflags" "$gcrypt_libs" ; then
gcrypt_xts=yes
qemu_private_xts=no
fi
elif test "$gcrypt" = "yes"; then
feature_not_found "gcrypt" "Install gcrypt devel >= 1.5.0"
else
@ -6326,6 +6340,11 @@ echo "VTE support $vte $(echo_version $vte $vteversion)"
echo "TLS priority $tls_priority"
echo "GNUTLS support $gnutls"
echo "libgcrypt $gcrypt"
if test "$gcrypt" = "yes"
then
echo " hmac $gcrypt_hmac"
echo " XTS $gcrypt_xts"
fi
echo "nettle $nettle $(echo_version $nettle $nettle_version)"
echo "libtasn1 $tasn1"
echo "PAM $auth_pam"
@ -6804,6 +6823,9 @@ if test "$nettle" = "yes" ; then
echo "CONFIG_NETTLE=y" >> $config_host_mak
echo "CONFIG_NETTLE_VERSION_MAJOR=${nettle_version%%.*}" >> $config_host_mak
fi
if test "$qemu_private_xts" = "yes" ; then
echo "CONFIG_QEMU_PRIVATE_XTS=y" >> $config_host_mak
fi
if test "$tasn1" = "yes" ; then
echo "CONFIG_TASN1=y" >> $config_host_mak
fi

View File

@ -31,7 +31,7 @@ crypto-obj-y += ivgen-essiv.o
crypto-obj-y += ivgen-plain.o
crypto-obj-y += ivgen-plain64.o
crypto-obj-y += afsplit.o
crypto-obj-y += xts.o
crypto-obj-$(CONFIG_QEMU_PRIVATE_XTS) += xts.o
crypto-obj-y += block.o
crypto-obj-y += block-qcow.o
crypto-obj-y += block-luks.o

View File

@ -19,7 +19,9 @@
*/
#include "qemu/osdep.h"
#ifdef CONFIG_QEMU_PRIVATE_XTS
#include "crypto/xts.h"
#endif
#include "cipherpriv.h"
#include <gcrypt.h>
@ -59,10 +61,12 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
typedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt;
struct QCryptoCipherGcrypt {
gcry_cipher_hd_t handle;
gcry_cipher_hd_t tweakhandle;
size_t blocksize;
#ifdef CONFIG_QEMU_PRIVATE_XTS
gcry_cipher_hd_t tweakhandle;
/* Initialization vector or Counter */
uint8_t *iv;
#endif
};
static void
@ -74,10 +78,12 @@ qcrypto_gcrypt_cipher_free_ctx(QCryptoCipherGcrypt *ctx,
}
gcry_cipher_close(ctx->handle);
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
gcry_cipher_close(ctx->tweakhandle);
}
g_free(ctx->iv);
#endif
g_free(ctx);
}
@ -94,9 +100,15 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
case QCRYPTO_CIPHER_MODE_XTS:
gcrymode = GCRY_CIPHER_MODE_ECB;
break;
case QCRYPTO_CIPHER_MODE_XTS:
#ifdef CONFIG_QEMU_PRIVATE_XTS
gcrymode = GCRY_CIPHER_MODE_ECB;
#else
gcrymode = GCRY_CIPHER_MODE_XTS;
#endif
break;
case QCRYPTO_CIPHER_MODE_CBC:
gcrymode = GCRY_CIPHER_MODE_CBC;
break;
@ -172,6 +184,7 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
gcry_strerror(err));
goto error;
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0);
if (err != 0) {
@ -180,6 +193,7 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
goto error;
}
}
#endif
if (alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
/* We're using standard DES cipher from gcrypt, so we need
@ -191,6 +205,7 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
g_free(rfbkey);
ctx->blocksize = 8;
} else {
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
nkey /= 2;
err = gcry_cipher_setkey(ctx->handle, key, nkey);
@ -201,8 +216,11 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
}
err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
} else {
#endif
err = gcry_cipher_setkey(ctx->handle, key, nkey);
#ifdef CONFIG_QEMU_PRIVATE_XTS
}
#endif
if (err != 0) {
error_setg(errp, "Cannot set key: %s",
gcry_strerror(err));
@ -228,6 +246,7 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
}
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
if (ctx->blocksize != XTS_BLOCK_SIZE) {
error_setg(errp,
@ -237,6 +256,7 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
}
ctx->iv = g_new0(uint8_t, ctx->blocksize);
}
#endif
return ctx;
@ -253,6 +273,7 @@ qcrypto_gcrypt_cipher_ctx_free(QCryptoCipher *cipher)
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
static void qcrypto_gcrypt_xts_encrypt(const void *ctx,
size_t length,
uint8_t *dst,
@ -272,6 +293,7 @@ static void qcrypto_gcrypt_xts_decrypt(const void *ctx,
err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
g_assert(err == 0);
}
#endif
static int
qcrypto_gcrypt_cipher_encrypt(QCryptoCipher *cipher,
@ -289,20 +311,23 @@ qcrypto_gcrypt_cipher_encrypt(QCryptoCipher *cipher,
return -1;
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
xts_encrypt(ctx->handle, ctx->tweakhandle,
qcrypto_gcrypt_xts_encrypt,
qcrypto_gcrypt_xts_decrypt,
ctx->iv, len, out, in);
} else {
err = gcry_cipher_encrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot encrypt data: %s",
gcry_strerror(err));
return -1;
}
return 0;
}
#endif
err = gcry_cipher_encrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot encrypt data: %s",
gcry_strerror(err));
return -1;
}
return 0;
@ -325,20 +350,23 @@ qcrypto_gcrypt_cipher_decrypt(QCryptoCipher *cipher,
return -1;
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
xts_decrypt(ctx->handle, ctx->tweakhandle,
qcrypto_gcrypt_xts_encrypt,
qcrypto_gcrypt_xts_decrypt,
ctx->iv, len, out, in);
} else {
err = gcry_cipher_decrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot decrypt data: %s",
gcry_strerror(err));
return -1;
}
return 0;
}
#endif
err = gcry_cipher_decrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot decrypt data: %s",
gcry_strerror(err));
return -1;
}
return 0;
@ -358,24 +386,27 @@ qcrypto_gcrypt_cipher_setiv(QCryptoCipher *cipher,
return -1;
}
#ifdef CONFIG_QEMU_PRIVATE_XTS
if (ctx->iv) {
memcpy(ctx->iv, iv, niv);
return 0;
}
#endif
if (cipher->mode == QCRYPTO_CIPHER_MODE_CTR) {
err = gcry_cipher_setctr(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set Counter: %s",
gcry_strerror(err));
return -1;
}
} else {
if (cipher->mode == QCRYPTO_CIPHER_MODE_CTR) {
err = gcry_cipher_setctr(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set Counter: %s",
gcry_cipher_reset(ctx->handle);
err = gcry_cipher_setiv(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set IV: %s",
gcry_strerror(err));
return -1;
}
} else {
gcry_cipher_reset(ctx->handle);
err = gcry_cipher_setiv(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set IV: %s",
gcry_strerror(err));
return -1;
}
return -1;
}
}

View File

@ -140,7 +140,7 @@ check-unit-y += tests/test-base64$(EXESUF)
check-unit-$(call land,$(CONFIG_BLOCK),$(if $(CONFIG_NETTLE),y,$(CONFIG_GCRYPT))) += tests/test-crypto-pbkdf$(EXESUF)
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-ivgen$(EXESUF)
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-afsplit$(EXESUF)
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-xts$(EXESUF)
check-unit-$(if $(CONFIG_BLOCK),$(CONFIG_QEMU_PRIVATE_XTS)) += tests/test-crypto-xts$(EXESUF)
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-block$(EXESUF)
check-unit-y += tests/test-logging$(EXESUF)
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_REPLICATION)) += tests/test-replication$(EXESUF)