diff --git a/configure b/configure index f3c7ca62cf..a5f19d892f 100755 --- a/configure +++ b/configure @@ -2127,6 +2127,7 @@ fi # GNUTLS probe gnutls_gcrypt=no +gnutls_nettle=no if test "$gnutls" != "no"; then if $pkg_config --exists "gnutls"; then gnutls_cflags=`$pkg_config --cflags gnutls` @@ -2145,14 +2146,25 @@ if test "$gnutls" != "no"; then if $pkg_config --exists 'gnutls >= 3.0'; then gnutls_gcrypt=no + gnutls_nettle=yes 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 ;; + *gcrypt*) + gnutls_gcrypt=yes + gnutls_nettle=no + ;; + *nettle*) + gnutls_gcrypt=no + gnutls_nettle=yes + ;; + *) + gnutls_gcrypt=yes + gnutls_nettle=no + ;; esac else gnutls_gcrypt=yes + gnutls_nettle=no fi elif test "$gnutls" = "yes"; then feature_not_found "gnutls" "Install gnutls devel" @@ -2177,6 +2189,19 @@ if test "$gnutls_gcrypt" != "no"; then fi +if test "$gnutls_nettle" != "no"; then + if $pkg_config --exists "nettle"; then + nettle_cflags=`$pkg_config --cflags nettle` + nettle_libs=`$pkg_config --libs nettle` + libs_softmmu="$nettle_libs $libs_softmmu" + libs_tools="$nettle_libs $libs_tools" + QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags" + else + feature_not_found "nettle" "Install nettle devel" + fi +fi + + ########################################## # VTE probe @@ -4489,6 +4514,7 @@ echo "GTK support $gtk" echo "GNUTLS support $gnutls" echo "GNUTLS hash $gnutls_hash" echo "GNUTLS gcrypt $gnutls_gcrypt" +echo "GNUTLS nettle $gnutls_nettle" echo "VTE support $vte" echo "curses support $curses" echo "curl support $curl" @@ -4856,6 +4882,9 @@ fi if test "$gnutls_gcrypt" = "yes" ; then echo "CONFIG_GNUTLS_GCRYPT=y" >> $config_host_mak fi +if test "$gnutls_nettle" = "yes" ; then + echo "CONFIG_GNUTLS_NETTLE=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-nettle.c b/crypto/cipher-nettle.c new file mode 100644 index 0000000000..e5a14bc139 --- /dev/null +++ b/crypto/cipher-nettle.c @@ -0,0 +1,206 @@ +/* + * QEMU Crypto cipher nettle 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 +#include +#include + +typedef struct QCryptoCipherNettle QCryptoCipherNettle; +struct QCryptoCipherNettle { + void *ctx_encrypt; + void *ctx_decrypt; + nettle_crypt_func *alg_encrypt; + nettle_crypt_func *alg_decrypt; + uint8_t *iv; + size_t niv; +}; + +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; + QCryptoCipherNettle *ctx; + uint8_t *rfbkey; + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + case QCRYPTO_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; + } + + cipher = g_new0(QCryptoCipher, 1); + cipher->alg = alg; + cipher->mode = mode; + + ctx = g_new0(QCryptoCipherNettle, 1); + + switch (alg) { + case QCRYPTO_CIPHER_ALG_DES_RFB: + ctx->ctx_encrypt = g_new0(struct des_ctx, 1); + ctx->ctx_decrypt = NULL; /* 1 ctx can do both */ + rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); + des_set_key(ctx->ctx_encrypt, rfbkey); + g_free(rfbkey); + + ctx->alg_encrypt = (nettle_crypt_func *)des_encrypt; + ctx->alg_decrypt = (nettle_crypt_func *)des_decrypt; + + ctx->niv = DES_BLOCK_SIZE; + break; + + case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALG_AES_256: + ctx->ctx_encrypt = g_new0(struct aes_ctx, 1); + ctx->ctx_decrypt = g_new0(struct aes_ctx, 1); + + aes_set_encrypt_key(ctx->ctx_encrypt, nkey, key); + aes_set_decrypt_key(ctx->ctx_decrypt, nkey, key); + + ctx->alg_encrypt = (nettle_crypt_func *)aes_encrypt; + ctx->alg_decrypt = (nettle_crypt_func *)aes_decrypt; + + ctx->niv = AES_BLOCK_SIZE; + break; + default: + error_setg(errp, "Unsupported cipher algorithm %d", alg); + goto error; + } + + ctx->iv = g_new0(uint8_t, ctx->niv); + cipher->opaque = ctx; + + return cipher; + + error: + g_free(cipher); + g_free(ctx); + return NULL; +} + + +void qcrypto_cipher_free(QCryptoCipher *cipher) +{ + QCryptoCipherNettle *ctx; + + if (!cipher) { + return; + } + + ctx = cipher->opaque; + g_free(ctx->iv); + g_free(ctx->ctx_encrypt); + g_free(ctx->ctx_decrypt); + g_free(ctx); + g_free(cipher); +} + + +int qcrypto_cipher_encrypt(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp) +{ + QCryptoCipherNettle *ctx = cipher->opaque; + + switch (cipher->mode) { + case QCRYPTO_CIPHER_MODE_ECB: + ctx->alg_encrypt(ctx->ctx_encrypt, len, out, in); + break; + + case QCRYPTO_CIPHER_MODE_CBC: + cbc_encrypt(ctx->ctx_encrypt, ctx->alg_encrypt, + ctx->niv, ctx->iv, + len, out, in); + break; + default: + error_setg(errp, "Unsupported cipher algorithm %d", + cipher->alg); + return -1; + } + return 0; +} + + +int qcrypto_cipher_decrypt(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp) +{ + QCryptoCipherNettle *ctx = cipher->opaque; + + switch (cipher->mode) { + case QCRYPTO_CIPHER_MODE_ECB: + ctx->alg_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt, + len, out, in); + break; + + case QCRYPTO_CIPHER_MODE_CBC: + cbc_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt, + ctx->alg_decrypt, ctx->niv, ctx->iv, + len, out, in); + break; + default: + error_setg(errp, "Unsupported cipher algorithm %d", + cipher->alg); + return -1; + } + return 0; +} + +int qcrypto_cipher_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherNettle *ctx = cipher->opaque; + if (niv != ctx->niv) { + error_setg(errp, "Expected IV size %zu not %zu", + ctx->niv, niv); + return -1; + } + memcpy(ctx->iv, iv, niv); + return 0; +} diff --git a/crypto/cipher.c b/crypto/cipher.c index ed4d854a13..024a00cb54 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -47,7 +47,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, return true; } -#if defined(CONFIG_GNUTLS_GCRYPT) +#if defined(CONFIG_GNUTLS_GCRYPT) || defined(CONFIG_GNUTLS_NETTLE) static uint8_t * qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, size_t nkey) @@ -63,10 +63,12 @@ qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, } return ret; } -#endif /* CONFIG_GNUTLS_GCRYPT */ +#endif /* CONFIG_GNUTLS_GCRYPT || CONFIG_GNUTLS_NETTLE */ #ifdef CONFIG_GNUTLS_GCRYPT #include "crypto/cipher-gcrypt.c" +#elif defined CONFIG_GNUTLS_NETTLE +#include "crypto/cipher-nettle.c" #else #include "crypto/cipher-builtin.c" #endif