Commit e0576942 authored by Daniel P. Berrangé's avatar Daniel P. Berrangé
Browse files

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: default avatarPhilippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: default avatarStefano Garzarella <sgarzare@redhat.com>
Signed-off-by: default avatarDaniel P. Berrangé <berrange@redhat.com>
parent b4296d7f
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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
+64 −33
Original line number Diff line number Diff line
@@ -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,8 +100,14 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,

    switch (mode) {
    case QCRYPTO_CIPHER_MODE_ECB:
        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;
@@ -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,12 +311,16 @@ 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 {
        return 0;
    }
#endif

    err = gcry_cipher_encrypt(ctx->handle,
                              out, len,
                              in, len);
@@ -303,7 +329,6 @@ qcrypto_gcrypt_cipher_encrypt(QCryptoCipher *cipher,
                   gcry_strerror(err));
        return -1;
    }
    }

    return 0;
}
@@ -325,12 +350,16 @@ 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 {
        return 0;
    }
#endif

    err = gcry_cipher_decrypt(ctx->handle,
                              out, len,
                              in, len);
@@ -339,7 +368,6 @@ qcrypto_gcrypt_cipher_decrypt(QCryptoCipher *cipher,
                   gcry_strerror(err));
        return -1;
    }
    }

    return 0;
}
@@ -358,9 +386,13 @@ qcrypto_gcrypt_cipher_setiv(QCryptoCipher *cipher,
        return -1;
    }

#ifdef CONFIG_QEMU_PRIVATE_XTS
    if (ctx->iv) {
        memcpy(ctx->iv, iv, niv);
    } else {
        return 0;
    }
#endif

    if (cipher->mode == QCRYPTO_CIPHER_MODE_CTR) {
        err = gcry_cipher_setctr(ctx->handle, iv, niv);
        if (err != 0) {
@@ -377,7 +409,6 @@ qcrypto_gcrypt_cipher_setiv(QCryptoCipher *cipher,
            return -1;
        }
    }
    }

    return 0;
}
+1 −1
Original line number Diff line number Diff line
@@ -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)