aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-07-08 20:46:35 +0100
committerPeter Maydell <peter.maydell@linaro.org>2015-07-08 20:46:35 +0100
commitacf7b7fdf31fa76b53803790917c8acf23a2badb (patch)
tree999bae49ca3a0ea7ef5476c1764c6166b17a0c12
parentc8e84287da7dd6a46c0bb0e53190e79ba4eedf24 (diff)
parent2828a307232ffceeddec9feb6a87ac660b68b693 (diff)
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>
-rw-r--r--MAINTAINERS7
-rw-r--r--Makefile.objs1
-rw-r--r--audio/ossaudio.c1
-rw-r--r--block/Makefile.objs2
-rw-r--r--block/qcow.c102
-rw-r--r--block/qcow2-cluster.c46
-rw-r--r--block/qcow2.c96
-rw-r--r--block/qcow2.h13
-rw-r--r--block/quorum.c39
-rwxr-xr-xconfigure160
-rw-r--r--crypto/Makefile.objs5
-rw-r--r--crypto/aes.c (renamed from util/aes.c)2
-rw-r--r--crypto/cipher-builtin.c398
-rw-r--r--crypto/cipher-gcrypt.c195
-rw-r--r--crypto/cipher-nettle.c206
-rw-r--r--crypto/cipher.c74
-rw-r--r--crypto/desrfb.c (renamed from ui/d3des.c)2
-rw-r--r--crypto/hash.c200
-rw-r--r--crypto/init.c150
-rw-r--r--hw/core/loader.c8
-rw-r--r--include/crypto/aes.h (renamed from include/qemu/aes.h)0
-rw-r--r--include/crypto/cipher.h210
-rw-r--r--include/crypto/desrfb.h (renamed from ui/d3des.h)0
-rw-r--r--include/crypto/hash.h189
-rw-r--r--include/crypto/init.h29
-rw-r--r--include/hw/loader.h3
-rw-r--r--target-arm/crypto_helper.c2
-rw-r--r--target-i386/fpu_helper.c1
-rw-r--r--target-i386/ops_sse.h2
-rw-r--r--target-ppc/int_helper.c2
-rw-r--r--tests/.gitignore2
-rw-r--r--tests/Makefile4
-rw-r--r--tests/test-crypto-cipher.c290
-rw-r--r--tests/test-crypto-hash.c209
-rw-r--r--ui/Makefile.objs4
-rw-r--r--ui/vnc-ws.c22
-rw-r--r--ui/vnc-ws.h2
-rw-r--r--ui/vnc.c119
-rw-r--r--ui/vnc.h8
-rw-r--r--util/Makefile.objs2
-rw-r--r--vl.c18
41 files changed, 2546 insertions, 279 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 3d48a6bd65..411da3cf57 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1052,6 +1052,13 @@ S: Supported
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 4881d2c2a6..f094eff15e 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 11e76a15a2..7dbe3332d8 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -853,6 +853,7 @@ static void *oss_audio_init (void)
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 c34fd7cdc2..58ef2ef3f2 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -3,7 +3,7 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c
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 733627fbf2..01fba54cef 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 @@ typedef struct BDRVQcowState {
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 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
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 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
BDRVQcowState *s = bs->opaque;
uint8_t keybuf[16];
int len, i;
+ Error *err;
memset(keybuf, 0, 16);
len = strlen(key);
@@ -271,38 +275,67 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
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)
- return -1;
- if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_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;
+ }
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 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
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 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
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 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
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 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
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 @@ done:
return ret;
fail:
+ error_free(err);
ret = -EIO;
goto done;
}
@@ -666,12 +707,17 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
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 @@ static void qcow_close(BlockDriverState *bs)
{
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 1a5c97a5ae..b43f186eb8 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -339,26 +339,47 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab
/* 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 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
}
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 d522ec7d14..76c331b387 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 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
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 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
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 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
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)
- return -1;
- if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_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 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 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
}
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 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
/* 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 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
}
if (bs->encrypted) {
- assert(s->crypt_method);
+ assert(s->cipher);
/*
* For encrypted images, read everything into a temporary
@@ -1264,9 +1260,15 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
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 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
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 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
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 @@ static void qcow2_close(BlockDriverState *bs)
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 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
{
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 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
* 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 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
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 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
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 5936d299a3..72e132838a 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 @@ typedef struct BDRVQcowState {
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_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
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 a7df17c185..4e66221461 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 void quorum_free_vote_list(QuorumVotes *votes)
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;
-
- ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256);
-
- if (ret < 0) {
- return ret;
- }
-
- 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;
- }
+ size_t len = sizeof(hash->h);
+ uint8_t *data = hash->h;
+
+ /* 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;
}
- gnutls_hash_deinit(dig, (void *) hash);
- return ret;
+ return 0;
}
static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes)
@@ -870,6 +865,12 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
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 c89b1eab9b..33b945530e 100755
--- a/configure
+++ b/configure
@@ -246,7 +246,6 @@ vnc_tls=""
vnc_sasl=""
vnc_jpeg=""
vnc_png=""
-vnc_ws=""
xen=""
xen_ctrl_version=""
xen_pci_passthrough=""
@@ -331,11 +330,12 @@ glusterfs_zerofill="no"
archipelago="no"
gtk=""
gtkabi=""
+gnutls=""
+gnutls_hash=""
vte=""
tpm="yes"
libssh2=""
vhdx=""
-quorum=""
numa=""
tcmalloc="no"
@@ -896,10 +896,6 @@ for opt do
;;
--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 @@ for opt do
;;
--enable-gtk) gtk="yes"
;;
+ --disable-gnutls) gnutls="no"
+ ;;
+ --enable-gnutls) gnutls="yes"
+ ;;
--enable-rdma) rdma="yes"
;;
--disable-rdma) rdma="no"
@@ -1141,10 +1141,6 @@ for opt do
;;
--disable-vhdx) vhdx="no"
;;
- --disable-quorum) quorum="no"
- ;;
- --enable-quorum) quorum="yes"
- ;;
--disable-numa) numa="no"
;;
--enable-numa) numa="yes"
@@ -1329,6 +1325,7 @@ disabled with --disable-FEATURE, default is enabled if available:
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 @@ disabled with --disable-FEATURE, default is enabled if available:
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 @@ if test "$gtk" != "no"; then
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 @@ fi
##########################################
# 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,48 +2350,14 @@ EOF
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
##########################################
@@ -4445,6 +4487,10 @@ fi
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 @@ if test "$vnc" = "yes" ; then
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 "libssh2 support $libssh2"
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 @@ fi
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 @@ if test "$gtk" = "yes" ; then
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 @@ if test "$libssh2" = "yes" ; then
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 0000000000..b05013831b
--- /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
index 3d7c4be9b6..244a388eba 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 0000000000..c625cb40f7
--- /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 0000000000..8cfc562500
--- /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 0000000000..e5a14bc139
--- /dev/null
+++ b/crypto/cipher-nettle.c
@@ -0,0 +1,206 @@
+/*
+ * QEMU Crypto cipher nettle algorithms
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <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 0000000000..024a00cb54
--- /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
index 5bc99b8ad7..fc20a30dfe 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 0000000000..81e74de868
--- /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 0000000000..7447882c7b
--- /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 7ee675c1df..216eeeb917 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -933,7 +933,7 @@ static void rom_reset(void *unused)
}
}
-int rom_load_all(void)
+int rom_check_and_register_reset(void)
{
hwaddr addr = 0;
MemoryRegionSection section;
@@ -957,12 +957,8 @@ int rom_load_all(void)
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
index a006da2224..a006da2224 100644
--- a/include/qemu/aes.h
+++ b/include/crypto/aes.h
diff --git a/include/crypto/cipher.h b/include/crypto/cipher.h
new file mode 100644
index 0000000000..b4d714f269
--- /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
index 773667ee79..773667ee79 100644
--- a/ui/d3des.h
+++ b/include/crypto/desrfb.h
diff --git a/include/crypto/hash.h b/include/crypto/hash.h
new file mode 100644
index 0000000000..b5acbf638c
--- /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 0000000000..5fc510c4f8
--- /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 485ff8f2f1..f7b43ab62f 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -75,8 +75,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
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 1fe975d0f1..5d22838065 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 30d34d5aee..280adbac50 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 0765073792..bee134bae1 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 4c2b71c708..0a55d5e54b 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 dc813c2713..ccc92e4761 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -10,6 +10,8 @@ rcutorture
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 e843a7e229..2c4b8dc5ec 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -74,6 +74,8 @@ check-unit-y += tests/test-qemu-opts$(EXESUF)
gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c
check-unit-y += tests/test-write-threshold$(EXESUF)
gcov-files-test-write-threshold-y = block/write-threshold.c
+check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF)
+check-unit-y += tests/test-crypto-cipher$(EXESUF)
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -343,6 +345,8 @@ tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) l
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 0000000000..f9b1a03a02
--- /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 0000000000..911437e60d
--- /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 023914c7c0..c62d4d9722 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 8c18268054..b4cb6bde70 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 char *vncws_extract_handshake_entry(const char *handshake,
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 14d4230eff..94942258ec 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 2ffd9e500f..94e4f19a60 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_STATS = { 0, 500000 };
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 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
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 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
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 @@ void vnc_disconnect_finish(VncState *vs)
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 @@ static void vnc_client_write_locked(void *opaque)
} 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 @@ void vnc_client_write(void *opaque)
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 @@ void vnc_client_read(void *opaque)
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 @@ void vnc_client_read(void *opaque)
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_write_u8(VncState *vs, uint8_t value)
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 void make_challenge(VncState *vs)
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 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
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 @@ static void vnc_connect(VncDisplay *vd, int csock,
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 @@ static void vnc_connect(VncDisplay *vd, int csock,
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 @@ static void vnc_connect(VncDisplay *vd, int csock,
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 @@ static void vnc_listen_read(void *opaque, bool websocket)
/* 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 @@ static void vnc_listen_regular_read(void *opaque)
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 @@ void vnc_display_init(const char *id)
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 @@ static void vnc_display_close(VncDisplay *vs)
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 @@ void vnc_display_open(const char *id, Error **errp)
}
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 @@ void vnc_display_open(const char *id, Error **errp)
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 @@ void vnc_display_open(const char *id, Error **errp)
/* 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 @@ void vnc_display_open(const char *id, Error **errp)
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 @@ void vnc_display_open(const char *id, Error **errp)
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 @@ fail:
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 3f7c6a9bc6..814d720df2 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -108,9 +108,7 @@ typedef struct VncDisplay VncDisplay;
#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 @@ struct VncDisplay
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 @@ struct VncState
#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 ceaba30939..114d6578c4 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -9,7 +9,7 @@ util-obj-y += acl.o
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 cfa6133308..3f269dc58d 100644
--- a/vl.c
+++ b/vl.c
@@ -121,6 +121,7 @@ int main(int argc, char **argv)
#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 @@ int main(int argc, char **argv, char **envp)
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 @@ int main(int argc, char **argv, char **envp)
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 @@ int main(int argc, char **argv, char **envp)
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) {