From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:52325) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YyBN7-0008Vk-Qu for qemu-devel@nongnu.org; Thu, 28 May 2015 23:54:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YyBN3-0000tP-KJ for qemu-devel@nongnu.org; Thu, 28 May 2015 23:54:13 -0400 Received: from szxga02-in.huawei.com ([119.145.14.65]:34620) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YyBN2-0000tC-JX for qemu-devel@nongnu.org; Thu, 28 May 2015 23:54:09 -0400 Message-ID: <5567E2C3.4080302@huawei.com> Date: Fri, 29 May 2015 11:53:39 +0800 From: Gonglei MIME-Version: 1.0 References: <1432205817-16414-1-git-send-email-berrange@redhat.com> <1432205817-16414-6-git-send-email-berrange@redhat.com> In-Reply-To: <1432205817-16414-6-git-send-email-berrange@redhat.com> Content-Type: text/plain; charset="windows-1252" Content-Transfer-Encoding: 7bit Subject: Re: [Qemu-devel] [PATCH 05/10] crypto: add a gcrypt cipher implementation List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: "Daniel P. Berrange" , qemu-devel@nongnu.org Cc: Kevin Wolf , Paolo Bonzini , Gerd Hoffmann On 2015/5/21 18:56, Daniel P. Berrange wrote: > If we are linking to gnutls already and gnutls is built against > gcrypt, then we should use gcrypt as a cipher backend in > preference to our built-in backend. > > This will be used when linking against GNUTLS 1.x and many > GNUTLS 2.x versions. > > Signed-off-by: Daniel P. Berrange > --- > configure | 29 +++++++ > crypto/cipher-gcrypt.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++ > crypto/cipher.c | 4 + > crypto/init.c | 90 ++++++++++++++++++++++ > 4 files changed, 326 insertions(+) > create mode 100644 crypto/cipher-gcrypt.c > > diff --git a/configure b/configure > index 6530e7a..6629346 100755 > --- a/configure > +++ b/configure > @@ -2162,6 +2162,7 @@ fi > ########################################## > # GNUTLS probe > > +gnutls_gcrypt=no > if test "$gnutls" != "no"; then > if $pkg_config --exists "gnutls"; then > gnutls_cflags=`$pkg_config --cflags gnutls` > @@ -2177,6 +2178,18 @@ if test "$gnutls" != "no"; then > else > gnutls_hash="no" > fi > + > + if $pkg_config --exists 'gnutls >= 3.0'; then > + gnutls_gcrypt=no > + elif $pkg_config --exists 'gnutls >= 2.12'; then > + case `$pkg_config --libs --static gnutls` in > + *gcrypt*) gnutls_gcrypt=yes ;; > + *nettle*) gnutls_gcrypt=no ;; > + *) gnutls_gcrypt=yes ;; > + esac > + else > + gnutls_gcrypt=yes > + fi > elif test "$gnutls" = "yes"; then > feature_not_found "gnutls" "Install gnutls devel" > else > @@ -2187,6 +2200,18 @@ else > gnutls_hash="no" > fi > > +if test "$gnutls_gcrypt" != "no"; then > + if has "libgcrypt-config"; then > + gcrypt_cflags=`libgcrypt-config --cflags` > + gcrypt_libs=`libgcrypt-config --libs` > + libs_softmmu="$gcrypt_libs $libs_softmmu" > + libs_tools="$gcrypt_libs $libs_tools" > + QEMU_CFLAGS="$QEMU_CFLAGS $gcrypt_cflags" > + else > + feature_not_found "gcrypt" "Install gcrypt devel" > + fi > +fi > + > > ########################################## > # VTE probe > @@ -4433,6 +4458,7 @@ echo "SDL support $sdl" > echo "GTK support $gtk" > echo "GNUTLS support $gnutls" > echo "GNUTLS hash $gnutls_hash" > +echo "GNUTLS gcrypt $gnutls_gcrypt" > echo "VTE support $vte" > echo "curses support $curses" > echo "curl support $curl" > @@ -4791,6 +4817,9 @@ fi > if test "$gnutls_hash" = "yes" ; then > echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak > fi > +if test "$gnutls_gcrypt" = "yes" ; then > + echo "CONFIG_GNUTLS_GCRYPT=y" >> $config_host_mak > +fi > if test "$vte" = "yes" ; then > echo "CONFIG_VTE=y" >> $config_host_mak > echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak > diff --git a/crypto/cipher-gcrypt.c b/crypto/cipher-gcrypt.c > new file mode 100644 > index 0000000..624a4f8 > --- /dev/null > +++ b/crypto/cipher-gcrypt.c > @@ -0,0 +1,203 @@ > +/* > + * QEMU Crypto cipher libgcrypt algorithms > + * > + * Copyright (c) 2015 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 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 . > + * > + */ > + > +#include > +#include > + > + > +static uint8_t *qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, > + size_t nkey) > +{ > + uint8_t *ret = g_new0(uint8_t, nkey); > + size_t i; > + for (i = 0; i < nkey; i++) { > + uint8_t r = key[i]; > + r = (r & 0xf0) >> 4 | (r & 0x0f) << 4; > + r = (r & 0xcc) >> 2 | (r & 0x33) << 2; > + r = (r & 0xaa) >> 1 | (r & 0x55) << 1; > + ret[i] = r; > + } > + return ret; > +} > + > +bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg) > +{ > + if (alg == QCRYPTO_CIPHER_ALG_DES_RFB || > + alg == QCRYPTO_CIPHER_ALG_AES) { > + return true; > + } > + return false; > +} > + > + > +QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, > + QCryptoCipherMode mode, > + const uint8_t *key, size_t nkey, > + Error **errp) > +{ > + QCryptoCipher *cipher; > + gcry_cipher_hd_t handle; > + gcry_error_t err; > + int gcryalg, gcrymode; > + > + switch (mode) { > + case QCRYPTO_CIPHER_MODE_ECB: > + gcrymode = GCRY_CIPHER_MODE_ECB; > + break; > + case QCRYPTO_CIPHER_MODE_CBC: > + gcrymode = GCRY_CIPHER_MODE_CBC; > + break; > + default: > + error_setg(errp, _("Unsupported cipher mode %d"), mode); > + return NULL; > + } > + > + switch (alg) { > + case QCRYPTO_CIPHER_ALG_DES_RFB: > + gcryalg = GCRY_CIPHER_DES; > + break; > + > + case QCRYPTO_CIPHER_ALG_AES: > + if (nkey == 16) { > + gcryalg = GCRY_CIPHER_AES128; > + } else if (nkey == 24) { > + gcryalg = GCRY_CIPHER_AES192; > + } else if (nkey == 32) { > + gcryalg = GCRY_CIPHER_AES256; > + } else { > + error_setg(errp, _("Expected key size 16, 24 or 32 not %zu"), > + nkey); > + return NULL; > + } > + break; > + > + default: > + error_setg(errp, _("Unsupported cipher algorithm %d"), alg); > + return NULL; > + } > + > + cipher = g_new0(QCryptoCipher, 1); > + cipher->alg = alg; > + cipher->mode = mode; > + > + err = gcry_cipher_open(&handle, gcryalg, gcrymode, 0); > + if (err != 0) { > + error_setg(errp, _("Cannot initialize cipher: %s"), > + gcry_strerror(err)); > + goto error; > + } > + > + if (cipher->alg == QCRYPTO_CIPHER_ALG_DES_RFB) { > + /* We're using standard DES cipher from gcrypt, so we need > + * to munge the key so that the results are the same as the > + * bizarre RFB variant of DES :-) > + */ > + uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); > + err = gcry_cipher_setkey(handle, rfbkey, nkey); > + g_free(rfbkey); > + } else { > + err = gcry_cipher_setkey(handle, key, nkey); > + } > + if (err != 0) { > + error_setg(errp, _("Cannot set key: %s"), > + gcry_strerror(err)); > + goto error; > + } > + > + cipher->opaque = handle; > + return cipher; > + > + error: > + gcry_cipher_close(handle); > + g_free(cipher); > + return NULL; > +} > + > + > +void qcrypto_cipher_free(QCryptoCipher *cipher) > +{ > + if (!cipher) { > + return; > + } > + gcry_cipher_close(cipher->opaque); Maybe it's better cast cipher->opaque to gcry_cipher_hd_t like other free functions. gcry_cipher_hd_t handle = cipher->opaque; > + g_free(cipher); > +} > + > + > +int qcrypto_cipher_encrypt(QCryptoCipher *cipher, > + const void *in, > + void *out, > + size_t len, > + Error **errp) > +{ > + gcry_cipher_hd_t handle = cipher->opaque; > + gcry_error_t err; > + > + err = gcry_cipher_encrypt(handle, > + out, len, > + in, len); > + if (err != 0) { > + error_setg(errp, _("Cannot encrypt data: %s"), > + gcry_strerror(err)); > + return -1; > + } > + > + return 0; > +} > + > + > +int qcrypto_cipher_decrypt(QCryptoCipher *cipher, > + const void *in, > + void *out, > + size_t len, > + Error **errp) > +{ > + gcry_cipher_hd_t handle = cipher->opaque; > + gcry_error_t err; > + > + err = gcry_cipher_decrypt(handle, > + out, len, > + in, len); > + if (err != 0) { > + error_setg(errp, _("Cannot decrypt data: %s"), > + gcry_strerror(err)); > + return -1; > + } > + > + return 0; > +} > + > +int qcrypto_cipher_setiv(QCryptoCipher *cipher, > + const uint8_t *iv, size_t niv, > + Error **errp) > +{ > + gcry_cipher_hd_t handle = cipher->opaque; > + gcry_error_t err; > + > + gcry_cipher_reset(handle); > + err = gcry_cipher_setiv(handle, iv, niv); > + if (err != 0) { > + error_setg(errp, _("Cannot set IV: %s"), > + gcry_strerror(err)); > + return -1; > + } > + > + return 0; > +} > diff --git a/crypto/cipher.c b/crypto/cipher.c > index 71e9eae..dc140fb 100644 > --- a/crypto/cipher.c > +++ b/crypto/cipher.c > @@ -20,4 +20,8 @@ > > #include "crypto/cipher.h" > > +#ifdef CONFIG_GNUTLS_GCRYPT > +#include "crypto/cipher-gcrypt.c" > +#else > #include "crypto/cipher-builtin.c" > +#endif > diff --git a/crypto/init.c b/crypto/init.c > index 8fd66d4..486af37 100644 > --- a/crypto/init.c > +++ b/crypto/init.c > @@ -19,6 +19,7 @@ > */ > > #include "crypto/init.h" > +#include "qemu/thread.h" > > #include > > @@ -26,8 +27,42 @@ > #include > #include > > +#ifdef CONFIG_GNUTLS_GCRYPT > +#include > +#endif > + > /* #define DEBUG_GNUTLS */ > > +/* > + * If GNUTLS is built against GCrypt then > + * > + * - When GNUTLS >= 2.12, we must not initialize gcrypt threading > + * because GNUTLS will do that itself > + * - When GNUTLS < 2.12 we must always initialize gcrypt threading > + * > + * But.... > + * > + * When gcrypt >= 1.6.0 we must not initialize gcrypt threading > + * because gcrypt will do that itself. > + * > + * So we need to init gcrypt threading if > + * > + * - gcrypt < 1.6.0 > + * AND > + * - gnutls < 2.12 > + * > + */ > + > +#if (defined(CONFIG_GNUTLS_GCRYPT) && \ > + (!defined(GNUTLS_VERSION_NUMBER) || \ > + (GNUTLS_VERSION_NUMBER < 0x020c00)) && \ > + (!defined(GCRYPT_VERSION_NUMBER) || \ > + (GCRYPT_VERSION_NUMBER < 0x010600))) > +#define QCRYPTO_INIT_GCRYPT_THREADS > +#else > +#undef QCRYPTO_INIT_GCRYPT_THREADS > +#endif > + > #ifdef DEBUG_GNUTLS > static void qcrypto_gnutls_log(int level, const char *str) > { > @@ -35,6 +70,49 @@ static void qcrypto_gnutls_log(int level, const char *str) > } > #endif > > +#ifdef QCRYPTO_INIT_GCRYPT_THREADS > +static int qcrypto_gcrypt_mutex_init(void **priv) > +{ \ > + QemuMutex *lock = NULL; > + lock = g_new0(QemuMutex, 1); > + qemu_mutex_init(lock); > + *priv = lock; > + return 0; > +} > + > +static int qcrypto_gcrypt_mutex_destroy(void **priv) > +{ > + QemuMutex *lock = *priv; > + qemu_mutex_destroy(lock); > + g_free(lock); > + return 0; > +} > + > +static int qcrypto_gcrypt_mutex_lock(void **priv) > +{ > + QemuMutex *lock = *priv; > + qemu_mutex_lock(lock); > + return 0; > +} > + > +static int qcrypto_gcrypt_mutex_unlock(void **priv) > +{ > + QemuMutex *lock = *priv; > + qemu_mutex_unlock(lock); > + return 0; > +} > + > +static struct gcry_thread_cbs qcrypto_gcrypt_thread_impl = { > + (GCRY_THREAD_OPTION_PTHREAD | (GCRY_THREAD_OPTION_VERSION << 8)), > + NULL, > + qcrypto_gcrypt_mutex_init, > + qcrypto_gcrypt_mutex_destroy, > + qcrypto_gcrypt_mutex_lock, > + qcrypto_gcrypt_mutex_unlock, > + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL > +}; > +#endif /* QCRYPTO_INIT_GCRYPT */ > + > int qcrypto_init(Error **errp) > { > int ret; > @@ -49,6 +127,18 @@ int qcrypto_init(Error **errp) > gnutls_global_set_log_level(10); > gnutls_global_set_log_function(qcrypto_gnutls_log); > #endif > + > +#ifdef CONFIG_GNUTLS_GCRYPT > + if (!gcry_check_version(GCRYPT_VERSION)) { > + error_setg(errp, "%s", _("Unable to initialize gcrypt")); > + return -1; Missing to call gnutls_global_deinit(). Regards, -Gonglei > + } > +#ifdef QCRYPTO_INIT_GCRYPT_THREADS > + gcry_control(GCRYCTL_SET_THREAD_CBS, &qcrypto_gcrypt_thread_impl); > +#endif /* QCRYPTO_INIT_GCRYPT_THREADS */ > + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); > +#endif > + > return 0; > } > >