summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xconfigure14
-rw-r--r--crypto/Makefile.objs3
-rw-r--r--crypto/hash-gcrypt.c106
-rw-r--r--crypto/hash-nettle.c126
-rw-r--r--crypto/hash-stub.c41
-rw-r--r--crypto/hash.c105
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/test-crypto-hash.c24
8 files changed, 299 insertions, 122 deletions
diff --git a/configure b/configure
index 93e4c95274..65bd1ff86c 100755
--- a/configure
+++ b/configure
@@ -306,7 +306,6 @@ gtk=""
 gtkabi=""
 gtk_gl="no"
 gnutls=""
-gnutls_hash=""
 gnutls_rnd=""
 nettle=""
 nettle_kdf="no"
@@ -2218,13 +2217,6 @@ if test "$gnutls" != "no"; then
 	QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags"
         gnutls="yes"
 
-	# gnutls_hash_init requires >= 2.9.10
-	if $pkg_config --exists "gnutls >= 2.9.10"; then
-            gnutls_hash="yes"
-	else
-	    gnutls_hash="no"
-	fi
-
 	# gnutls_rnd requires >= 2.11.0
 	if $pkg_config --exists "gnutls >= 2.11.0"; then
 	    gnutls_rnd="yes"
@@ -2258,11 +2250,9 @@ if test "$gnutls" != "no"; then
 	feature_not_found "gnutls" "Install gnutls devel"
     else
         gnutls="no"
-        gnutls_hash="no"
         gnutls_rnd="no"
     fi
 else
-    gnutls_hash="no"
     gnutls_rnd="no"
 fi
 
@@ -4813,7 +4803,6 @@ echo "GTK support       $gtk $(echo_version $gtk $gtk_version)"
 echo "GTK GL support    $gtk_gl"
 echo "VTE support       $vte $(echo_version $vte $vteversion)"
 echo "GNUTLS support    $gnutls"
-echo "GNUTLS hash       $gnutls_hash"
 echo "GNUTLS rnd        $gnutls_rnd"
 echo "libgcrypt         $gcrypt"
 echo "libgcrypt kdf     $gcrypt_kdf"
@@ -5179,9 +5168,6 @@ fi
 if test "$gnutls" = "yes" ; then
   echo "CONFIG_GNUTLS=y" >> $config_host_mak
 fi
-if test "$gnutls_hash" = "yes" ; then
-  echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak
-fi
 if test "$gnutls_rnd" = "yes" ; then
   echo "CONFIG_GNUTLS_RND=y" >> $config_host_mak
 fi
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index 0737f48118..1f86f4f07f 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -1,5 +1,7 @@
 crypto-obj-y = init.o
 crypto-obj-y += hash.o
+crypto-obj-$(CONFIG_NETTLE) += hash-nettle.o
+crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += hash-gcrypt.o
 crypto-obj-y += aes.o
 crypto-obj-y += desrfb.o
 crypto-obj-y += cipher.o
@@ -28,3 +30,4 @@ crypto-aes-obj-y = aes.o
 
 stub-obj-y += random-stub.o
 stub-obj-y += pbkdf-stub.o
+stub-obj-y += hash-stub.o
diff --git a/crypto/hash-gcrypt.c b/crypto/hash-gcrypt.c
new file mode 100644
index 0000000000..e0456897ac
--- /dev/null
+++ b/crypto/hash-gcrypt.c
@@ -0,0 +1,106 @@
+/*
+ * QEMU Crypto hash algorithms
+ *
+ * Copyright (c) 2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/hash.h"
+#include "gcrypt.h"
+
+
+static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
+    [QCRYPTO_HASH_ALG_MD5] = GCRY_MD_MD5,
+    [QCRYPTO_HASH_ALG_SHA1] = GCRY_MD_SHA1,
+    [QCRYPTO_HASH_ALG_SHA256] = GCRY_MD_SHA256,
+};
+
+gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
+{
+    if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) &&
+        qcrypto_hash_alg_map[alg] != GCRY_MD_NONE) {
+        return true;
+    }
+    return false;
+}
+
+
+int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
+                        const struct iovec *iov,
+                        size_t niov,
+                        uint8_t **result,
+                        size_t *resultlen,
+                        Error **errp)
+{
+    int i, ret;
+    gcry_md_hd_t md;
+    unsigned char *digest;
+
+    if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map) ||
+        qcrypto_hash_alg_map[alg] == GCRY_MD_NONE) {
+        error_setg(errp,
+                   "Unknown hash algorithm %d",
+                   alg);
+        return -1;
+    }
+
+    ret = gcry_md_open(&md, qcrypto_hash_alg_map[alg], 0);
+
+    if (ret < 0) {
+        error_setg(errp,
+                   "Unable to initialize hash algorithm: %s",
+                   gcry_strerror(ret));
+        return -1;
+    }
+
+    for (i = 0; i < niov; i++) {
+        gcry_md_write(md, iov[i].iov_base, iov[i].iov_len);
+    }
+
+    ret = gcry_md_get_algo_dlen(qcrypto_hash_alg_map[alg]);
+    if (ret <= 0) {
+        error_setg(errp,
+                   "Unable to get hash length: %s",
+                   gcry_strerror(ret));
+        goto error;
+    }
+    if (*resultlen == 0) {
+        *resultlen = ret;
+        *result = g_new0(uint8_t, *resultlen);
+    } else if (*resultlen != ret) {
+        error_setg(errp,
+                   "Result buffer size %zu is smaller than hash %d",
+                   *resultlen, ret);
+        goto error;
+    }
+
+    digest = gcry_md_read(md, 0);
+    if (!digest) {
+        error_setg(errp,
+                   "No digest produced");
+        goto error;
+    }
+    memcpy(*result, digest, *resultlen);
+
+    gcry_md_close(md);
+    return 0;
+
+ error:
+    gcry_md_close(md);
+    return -1;
+}
diff --git a/crypto/hash-nettle.c b/crypto/hash-nettle.c
new file mode 100644
index 0000000000..8ec5572aff
--- /dev/null
+++ b/crypto/hash-nettle.c
@@ -0,0 +1,126 @@
+/*
+ * QEMU Crypto hash algorithms
+ *
+ * Copyright (c) 2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/hash.h"
+#include <nettle/md5.h>
+#include <nettle/sha.h>
+
+typedef void (*qcrypto_nettle_init)(void *ctx);
+typedef void (*qcrypto_nettle_write)(void *ctx,
+                                     unsigned int len,
+                                     const uint8_t *buf);
+typedef void (*qcrypto_nettle_result)(void *ctx,
+                                      unsigned int len,
+                                      uint8_t *buf);
+
+union qcrypto_hash_ctx {
+    struct md5_ctx md5;
+    struct sha1_ctx sha1;
+    struct sha256_ctx sha256;
+};
+
+struct qcrypto_hash_alg {
+    qcrypto_nettle_init init;
+    qcrypto_nettle_write write;
+    qcrypto_nettle_result result;
+    size_t len;
+} qcrypto_hash_alg_map[] = {
+    [QCRYPTO_HASH_ALG_MD5] = {
+        .init = (qcrypto_nettle_init)md5_init,
+        .write = (qcrypto_nettle_write)md5_update,
+        .result = (qcrypto_nettle_result)md5_digest,
+        .len = MD5_DIGEST_SIZE,
+    },
+    [QCRYPTO_HASH_ALG_SHA1] = {
+        .init = (qcrypto_nettle_init)sha1_init,
+        .write = (qcrypto_nettle_write)sha1_update,
+        .result = (qcrypto_nettle_result)sha1_digest,
+        .len = SHA1_DIGEST_SIZE,
+    },
+    [QCRYPTO_HASH_ALG_SHA256] = {
+        .init = (qcrypto_nettle_init)sha256_init,
+        .write = (qcrypto_nettle_write)sha256_update,
+        .result = (qcrypto_nettle_result)sha256_digest,
+        .len = SHA256_DIGEST_SIZE,
+    },
+};
+
+gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
+{
+    if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) &&
+        qcrypto_hash_alg_map[alg].init != NULL) {
+        return true;
+    }
+    return false;
+}
+
+
+int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
+                        const struct iovec *iov,
+                        size_t niov,
+                        uint8_t **result,
+                        size_t *resultlen,
+                        Error **errp)
+{
+    int i;
+    union qcrypto_hash_ctx ctx;
+
+    if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map) ||
+        qcrypto_hash_alg_map[alg].init == NULL) {
+        error_setg(errp,
+                   "Unknown hash algorithm %d",
+                   alg);
+        return -1;
+    }
+
+    qcrypto_hash_alg_map[alg].init(&ctx);
+
+    for (i = 0; i < niov; i++) {
+        /* Some versions of nettle have functions
+         * declared with 'int' instead of 'size_t'
+         * so to be safe avoid writing more than
+         * UINT_MAX bytes at a time
+         */
+        size_t len = iov[i].iov_len;
+        uint8_t *base = iov[i].iov_base;
+        while (len) {
+            size_t shortlen = MIN(len, UINT_MAX);
+            qcrypto_hash_alg_map[alg].write(&ctx, len, base);
+            len -= shortlen;
+            base += len;
+        }
+    }
+
+    if (*resultlen == 0) {
+        *resultlen = qcrypto_hash_alg_map[alg].len;
+        *result = g_new0(uint8_t, *resultlen);
+    } else if (*resultlen != qcrypto_hash_alg_map[alg].len) {
+        error_setg(errp,
+                   "Result buffer size %zu is smaller than hash %zu",
+                   *resultlen, qcrypto_hash_alg_map[alg].len);
+        return -1;
+    }
+
+    qcrypto_hash_alg_map[alg].result(&ctx, *resultlen, *result);
+
+    return 0;
+}
diff --git a/crypto/hash-stub.c b/crypto/hash-stub.c
new file mode 100644
index 0000000000..8a9b8d4c09
--- /dev/null
+++ b/crypto/hash-stub.c
@@ -0,0 +1,41 @@
+/*
+ * QEMU Crypto hash algorithms
+ *
+ * Copyright (c) 2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/hash.h"
+
+gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED)
+{
+    return false;
+}
+
+int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
+                        const struct iovec *iov G_GNUC_UNUSED,
+                        size_t niov G_GNUC_UNUSED,
+                        uint8_t **result G_GNUC_UNUSED,
+                        size_t *resultlen G_GNUC_UNUSED,
+                        Error **errp)
+{
+    error_setg(errp,
+               "Hash algorithm %d not supported without GNUTLS",
+               alg);
+    return -1;
+}
diff --git a/crypto/hash.c b/crypto/hash.c
index 2907bffd2e..aa2a26e8a6 100644
--- a/crypto/hash.c
+++ b/crypto/hash.c
@@ -22,12 +22,6 @@
 #include "qapi/error.h"
 #include "crypto/hash.h"
 
-#ifdef CONFIG_GNUTLS_HASH
-#include <gnutls/gnutls.h>
-#include <gnutls/crypto.h>
-#endif
-
-
 static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = {
     [QCRYPTO_HASH_ALG_MD5] = 16,
     [QCRYPTO_HASH_ALG_SHA1] = 20,
@@ -41,105 +35,6 @@ size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg)
 }
 
 
-#ifdef CONFIG_GNUTLS_HASH
-static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
-    [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
-    [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
-    [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
-};
-
-gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
-{
-    if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) {
-        return true;
-    }
-    return false;
-}
-
-
-int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
-                        const struct iovec *iov,
-                        size_t niov,
-                        uint8_t **result,
-                        size_t *resultlen,
-                        Error **errp)
-{
-    int i, ret;
-    gnutls_hash_hd_t dig;
-
-    if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map)) {
-        error_setg(errp,
-                   "Unknown hash algorithm %d",
-                   alg);
-        return -1;
-    }
-
-    ret = gnutls_hash_init(&dig, qcrypto_hash_alg_map[alg]);
-
-    if (ret < 0) {
-        error_setg(errp,
-                   "Unable to initialize hash algorithm: %s",
-                   gnutls_strerror(ret));
-        return -1;
-    }
-
-    for (i = 0; i < niov; i++) {
-        ret = gnutls_hash(dig, iov[i].iov_base, iov[i].iov_len);
-        if (ret < 0) {
-            error_setg(errp,
-                       "Unable process hash data: %s",
-                       gnutls_strerror(ret));
-            goto error;
-        }
-    }
-
-    ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]);
-    if (ret <= 0) {
-        error_setg(errp,
-                   "Unable to get hash length: %s",
-                   gnutls_strerror(ret));
-        goto error;
-    }
-    if (*resultlen == 0) {
-        *resultlen = ret;
-        *result = g_new0(uint8_t, *resultlen);
-    } else if (*resultlen != ret) {
-        error_setg(errp,
-                   "Result buffer size %zu is smaller than hash %d",
-                   *resultlen, ret);
-        goto error;
-    }
-
-    gnutls_hash_deinit(dig, *result);
-    return 0;
-
- error:
-    gnutls_hash_deinit(dig, NULL);
-    return -1;
-}
-
-#else /* ! CONFIG_GNUTLS_HASH */
-
-gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED)
-{
-    return false;
-}
-
-int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
-                        const struct iovec *iov G_GNUC_UNUSED,
-                        size_t niov G_GNUC_UNUSED,
-                        uint8_t **result G_GNUC_UNUSED,
-                        size_t *resultlen G_GNUC_UNUSED,
-                        Error **errp)
-{
-    error_setg(errp,
-               "Hash algorithm %d not supported without GNUTLS",
-               alg);
-    return -1;
-}
-
-#endif /* ! CONFIG_GNUTLS_HASH */
-
 int qcrypto_hash_bytes(QCryptoHashAlgorithm alg,
                        const char *buf,
                        size_t len,
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 6c09962f75..f8e3c6b35a 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -86,7 +86,7 @@ check-unit-y += tests/test-qemu-opts$(EXESUF)
 gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c
 check-unit-y += tests/test-write-threshold$(EXESUF)
 gcov-files-test-write-threshold-y = block/write-threshold.c
-check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF)
+check-unit-y += tests/test-crypto-hash$(EXESUF)
 check-unit-y += tests/test-crypto-cipher$(EXESUF)
 check-unit-y += tests/test-crypto-secret$(EXESUF)
 check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
diff --git a/tests/test-crypto-hash.c b/tests/test-crypto-hash.c
index 6e0e89f7d6..8a55e74458 100644
--- a/tests/test-crypto-hash.c
+++ b/tests/test-crypto-hash.c
@@ -68,6 +68,10 @@ static void test_hash_alloc(void)
         int ret;
         size_t j;
 
+        if (!qcrypto_hash_supports(i)) {
+            continue;
+        }
+
         ret = qcrypto_hash_bytes(i,
                                  INPUT_TEXT,
                                  strlen(INPUT_TEXT),
@@ -98,6 +102,10 @@ static void test_hash_prealloc(void)
         int ret;
         size_t j;
 
+        if (!qcrypto_hash_supports(i)) {
+            continue;
+        }
+
         resultlen = expected_lens[i];
         result = g_new0(uint8_t, resultlen);
 
@@ -137,6 +145,10 @@ static void test_hash_iov(void)
         int ret;
         size_t j;
 
+        if (!qcrypto_hash_supports(i)) {
+            continue;
+        }
+
         ret = qcrypto_hash_bytesv(i,
                                   iov, 3,
                                   &result,
@@ -165,6 +177,10 @@ static void test_hash_digest(void)
         char *digest;
         size_t digestsize;
 
+        if (!qcrypto_hash_supports(i)) {
+            continue;
+        }
+
         digestsize = qcrypto_hash_digest_len(i);
 
         g_assert_cmpint(digestsize * 2, ==, strlen(expected_outputs[i]));
@@ -175,7 +191,7 @@ static void test_hash_digest(void)
                                   &digest,
                                   NULL);
         g_assert(ret == 0);
-        g_assert(g_str_equal(digest, expected_outputs[i]));
+        g_assert_cmpstr(digest, ==, expected_outputs[i]);
         g_free(digest);
     }
 }
@@ -191,13 +207,17 @@ static void test_hash_base64(void)
         int ret;
         char *digest;
 
+        if (!qcrypto_hash_supports(i)) {
+            continue;
+        }
+
         ret = qcrypto_hash_base64(i,
                                   INPUT_TEXT,
                                   strlen(INPUT_TEXT),
                                   &digest,
                                   NULL);
         g_assert(ret == 0);
-        g_assert(g_str_equal(digest, expected_outputs_b64[i]));
+        g_assert_cmpstr(digest, ==, expected_outputs_b64[i]);
         g_free(digest);
     }
 }