diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2015-07-08 20:46:35 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2015-07-08 20:46:35 +0100 |
commit | acf7b7fdf31fa76b53803790917c8acf23a2badb (patch) | |
tree | 999bae49ca3a0ea7ef5476c1764c6166b17a0c12 | |
parent | c8e84287da7dd6a46c0bb0e53190e79ba4eedf24 (diff) | |
parent | 2828a307232ffceeddec9feb6a87ac660b68b693 (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>
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 */ @@ -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 @@ -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) @@ -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 @@ -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) { |