Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging

Bugfixes and Daniel Berrange's crypto library.

# gpg: Signature made Wed Jul  8 12:12:29 2015 BST using RSA key ID 78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* remotes/bonzini/tags/for-upstream:
  ossaudio: fix memory leak
  ui: convert VNC to use generic cipher API
  block: convert qcow/qcow2 to use generic cipher API
  ui: convert VNC websockets to use crypto APIs
  block: convert quorum blockdrv to use crypto APIs
  crypto: add a nettle cipher implementation
  crypto: add a gcrypt cipher implementation
  crypto: introduce generic cipher API & built-in implementation
  crypto: move built-in D3DES implementation into crypto/
  crypto: move built-in AES implementation into crypto/
  crypto: introduce new module for computing hash digests
  vl: move rom_load_all after machine init done

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 3d48a6b..411da3c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1052,6 +1052,13 @@
 F: qemu-seccomp.c
 F: include/sysemu/seccomp.h
 
+Cryptography
+M: Daniel P. Berrange <berrange@redhat.com>
+S: Maintained
+F: crypto/
+F: include/crypto/
+F: tests/test-crypto-*
+
 Usermode Emulation
 ------------------
 Overall
diff --git a/Makefile.objs b/Makefile.objs
index 4881d2c..f094eff 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,6 +2,7 @@
 # Common libraries for tools and emulators
 stub-obj-y = stubs/
 util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
+util-obj-y += crypto/
 
 #######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 11e76a1..7dbe333 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -853,6 +853,7 @@
 
     if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
         access(conf->devpath_out, R_OK | W_OK) < 0) {
+        g_free(conf);
         return NULL;
     }
     return conf;
diff --git a/block/Makefile.objs b/block/Makefile.objs
index c34fd7c..58ef2ef 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -3,7 +3,7 @@
 block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
 block-obj-y += qed-check.o
 block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
-block-obj-$(CONFIG_QUORUM) += quorum.o
+block-obj-y += quorum.o
 block-obj-y += parallels.o blkdebug.o blkverify.o
 block-obj-y += block-backend.o snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
diff --git a/block/qcow.c b/block/qcow.c
index 733627f..01fba54 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -26,7 +26,7 @@
 #include "qemu/module.h"
 #include <zlib.h>
 #include "qapi/qmp/qerror.h"
-#include "qemu/aes.h"
+#include "crypto/cipher.h"
 #include "migration/migration.h"
 
 /**************************************************************/
@@ -72,10 +72,8 @@
     uint8_t *cluster_cache;
     uint8_t *cluster_data;
     uint64_t cluster_cache_offset;
-    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+    QCryptoCipher *cipher; /* NULL if no key yet */
     uint32_t crypt_method_header;
-    AES_KEY aes_encrypt_key;
-    AES_KEY aes_decrypt_key;
     CoMutex lock;
     Error *migration_blocker;
 } BDRVQcowState;
@@ -154,6 +152,11 @@
         ret = -EINVAL;
         goto fail;
     }
+    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
+        error_setg(errp, "AES cipher not available");
+        ret = -EINVAL;
+        goto fail;
+    }
     s->crypt_method_header = header.crypt_method;
     if (s->crypt_method_header) {
         bs->encrypted = 1;
@@ -260,6 +263,7 @@
     BDRVQcowState *s = bs->opaque;
     uint8_t keybuf[16];
     int len, i;
+    Error *err;
 
     memset(keybuf, 0, 16);
     len = strlen(key);
@@ -271,38 +275,67 @@
         keybuf[i] = key[i];
     }
     assert(bs->encrypted);
-    s->crypt_method = s->crypt_method_header;
 
-    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
+    qcrypto_cipher_free(s->cipher);
+    s->cipher = qcrypto_cipher_new(
+        QCRYPTO_CIPHER_ALG_AES_128,
+        QCRYPTO_CIPHER_MODE_CBC,
+        keybuf, G_N_ELEMENTS(keybuf),
+        &err);
+
+    if (!s->cipher) {
+        /* XXX would be nice if errors in this method could
+         * be properly propagate to the caller. Would need
+         * the bdrv_set_key() API signature to be fixed. */
+        error_free(err);
         return -1;
-    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
-        return -1;
+    }
     return 0;
 }
 
 /* The crypt function is compatible with the linux cryptoloop
    algorithm for < 4 GB images. NOTE: out_buf == in_buf is
    supported */
-static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
-                            uint8_t *out_buf, const uint8_t *in_buf,
-                            int nb_sectors, int enc,
-                            const AES_KEY *key)
+static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+                           uint8_t *out_buf, const uint8_t *in_buf,
+                           int nb_sectors, bool enc, Error **errp)
 {
     union {
         uint64_t ll[2];
         uint8_t b[16];
     } ivec;
     int i;
+    int ret;
 
     for(i = 0; i < nb_sectors; i++) {
         ivec.ll[0] = cpu_to_le64(sector_num);
         ivec.ll[1] = 0;
-        AES_cbc_encrypt(in_buf, out_buf, 512, key,
-                        ivec.b, enc);
+        if (qcrypto_cipher_setiv(s->cipher,
+                                 ivec.b, G_N_ELEMENTS(ivec.b),
+                                 errp) < 0) {
+            return -1;
+        }
+        if (enc) {
+            ret = qcrypto_cipher_encrypt(s->cipher,
+                                         in_buf,
+                                         out_buf,
+                                         512,
+                                         errp);
+        } else {
+            ret = qcrypto_cipher_decrypt(s->cipher,
+                                         in_buf,
+                                         out_buf,
+                                         512,
+                                         errp);
+        }
+        if (ret < 0) {
+            return -1;
+        }
         sector_num++;
         in_buf += 512;
         out_buf += 512;
     }
+    return 0;
 }
 
 /* 'allocate' is:
@@ -416,15 +449,20 @@
                 if (bs->encrypted &&
                     (n_end - n_start) < s->cluster_sectors) {
                     uint64_t start_sect;
-                    assert(s->crypt_method);
+                    assert(s->cipher);
                     start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
                     memset(s->cluster_data + 512, 0x00, 512);
                     for(i = 0; i < s->cluster_sectors; i++) {
                         if (i < n_start || i >= n_end) {
-                            encrypt_sectors(s, start_sect + i,
-                                            s->cluster_data,
-                                            s->cluster_data + 512, 1, 1,
-                                            &s->aes_encrypt_key);
+                            Error *err = NULL;
+                            if (encrypt_sectors(s, start_sect + i,
+                                                s->cluster_data,
+                                                s->cluster_data + 512, 1,
+                                                true, &err) < 0) {
+                                error_free(err);
+                                errno = EIO;
+                                return -1;
+                            }
                             if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
                                             s->cluster_data, 512) != 512)
                                 return -1;
@@ -464,7 +502,7 @@
     if (!cluster_offset) {
         return 0;
     }
-    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) {
+    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) {
         return BDRV_BLOCK_DATA;
     }
     cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
@@ -531,6 +569,7 @@
     QEMUIOVector hd_qiov;
     uint8_t *buf;
     void *orig_buf;
+    Error *err = NULL;
 
     if (qiov->niov > 1) {
         buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
@@ -594,10 +633,11 @@
                 break;
             }
             if (bs->encrypted) {
-                assert(s->crypt_method);
-                encrypt_sectors(s, sector_num, buf, buf,
-                                n, 0,
-                                &s->aes_decrypt_key);
+                assert(s->cipher);
+                if (encrypt_sectors(s, sector_num, buf, buf,
+                                    n, false, &err) < 0) {
+                    goto fail;
+                }
             }
         }
         ret = 0;
@@ -618,6 +658,7 @@
     return ret;
 
 fail:
+    error_free(err);
     ret = -EIO;
     goto done;
 }
@@ -666,12 +707,17 @@
             break;
         }
         if (bs->encrypted) {
-            assert(s->crypt_method);
+            Error *err = NULL;
+            assert(s->cipher);
             if (!cluster_data) {
                 cluster_data = g_malloc0(s->cluster_size);
             }
-            encrypt_sectors(s, sector_num, cluster_data, buf,
-                            n, 1, &s->aes_encrypt_key);
+            if (encrypt_sectors(s, sector_num, cluster_data, buf,
+                                n, true, &err) < 0) {
+                error_free(err);
+                ret = -EIO;
+                break;
+            }
             src_buf = cluster_data;
         } else {
             src_buf = buf;
@@ -708,6 +754,8 @@
 {
     BDRVQcowState *s = bs->opaque;
 
+    qcrypto_cipher_free(s->cipher);
+    s->cipher = NULL;
     g_free(s->l1_table);
     qemu_vfree(s->l2_cache);
     g_free(s->cluster_cache);
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 1a5c97a..b43f186 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -339,26 +339,47 @@
 /* The crypt function is compatible with the linux cryptoloop
    algorithm for < 4 GB images. NOTE: out_buf == in_buf is
    supported */
-void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
-                           uint8_t *out_buf, const uint8_t *in_buf,
-                           int nb_sectors, int enc,
-                           const AES_KEY *key)
+int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+                          uint8_t *out_buf, const uint8_t *in_buf,
+                          int nb_sectors, bool enc,
+                          Error **errp)
 {
     union {
         uint64_t ll[2];
         uint8_t b[16];
     } ivec;
     int i;
+    int ret;
 
     for(i = 0; i < nb_sectors; i++) {
         ivec.ll[0] = cpu_to_le64(sector_num);
         ivec.ll[1] = 0;
-        AES_cbc_encrypt(in_buf, out_buf, 512, key,
-                        ivec.b, enc);
+        if (qcrypto_cipher_setiv(s->cipher,
+                                 ivec.b, G_N_ELEMENTS(ivec.b),
+                                 errp) < 0) {
+            return -1;
+        }
+        if (enc) {
+            ret = qcrypto_cipher_encrypt(s->cipher,
+                                         in_buf,
+                                         out_buf,
+                                         512,
+                                         errp);
+        } else {
+            ret = qcrypto_cipher_decrypt(s->cipher,
+                                         in_buf,
+                                         out_buf,
+                                         512,
+                                         errp);
+        }
+        if (ret < 0) {
+            return -1;
+        }
         sector_num++;
         in_buf += 512;
         out_buf += 512;
     }
+    return 0;
 }
 
 static int coroutine_fn copy_sectors(BlockDriverState *bs,
@@ -401,10 +422,15 @@
     }
 
     if (bs->encrypted) {
-        assert(s->crypt_method);
-        qcow2_encrypt_sectors(s, start_sect + n_start,
-                        iov.iov_base, iov.iov_base, n, 1,
-                        &s->aes_encrypt_key);
+        Error *err = NULL;
+        assert(s->cipher);
+        if (qcow2_encrypt_sectors(s, start_sect + n_start,
+                                  iov.iov_base, iov.iov_base, n,
+                                  true, &err) < 0) {
+            ret = -EIO;
+            error_free(err);
+            goto out;
+        }
     }
 
     ret = qcow2_pre_write_overlap_check(bs, 0,
diff --git a/block/qcow2.c b/block/qcow2.c
index d522ec7..76c331b 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -25,7 +25,6 @@
 #include "block/block_int.h"
 #include "qemu/module.h"
 #include <zlib.h>
-#include "qemu/aes.h"
 #include "block/qcow2.h"
 #include "qemu/error-report.h"
 #include "qapi/qmp/qerror.h"
@@ -699,6 +698,11 @@
         ret = -EINVAL;
         goto fail;
     }
+    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
+        error_setg(errp, "AES cipher not available");
+        ret = -EINVAL;
+        goto fail;
+    }
     s->crypt_method_header = header.crypt_method;
     if (s->crypt_method_header) {
         bs->encrypted = 1;
@@ -1032,6 +1036,7 @@
     BDRVQcowState *s = bs->opaque;
     uint8_t keybuf[16];
     int len, i;
+    Error *err = NULL;
 
     memset(keybuf, 0, 16);
     len = strlen(key);
@@ -1043,30 +1048,21 @@
         keybuf[i] = key[i];
     }
     assert(bs->encrypted);
-    s->crypt_method = s->crypt_method_header;
 
-    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
+    qcrypto_cipher_free(s->cipher);
+    s->cipher = qcrypto_cipher_new(
+        QCRYPTO_CIPHER_ALG_AES_128,
+        QCRYPTO_CIPHER_MODE_CBC,
+        keybuf, G_N_ELEMENTS(keybuf),
+        &err);
+
+    if (!s->cipher) {
+        /* XXX would be nice if errors in this method could
+         * be properly propagate to the caller. Would need
+         * the bdrv_set_key() API signature to be fixed. */
+        error_free(err);
         return -1;
-    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
-        return -1;
-#if 0
-    /* test */
-    {
-        uint8_t in[16];
-        uint8_t out[16];
-        uint8_t tmp[16];
-        for(i=0;i<16;i++)
-            in[i] = i;
-        AES_encrypt(in, tmp, &s->aes_encrypt_key);
-        AES_decrypt(tmp, out, &s->aes_decrypt_key);
-        for(i = 0; i < 16; i++)
-            printf(" %02x", tmp[i]);
-        printf("\n");
-        for(i = 0; i < 16; i++)
-            printf(" %02x", out[i]);
-        printf("\n");
     }
-#endif
     return 0;
 }
 
@@ -1109,7 +1105,7 @@
     }
 
     if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
-        !s->crypt_method) {
+        !s->cipher) {
         index_in_cluster = sector_num & (s->cluster_sectors - 1);
         cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
         status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
@@ -1159,7 +1155,7 @@
 
         /* prepare next request */
         cur_nr_sectors = remaining_sectors;
-        if (s->crypt_method) {
+        if (s->cipher) {
             cur_nr_sectors = MIN(cur_nr_sectors,
                 QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
         }
@@ -1231,7 +1227,7 @@
             }
 
             if (bs->encrypted) {
-                assert(s->crypt_method);
+                assert(s->cipher);
 
                 /*
                  * For encrypted images, read everything into a temporary
@@ -1264,9 +1260,15 @@
                 goto fail;
             }
             if (bs->encrypted) {
-                assert(s->crypt_method);
-                qcow2_encrypt_sectors(s, sector_num,  cluster_data,
-                    cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key);
+                assert(s->cipher);
+                Error *err = NULL;
+                if (qcow2_encrypt_sectors(s, sector_num,  cluster_data,
+                                          cluster_data, cur_nr_sectors, false,
+                                          &err) < 0) {
+                    error_free(err);
+                    ret = -EIO;
+                    goto fail;
+                }
                 qemu_iovec_from_buf(qiov, bytes_done,
                     cluster_data, 512 * cur_nr_sectors);
             }
@@ -1344,7 +1346,8 @@
             cur_nr_sectors * 512);
 
         if (bs->encrypted) {
-            assert(s->crypt_method);
+            Error *err = NULL;
+            assert(s->cipher);
             if (!cluster_data) {
                 cluster_data = qemu_try_blockalign(bs->file,
                                                    QCOW_MAX_CRYPT_CLUSTERS
@@ -1359,8 +1362,13 @@
                    QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
             qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
 
-            qcow2_encrypt_sectors(s, sector_num, cluster_data,
-                cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key);
+            if (qcow2_encrypt_sectors(s, sector_num, cluster_data,
+                                      cluster_data, cur_nr_sectors,
+                                      true, &err) < 0) {
+                error_free(err);
+                ret = -EIO;
+                goto fail;
+            }
 
             qemu_iovec_reset(&hd_qiov);
             qemu_iovec_add(&hd_qiov, cluster_data,
@@ -1466,6 +1474,9 @@
     qcow2_cache_destroy(bs, s->l2_table_cache);
     qcow2_cache_destroy(bs, s->refcount_block_cache);
 
+    qcrypto_cipher_free(s->cipher);
+    s->cipher = NULL;
+
     g_free(s->unknown_header_fields);
     cleanup_unknown_header_ext(bs);
 
@@ -1482,9 +1493,7 @@
 {
     BDRVQcowState *s = bs->opaque;
     int flags = s->flags;
-    AES_KEY aes_encrypt_key;
-    AES_KEY aes_decrypt_key;
-    uint32_t crypt_method = 0;
+    QCryptoCipher *cipher = NULL;
     QDict *options;
     Error *local_err = NULL;
     int ret;
@@ -1494,12 +1503,8 @@
      * that means we don't have to worry about reopening them here.
      */
 
-    if (bs->encrypted) {
-        assert(s->crypt_method);
-        crypt_method = s->crypt_method;
-        memcpy(&aes_encrypt_key, &s->aes_encrypt_key, sizeof(aes_encrypt_key));
-        memcpy(&aes_decrypt_key, &s->aes_decrypt_key, sizeof(aes_decrypt_key));
-    }
+    cipher = s->cipher;
+    s->cipher = NULL;
 
     qcow2_close(bs);
 
@@ -1524,11 +1529,7 @@
         return;
     }
 
-    if (bs->encrypted) {
-        s->crypt_method = crypt_method;
-        memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
-        memcpy(&s->aes_decrypt_key, &aes_decrypt_key, sizeof(aes_decrypt_key));
-    }
+    s->cipher = cipher;
 }
 
 static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
@@ -2729,8 +2730,9 @@
             backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
         } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
             encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
-                                        s->crypt_method);
-            if (encrypt != !!s->crypt_method) {
+                                        !!s->cipher);
+
+            if (encrypt != !!s->cipher) {
                 fprintf(stderr, "Changing the encryption flag is not "
                         "supported.\n");
                 return -ENOTSUP;
diff --git a/block/qcow2.h b/block/qcow2.h
index 5936d29..72e1328 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -25,7 +25,7 @@
 #ifndef BLOCK_QCOW2_H
 #define BLOCK_QCOW2_H
 
-#include "qemu/aes.h"
+#include "crypto/cipher.h"
 #include "block/coroutine.h"
 
 //#define DEBUG_ALLOC
@@ -253,10 +253,8 @@
 
     CoMutex lock;
 
-    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+    QCryptoCipher *cipher; /* current cipher, NULL if no key yet */
     uint32_t crypt_method_header;
-    AES_KEY aes_encrypt_key;
-    AES_KEY aes_decrypt_key;
     uint64_t snapshots_offset;
     int snapshots_size;
     unsigned int nb_snapshots;
@@ -536,10 +534,9 @@
 int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
 void qcow2_l2_cache_reset(BlockDriverState *bs);
 int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
-void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
-                     uint8_t *out_buf, const uint8_t *in_buf,
-                     int nb_sectors, int enc,
-                     const AES_KEY *key);
+int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+                          uint8_t *out_buf, const uint8_t *in_buf,
+                          int nb_sectors, bool enc, Error **errp);
 
 int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
     int *num, uint64_t *cluster_offset);
diff --git a/block/quorum.c b/block/quorum.c
index a7df17c..4e66221 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -13,8 +13,6 @@
  * See the COPYING file in the top-level directory.
  */
 
-#include <gnutls/gnutls.h>
-#include <gnutls/crypto.h>
 #include "block/block_int.h"
 #include "qapi/qmp/qbool.h"
 #include "qapi/qmp/qdict.h"
@@ -24,6 +22,7 @@
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qstring.h"
 #include "qapi-event.h"
+#include "crypto/hash.h"
 
 #define HASH_LENGTH 32
 
@@ -34,7 +33,7 @@
 
 /* This union holds a vote hash value */
 typedef union QuorumVoteValue {
-    char h[HASH_LENGTH];       /* SHA-256 hash */
+    uint8_t h[HASH_LENGTH];    /* SHA-256 hash */
     int64_t l;                 /* simpler 64 bits hash */
 } QuorumVoteValue;
 
@@ -428,25 +427,21 @@
 
 static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash)
 {
-    int j, ret;
-    gnutls_hash_hd_t dig;
     QEMUIOVector *qiov = &acb->qcrs[i].qiov;
+    size_t len = sizeof(hash->h);
+    uint8_t *data = hash->h;
 
-    ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256);
-
-    if (ret < 0) {
-        return ret;
+    /* XXX - would be nice if we could pass in the Error **
+     * and propagate that back, but this quorum code is
+     * restricted to just errno values currently */
+    if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256,
+                            qiov->iov, qiov->niov,
+                            &data, &len,
+                            NULL) < 0) {
+        return -EINVAL;
     }
 
-    for (j = 0; j < qiov->niov; j++) {
-        ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len);
-        if (ret < 0) {
-            break;
-        }
-    }
-
-    gnutls_hash_deinit(dig, (void *) hash);
-    return ret;
+    return 0;
 }
 
 static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes)
@@ -870,6 +865,12 @@
     int i;
     int ret = 0;
 
+    if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA256)) {
+        error_setg(errp,
+                   "SHA256 hash support is required for quorum device");
+        return -EINVAL;
+    }
+
     qdict_flatten(options);
 
     /* count how many different children are present */
diff --git a/configure b/configure
index c89b1ea..33b9455 100755
--- a/configure
+++ b/configure
@@ -246,7 +246,6 @@
 vnc_sasl=""
 vnc_jpeg=""
 vnc_png=""
-vnc_ws=""
 xen=""
 xen_ctrl_version=""
 xen_pci_passthrough=""
@@ -331,11 +330,12 @@
 archipelago="no"
 gtk=""
 gtkabi=""
+gnutls=""
+gnutls_hash=""
 vte=""
 tpm="yes"
 libssh2=""
 vhdx=""
-quorum=""
 numa=""
 tcmalloc="no"
 
@@ -896,10 +896,6 @@
   ;;
   --enable-vnc-png) vnc_png="yes"
   ;;
-  --disable-vnc-ws) vnc_ws="no"
-  ;;
-  --enable-vnc-ws) vnc_ws="yes"
-  ;;
   --disable-slirp) slirp="no"
   ;;
   --disable-uuid) uuid="no"
@@ -1119,6 +1115,10 @@
   ;;
   --enable-gtk) gtk="yes"
   ;;
+  --disable-gnutls) gnutls="no"
+  ;;
+  --enable-gnutls) gnutls="yes"
+  ;;
   --enable-rdma) rdma="yes"
   ;;
   --disable-rdma) rdma="no"
@@ -1141,10 +1141,6 @@
   ;;
   --disable-vhdx) vhdx="no"
   ;;
-  --disable-quorum) quorum="no"
-  ;;
-  --enable-quorum) quorum="yes"
-  ;;
   --disable-numa) numa="no"
   ;;
   --enable-numa) numa="yes"
@@ -1329,6 +1325,7 @@
   debug-info      debugging information
   sparse          sparse checker
 
+  gnutls          GNUTLS cryptography support
   sdl             SDL UI
   --with-sdlabi     select preferred SDL ABI 1.2 or 2.0
   gtk             gtk UI
@@ -1376,7 +1373,6 @@
   tpm             TPM support
   libssh2         ssh block device support
   vhdx            support for the Microsoft VHDX image format
-  quorum          quorum block filter support
   numa            libnuma support
   tcmalloc        tcmalloc support
 
@@ -2116,6 +2112,86 @@
     fi
 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`
+        gnutls_libs=`$pkg_config --libs gnutls`
+        libs_softmmu="$gnutls_libs $libs_softmmu"
+        libs_tools="$gnutls_libs $libs_tools"
+	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
+
+	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
+		    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"
+    else
+        gnutls="no"
+        gnutls_hash="no"
+    fi
+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
+
+
+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
 
@@ -2263,7 +2339,7 @@
 
 ##########################################
 # VNC TLS/WS detection
-if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then
+if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then
   cat > $TMPC <<EOF
 #include <gnutls/gnutls.h>
 int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
@@ -2274,51 +2350,17 @@
     if test "$vnc_tls" != "no" ; then
       vnc_tls=yes
     fi
-    if test "$vnc_ws" != "no" ; then
-      vnc_ws=yes
-    fi
     libs_softmmu="$vnc_tls_libs $libs_softmmu"
     QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
   else
     if test "$vnc_tls" = "yes" ; then
       feature_not_found "vnc-tls" "Install gnutls devel"
     fi
-    if test "$vnc_ws" = "yes" ; then
-      feature_not_found "vnc-ws" "Install gnutls devel"
-    fi
     vnc_tls=no
-    vnc_ws=no
   fi
 fi
 
 ##########################################
-# Quorum probe (check for gnutls)
-if test "$quorum" != "no" ; then
-cat > $TMPC <<EOF
-#include <gnutls/gnutls.h>
-#include <gnutls/crypto.h>
-int main(void) {char data[4096], digest[32];
-gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest);
-return 0;
-}
-EOF
-quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
-quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
-if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then
-  qcow_tls=yes
-  libs_softmmu="$quorum_tls_libs $libs_softmmu"
-  libs_tools="$quorum_tls_libs $libs_softmmu"
-  QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags"
-  quorum="yes"
-else
-  if test "$quorum" = "yes"; then
-    feature_not_found "gnutls" "gnutls > 2.10.0 required to compile Quorum"
-  fi
-  quorum="no"
-fi
-fi
-
-##########################################
 # VNC SASL detection
 if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then
   cat > $TMPC <<EOF
@@ -4445,6 +4487,10 @@
 echo "pixman            $pixman"
 echo "SDL support       $sdl"
 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"
@@ -4459,7 +4505,6 @@
     echo "VNC SASL support  $vnc_sasl"
     echo "VNC JPEG support  $vnc_jpeg"
     echo "VNC PNG support   $vnc_png"
-    echo "VNC WS support    $vnc_ws"
 fi
 if test -n "$sparc_cpu"; then
     echo "Target Sparc Arch $sparc_cpu"
@@ -4523,7 +4568,6 @@
 echo "TPM passthrough   $tpm_passthrough"
 echo "QOM debugging     $qom_cast_debug"
 echo "vhdx              $vhdx"
-echo "Quorum            $quorum"
 echo "lzo support       $lzo"
 echo "snappy support    $snappy"
 echo "bzip2 support     $bzip2"
@@ -4676,10 +4720,6 @@
 if test "$vnc_png" = "yes" ; then
   echo "CONFIG_VNC_PNG=y" >> $config_host_mak
 fi
-if test "$vnc_ws" = "yes" ; then
-  echo "CONFIG_VNC_WS=y" >> $config_host_mak
-  echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak
-fi
 if test "$fnmatch" = "yes" ; then
   echo "CONFIG_FNMATCH=y" >> $config_host_mak
 fi
@@ -4807,6 +4847,18 @@
   echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
   echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
 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_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
@@ -4996,10 +5048,6 @@
   echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
 fi
 
-if test "$quorum" = "yes" ; then
-  echo "CONFIG_QUORUM=y" >> $config_host_mak
-fi
-
 if test "$vhdx" = "yes" ; then
   echo "CONFIG_VHDX=y" >> $config_host_mak
 fi
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
new file mode 100644
index 0000000..b050138
--- /dev/null
+++ b/crypto/Makefile.objs
@@ -0,0 +1,5 @@
+util-obj-y += init.o
+util-obj-y += hash.o
+util-obj-y += aes.o
+util-obj-y += desrfb.o
+util-obj-y += cipher.o
diff --git a/util/aes.c b/crypto/aes.c
similarity index 99%
rename from util/aes.c
rename to crypto/aes.c
index 3d7c4be..244a388 100644
--- a/util/aes.c
+++ b/crypto/aes.c
@@ -28,7 +28,7 @@
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "qemu-common.h"
-#include "qemu/aes.h"
+#include "crypto/aes.h"
 
 typedef uint32_t u32;
 typedef uint8_t u8;
diff --git a/crypto/cipher-builtin.c b/crypto/cipher-builtin.c
new file mode 100644
index 0000000..c625cb4
--- /dev/null
+++ b/crypto/cipher-builtin.c
@@ -0,0 +1,398 @@
+/*
+ * QEMU Crypto cipher built-in 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 "crypto/aes.h"
+#include "crypto/desrfb.h"
+
+typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
+struct QCryptoCipherBuiltinAES {
+    AES_KEY encrypt_key;
+    AES_KEY decrypt_key;
+    uint8_t *iv;
+    size_t niv;
+};
+typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB;
+struct QCryptoCipherBuiltinDESRFB {
+    uint8_t *key;
+    size_t nkey;
+};
+
+typedef struct QCryptoCipherBuiltin QCryptoCipherBuiltin;
+struct QCryptoCipherBuiltin {
+    union {
+        QCryptoCipherBuiltinAES aes;
+        QCryptoCipherBuiltinDESRFB desrfb;
+    } state;
+    void (*free)(QCryptoCipher *cipher);
+    int (*setiv)(QCryptoCipher *cipher,
+                 const uint8_t *iv, size_t niv,
+                 Error **errp);
+    int (*encrypt)(QCryptoCipher *cipher,
+                   const void *in,
+                   void *out,
+                   size_t len,
+                   Error **errp);
+    int (*decrypt)(QCryptoCipher *cipher,
+                   const void *in,
+                   void *out,
+                   size_t len,
+                   Error **errp);
+};
+
+
+static void qcrypto_cipher_free_aes(QCryptoCipher *cipher)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+
+    g_free(ctxt->state.aes.iv);
+    g_free(ctxt);
+    cipher->opaque = NULL;
+}
+
+
+static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher,
+                                      const void *in,
+                                      void *out,
+                                      size_t len,
+                                      Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+
+    if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) {
+        const uint8_t *inptr = in;
+        uint8_t *outptr = out;
+        while (len) {
+            if (len > AES_BLOCK_SIZE) {
+                AES_encrypt(inptr, outptr, &ctxt->state.aes.encrypt_key);
+                inptr += AES_BLOCK_SIZE;
+                outptr += AES_BLOCK_SIZE;
+                len -= AES_BLOCK_SIZE;
+            } else {
+                uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
+                memcpy(tmp1, inptr, len);
+                /* Fill with 0 to avoid valgrind uninitialized reads */
+                memset(tmp1 + len, 0, sizeof(tmp1) - len);
+                AES_encrypt(tmp1, tmp2, &ctxt->state.aes.encrypt_key);
+                memcpy(outptr, tmp2, len);
+                len = 0;
+            }
+        }
+    } else {
+        AES_cbc_encrypt(in, out, len,
+                        &ctxt->state.aes.encrypt_key,
+                        ctxt->state.aes.iv, 1);
+    }
+
+    return 0;
+}
+
+
+static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher,
+                                      const void *in,
+                                      void *out,
+                                      size_t len,
+                                      Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+
+    if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) {
+        const uint8_t *inptr = in;
+        uint8_t *outptr = out;
+        while (len) {
+            if (len > AES_BLOCK_SIZE) {
+                AES_decrypt(inptr, outptr, &ctxt->state.aes.encrypt_key);
+                inptr += AES_BLOCK_SIZE;
+                outptr += AES_BLOCK_SIZE;
+                len -= AES_BLOCK_SIZE;
+            } else {
+                uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
+                memcpy(tmp1, inptr, len);
+                /* Fill with 0 to avoid valgrind uninitialized reads */
+                memset(tmp1 + len, 0, sizeof(tmp1) - len);
+                AES_decrypt(tmp1, tmp2, &ctxt->state.aes.encrypt_key);
+                memcpy(outptr, tmp2, len);
+                len = 0;
+            }
+        }
+    } else {
+        AES_cbc_encrypt(in, out, len,
+                        &ctxt->state.aes.encrypt_key,
+                        ctxt->state.aes.iv, 1);
+    }
+
+    return 0;
+}
+
+static int qcrypto_cipher_setiv_aes(QCryptoCipher *cipher,
+                                     const uint8_t *iv, size_t niv,
+                                     Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+    if (niv != 16) {
+        error_setg(errp, "IV must be 16 bytes not %zu", niv);
+        return -1;
+    }
+
+    g_free(ctxt->state.aes.iv);
+    ctxt->state.aes.iv = g_new0(uint8_t, niv);
+    memcpy(ctxt->state.aes.iv, iv, niv);
+    ctxt->state.aes.niv = niv;
+
+    return 0;
+}
+
+
+
+
+static int qcrypto_cipher_init_aes(QCryptoCipher *cipher,
+                                   const uint8_t *key, size_t nkey,
+                                   Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt;
+
+    if (cipher->mode != QCRYPTO_CIPHER_MODE_CBC &&
+        cipher->mode != QCRYPTO_CIPHER_MODE_ECB) {
+        error_setg(errp, "Unsupported cipher mode %d", cipher->mode);
+        return -1;
+    }
+
+    ctxt = g_new0(QCryptoCipherBuiltin, 1);
+
+    if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.encrypt_key) != 0) {
+        error_setg(errp, "Failed to set encryption key");
+        goto error;
+    }
+
+    if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.decrypt_key) != 0) {
+        error_setg(errp, "Failed to set decryption key");
+        goto error;
+    }
+
+    ctxt->free = qcrypto_cipher_free_aes;
+    ctxt->setiv = qcrypto_cipher_setiv_aes;
+    ctxt->encrypt = qcrypto_cipher_encrypt_aes;
+    ctxt->decrypt = qcrypto_cipher_decrypt_aes;
+
+    cipher->opaque = ctxt;
+
+    return 0;
+
+ error:
+    g_free(ctxt);
+    return -1;
+}
+
+
+static void qcrypto_cipher_free_des_rfb(QCryptoCipher *cipher)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+
+    g_free(ctxt->state.desrfb.key);
+    g_free(ctxt);
+    cipher->opaque = NULL;
+}
+
+
+static int qcrypto_cipher_encrypt_des_rfb(QCryptoCipher *cipher,
+                                          const void *in,
+                                          void *out,
+                                          size_t len,
+                                          Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+    size_t i;
+
+    if (len % 8) {
+        error_setg(errp, "Buffer size must be multiple of 8 not %zu",
+                   len);
+        return -1;
+    }
+
+    deskey(ctxt->state.desrfb.key, EN0);
+
+    for (i = 0; i < len; i += 8) {
+        des((void *)in + i, out + i);
+    }
+
+    return 0;
+}
+
+
+static int qcrypto_cipher_decrypt_des_rfb(QCryptoCipher *cipher,
+                                          const void *in,
+                                          void *out,
+                                          size_t len,
+                                          Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+    size_t i;
+
+    if (len % 8) {
+        error_setg(errp, "Buffer size must be multiple of 8 not %zu",
+                   len);
+        return -1;
+    }
+
+    deskey(ctxt->state.desrfb.key, DE1);
+
+    for (i = 0; i < len; i += 8) {
+        des((void *)in + i, out + i);
+    }
+
+    return 0;
+}
+
+
+static int qcrypto_cipher_setiv_des_rfb(QCryptoCipher *cipher,
+                                        const uint8_t *iv, size_t niv,
+                                        Error **errp)
+{
+    error_setg(errp, "Setting IV is not supported");
+    return -1;
+}
+
+
+static int qcrypto_cipher_init_des_rfb(QCryptoCipher *cipher,
+                                       const uint8_t *key, size_t nkey,
+                                       Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt;
+
+    if (cipher->mode != QCRYPTO_CIPHER_MODE_ECB) {
+        error_setg(errp, "Unsupported cipher mode %d", cipher->mode);
+        return -1;
+    }
+
+    ctxt = g_new0(QCryptoCipherBuiltin, 1);
+
+    ctxt->state.desrfb.key = g_new0(uint8_t, nkey);
+    memcpy(ctxt->state.desrfb.key, key, nkey);
+    ctxt->state.desrfb.nkey = nkey;
+
+    ctxt->free = qcrypto_cipher_free_des_rfb;
+    ctxt->setiv = qcrypto_cipher_setiv_des_rfb;
+    ctxt->encrypt = qcrypto_cipher_encrypt_des_rfb;
+    ctxt->decrypt = qcrypto_cipher_decrypt_des_rfb;
+
+    cipher->opaque = ctxt;
+
+    return 0;
+}
+
+
+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;
+
+    cipher = g_new0(QCryptoCipher, 1);
+    cipher->alg = alg;
+    cipher->mode = mode;
+
+    if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
+        goto error;
+    }
+
+    switch (cipher->alg) {
+    case QCRYPTO_CIPHER_ALG_DES_RFB:
+        if (qcrypto_cipher_init_des_rfb(cipher, key, nkey, errp) < 0) {
+            goto error;
+        }
+        break;
+    case QCRYPTO_CIPHER_ALG_AES_128:
+    case QCRYPTO_CIPHER_ALG_AES_192:
+    case QCRYPTO_CIPHER_ALG_AES_256:
+        if (qcrypto_cipher_init_aes(cipher, key, nkey, errp) < 0) {
+            goto error;
+        }
+        break;
+    default:
+        error_setg(errp,
+                   "Unsupported cipher algorithm %d", cipher->alg);
+        goto error;
+    }
+
+    return cipher;
+
+ error:
+    g_free(cipher);
+    return NULL;
+}
+
+void qcrypto_cipher_free(QCryptoCipher *cipher)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+    if (!cipher) {
+        return;
+    }
+
+    ctxt->free(cipher);
+    g_free(cipher);
+}
+
+
+int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
+                           const void *in,
+                           void *out,
+                           size_t len,
+                           Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+
+    return ctxt->encrypt(cipher, in, out, len, errp);
+}
+
+
+int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
+                           const void *in,
+                           void *out,
+                           size_t len,
+                           Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+
+    return ctxt->decrypt(cipher, in, out, len, errp);
+}
+
+
+int qcrypto_cipher_setiv(QCryptoCipher *cipher,
+                         const uint8_t *iv, size_t niv,
+                         Error **errp)
+{
+    QCryptoCipherBuiltin *ctxt = cipher->opaque;
+
+    return ctxt->setiv(cipher, iv, niv, errp);
+}
diff --git a/crypto/cipher-gcrypt.c b/crypto/cipher-gcrypt.c
new file mode 100644
index 0000000..8cfc562
--- /dev/null
+++ b/crypto/cipher-gcrypt.c
@@ -0,0 +1,195 @@
+/*
+ * 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;
+}
diff --git a/crypto/cipher-nettle.c b/crypto/cipher-nettle.c
new file mode 100644
index 0000000..e5a14bc
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <nettle/nettle-types.h>
+#include <nettle/aes.h>
+#include <nettle/des.h>
+#include <nettle/cbc.h>
+
+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
new file mode 100644
index 0000000..024a00c
--- /dev/null
+++ b/crypto/cipher.c
@@ -0,0 +1,74 @@
+/*
+ * QEMU Crypto cipher 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 "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,
+    [QCRYPTO_CIPHER_ALG_AES_256] = 32,
+    [QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
+};
+
+static bool
+qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
+                                   size_t nkey,
+                                   Error **errp)
+{
+    if ((unsigned)alg >= QCRYPTO_CIPHER_ALG_LAST) {
+        error_setg(errp, "Cipher algorithm %d out of range",
+                   alg);
+        return false;
+    }
+
+    if (alg_key_len[alg] != nkey) {
+        error_setg(errp, "Cipher key length %zu should be %zu",
+                   alg_key_len[alg], nkey);
+        return false;
+    }
+    return true;
+}
+
+#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)
+{
+    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 || 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
diff --git a/ui/d3des.c b/crypto/desrfb.c
similarity index 98%
rename from ui/d3des.c
rename to crypto/desrfb.c
index 5bc99b8..fc20a30 100644
--- a/ui/d3des.c
+++ b/crypto/desrfb.c
@@ -26,7 +26,7 @@
  * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
  */
 
-#include "d3des.h"
+#include "crypto/desrfb.h"
 
 static void scrunch(unsigned char *, unsigned long *);
 static void unscrun(unsigned long *, unsigned char *);
diff --git a/crypto/hash.c b/crypto/hash.c
new file mode 100644
index 0000000..81e74de
--- /dev/null
+++ b/crypto/hash.c
@@ -0,0 +1,200 @@
+/*
+ * QEMU Crypto hash 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 "crypto/hash.h"
+
+#ifdef CONFIG_GNUTLS_HASH
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = {
+    [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,
+                       uint8_t **result,
+                       size_t *resultlen,
+                       Error **errp)
+{
+    struct iovec iov = { .iov_base = (char *)buf,
+                         .iov_len = len };
+    return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp);
+}
+
+static const char hex[] = "0123456789abcdef";
+
+int qcrypto_hash_digestv(QCryptoHashAlgorithm alg,
+                         const struct iovec *iov,
+                         size_t niov,
+                         char **digest,
+                         Error **errp)
+{
+    uint8_t *result = NULL;
+    size_t resultlen = 0;
+    size_t i;
+
+    if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
+        return -1;
+    }
+
+    *digest = g_new0(char, (resultlen * 2) + 1);
+    for (i = 0 ; i < resultlen ; i++) {
+        (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf];
+        (*digest)[(i * 2) + 1] = hex[result[i] & 0xf];
+    }
+    (*digest)[resultlen * 2] = '\0';
+    g_free(result);
+    return 0;
+}
+
+int qcrypto_hash_digest(QCryptoHashAlgorithm alg,
+                        const char *buf,
+                        size_t len,
+                        char **digest,
+                        Error **errp)
+{
+    struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };
+
+    return qcrypto_hash_digestv(alg, &iov, 1, digest, errp);
+}
+
+int qcrypto_hash_base64v(QCryptoHashAlgorithm alg,
+                         const struct iovec *iov,
+                         size_t niov,
+                         char **base64,
+                         Error **errp)
+{
+    uint8_t *result = NULL;
+    size_t resultlen = 0;
+
+    if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
+        return -1;
+    }
+
+    *base64 = g_base64_encode(result, resultlen);
+    g_free(result);
+    return 0;
+}
+
+int qcrypto_hash_base64(QCryptoHashAlgorithm alg,
+                        const char *buf,
+                        size_t len,
+                        char **base64,
+                        Error **errp)
+{
+    struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };
+
+    return qcrypto_hash_base64v(alg, &iov, 1, base64, errp);
+}
diff --git a/crypto/init.c b/crypto/init.c
new file mode 100644
index 0000000..7447882
--- /dev/null
+++ b/crypto/init.c
@@ -0,0 +1,150 @@
+/*
+ * QEMU Crypto initialization
+ *
+ * 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 "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)
+{
+    fprintf(stderr, "%d: %s", level, 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;
+    ret = gnutls_global_init();
+    if (ret < 0) {
+        error_setg(errp,
+                   "Unable to initialize GNUTLS library: %s",
+                   gnutls_strerror(ret));
+        return -1;
+    }
+#ifdef DEBUG_GNUTLS
+    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;
+}
+
+#else /* ! CONFIG_GNUTLS */
+
+int qcrypto_init(Error **errp G_GNUC_UNUSED)
+{
+    return 0;
+}
+
+#endif /* ! CONFIG_GNUTLS */
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 7ee675c..216eeeb 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -933,7 +933,7 @@
     }
 }
 
-int rom_load_all(void)
+int rom_check_and_register_reset(void)
 {
     hwaddr addr = 0;
     MemoryRegionSection section;
@@ -957,12 +957,8 @@
         memory_region_unref(section.mr);
     }
     qemu_register_reset(rom_reset, NULL);
-    return 0;
-}
-
-void rom_load_done(void)
-{
     roms_loaded = 1;
+    return 0;
 }
 
 void rom_set_fw(FWCfgState *f)
diff --git a/include/qemu/aes.h b/include/crypto/aes.h
similarity index 100%
rename from include/qemu/aes.h
rename to include/crypto/aes.h
diff --git a/include/crypto/cipher.h b/include/crypto/cipher.h
new file mode 100644
index 0000000..b4d714f
--- /dev/null
+++ b/include/crypto/cipher.h
@@ -0,0 +1,210 @@
+/*
+ * QEMU Crypto cipher 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_CIPHER_H__
+#define QCRYPTO_CIPHER_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+
+typedef struct QCryptoCipher QCryptoCipher;
+
+typedef enum {
+    QCRYPTO_CIPHER_ALG_AES_128,
+    QCRYPTO_CIPHER_ALG_AES_192,
+    QCRYPTO_CIPHER_ALG_AES_256,
+    QCRYPTO_CIPHER_ALG_DES_RFB, /* A stupid variant on DES for VNC */
+
+    QCRYPTO_CIPHER_ALG_LAST
+} QCryptoCipherAlgorithm;
+
+typedef enum {
+    QCRYPTO_CIPHER_MODE_ECB,
+    QCRYPTO_CIPHER_MODE_CBC,
+
+    QCRYPTO_CIPHER_MODE_LAST
+} QCryptoCipherMode;
+
+/**
+ * QCryptoCipher:
+ *
+ * The QCryptoCipher object provides a way to perform encryption
+ * and decryption of data, with a standard API, regardless of the
+ * algorithm used. It further isolates the calling code from the
+ * details of the specific underlying implementation, whether
+ * built-in, libgcrypt or nettle.
+ *
+ * Each QCryptoCipher object is capable of performing both
+ * encryption and decryption, and can operate in a number
+ * or modes including ECB, CBC.
+ *
+ * <example>
+ *   <title>Encrypting data with AES-128 in CBC mode</title>
+ *   <programlisting>
+ * QCryptoCipher *cipher;
+ * uint8_t key = ....;
+ * size_t keylen = 16;
+ * uint8_t iv = ....;
+ *
+ * if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
+ *    error_report(errp, "Feature <blah> requires AES cipher support");
+ *    return -1;
+ * }
+ *
+ * cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128,
+ *                             QCRYPTO_CIPHER_MODE_CBC,
+ *                             key, keylen,
+ *                             errp);
+ * if (!cipher) {
+ *    return -1;
+ * }
+ *
+ * if (qcrypto_cipher_set_iv(cipher, iv, keylen, errp) < 0) {
+ *    return -1;
+ * }
+ *
+ * if (qcrypto_cipher_encrypt(cipher, rawdata, encdata, datalen, errp) < 0) {
+ *    return -1;
+ * }
+ *
+ * qcrypto_cipher_free(cipher);
+ *   </programlisting>
+ * </example>
+ *
+ */
+
+struct QCryptoCipher {
+    QCryptoCipherAlgorithm alg;
+    QCryptoCipherMode mode;
+    void *opaque;
+};
+
+/**
+ * qcrypto_cipher_supports:
+ * @alg: the cipher algorithm
+ *
+ * Determine if @alg cipher algorithm is supported by the
+ * current configured build
+ *
+ * Returns: true if the algorithm is supported, false otherwise
+ */
+bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg);
+
+
+/**
+ * qcrypto_cipher_new:
+ * @alg: the cipher algorithm
+ * @mode: the cipher usage mode
+ * @key: the private key bytes
+ * @nkey: the length of @key
+ * @errp: pointer to an uninitialized error object
+ *
+ * Creates a new cipher object for encrypting/decrypting
+ * data with the algorithm @alg in the usage mode @mode.
+ *
+ * The @key parameter provides the bytes representing
+ * the encryption/decryption key to use. The @nkey parameter
+ * specifies the length of @key in bytes. Each algorithm has
+ * one or more valid key lengths, and it is an error to provide
+ * a key of the incorrect length.
+ *
+ * The returned cipher object must be released with
+ * qcrypto_cipher_free() when no longer required
+ *
+ * Returns: a new cipher object, or NULL on error
+ */
+QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
+                                  QCryptoCipherMode mode,
+                                  const uint8_t *key, size_t nkey,
+                                  Error **errp);
+
+/**
+ * qcrypto_cipher_free:
+ * @cipher: the cipher object
+ *
+ * Release the memory associated with @cipher that
+ * was previously allocated by qcrypto_cipher_new()
+ */
+void qcrypto_cipher_free(QCryptoCipher *cipher);
+
+/**
+ * qcrypto_cipher_encrypt:
+ * @cipher: the cipher object
+ * @in: buffer holding the plain text input data
+ * @out: buffer to fill with the cipher text output data
+ * @len: the length of @in and @out buffers
+ * @errp: pointer to an uninitialized error object
+ *
+ * Encrypts the plain text stored in @in, filling
+ * @out with the resulting ciphered text. Both the
+ * @in and @out buffers must have the same size,
+ * given by @len.
+ *
+ * Returns: 0 on success, or -1 on error
+ */
+int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
+                           const void *in,
+                           void *out,
+                           size_t len,
+                           Error **errp);
+
+
+/**
+ * qcrypto_cipher_decrypt:
+ * @cipher: the cipher object
+ * @in: buffer holding the cipher text input data
+ * @out: buffer to fill with the plain text output data
+ * @len: the length of @in and @out buffers
+ * @errp: pointer to an uninitialized error object
+ *
+ * Decrypts the cipher text stored in @in, filling
+ * @out with the resulting plain text. Both the
+ * @in and @out buffers must have the same size,
+ * given by @len.
+ *
+ * Returns: 0 on success, or -1 on error
+ */
+int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
+                           const void *in,
+                           void *out,
+                           size_t len,
+                           Error **errp);
+
+/**
+ * qcrypto_cipher_setiv:
+ * @cipher: the cipher object
+ * @iv: the initialization vector bytes
+ * @niv: the length of @iv
+ * @errpr: pointer to an uninitialized error object
+ *
+ * If the @cipher object is setup to use a mode that requires
+ * initialization vectors, this sets the initialization vector
+ * bytes. The @iv data should have the same length as the
+ * cipher key used when originally constructing the cipher
+ * object. It is an error to set an initialization vector
+ * if the cipher mode does not require one.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_cipher_setiv(QCryptoCipher *cipher,
+                         const uint8_t *iv, size_t niv,
+                         Error **errp);
+
+#endif /* QCRYPTO_CIPHER_H__ */
diff --git a/ui/d3des.h b/include/crypto/desrfb.h
similarity index 100%
rename from ui/d3des.h
rename to include/crypto/desrfb.h
diff --git a/include/crypto/hash.h b/include/crypto/hash.h
new file mode 100644
index 0000000..b5acbf6
--- /dev/null
+++ b/include/crypto/hash.h
@@ -0,0 +1,189 @@
+/*
+ * QEMU Crypto hash 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_HASH_H__
+#define QCRYPTO_HASH_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+
+typedef enum {
+    QCRYPTO_HASH_ALG_MD5,
+    QCRYPTO_HASH_ALG_SHA1,
+    QCRYPTO_HASH_ALG_SHA256,
+
+    QCRYPTO_HASH_ALG_LAST
+} QCryptoHashAlgorithm;
+
+
+/**
+ * qcrypto_hash_supports:
+ * @alg: the hash algorithm
+ *
+ * Determine if @alg hash algorithm is supported by the
+ * current configured build.
+ *
+ * Returns: true if the algorithm is supported, false otherwise
+ */
+gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg);
+
+/**
+ * qcrypto_hash_bytesv:
+ * @alg: the hash algorithm
+ * @iov: the array of memory regions to hash
+ * @niov: the length of @iov
+ * @result: pointer to hold output hash
+ * @resultlen: pointer to hold length of @result
+ * @errp: pointer to uninitialized error object
+ *
+ * Computes the hash across all the memory regions
+ * present in @iov. The @result pointer will be
+ * filled with raw bytes representing the computed
+ * hash, which will have length @resultlen. The
+ * memory pointer in @result must be released
+ * with a call to g_free() when no longer required.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
+                        const struct iovec *iov,
+                        size_t niov,
+                        uint8_t **result,
+                        size_t *resultlen,
+                        Error **errp);
+
+/**
+ * qcrypto_hash_bytes:
+ * @alg: the hash algorithm
+ * @buf: the memory region to hash
+ * @len: the length of @buf
+ * @result: pointer to hold output hash
+ * @resultlen: pointer to hold length of @result
+ * @errp: pointer to uninitialized error object
+ *
+ * Computes the hash across all the memory region
+ * @buf of length @len. The @result pointer will be
+ * filled with raw bytes representing the computed
+ * hash, which will have length @resultlen. The
+ * memory pointer in @result must be released
+ * with a call to g_free() when no longer required.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_hash_bytes(QCryptoHashAlgorithm alg,
+                       const char *buf,
+                       size_t len,
+                       uint8_t **result,
+                       size_t *resultlen,
+                       Error **errp);
+
+/**
+ * qcrypto_hash_digestv:
+ * @alg: the hash algorithm
+ * @iov: the array of memory regions to hash
+ * @niov: the length of @iov
+ * @digest: pointer to hold output hash
+ * @errp: pointer to uninitialized error object
+ *
+ * Computes the hash across all the memory regions
+ * present in @iov. The @digest pointer will be
+ * filled with the printable hex digest of the computed
+ * hash, which will be terminated by '\0'. The
+ * memory pointer in @digest must be released
+ * with a call to g_free() when no longer required.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_hash_digestv(QCryptoHashAlgorithm alg,
+                         const struct iovec *iov,
+                         size_t niov,
+                         char **digest,
+                         Error **errp);
+
+/**
+ * qcrypto_hash_digest:
+ * @alg: the hash algorithm
+ * @buf: the memory region to hash
+ * @len: the length of @buf
+ * @digest: pointer to hold output hash
+ * @errp: pointer to uninitialized error object
+ *
+ * Computes the hash across all the memory region
+ * @buf of length @len. The @digest pointer will be
+ * filled with the printable hex digest of the computed
+ * hash, which will be terminated by '\0'. The
+ * memory pointer in @digest must be released
+ * with a call to g_free() when no longer required.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_hash_digest(QCryptoHashAlgorithm alg,
+                        const char *buf,
+                        size_t len,
+                        char **digest,
+                        Error **errp);
+
+/**
+ * qcrypto_hash_base64v:
+ * @alg: the hash algorithm
+ * @iov: the array of memory regions to hash
+ * @niov: the length of @iov
+ * @base64: pointer to hold output hash
+ * @errp: pointer to uninitialized error object
+ *
+ * Computes the hash across all the memory regions
+ * present in @iov. The @base64 pointer will be
+ * filled with the base64 encoding of the computed
+ * hash, which will be terminated by '\0'. The
+ * memory pointer in @base64 must be released
+ * with a call to g_free() when no longer required.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_hash_base64v(QCryptoHashAlgorithm alg,
+                         const struct iovec *iov,
+                         size_t niov,
+                         char **base64,
+                         Error **errp);
+
+/**
+ * qcrypto_hash_base64:
+ * @alg: the hash algorithm
+ * @buf: the memory region to hash
+ * @len: the length of @buf
+ * @base64: pointer to hold output hash
+ * @errp: pointer to uninitialized error object
+ *
+ * Computes the hash across all the memory region
+ * @buf of length @len. The @base64 pointer will be
+ * filled with the base64 encoding of the computed
+ * hash, which will be terminated by '\0'. The
+ * memory pointer in @base64 must be released
+ * with a call to g_free() when no longer required.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_hash_base64(QCryptoHashAlgorithm alg,
+                        const char *buf,
+                        size_t len,
+                        char **base64,
+                        Error **errp);
+
+#endif /* QCRYPTO_HASH_H__ */
diff --git a/include/crypto/init.h b/include/crypto/init.h
new file mode 100644
index 0000000..5fc510c
--- /dev/null
+++ b/include/crypto/init.h
@@ -0,0 +1,29 @@
+/*
+ * QEMU Crypto initialization
+ *
+ * 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_INIT_H__
+#define QCRYPTO_INIT_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+
+int qcrypto_init(Error **errp);
+
+#endif /* QCRYPTO_INIT_H__ */
diff --git a/include/hw/loader.h b/include/hw/loader.h
index 485ff8f..f7b43ab 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -75,8 +75,7 @@
                            void *callback_opaque);
 int rom_add_elf_program(const char *name, void *data, size_t datasize,
                         size_t romsize, hwaddr addr);
-int rom_load_all(void);
-void rom_load_done(void);
+int rom_check_and_register_reset(void);
 void rom_set_fw(FWCfgState *f);
 int rom_copy(uint8_t *dest, hwaddr addr, size_t size);
 void *rom_ptr(hwaddr addr);
diff --git a/target-arm/crypto_helper.c b/target-arm/crypto_helper.c
index 1fe975d..5d22838 100644
--- a/target-arm/crypto_helper.c
+++ b/target-arm/crypto_helper.c
@@ -14,7 +14,7 @@
 #include "cpu.h"
 #include "exec/exec-all.h"
 #include "exec/helper-proto.h"
-#include "qemu/aes.h"
+#include "crypto/aes.h"
 
 union CRYPTO_STATE {
     uint8_t    bytes[16];
diff --git a/target-i386/fpu_helper.c b/target-i386/fpu_helper.c
index 30d34d5..280adba 100644
--- a/target-i386/fpu_helper.c
+++ b/target-i386/fpu_helper.c
@@ -20,7 +20,6 @@
 #include <math.h>
 #include "cpu.h"
 #include "exec/helper-proto.h"
-#include "qemu/aes.h"
 #include "qemu/host-utils.h"
 #include "exec/cpu_ldst.h"
 
diff --git a/target-i386/ops_sse.h b/target-i386/ops_sse.h
index 0765073..bee134b 100644
--- a/target-i386/ops_sse.h
+++ b/target-i386/ops_sse.h
@@ -18,7 +18,7 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "qemu/aes.h"
+#include "crypto/aes.h"
 
 #if SHIFT == 0
 #define Reg MMXReg
diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c
index 4c2b71c..0a55d5e 100644
--- a/target-ppc/int_helper.c
+++ b/target-ppc/int_helper.c
@@ -19,7 +19,7 @@
 #include "cpu.h"
 #include "qemu/host-utils.h"
 #include "exec/helper-proto.h"
-#include "qemu/aes.h"
+#include "crypto/aes.h"
 
 #include "helper_regs.h"
 /*****************************************************************************/
diff --git a/tests/.gitignore b/tests/.gitignore
index dc813c2..ccc92e4 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -10,6 +10,8 @@
 test-aio
 test-bitops
 test-coroutine
+test-crypto-cipher
+test-crypto-hash
 test-cutils
 test-hbitmap
 test-int128
diff --git a/tests/Makefile b/tests/Makefile
index e843a7e..2c4b8dc 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -74,6 +74,8 @@
 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-cipher$(EXESUF)
 
 check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -343,6 +345,8 @@
 
 tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
 tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a
+tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a
+tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a
 
 libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
 libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
diff --git a/tests/test-crypto-cipher.c b/tests/test-crypto-cipher.c
new file mode 100644
index 0000000..f9b1a03
--- /dev/null
+++ b/tests/test-crypto-cipher.c
@@ -0,0 +1,290 @@
+/*
+ * QEMU Crypto cipher 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 <glib.h>
+
+#include "crypto/init.h"
+#include "crypto/cipher.h"
+
+typedef struct QCryptoCipherTestData QCryptoCipherTestData;
+struct QCryptoCipherTestData {
+    const char *path;
+    QCryptoCipherAlgorithm alg;
+    QCryptoCipherMode mode;
+    const char *key;
+    const char *plaintext;
+    const char *ciphertext;
+    const char *iv;
+};
+
+/* AES test data comes from appendix F of:
+ *
+ * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
+ */
+static QCryptoCipherTestData test_data[] = {
+    {
+        /* NIST F.1.1 ECB-AES128.Encrypt */
+        .path = "/crypto/cipher/aes-ecb-128",
+        .alg = QCRYPTO_CIPHER_ALG_AES_128,
+        .mode = QCRYPTO_CIPHER_MODE_ECB,
+        .key = "2b7e151628aed2a6abf7158809cf4f3c",
+        .plaintext =
+            "6bc1bee22e409f96e93d7e117393172a"
+            "ae2d8a571e03ac9c9eb76fac45af8e51"
+            "30c81c46a35ce411e5fbc1191a0a52ef"
+            "f69f2445df4f9b17ad2b417be66c3710",
+        .ciphertext =
+            "3ad77bb40d7a3660a89ecaf32466ef97"
+            "f5d3d58503b9699de785895a96fdbaaf"
+            "43b1cd7f598ece23881b00e3ed030688"
+            "7b0c785e27e8ad3f8223207104725dd4"
+    },
+    {
+        /* NIST F.1.3 ECB-AES192.Encrypt */
+        .path = "/crypto/cipher/aes-ecb-192",
+        .alg = QCRYPTO_CIPHER_ALG_AES_192,
+        .mode = QCRYPTO_CIPHER_MODE_ECB,
+        .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
+        .plaintext  =
+            "6bc1bee22e409f96e93d7e117393172a"
+            "ae2d8a571e03ac9c9eb76fac45af8e51"
+            "30c81c46a35ce411e5fbc1191a0a52ef"
+            "f69f2445df4f9b17ad2b417be66c3710",
+        .ciphertext =
+            "bd334f1d6e45f25ff712a214571fa5cc"
+            "974104846d0ad3ad7734ecb3ecee4eef"
+            "ef7afd2270e2e60adce0ba2face6444e"
+            "9a4b41ba738d6c72fb16691603c18e0e"
+    },
+    {
+        /* NIST F.1.5 ECB-AES256.Encrypt */
+        .path = "/crypto/cipher/aes-ecb-256",
+        .alg = QCRYPTO_CIPHER_ALG_AES_256,
+        .mode = QCRYPTO_CIPHER_MODE_ECB,
+        .key =
+            "603deb1015ca71be2b73aef0857d7781"
+            "1f352c073b6108d72d9810a30914dff4",
+        .plaintext  =
+            "6bc1bee22e409f96e93d7e117393172a"
+            "ae2d8a571e03ac9c9eb76fac45af8e51"
+            "30c81c46a35ce411e5fbc1191a0a52ef"
+            "f69f2445df4f9b17ad2b417be66c3710",
+        .ciphertext =
+            "f3eed1bdb5d2a03c064b5a7e3db181f8"
+            "591ccb10d410ed26dc5ba74a31362870"
+            "b6ed21b99ca6f4f9f153e7b1beafed1d"
+            "23304b7a39f9f3ff067d8d8f9e24ecc7",
+    },
+    {
+        /* NIST F.2.1 CBC-AES128.Encrypt */
+        .path = "/crypto/cipher/aes-cbc-128",
+        .alg = QCRYPTO_CIPHER_ALG_AES_128,
+        .mode = QCRYPTO_CIPHER_MODE_CBC,
+        .key = "2b7e151628aed2a6abf7158809cf4f3c",
+        .iv = "000102030405060708090a0b0c0d0e0f",
+        .plaintext  =
+            "6bc1bee22e409f96e93d7e117393172a"
+            "ae2d8a571e03ac9c9eb76fac45af8e51"
+            "30c81c46a35ce411e5fbc1191a0a52ef"
+            "f69f2445df4f9b17ad2b417be66c3710",
+        .ciphertext =
+            "7649abac8119b246cee98e9b12e9197d"
+            "5086cb9b507219ee95db113a917678b2"
+            "73bed6b8e3c1743b7116e69e22229516"
+            "3ff1caa1681fac09120eca307586e1a7",
+    },
+    {
+        /* NIST F.2.3 CBC-AES128.Encrypt */
+        .path = "/crypto/cipher/aes-cbc-192",
+        .alg = QCRYPTO_CIPHER_ALG_AES_192,
+        .mode = QCRYPTO_CIPHER_MODE_CBC,
+        .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
+        .iv = "000102030405060708090a0b0c0d0e0f",
+        .plaintext  =
+            "6bc1bee22e409f96e93d7e117393172a"
+            "ae2d8a571e03ac9c9eb76fac45af8e51"
+            "30c81c46a35ce411e5fbc1191a0a52ef"
+            "f69f2445df4f9b17ad2b417be66c3710",
+        .ciphertext =
+            "4f021db243bc633d7178183a9fa071e8"
+            "b4d9ada9ad7dedf4e5e738763f69145a"
+            "571b242012fb7ae07fa9baac3df102e0"
+            "08b0e27988598881d920a9e64f5615cd",
+    },
+    {
+        /* NIST F.2.5 CBC-AES128.Encrypt */
+        .path = "/crypto/cipher/aes-cbc-256",
+        .alg = QCRYPTO_CIPHER_ALG_AES_256,
+        .mode = QCRYPTO_CIPHER_MODE_CBC,
+        .key =
+            "603deb1015ca71be2b73aef0857d7781"
+            "1f352c073b6108d72d9810a30914dff4",
+        .iv = "000102030405060708090a0b0c0d0e0f",
+        .plaintext  =
+            "6bc1bee22e409f96e93d7e117393172a"
+            "ae2d8a571e03ac9c9eb76fac45af8e51"
+            "30c81c46a35ce411e5fbc1191a0a52ef"
+            "f69f2445df4f9b17ad2b417be66c3710",
+        .ciphertext =
+            "f58c4c04d6e5f1ba779eabfb5f7bfbd6"
+            "9cfc4e967edb808d679f777bc6702c7d"
+            "39f23369a9d9bacfa530e26304231461"
+            "b2eb05e2c39be9fcda6c19078c6a9d1b",
+    },
+    {
+        .path = "/crypto/cipher/des-rfb-ecb-56",
+        .alg = QCRYPTO_CIPHER_ALG_DES_RFB,
+        .mode = QCRYPTO_CIPHER_MODE_ECB,
+        .key = "0123456789abcdef",
+        .plaintext =
+            "6bc1bee22e409f96e93d7e117393172a"
+            "ae2d8a571e03ac9c9eb76fac45af8e51"
+            "30c81c46a35ce411e5fbc1191a0a52ef"
+            "f69f2445df4f9b17ad2b417be66c3710",
+        .ciphertext =
+            "8f346aaf64eaf24040720d80648c52e7"
+            "aefc616be53ab1a3d301e69d91e01838"
+            "ffd29f1bb5596ad94ea2d8e6196b7f09"
+            "30d8ed0bf2773af36dd82a6280c20926",
+    },
+};
+
+
+static inline int unhex(char c)
+{
+    if (c >= 'a' && c <= 'f') {
+        return 10 + (c - 'a');
+    }
+    if (c >= 'A' && c <= 'F') {
+        return 10 + (c - 'A');
+    }
+    return c - '0';
+}
+
+static inline char hex(int i)
+{
+    if (i < 10) {
+        return '0' + i;
+    }
+    return 'a' + (i - 10);
+}
+
+static size_t unhex_string(const char *hexstr,
+                           uint8_t **data)
+{
+    size_t len;
+    size_t i;
+
+    if (!hexstr) {
+        *data = NULL;
+        return 0;
+    }
+
+    len = strlen(hexstr);
+    *data = g_new0(uint8_t, len / 2);
+
+    for (i = 0; i < len; i += 2) {
+        (*data)[i/2] = (unhex(hexstr[i]) << 4) | unhex(hexstr[i+1]);
+    }
+    return len / 2;
+}
+
+static char *hex_string(const uint8_t *bytes,
+                        size_t len)
+{
+    char *hexstr = g_new0(char, len * 2 + 1);
+    size_t i;
+
+    for (i = 0; i < len; i++) {
+        hexstr[i*2] = hex((bytes[i] >> 4) & 0xf);
+        hexstr[i*2+1] = hex(bytes[i] & 0xf);
+    }
+    hexstr[len*2] = '\0';
+
+    return hexstr;
+}
+
+static void test_cipher(const void *opaque)
+{
+    const QCryptoCipherTestData *data = opaque;
+
+    QCryptoCipher *cipher;
+    Error *err = NULL;
+    uint8_t *key, *iv, *ciphertext, *plaintext, *outtext;
+    size_t nkey, niv, nciphertext, nplaintext;
+    char *outtexthex;
+
+    g_test_message("foo");
+    nkey = unhex_string(data->key, &key);
+    niv = unhex_string(data->iv, &iv);
+    nciphertext = unhex_string(data->ciphertext, &ciphertext);
+    nplaintext = unhex_string(data->plaintext, &plaintext);
+
+    g_assert(nciphertext == nplaintext);
+
+    outtext = g_new0(uint8_t, nciphertext);
+
+    cipher = qcrypto_cipher_new(
+        data->alg, data->mode,
+        key, nkey,
+        &err);
+    g_assert(cipher != NULL);
+    g_assert(err == NULL);
+
+
+    if (iv) {
+        g_assert(qcrypto_cipher_setiv(cipher,
+                                      iv, niv,
+                                      &err) == 0);
+        g_assert(err == NULL);
+    }
+    g_assert(qcrypto_cipher_encrypt(cipher,
+                                    plaintext,
+                                    outtext,
+                                    nplaintext,
+                                    &err) == 0);
+    g_assert(err == NULL);
+
+    outtexthex = hex_string(outtext, nciphertext);
+
+    g_assert_cmpstr(outtexthex, ==, data->ciphertext);
+
+    g_free(outtext);
+    g_free(outtexthex);
+    g_free(key);
+    g_free(iv);
+    g_free(ciphertext);
+    g_free(plaintext);
+    qcrypto_cipher_free(cipher);
+}
+
+int main(int argc, char **argv)
+{
+    size_t i;
+
+    g_test_init(&argc, &argv, NULL);
+
+    g_assert(qcrypto_init(NULL) == 0);
+
+    for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
+        g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher);
+    }
+    return g_test_run();
+}
diff --git a/tests/test-crypto-hash.c b/tests/test-crypto-hash.c
new file mode 100644
index 0000000..911437e
--- /dev/null
+++ b/tests/test-crypto-hash.c
@@ -0,0 +1,209 @@
+/*
+ * QEMU Crypto hash 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 <glib.h>
+
+#include "crypto/init.h"
+#include "crypto/hash.h"
+
+#define INPUT_TEXT "Hiss hisss Hissss hiss Hiss hisss Hiss hiss"
+#define INPUT_TEXT1 "Hiss hisss "
+#define INPUT_TEXT2 "Hissss hiss "
+#define INPUT_TEXT3 "Hiss hisss Hiss hiss"
+
+#define OUTPUT_MD5 "628d206371563035ab8ef62f492bdec9"
+#define OUTPUT_SHA1 "b2e74f26758a3a421e509cee045244b78753cc02"
+#define OUTPUT_SHA256 "bc757abb0436586f392b437e5dd24096" \
+                      "f7f224de6b74d4d86e2abc6121b160d0"
+
+#define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ=="
+#define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI="
+#define OUTPUT_SHA256_B64 "vHV6uwQ2WG85K0N+XdJAlvfyJN5rdNTYbiq8YSGxYNA="
+
+static const char *expected_outputs[] = {
+    [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5,
+    [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1,
+    [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256,
+};
+static const char *expected_outputs_b64[] = {
+    [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5_B64,
+    [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1_B64,
+    [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256_B64,
+};
+static const int expected_lens[] = {
+    [QCRYPTO_HASH_ALG_MD5] = 16,
+    [QCRYPTO_HASH_ALG_SHA1] = 20,
+    [QCRYPTO_HASH_ALG_SHA256] = 32,
+};
+
+static const char hex[] = "0123456789abcdef";
+
+/* Test with dynamic allocation */
+static void test_hash_alloc(void)
+{
+    size_t i;
+
+    g_assert(qcrypto_init(NULL) == 0);
+
+    for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+        uint8_t *result = NULL;
+        size_t resultlen = 0;
+        int ret;
+        size_t j;
+
+        ret = qcrypto_hash_bytes(i,
+                                 INPUT_TEXT,
+                                 strlen(INPUT_TEXT),
+                                 &result,
+                                 &resultlen,
+                                 NULL);
+        g_assert(ret == 0);
+        g_assert(resultlen == expected_lens[i]);
+
+        for (j = 0; j < resultlen; j++) {
+            g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]);
+            g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]);
+        }
+        g_free(result);
+    }
+}
+
+/* Test with caller preallocating */
+static void test_hash_prealloc(void)
+{
+    size_t i;
+
+    g_assert(qcrypto_init(NULL) == 0);
+
+    for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+        uint8_t *result;
+        size_t resultlen;
+        int ret;
+        size_t j;
+
+        resultlen = expected_lens[i];
+        result = g_new0(uint8_t, resultlen);
+
+        ret = qcrypto_hash_bytes(i,
+                                 INPUT_TEXT,
+                                 strlen(INPUT_TEXT),
+                                 &result,
+                                 &resultlen,
+                                 NULL);
+        g_assert(ret == 0);
+
+        g_assert(resultlen == expected_lens[i]);
+        for (j = 0; j < resultlen; j++) {
+            g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]);
+            g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]);
+        }
+        g_free(result);
+    }
+}
+
+
+/* Test with dynamic allocation */
+static void test_hash_iov(void)
+{
+    size_t i;
+
+    g_assert(qcrypto_init(NULL) == 0);
+
+    for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+        struct iovec iov[3] = {
+            { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) },
+            { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) },
+            { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) },
+        };
+        uint8_t *result = NULL;
+        size_t resultlen = 0;
+        int ret;
+        size_t j;
+
+        ret = qcrypto_hash_bytesv(i,
+                                  iov, 3,
+                                  &result,
+                                  &resultlen,
+                                  NULL);
+        g_assert(ret == 0);
+        g_assert(resultlen == expected_lens[i]);
+        for (j = 0; j < resultlen; j++) {
+            g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]);
+            g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]);
+        }
+        g_free(result);
+    }
+}
+
+
+/* Test with printable hashing */
+static void test_hash_digest(void)
+{
+    size_t i;
+
+    g_assert(qcrypto_init(NULL) == 0);
+
+    for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+        int ret;
+        char *digest;
+
+        ret = qcrypto_hash_digest(i,
+                                  INPUT_TEXT,
+                                  strlen(INPUT_TEXT),
+                                  &digest,
+                                  NULL);
+        g_assert(ret == 0);
+        g_assert(g_str_equal(digest, expected_outputs[i]));
+        g_free(digest);
+    }
+}
+
+/* Test with base64 encoding */
+static void test_hash_base64(void)
+{
+    size_t i;
+
+    g_assert(qcrypto_init(NULL) == 0);
+
+    for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+        int ret;
+        char *digest;
+
+        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_free(digest);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+    g_test_add_func("/crypto/hash/iov", test_hash_iov);
+    g_test_add_func("/crypto/hash/alloc", test_hash_alloc);
+    g_test_add_func("/crypto/hash/prealloc", test_hash_prealloc);
+    g_test_add_func("/crypto/hash/digest", test_hash_digest);
+    g_test_add_func("/crypto/hash/base64", test_hash_base64);
+    return g_test_run();
+}
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 023914c..c62d4d9 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -1,10 +1,10 @@
-vnc-obj-y += vnc.o d3des.o
+vnc-obj-y += vnc.o
 vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
 vnc-obj-y += vnc-enc-tight.o vnc-palette.o
 vnc-obj-y += vnc-enc-zrle.o
 vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
 vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
-vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o
+vnc-obj-y += vnc-ws.o
 vnc-obj-y += vnc-jobs.o
 
 common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 8c18268..b4cb6bd 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -20,6 +20,7 @@
 
 #include "vnc.h"
 #include "qemu/main-loop.h"
+#include "crypto/hash.h"
 
 #ifdef CONFIG_VNC_TLS
 #include "qemu/sockets.h"
@@ -203,24 +204,21 @@
 static void vncws_send_handshake_response(VncState *vs, const char* key)
 {
     char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1];
-    unsigned char hash[SHA1_DIGEST_LEN];
-    size_t hash_size = sizeof(hash);
     char *accept = NULL, *response = NULL;
-    gnutls_datum_t in;
-    int ret;
+    Error *err = NULL;
 
     g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1);
     g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1);
 
     /* hash and encode it */
-    in.data = (void *)combined_key;
-    in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN;
-    ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size);
-    if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) {
-        accept = g_base64_encode(hash, hash_size);
-    }
-    if (accept == NULL) {
-        VNC_DEBUG("Hashing Websocket combined key failed\n");
+    if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
+                            combined_key,
+                            WS_CLIENT_KEY_LEN + WS_GUID_LEN,
+                            &accept,
+                            &err) < 0) {
+        VNC_DEBUG("Hashing Websocket combined key failed %s\n",
+                  error_get_pretty(err));
+        error_free(err);
         vnc_client_error(vs);
         return;
     }
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
index 14d4230..9494225 100644
--- a/ui/vnc-ws.h
+++ b/ui/vnc-ws.h
@@ -21,8 +21,6 @@
 #ifndef __QEMU_UI_VNC_WS_H
 #define __QEMU_UI_VNC_WS_H
 
-#include <gnutls/gnutls.h>
-
 #define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
 #define SHA1_DIGEST_LEN 20
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 2ffd9e5..94e4f19 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -40,6 +40,7 @@
 #include "qemu/osdep.h"
 #include "ui/input.h"
 #include "qapi-event.h"
+#include "crypto/hash.h"
 
 #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
 #define VNC_REFRESH_INTERVAL_INC  50
@@ -48,7 +49,7 @@
 static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
 
 #include "vnc_keysym.h"
-#include "d3des.h"
+#include "crypto/cipher.h"
 
 static QTAILQ_HEAD(, VncDisplay) vnc_displays =
     QTAILQ_HEAD_INITIALIZER(vnc_displays);
@@ -355,9 +356,7 @@
     info->base->host = g_strdup(host);
     info->base->service = g_strdup(serv);
     info->base->family = inet_netfamily(sa.ss_family);
-#ifdef CONFIG_VNC_WS
     info->base->websocket = client->websocket;
-#endif
 
 #ifdef CONFIG_VNC_TLS
     if (client->tls.session && client->tls.dname) {
@@ -582,12 +581,10 @@
             info->server = qmp_query_server_entry(vd->lsock, false,
                                                   info->server);
         }
-#ifdef CONFIG_VNC_WS
         if (vd->lwebsock != -1) {
             info->server = qmp_query_server_entry(vd->lwebsock, true,
                                                   info->server);
         }
-#endif
 
         item = g_new0(VncInfo2List, 1);
         item->value = info;
@@ -1231,10 +1228,8 @@
 
     buffer_free(&vs->input);
     buffer_free(&vs->output);
-#ifdef CONFIG_VNC_WS
     buffer_free(&vs->ws_input);
     buffer_free(&vs->ws_output);
-#endif /* CONFIG_VNC_WS */
 
     qapi_free_VncClientInfo(vs->info);
 
@@ -1413,12 +1408,9 @@
     } else
 #endif /* CONFIG_VNC_SASL */
     {
-#ifdef CONFIG_VNC_WS
         if (vs->encode_ws) {
             vnc_client_write_ws(vs);
-        } else
-#endif /* CONFIG_VNC_WS */
-        {
+        } else {
             vnc_client_write_plain(vs);
         }
     }
@@ -1429,11 +1421,7 @@
     VncState *vs = opaque;
 
     vnc_lock_output(vs);
-    if (vs->output.offset
-#ifdef CONFIG_VNC_WS
-            || vs->ws_output.offset
-#endif
-            ) {
+    if (vs->output.offset || vs->ws_output.offset) {
         vnc_client_write_locked(opaque);
     } else if (vs->csock != -1) {
         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
@@ -1539,7 +1527,6 @@
         ret = vnc_client_read_sasl(vs);
     else
 #endif /* CONFIG_VNC_SASL */
-#ifdef CONFIG_VNC_WS
         if (vs->encode_ws) {
             ret = vnc_client_read_ws(vs);
             if (ret == -1) {
@@ -1549,10 +1536,8 @@
                 vnc_client_error(vs);
                 return;
             }
-        } else
-#endif /* CONFIG_VNC_WS */
-        {
-        ret = vnc_client_read_plain(vs);
+        } else {
+            ret = vnc_client_read_plain(vs);
         }
     if (!ret) {
         if (vs->csock == -1)
@@ -1624,11 +1609,8 @@
 void vnc_flush(VncState *vs)
 {
     vnc_lock_output(vs);
-    if (vs->csock != -1 && (vs->output.offset
-#ifdef CONFIG_VNC_WS
-                || vs->ws_output.offset
-#endif
-                )) {
+    if (vs->csock != -1 && (vs->output.offset ||
+                            vs->ws_output.offset)) {
         vnc_client_write_locked(vs);
     }
     vnc_unlock_output(vs);
@@ -2535,9 +2517,11 @@
 static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
 {
     unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
-    int i, j, pwlen;
+    size_t i, pwlen;
     unsigned char key[8];
     time_t now = time(NULL);
+    QCryptoCipher *cipher;
+    Error *err = NULL;
 
     if (!vs->vd->password) {
         VNC_DEBUG("No password configured on server");
@@ -2554,9 +2538,29 @@
     pwlen = strlen(vs->vd->password);
     for (i=0; i<sizeof(key); i++)
         key[i] = i<pwlen ? vs->vd->password[i] : 0;
-    deskey(key, EN0);
-    for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
-        des(response+j, response+j);
+
+    cipher = qcrypto_cipher_new(
+        QCRYPTO_CIPHER_ALG_DES_RFB,
+        QCRYPTO_CIPHER_MODE_ECB,
+        key, G_N_ELEMENTS(key),
+        &err);
+    if (!cipher) {
+        VNC_DEBUG("Cannot initialize cipher %s",
+                  error_get_pretty(err));
+        error_free(err);
+        goto reject;
+    }
+
+    if (qcrypto_cipher_decrypt(cipher,
+                               vs->challenge,
+                               response,
+                               VNC_AUTH_CHALLENGE_SIZE,
+                               &err) < 0) {
+        VNC_DEBUG("Cannot encrypt challenge %s",
+                  error_get_pretty(err));
+        error_free(err);
+        goto reject;
+    }
 
     /* Compare expected vs actual challenge response */
     if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
@@ -3019,7 +3023,6 @@
     VNC_DEBUG("New client on socket %d\n", csock);
     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
     qemu_set_nonblock(vs->csock);
-#ifdef CONFIG_VNC_WS
     if (websocket) {
         vs->websocket = 1;
 #ifdef CONFIG_VNC_TLS
@@ -3031,7 +3034,6 @@
             qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
         }
     } else
-#endif /* CONFIG_VNC_WS */
     {
         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
     }
@@ -3040,10 +3042,7 @@
     vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
     vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
 
-#ifdef CONFIG_VNC_WS
-    if (!vs->websocket)
-#endif
-    {
+    if (!vs->websocket) {
         vnc_init_state(vs);
     }
 
@@ -3099,12 +3098,9 @@
 
     /* Catch-up */
     graphic_hw_update(vs->dcl.con);
-#ifdef CONFIG_VNC_WS
     if (websocket) {
         csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
-    } else
-#endif /* CONFIG_VNC_WS */
-    {
+    } else {
         csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
     }
 
@@ -3119,12 +3115,10 @@
     vnc_listen_read(opaque, false);
 }
 
-#ifdef CONFIG_VNC_WS
 static void vnc_listen_websocket_read(void *opaque)
 {
     vnc_listen_read(opaque, true);
 }
-#endif /* CONFIG_VNC_WS */
 
 static const DisplayChangeListenerOps dcl_ops = {
     .dpy_name             = "vnc",
@@ -3150,9 +3144,7 @@
     QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
 
     vs->lsock = -1;
-#ifdef CONFIG_VNC_WS
     vs->lwebsock = -1;
-#endif
 
     QTAILQ_INIT(&vs->clients);
     vs->expires = TIME_MAX;
@@ -3186,14 +3178,12 @@
         close(vs->lsock);
         vs->lsock = -1;
     }
-#ifdef CONFIG_VNC_WS
     vs->ws_enabled = false;
     if (vs->lwebsock != -1) {
         qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
         close(vs->lwebsock);
         vs->lwebsock = -1;
     }
-#endif /* CONFIG_VNC_WS */
     vs->auth = VNC_AUTH_INVALID;
     vs->subauth = VNC_AUTH_INVALID;
 #ifdef CONFIG_VNC_TLS
@@ -3516,12 +3506,20 @@
     }
 
     password = qemu_opt_get_bool(opts, "password", false);
-    if (password && fips_get_state()) {
-        error_setg(errp,
-                   "VNC password auth disabled due to FIPS mode, "
-                   "consider using the VeNCrypt or SASL authentication "
-                   "methods as an alternative");
-        goto fail;
+    if (password) {
+        if (fips_get_state()) {
+            error_setg(errp,
+                       "VNC password auth disabled due to FIPS mode, "
+                       "consider using the VeNCrypt or SASL authentication "
+                       "methods as an alternative");
+            goto fail;
+        }
+        if (!qcrypto_cipher_supports(
+                QCRYPTO_CIPHER_ALG_DES_RFB)) {
+            error_setg(errp,
+                       "Cipher backend does not support DES RFB algorithm");
+            goto fail;
+        }
     }
 
     reverse = qemu_opt_get_bool(opts, "reverse", false);
@@ -3579,13 +3577,12 @@
 
     websocket = qemu_opt_get(opts, "websocket");
     if (websocket) {
-#ifdef CONFIG_VNC_WS
         vs->ws_enabled = true;
         qemu_opt_set(wsopts, "port", websocket, &error_abort);
-#else /* ! CONFIG_VNC_WS */
-        error_setg(errp, "Websockets protocol requires gnutls support");
-        goto fail;
-#endif /* ! CONFIG_VNC_WS */
+        if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
+            error_setg(errp, "SHA1 hash support is required for websockets");
+            goto fail;
+        }
     }
 
 #ifdef CONFIG_VNC_JPEG
@@ -3668,9 +3665,7 @@
         /* connect to viewer */
         int csock;
         vs->lsock = -1;
-#ifdef CONFIG_VNC_WS
         vs->lwebsock = -1;
-#endif
         if (strncmp(vnc, "unix:", 5) == 0) {
             csock = unix_connect(vnc+5, errp);
         } else {
@@ -3693,7 +3688,6 @@
             if (vs->lsock < 0) {
                 goto fail;
             }
-#ifdef CONFIG_VNC_WS
             if (vs->ws_enabled) {
                 vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
                 if (vs->lwebsock < 0) {
@@ -3704,16 +3698,13 @@
                     goto fail;
                 }
             }
-#endif /* CONFIG_VNC_WS */
         }
         vs->enabled = true;
         qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
-#ifdef CONFIG_VNC_WS
         if (vs->ws_enabled) {
             qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
                                 NULL, vs);
         }
-#endif /* CONFIG_VNC_WS */
     }
     qemu_opts_del(sopts);
     qemu_opts_del(wsopts);
@@ -3723,9 +3714,7 @@
     qemu_opts_del(sopts);
     qemu_opts_del(wsopts);
     vs->enabled = false;
-#ifdef CONFIG_VNC_WS
     vs->ws_enabled = false;
-#endif /* CONFIG_VNC_WS */
 }
 
 void vnc_display_add_client(const char *id, int csock, bool skipauth)
diff --git a/ui/vnc.h b/ui/vnc.h
index 3f7c6a9..814d720 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -108,9 +108,7 @@
 #ifdef CONFIG_VNC_SASL
 #include "vnc-auth-sasl.h"
 #endif
-#ifdef CONFIG_VNC_WS
 #include "vnc-ws.h"
-#endif
 
 struct VncRectStat
 {
@@ -156,10 +154,8 @@
     int connections_limit;
     VncSharePolicy share_policy;
     int lsock;
-#ifdef CONFIG_VNC_WS
     int lwebsock;
     bool ws_enabled;
-#endif
     DisplaySurface *ds;
     DisplayChangeListener dcl;
     kbd_layout_t *kbd_layout;
@@ -294,21 +290,17 @@
 #ifdef CONFIG_VNC_SASL
     VncStateSASL sasl;
 #endif
-#ifdef CONFIG_VNC_WS
     bool encode_ws;
     bool websocket;
-#endif /* CONFIG_VNC_WS */
 
     VncClientInfo *info;
 
     Buffer output;
     Buffer input;
-#ifdef CONFIG_VNC_WS
     Buffer ws_input;
     Buffer ws_output;
     size_t ws_payload_remain;
     WsMask ws_payload_mask;
-#endif
     /* current output mode information */
     VncWritePixels *write_pixels;
     PixelFormat client_pf;
diff --git a/util/Makefile.objs b/util/Makefile.objs
index ceaba30..114d657 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -9,7 +9,7 @@
 util-obj-y += error.o qemu-error.o
 util-obj-$(CONFIG_POSIX) += compatfd.o
 util-obj-y += id.o
-util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o uri.o notify.o
+util-obj-y += iov.o qemu-config.o qemu-sockets.o uri.o notify.o
 util-obj-y += qemu-option.o qemu-progress.o
 util-obj-y += hexdump.o
 util-obj-y += crc32c.o
diff --git a/vl.c b/vl.c
index cfa6133..3f269dc 100644
--- a/vl.c
+++ b/vl.c
@@ -121,6 +121,7 @@
 #include "qom/object_interfaces.h"
 #include "qapi-event.h"
 #include "exec/semihost.h"
+#include "crypto/init.h"
 
 #define MAX_VIRTIO_CONSOLES 1
 #define MAX_SCLP_CONSOLES 1
@@ -2976,6 +2977,7 @@
     uint64_t ram_slots = 0;
     FILE *vmstate_dump_file = NULL;
     Error *main_loop_err = NULL;
+    Error *err = NULL;
 
     qemu_init_cpu_loop();
     qemu_mutex_lock_iothread();
@@ -3019,6 +3021,11 @@
 
     runstate_init();
 
+    if (qcrypto_init(&err) < 0) {
+        fprintf(stderr, "Cannot initialize crypto: %s\n",
+                error_get_pretty(err));
+        exit(1);
+    }
     rtc_clock = QEMU_CLOCK_HOST;
 
     QLIST_INIT (&vm_change_state_head);
@@ -4597,18 +4604,15 @@
 
     qdev_machine_creation_done();
 
-    if (rom_load_all() != 0) {
-        fprintf(stderr, "rom loading failed\n");
-        exit(1);
-    }
-
     /* TODO: once all bus devices are qdevified, this should be done
      * when bus is created by qdev.c */
     qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());
     qemu_run_machine_init_done_notifiers();
 
-    /* Done notifiers can load ROMs */
-    rom_load_done();
+    if (rom_check_and_register_reset() != 0) {
+        fprintf(stderr, "rom check and register reset failed\n");
+        exit(1);
+    }
 
     qemu_system_reset(VMRESET_SILENT);
     if (loadvm) {