From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:35559) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YvOAK-0000lA-GE for qemu-devel@nongnu.org; Thu, 21 May 2015 06:57:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YvOAG-0007hV-R7 for qemu-devel@nongnu.org; Thu, 21 May 2015 06:57:28 -0400 Received: from mx1.redhat.com ([209.132.183.28]:37647) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YvOAG-0007g7-Hi for qemu-devel@nongnu.org; Thu, 21 May 2015 06:57:24 -0400 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id t4LAvM3V027973 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Thu, 21 May 2015 06:57:23 -0400 From: "Daniel P. Berrange" Date: Thu, 21 May 2015 11:56:52 +0100 Message-Id: <1432205817-16414-6-git-send-email-berrange@redhat.com> In-Reply-To: <1432205817-16414-1-git-send-email-berrange@redhat.com> References: <1432205817-16414-1-git-send-email-berrange@redhat.com> Subject: [Qemu-devel] [PATCH 05/10] crypto: add a gcrypt cipher implementation List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Kevin Wolf , Paolo Bonzini , Gerd Hoffmann 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); + 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; + } +#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; } -- 2.1.0