Commit 62893b67 authored by Daniel P. Berrangé's avatar Daniel P. Berrangé Committed by Paolo Bonzini
Browse files

crypto: add a gcrypt cipher implementation



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: default avatarDaniel P. Berrange <berrange@redhat.com>
Message-Id: <1435770638-25715-6-git-send-email-berrange@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent ca38a4cc
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -2126,6 +2126,7 @@ fi
##########################################
# GNUTLS probe

gnutls_gcrypt=no
if test "$gnutls" != "no"; then
    if $pkg_config --exists "gnutls"; then
        gnutls_cflags=`$pkg_config --cflags gnutls`
@@ -2141,6 +2142,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
@@ -2151,6 +2164,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
@@ -4463,6 +4488,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"
@@ -4827,6 +4853,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

crypto/cipher-gcrypt.c

0 → 100644
+195 −0
Original line number Diff line number Diff line
/*
 * 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 <http://www.gnu.org/licenses/>.
 *
 */

#include <gcrypt.h>


bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
{
    switch (alg) {
    case QCRYPTO_CIPHER_ALG_DES_RFB:
    case QCRYPTO_CIPHER_ALG_AES_128:
    case QCRYPTO_CIPHER_ALG_AES_192:
    case QCRYPTO_CIPHER_ALG_AES_256:
        return true;
    default:
        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;
    }

    if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
        return NULL;
    }

    switch (alg) {
    case QCRYPTO_CIPHER_ALG_DES_RFB:
        gcryalg = GCRY_CIPHER_DES;
        break;

    case QCRYPTO_CIPHER_ALG_AES_128:
        gcryalg = GCRY_CIPHER_AES128;
        break;

    case QCRYPTO_CIPHER_ALG_AES_192:
        gcryalg = GCRY_CIPHER_AES192;
        break;

    case QCRYPTO_CIPHER_ALG_AES_256:
        gcryalg = GCRY_CIPHER_AES256;
        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)
{
    gcry_cipher_hd_t handle;
    if (!cipher) {
        return;
    }
    handle = cipher->opaque;
    gcry_cipher_close(handle);
    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;
}
+23 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

#include "crypto/cipher.h"


static size_t alg_key_len[QCRYPTO_CIPHER_ALG_LAST] = {
    [QCRYPTO_CIPHER_ALG_AES_128] = 16,
    [QCRYPTO_CIPHER_ALG_AES_192] = 24,
@@ -46,4 +47,26 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
    return true;
}

#if defined(CONFIG_GNUTLS_GCRYPT)
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;
}
#endif /* CONFIG_GNUTLS_GCRYPT */

#ifdef CONFIG_GNUTLS_GCRYPT
#include "crypto/cipher-gcrypt.c"
#else
#include "crypto/cipher-builtin.c"
#endif
+90 −0
Original line number Diff line number Diff line
@@ -19,13 +19,48 @@
 */

#include "crypto/init.h"
#include "qemu/thread.h"

#ifdef CONFIG_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>

#ifdef CONFIG_GNUTLS_GCRYPT
#include <gcrypt.h>
#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)
{
@@ -33,6 +68,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;
@@ -47,6 +125,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, "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;
}