Bugfixes and Daniel Berrange's crypto library.

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQEcBAABCAAGBQJVnQWdAAoJEL/70l94x66D6OgIAKJlzQfmy5w7Q9WD4vCMhD76
 JrpLSsn7Gx/Bws0Nu9nLQlqun5z4hiUxyG2kP/WqD9+tV3cpSMSyrG6ImVdqKnQ5
 +Z8WJZuREkQv0aqDUjQVST+eIDZuh2LWJXAjhgsCXUHY77eWb/7WmKT79xJOa+5C
 5xB1qxudqX5IsTvpiKKPbmUGYkAcvRX1dUSaFwRIMO0UyKn59B9WfM9a5slIbLW7
 XfI8+wEJshTVLuQkkTfdidWQc5M5DwlmO7ESUNR/BRPCPFeyjcDqgQY5pBM5XVo9
 C+S0R3zIt3Ew0fhCtLRyjlIT0bGfwjbU5HRiHcyldBKhNUZZjSUoOWJnYRHXUDY=
 =H8wA
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2015-07-08 20:46:35 +01:00
commit acf7b7fdf3
41 changed files with 2545 additions and 278 deletions

View File

@ -1052,6 +1052,13 @@ S: Supported
F: qemu-seccomp.c F: qemu-seccomp.c
F: include/sysemu/seccomp.h 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 Usermode Emulation
------------------ ------------------
Overall Overall

View File

@ -2,6 +2,7 @@
# Common libraries for tools and emulators # Common libraries for tools and emulators
stub-obj-y = stubs/ stub-obj-y = stubs/
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o 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 # block-obj-y is code used by both qemu system emulation and qemu-img

View File

@ -853,6 +853,7 @@ static void *oss_audio_init (void)
if (access(conf->devpath_in, R_OK | W_OK) < 0 || if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
access(conf->devpath_out, R_OK | W_OK) < 0) { access(conf->devpath_out, R_OK | W_OK) < 0) {
g_free(conf);
return NULL; return NULL;
} }
return conf; return conf;

View File

@ -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.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
block-obj-y += qed-check.o block-obj-y += qed-check.o
block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.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 += parallels.o blkdebug.o blkverify.o
block-obj-y += block-backend.o snapshot.o qapi.o block-obj-y += block-backend.o snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o

View File

@ -26,7 +26,7 @@
#include "qemu/module.h" #include "qemu/module.h"
#include <zlib.h> #include <zlib.h>
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/aes.h" #include "crypto/cipher.h"
#include "migration/migration.h" #include "migration/migration.h"
/**************************************************************/ /**************************************************************/
@ -72,10 +72,8 @@ typedef struct BDRVQcowState {
uint8_t *cluster_cache; uint8_t *cluster_cache;
uint8_t *cluster_data; uint8_t *cluster_data;
uint64_t cluster_cache_offset; 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; uint32_t crypt_method_header;
AES_KEY aes_encrypt_key;
AES_KEY aes_decrypt_key;
CoMutex lock; CoMutex lock;
Error *migration_blocker; Error *migration_blocker;
} BDRVQcowState; } BDRVQcowState;
@ -154,6 +152,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL; ret = -EINVAL;
goto fail; 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; s->crypt_method_header = header.crypt_method;
if (s->crypt_method_header) { if (s->crypt_method_header) {
bs->encrypted = 1; bs->encrypted = 1;
@ -260,6 +263,7 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
uint8_t keybuf[16]; uint8_t keybuf[16];
int len, i; int len, i;
Error *err;
memset(keybuf, 0, 16); memset(keybuf, 0, 16);
len = strlen(key); len = strlen(key);
@ -271,38 +275,67 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
keybuf[i] = key[i]; keybuf[i] = key[i];
} }
assert(bs->encrypted); assert(bs->encrypted);
s->crypt_method = s->crypt_method_header;
if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) qcrypto_cipher_free(s->cipher);
return -1; s->cipher = qcrypto_cipher_new(
if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) 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 -1;
}
return 0; return 0;
} }
/* The crypt function is compatible with the linux cryptoloop /* The crypt function is compatible with the linux cryptoloop
algorithm for < 4 GB images. NOTE: out_buf == in_buf is algorithm for < 4 GB images. NOTE: out_buf == in_buf is
supported */ supported */
static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
uint8_t *out_buf, const uint8_t *in_buf, uint8_t *out_buf, const uint8_t *in_buf,
int nb_sectors, int enc, int nb_sectors, bool enc, Error **errp)
const AES_KEY *key)
{ {
union { union {
uint64_t ll[2]; uint64_t ll[2];
uint8_t b[16]; uint8_t b[16];
} ivec; } ivec;
int i; int i;
int ret;
for(i = 0; i < nb_sectors; i++) { for(i = 0; i < nb_sectors; i++) {
ivec.ll[0] = cpu_to_le64(sector_num); ivec.ll[0] = cpu_to_le64(sector_num);
ivec.ll[1] = 0; ivec.ll[1] = 0;
AES_cbc_encrypt(in_buf, out_buf, 512, key, if (qcrypto_cipher_setiv(s->cipher,
ivec.b, enc); 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++; sector_num++;
in_buf += 512; in_buf += 512;
out_buf += 512; out_buf += 512;
} }
return 0;
} }
/* 'allocate' is: /* 'allocate' is:
@ -416,15 +449,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
if (bs->encrypted && if (bs->encrypted &&
(n_end - n_start) < s->cluster_sectors) { (n_end - n_start) < s->cluster_sectors) {
uint64_t start_sect; uint64_t start_sect;
assert(s->crypt_method); assert(s->cipher);
start_sect = (offset & ~(s->cluster_size - 1)) >> 9; start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
memset(s->cluster_data + 512, 0x00, 512); memset(s->cluster_data + 512, 0x00, 512);
for(i = 0; i < s->cluster_sectors; i++) { for(i = 0; i < s->cluster_sectors; i++) {
if (i < n_start || i >= n_end) { if (i < n_start || i >= n_end) {
encrypt_sectors(s, start_sect + i, Error *err = NULL;
s->cluster_data, if (encrypt_sectors(s, start_sect + i,
s->cluster_data + 512, 1, 1, s->cluster_data,
&s->aes_encrypt_key); s->cluster_data + 512, 1,
true, &err) < 0) {
error_free(err);
errno = EIO;
return -1;
}
if (bdrv_pwrite(bs->file, cluster_offset + i * 512, if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
s->cluster_data, 512) != 512) s->cluster_data, 512) != 512)
return -1; return -1;
@ -464,7 +502,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
if (!cluster_offset) { if (!cluster_offset) {
return 0; return 0;
} }
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) { if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) {
return BDRV_BLOCK_DATA; return BDRV_BLOCK_DATA;
} }
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); 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; QEMUIOVector hd_qiov;
uint8_t *buf; uint8_t *buf;
void *orig_buf; void *orig_buf;
Error *err = NULL;
if (qiov->niov > 1) { if (qiov->niov > 1) {
buf = orig_buf = qemu_try_blockalign(bs, qiov->size); 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; break;
} }
if (bs->encrypted) { if (bs->encrypted) {
assert(s->crypt_method); assert(s->cipher);
encrypt_sectors(s, sector_num, buf, buf, if (encrypt_sectors(s, sector_num, buf, buf,
n, 0, n, false, &err) < 0) {
&s->aes_decrypt_key); goto fail;
}
} }
} }
ret = 0; ret = 0;
@ -618,6 +658,7 @@ done:
return ret; return ret;
fail: fail:
error_free(err);
ret = -EIO; ret = -EIO;
goto done; goto done;
} }
@ -666,12 +707,17 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
break; break;
} }
if (bs->encrypted) { if (bs->encrypted) {
assert(s->crypt_method); Error *err = NULL;
assert(s->cipher);
if (!cluster_data) { if (!cluster_data) {
cluster_data = g_malloc0(s->cluster_size); cluster_data = g_malloc0(s->cluster_size);
} }
encrypt_sectors(s, sector_num, cluster_data, buf, if (encrypt_sectors(s, sector_num, cluster_data, buf,
n, 1, &s->aes_encrypt_key); n, true, &err) < 0) {
error_free(err);
ret = -EIO;
break;
}
src_buf = cluster_data; src_buf = cluster_data;
} else { } else {
src_buf = buf; src_buf = buf;
@ -708,6 +754,8 @@ static void qcow_close(BlockDriverState *bs)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
qcrypto_cipher_free(s->cipher);
s->cipher = NULL;
g_free(s->l1_table); g_free(s->l1_table);
qemu_vfree(s->l2_cache); qemu_vfree(s->l2_cache);
g_free(s->cluster_cache); g_free(s->cluster_cache);

View File

@ -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 /* The crypt function is compatible with the linux cryptoloop
algorithm for < 4 GB images. NOTE: out_buf == in_buf is algorithm for < 4 GB images. NOTE: out_buf == in_buf is
supported */ supported */
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
uint8_t *out_buf, const uint8_t *in_buf, uint8_t *out_buf, const uint8_t *in_buf,
int nb_sectors, int enc, int nb_sectors, bool enc,
const AES_KEY *key) Error **errp)
{ {
union { union {
uint64_t ll[2]; uint64_t ll[2];
uint8_t b[16]; uint8_t b[16];
} ivec; } ivec;
int i; int i;
int ret;
for(i = 0; i < nb_sectors; i++) { for(i = 0; i < nb_sectors; i++) {
ivec.ll[0] = cpu_to_le64(sector_num); ivec.ll[0] = cpu_to_le64(sector_num);
ivec.ll[1] = 0; ivec.ll[1] = 0;
AES_cbc_encrypt(in_buf, out_buf, 512, key, if (qcrypto_cipher_setiv(s->cipher,
ivec.b, enc); 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++; sector_num++;
in_buf += 512; in_buf += 512;
out_buf += 512; out_buf += 512;
} }
return 0;
} }
static int coroutine_fn copy_sectors(BlockDriverState *bs, static int coroutine_fn copy_sectors(BlockDriverState *bs,
@ -401,10 +422,15 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
} }
if (bs->encrypted) { if (bs->encrypted) {
assert(s->crypt_method); Error *err = NULL;
qcow2_encrypt_sectors(s, start_sect + n_start, assert(s->cipher);
iov.iov_base, iov.iov_base, n, 1, if (qcow2_encrypt_sectors(s, start_sect + n_start,
&s->aes_encrypt_key); 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, ret = qcow2_pre_write_overlap_check(bs, 0,

View File

@ -25,7 +25,6 @@
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/module.h" #include "qemu/module.h"
#include <zlib.h> #include <zlib.h>
#include "qemu/aes.h"
#include "block/qcow2.h" #include "block/qcow2.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
@ -699,6 +698,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL; ret = -EINVAL;
goto fail; 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; s->crypt_method_header = header.crypt_method;
if (s->crypt_method_header) { if (s->crypt_method_header) {
bs->encrypted = 1; bs->encrypted = 1;
@ -1032,6 +1036,7 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
uint8_t keybuf[16]; uint8_t keybuf[16];
int len, i; int len, i;
Error *err = NULL;
memset(keybuf, 0, 16); memset(keybuf, 0, 16);
len = strlen(key); len = strlen(key);
@ -1043,30 +1048,21 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
keybuf[i] = key[i]; keybuf[i] = key[i];
} }
assert(bs->encrypted); assert(bs->encrypted);
s->crypt_method = s->crypt_method_header;
if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) qcrypto_cipher_free(s->cipher);
s->cipher = qcrypto_cipher_new(
QCRYPTO_CIPHER_ALG_AES_128,
QCRYPTO_CIPHER_MODE_CBC,
keybuf, G_N_ELEMENTS(keybuf),
&err);
if (!s->cipher) {
/* XXX would be nice if errors in this method could
* be properly propagate to the caller. Would need
* the bdrv_set_key() API signature to be fixed. */
error_free(err);
return -1; return -1;
if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
return -1;
#if 0
/* test */
{
uint8_t in[16];
uint8_t out[16];
uint8_t tmp[16];
for(i=0;i<16;i++)
in[i] = i;
AES_encrypt(in, tmp, &s->aes_encrypt_key);
AES_decrypt(tmp, out, &s->aes_decrypt_key);
for(i = 0; i < 16; i++)
printf(" %02x", tmp[i]);
printf("\n");
for(i = 0; i < 16; i++)
printf(" %02x", out[i]);
printf("\n");
} }
#endif
return 0; 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 && if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
!s->crypt_method) { !s->cipher) {
index_in_cluster = sector_num & (s->cluster_sectors - 1); index_in_cluster = sector_num & (s->cluster_sectors - 1);
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; 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 */ /* prepare next request */
cur_nr_sectors = remaining_sectors; cur_nr_sectors = remaining_sectors;
if (s->crypt_method) { if (s->cipher) {
cur_nr_sectors = MIN(cur_nr_sectors, cur_nr_sectors = MIN(cur_nr_sectors,
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_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) { if (bs->encrypted) {
assert(s->crypt_method); assert(s->cipher);
/* /*
* For encrypted images, read everything into a temporary * 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; goto fail;
} }
if (bs->encrypted) { if (bs->encrypted) {
assert(s->crypt_method); assert(s->cipher);
qcow2_encrypt_sectors(s, sector_num, cluster_data, Error *err = NULL;
cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key); 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, qemu_iovec_from_buf(qiov, bytes_done,
cluster_data, 512 * cur_nr_sectors); cluster_data, 512 * cur_nr_sectors);
} }
@ -1344,7 +1346,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
cur_nr_sectors * 512); cur_nr_sectors * 512);
if (bs->encrypted) { if (bs->encrypted) {
assert(s->crypt_method); Error *err = NULL;
assert(s->cipher);
if (!cluster_data) { if (!cluster_data) {
cluster_data = qemu_try_blockalign(bs->file, cluster_data = qemu_try_blockalign(bs->file,
QCOW_MAX_CRYPT_CLUSTERS QCOW_MAX_CRYPT_CLUSTERS
@ -1359,8 +1362,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size); qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
qcow2_encrypt_sectors(s, sector_num, cluster_data, if (qcow2_encrypt_sectors(s, sector_num, cluster_data,
cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key); cluster_data, cur_nr_sectors,
true, &err) < 0) {
error_free(err);
ret = -EIO;
goto fail;
}
qemu_iovec_reset(&hd_qiov); qemu_iovec_reset(&hd_qiov);
qemu_iovec_add(&hd_qiov, cluster_data, 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->l2_table_cache);
qcow2_cache_destroy(bs, s->refcount_block_cache); qcow2_cache_destroy(bs, s->refcount_block_cache);
qcrypto_cipher_free(s->cipher);
s->cipher = NULL;
g_free(s->unknown_header_fields); g_free(s->unknown_header_fields);
cleanup_unknown_header_ext(bs); cleanup_unknown_header_ext(bs);
@ -1482,9 +1493,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int flags = s->flags; int flags = s->flags;
AES_KEY aes_encrypt_key; QCryptoCipher *cipher = NULL;
AES_KEY aes_decrypt_key;
uint32_t crypt_method = 0;
QDict *options; QDict *options;
Error *local_err = NULL; Error *local_err = NULL;
int ret; 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. * that means we don't have to worry about reopening them here.
*/ */
if (bs->encrypted) { cipher = s->cipher;
assert(s->crypt_method); s->cipher = NULL;
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));
}
qcow2_close(bs); qcow2_close(bs);
@ -1524,11 +1529,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
return; return;
} }
if (bs->encrypted) { s->cipher = cipher;
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));
}
} }
static size_t header_ext_add(char *buf, uint32_t magic, const void *s, 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); backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
} else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) { } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
s->crypt_method); !!s->cipher);
if (encrypt != !!s->crypt_method) {
if (encrypt != !!s->cipher) {
fprintf(stderr, "Changing the encryption flag is not " fprintf(stderr, "Changing the encryption flag is not "
"supported.\n"); "supported.\n");
return -ENOTSUP; return -ENOTSUP;

View File

@ -25,7 +25,7 @@
#ifndef BLOCK_QCOW2_H #ifndef BLOCK_QCOW2_H
#define BLOCK_QCOW2_H #define BLOCK_QCOW2_H
#include "qemu/aes.h" #include "crypto/cipher.h"
#include "block/coroutine.h" #include "block/coroutine.h"
//#define DEBUG_ALLOC //#define DEBUG_ALLOC
@ -253,10 +253,8 @@ typedef struct BDRVQcowState {
CoMutex lock; 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; uint32_t crypt_method_header;
AES_KEY aes_encrypt_key;
AES_KEY aes_decrypt_key;
uint64_t snapshots_offset; uint64_t snapshots_offset;
int snapshots_size; int snapshots_size;
unsigned int nb_snapshots; 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); int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
void qcow2_l2_cache_reset(BlockDriverState *bs); void qcow2_l2_cache_reset(BlockDriverState *bs);
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
uint8_t *out_buf, const uint8_t *in_buf, uint8_t *out_buf, const uint8_t *in_buf,
int nb_sectors, int enc, int nb_sectors, bool enc, Error **errp);
const AES_KEY *key);
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *cluster_offset); int *num, uint64_t *cluster_offset);

View File

@ -13,8 +13,6 @@
* See the COPYING file in the top-level directory. * See the COPYING file in the top-level directory.
*/ */
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include "block/block_int.h" #include "block/block_int.h"
#include "qapi/qmp/qbool.h" #include "qapi/qmp/qbool.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
@ -24,6 +22,7 @@
#include "qapi/qmp/qlist.h" #include "qapi/qmp/qlist.h"
#include "qapi/qmp/qstring.h" #include "qapi/qmp/qstring.h"
#include "qapi-event.h" #include "qapi-event.h"
#include "crypto/hash.h"
#define HASH_LENGTH 32 #define HASH_LENGTH 32
@ -34,7 +33,7 @@
/* This union holds a vote hash value */ /* This union holds a vote hash value */
typedef union QuorumVoteValue { 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 */ int64_t l; /* simpler 64 bits hash */
} QuorumVoteValue; } QuorumVoteValue;
@ -428,25 +427,21 @@ static void quorum_free_vote_list(QuorumVotes *votes)
static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash) 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; QEMUIOVector *qiov = &acb->qcrs[i].qiov;
size_t len = sizeof(hash->h);
uint8_t *data = hash->h;
ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256); /* XXX - would be nice if we could pass in the Error **
* and propagate that back, but this quorum code is
if (ret < 0) { * restricted to just errno values currently */
return ret; if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256,
qiov->iov, qiov->niov,
&data, &len,
NULL) < 0) {
return -EINVAL;
} }
for (j = 0; j < qiov->niov; j++) { return 0;
ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len);
if (ret < 0) {
break;
}
}
gnutls_hash_deinit(dig, (void *) hash);
return ret;
} }
static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes) 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 i;
int ret = 0; 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); qdict_flatten(options);
/* count how many different children are present */ /* count how many different children are present */

160
configure vendored
View File

@ -246,7 +246,6 @@ vnc_tls=""
vnc_sasl="" vnc_sasl=""
vnc_jpeg="" vnc_jpeg=""
vnc_png="" vnc_png=""
vnc_ws=""
xen="" xen=""
xen_ctrl_version="" xen_ctrl_version=""
xen_pci_passthrough="" xen_pci_passthrough=""
@ -331,11 +330,12 @@ glusterfs_zerofill="no"
archipelago="no" archipelago="no"
gtk="" gtk=""
gtkabi="" gtkabi=""
gnutls=""
gnutls_hash=""
vte="" vte=""
tpm="yes" tpm="yes"
libssh2="" libssh2=""
vhdx="" vhdx=""
quorum=""
numa="" numa=""
tcmalloc="no" tcmalloc="no"
@ -896,10 +896,6 @@ for opt do
;; ;;
--enable-vnc-png) vnc_png="yes" --enable-vnc-png) vnc_png="yes"
;; ;;
--disable-vnc-ws) vnc_ws="no"
;;
--enable-vnc-ws) vnc_ws="yes"
;;
--disable-slirp) slirp="no" --disable-slirp) slirp="no"
;; ;;
--disable-uuid) uuid="no" --disable-uuid) uuid="no"
@ -1119,6 +1115,10 @@ for opt do
;; ;;
--enable-gtk) gtk="yes" --enable-gtk) gtk="yes"
;; ;;
--disable-gnutls) gnutls="no"
;;
--enable-gnutls) gnutls="yes"
;;
--enable-rdma) rdma="yes" --enable-rdma) rdma="yes"
;; ;;
--disable-rdma) rdma="no" --disable-rdma) rdma="no"
@ -1141,10 +1141,6 @@ for opt do
;; ;;
--disable-vhdx) vhdx="no" --disable-vhdx) vhdx="no"
;; ;;
--disable-quorum) quorum="no"
;;
--enable-quorum) quorum="yes"
;;
--disable-numa) numa="no" --disable-numa) numa="no"
;; ;;
--enable-numa) numa="yes" --enable-numa) numa="yes"
@ -1329,6 +1325,7 @@ disabled with --disable-FEATURE, default is enabled if available:
debug-info debugging information debug-info debugging information
sparse sparse checker sparse sparse checker
gnutls GNUTLS cryptography support
sdl SDL UI sdl SDL UI
--with-sdlabi select preferred SDL ABI 1.2 or 2.0 --with-sdlabi select preferred SDL ABI 1.2 or 2.0
gtk gtk UI gtk gtk UI
@ -1376,7 +1373,6 @@ disabled with --disable-FEATURE, default is enabled if available:
tpm TPM support tpm TPM support
libssh2 ssh block device support libssh2 ssh block device support
vhdx support for the Microsoft VHDX image format vhdx support for the Microsoft VHDX image format
quorum quorum block filter support
numa libnuma support numa libnuma support
tcmalloc tcmalloc support tcmalloc tcmalloc support
@ -2116,6 +2112,86 @@ if test "$gtk" != "no"; then
fi fi
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 # VTE probe
@ -2263,7 +2339,7 @@ fi
########################################## ##########################################
# VNC TLS/WS detection # 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 cat > $TMPC <<EOF
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
@ -2274,50 +2350,16 @@ EOF
if test "$vnc_tls" != "no" ; then if test "$vnc_tls" != "no" ; then
vnc_tls=yes vnc_tls=yes
fi fi
if test "$vnc_ws" != "no" ; then
vnc_ws=yes
fi
libs_softmmu="$vnc_tls_libs $libs_softmmu" libs_softmmu="$vnc_tls_libs $libs_softmmu"
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
else else
if test "$vnc_tls" = "yes" ; then if test "$vnc_tls" = "yes" ; then
feature_not_found "vnc-tls" "Install gnutls devel" feature_not_found "vnc-tls" "Install gnutls devel"
fi fi
if test "$vnc_ws" = "yes" ; then
feature_not_found "vnc-ws" "Install gnutls devel"
fi
vnc_tls=no vnc_tls=no
vnc_ws=no
fi fi
fi fi
##########################################
# Quorum probe (check for gnutls)
if test "$quorum" != "no" ; then
cat > $TMPC <<EOF
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
int main(void) {char data[4096], digest[32];
gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest);
return 0;
}
EOF
quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then
qcow_tls=yes
libs_softmmu="$quorum_tls_libs $libs_softmmu"
libs_tools="$quorum_tls_libs $libs_softmmu"
QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags"
quorum="yes"
else
if test "$quorum" = "yes"; then
feature_not_found "gnutls" "gnutls > 2.10.0 required to compile Quorum"
fi
quorum="no"
fi
fi
########################################## ##########################################
# VNC SASL detection # VNC SASL detection
if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then
@ -4445,6 +4487,10 @@ fi
echo "pixman $pixman" echo "pixman $pixman"
echo "SDL support $sdl" echo "SDL support $sdl"
echo "GTK support $gtk" 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 "VTE support $vte"
echo "curses support $curses" echo "curses support $curses"
echo "curl support $curl" echo "curl support $curl"
@ -4459,7 +4505,6 @@ if test "$vnc" = "yes" ; then
echo "VNC SASL support $vnc_sasl" echo "VNC SASL support $vnc_sasl"
echo "VNC JPEG support $vnc_jpeg" echo "VNC JPEG support $vnc_jpeg"
echo "VNC PNG support $vnc_png" echo "VNC PNG support $vnc_png"
echo "VNC WS support $vnc_ws"
fi fi
if test -n "$sparc_cpu"; then if test -n "$sparc_cpu"; then
echo "Target Sparc Arch $sparc_cpu" echo "Target Sparc Arch $sparc_cpu"
@ -4523,7 +4568,6 @@ echo "libssh2 support $libssh2"
echo "TPM passthrough $tpm_passthrough" echo "TPM passthrough $tpm_passthrough"
echo "QOM debugging $qom_cast_debug" echo "QOM debugging $qom_cast_debug"
echo "vhdx $vhdx" echo "vhdx $vhdx"
echo "Quorum $quorum"
echo "lzo support $lzo" echo "lzo support $lzo"
echo "snappy support $snappy" echo "snappy support $snappy"
echo "bzip2 support $bzip2" echo "bzip2 support $bzip2"
@ -4676,10 +4720,6 @@ fi
if test "$vnc_png" = "yes" ; then if test "$vnc_png" = "yes" ; then
echo "CONFIG_VNC_PNG=y" >> $config_host_mak echo "CONFIG_VNC_PNG=y" >> $config_host_mak
fi 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 if test "$fnmatch" = "yes" ; then
echo "CONFIG_FNMATCH=y" >> $config_host_mak echo "CONFIG_FNMATCH=y" >> $config_host_mak
fi fi
@ -4807,6 +4847,18 @@ if test "$gtk" = "yes" ; then
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
fi 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 if test "$vte" = "yes" ; then
echo "CONFIG_VTE=y" >> $config_host_mak echo "CONFIG_VTE=y" >> $config_host_mak
echo "VTE_CFLAGS=$vte_cflags" >> $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 echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
fi fi
if test "$quorum" = "yes" ; then
echo "CONFIG_QUORUM=y" >> $config_host_mak
fi
if test "$vhdx" = "yes" ; then if test "$vhdx" = "yes" ; then
echo "CONFIG_VHDX=y" >> $config_host_mak echo "CONFIG_VHDX=y" >> $config_host_mak
fi fi

5
crypto/Makefile.objs Normal file
View File

@ -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

View File

@ -28,7 +28,7 @@
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "qemu-common.h" #include "qemu-common.h"
#include "qemu/aes.h" #include "crypto/aes.h"
typedef uint32_t u32; typedef uint32_t u32;
typedef uint8_t u8; typedef uint8_t u8;

398
crypto/cipher-builtin.c Normal file
View File

@ -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);
}

195
crypto/cipher-gcrypt.c Normal file
View File

@ -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;
}

206
crypto/cipher-nettle.c Normal file
View File

@ -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;
}

74
crypto/cipher.c Normal file
View File

@ -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

View File

@ -26,7 +26,7 @@
* (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
*/ */
#include "d3des.h" #include "crypto/desrfb.h"
static void scrunch(unsigned char *, unsigned long *); static void scrunch(unsigned char *, unsigned long *);
static void unscrun(unsigned long *, unsigned char *); static void unscrun(unsigned long *, unsigned char *);

200
crypto/hash.c Normal file
View File

@ -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);
}

150
crypto/init.c Normal file
View File

@ -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 */

View File

@ -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; hwaddr addr = 0;
MemoryRegionSection section; MemoryRegionSection section;
@ -957,12 +957,8 @@ int rom_load_all(void)
memory_region_unref(section.mr); memory_region_unref(section.mr);
} }
qemu_register_reset(rom_reset, NULL); qemu_register_reset(rom_reset, NULL);
return 0;
}
void rom_load_done(void)
{
roms_loaded = 1; roms_loaded = 1;
return 0;
} }
void rom_set_fw(FWCfgState *f) void rom_set_fw(FWCfgState *f)

210
include/crypto/cipher.h Normal file
View File

@ -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__ */

189
include/crypto/hash.h Normal file
View File

@ -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__ */

29
include/crypto/init.h Normal file
View File

@ -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__ */

View File

@ -75,8 +75,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
void *callback_opaque); void *callback_opaque);
int rom_add_elf_program(const char *name, void *data, size_t datasize, int rom_add_elf_program(const char *name, void *data, size_t datasize,
size_t romsize, hwaddr addr); size_t romsize, hwaddr addr);
int rom_load_all(void); int rom_check_and_register_reset(void);
void rom_load_done(void);
void rom_set_fw(FWCfgState *f); void rom_set_fw(FWCfgState *f);
int rom_copy(uint8_t *dest, hwaddr addr, size_t size); int rom_copy(uint8_t *dest, hwaddr addr, size_t size);
void *rom_ptr(hwaddr addr); void *rom_ptr(hwaddr addr);

View File

@ -14,7 +14,7 @@
#include "cpu.h" #include "cpu.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "qemu/aes.h" #include "crypto/aes.h"
union CRYPTO_STATE { union CRYPTO_STATE {
uint8_t bytes[16]; uint8_t bytes[16];

View File

@ -20,7 +20,6 @@
#include <math.h> #include <math.h>
#include "cpu.h" #include "cpu.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "qemu/aes.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"

View File

@ -18,7 +18,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "qemu/aes.h" #include "crypto/aes.h"
#if SHIFT == 0 #if SHIFT == 0
#define Reg MMXReg #define Reg MMXReg

View File

@ -19,7 +19,7 @@
#include "cpu.h" #include "cpu.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "qemu/aes.h" #include "crypto/aes.h"
#include "helper_regs.h" #include "helper_regs.h"
/*****************************************************************************/ /*****************************************************************************/

2
tests/.gitignore vendored
View File

@ -10,6 +10,8 @@ rcutorture
test-aio test-aio
test-bitops test-bitops
test-coroutine test-coroutine
test-crypto-cipher
test-crypto-hash
test-cutils test-cutils
test-hbitmap test-hbitmap
test-int128 test-int128

View File

@ -74,6 +74,8 @@ check-unit-y += tests/test-qemu-opts$(EXESUF)
gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c
check-unit-y += tests/test-write-threshold$(EXESUF) check-unit-y += tests/test-write-threshold$(EXESUF)
gcov-files-test-write-threshold-y = block/write-threshold.c 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 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-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
tests/test-bitops$(EXESUF): tests/test-bitops.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/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o

290
tests/test-crypto-cipher.c Normal file
View File

@ -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();
}

209
tests/test-crypto-hash.c Normal file
View File

@ -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();
}

View File

@ -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-zlib.o vnc-enc-hextile.o
vnc-obj-y += vnc-enc-tight.o vnc-palette.o vnc-obj-y += vnc-enc-tight.o vnc-palette.o
vnc-obj-y += vnc-enc-zrle.o vnc-obj-y += vnc-enc-zrle.o
vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.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_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 vnc-obj-y += vnc-jobs.o
common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o

View File

@ -20,6 +20,7 @@
#include "vnc.h" #include "vnc.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "crypto/hash.h"
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
#include "qemu/sockets.h" #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) static void vncws_send_handshake_response(VncState *vs, const char* key)
{ {
char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1]; 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; char *accept = NULL, *response = NULL;
gnutls_datum_t in; Error *err = NULL;
int ret;
g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1); g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1);
g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1); g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1);
/* hash and encode it */ /* hash and encode it */
in.data = (void *)combined_key; if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN; combined_key,
ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size); WS_CLIENT_KEY_LEN + WS_GUID_LEN,
if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) { &accept,
accept = g_base64_encode(hash, hash_size); &err) < 0) {
} VNC_DEBUG("Hashing Websocket combined key failed %s\n",
if (accept == NULL) { error_get_pretty(err));
VNC_DEBUG("Hashing Websocket combined key failed\n"); error_free(err);
vnc_client_error(vs); vnc_client_error(vs);
return; return;
} }

View File

@ -21,8 +21,6 @@
#ifndef __QEMU_UI_VNC_WS_H #ifndef __QEMU_UI_VNC_WS_H
#define __QEMU_UI_VNC_WS_H #define __QEMU_UI_VNC_WS_H
#include <gnutls/gnutls.h>
#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) #define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
#define SHA1_DIGEST_LEN 20 #define SHA1_DIGEST_LEN 20

119
ui/vnc.c
View File

@ -40,6 +40,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "ui/input.h" #include "ui/input.h"
#include "qapi-event.h" #include "qapi-event.h"
#include "crypto/hash.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50 #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 }; static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
#include "vnc_keysym.h" #include "vnc_keysym.h"
#include "d3des.h" #include "crypto/cipher.h"
static QTAILQ_HEAD(, VncDisplay) vnc_displays = static QTAILQ_HEAD(, VncDisplay) vnc_displays =
QTAILQ_HEAD_INITIALIZER(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->host = g_strdup(host);
info->base->service = g_strdup(serv); info->base->service = g_strdup(serv);
info->base->family = inet_netfamily(sa.ss_family); info->base->family = inet_netfamily(sa.ss_family);
#ifdef CONFIG_VNC_WS
info->base->websocket = client->websocket; info->base->websocket = client->websocket;
#endif
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
if (client->tls.session && client->tls.dname) { 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 = qmp_query_server_entry(vd->lsock, false,
info->server); info->server);
} }
#ifdef CONFIG_VNC_WS
if (vd->lwebsock != -1) { if (vd->lwebsock != -1) {
info->server = qmp_query_server_entry(vd->lwebsock, true, info->server = qmp_query_server_entry(vd->lwebsock, true,
info->server); info->server);
} }
#endif
item = g_new0(VncInfo2List, 1); item = g_new0(VncInfo2List, 1);
item->value = info; item->value = info;
@ -1231,10 +1228,8 @@ void vnc_disconnect_finish(VncState *vs)
buffer_free(&vs->input); buffer_free(&vs->input);
buffer_free(&vs->output); buffer_free(&vs->output);
#ifdef CONFIG_VNC_WS
buffer_free(&vs->ws_input); buffer_free(&vs->ws_input);
buffer_free(&vs->ws_output); buffer_free(&vs->ws_output);
#endif /* CONFIG_VNC_WS */
qapi_free_VncClientInfo(vs->info); qapi_free_VncClientInfo(vs->info);
@ -1413,12 +1408,9 @@ static void vnc_client_write_locked(void *opaque)
} else } else
#endif /* CONFIG_VNC_SASL */ #endif /* CONFIG_VNC_SASL */
{ {
#ifdef CONFIG_VNC_WS
if (vs->encode_ws) { if (vs->encode_ws) {
vnc_client_write_ws(vs); vnc_client_write_ws(vs);
} else } else {
#endif /* CONFIG_VNC_WS */
{
vnc_client_write_plain(vs); vnc_client_write_plain(vs);
} }
} }
@ -1429,11 +1421,7 @@ void vnc_client_write(void *opaque)
VncState *vs = opaque; VncState *vs = opaque;
vnc_lock_output(vs); vnc_lock_output(vs);
if (vs->output.offset if (vs->output.offset || vs->ws_output.offset) {
#ifdef CONFIG_VNC_WS
|| vs->ws_output.offset
#endif
) {
vnc_client_write_locked(opaque); vnc_client_write_locked(opaque);
} else if (vs->csock != -1) { } else if (vs->csock != -1) {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); 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); ret = vnc_client_read_sasl(vs);
else else
#endif /* CONFIG_VNC_SASL */ #endif /* CONFIG_VNC_SASL */
#ifdef CONFIG_VNC_WS
if (vs->encode_ws) { if (vs->encode_ws) {
ret = vnc_client_read_ws(vs); ret = vnc_client_read_ws(vs);
if (ret == -1) { if (ret == -1) {
@ -1549,10 +1536,8 @@ void vnc_client_read(void *opaque)
vnc_client_error(vs); vnc_client_error(vs);
return; return;
} }
} else } else {
#endif /* CONFIG_VNC_WS */ ret = vnc_client_read_plain(vs);
{
ret = vnc_client_read_plain(vs);
} }
if (!ret) { if (!ret) {
if (vs->csock == -1) if (vs->csock == -1)
@ -1624,11 +1609,8 @@ void vnc_write_u8(VncState *vs, uint8_t value)
void vnc_flush(VncState *vs) void vnc_flush(VncState *vs)
{ {
vnc_lock_output(vs); vnc_lock_output(vs);
if (vs->csock != -1 && (vs->output.offset if (vs->csock != -1 && (vs->output.offset ||
#ifdef CONFIG_VNC_WS vs->ws_output.offset)) {
|| vs->ws_output.offset
#endif
)) {
vnc_client_write_locked(vs); vnc_client_write_locked(vs);
} }
vnc_unlock_output(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) static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
{ {
unsigned char response[VNC_AUTH_CHALLENGE_SIZE]; unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
int i, j, pwlen; size_t i, pwlen;
unsigned char key[8]; unsigned char key[8];
time_t now = time(NULL); time_t now = time(NULL);
QCryptoCipher *cipher;
Error *err = NULL;
if (!vs->vd->password) { if (!vs->vd->password) {
VNC_DEBUG("No password configured on server"); 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); pwlen = strlen(vs->vd->password);
for (i=0; i<sizeof(key); i++) for (i=0; i<sizeof(key); i++)
key[i] = i<pwlen ? vs->vd->password[i] : 0; key[i] = i<pwlen ? vs->vd->password[i] : 0;
deskey(key, EN0);
for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8) cipher = qcrypto_cipher_new(
des(response+j, response+j); 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 */ /* Compare expected vs actual challenge response */
if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) { 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); VNC_DEBUG("New client on socket %d\n", csock);
update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
qemu_set_nonblock(vs->csock); qemu_set_nonblock(vs->csock);
#ifdef CONFIG_VNC_WS
if (websocket) { if (websocket) {
vs->websocket = 1; vs->websocket = 1;
#ifdef CONFIG_VNC_TLS #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); qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
} }
} else } else
#endif /* CONFIG_VNC_WS */
{ {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); 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_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
#ifdef CONFIG_VNC_WS if (!vs->websocket) {
if (!vs->websocket)
#endif
{
vnc_init_state(vs); vnc_init_state(vs);
} }
@ -3099,12 +3098,9 @@ static void vnc_listen_read(void *opaque, bool websocket)
/* Catch-up */ /* Catch-up */
graphic_hw_update(vs->dcl.con); graphic_hw_update(vs->dcl.con);
#ifdef CONFIG_VNC_WS
if (websocket) { if (websocket) {
csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
} else } else {
#endif /* CONFIG_VNC_WS */
{
csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); 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); vnc_listen_read(opaque, false);
} }
#ifdef CONFIG_VNC_WS
static void vnc_listen_websocket_read(void *opaque) static void vnc_listen_websocket_read(void *opaque)
{ {
vnc_listen_read(opaque, true); vnc_listen_read(opaque, true);
} }
#endif /* CONFIG_VNC_WS */
static const DisplayChangeListenerOps dcl_ops = { static const DisplayChangeListenerOps dcl_ops = {
.dpy_name = "vnc", .dpy_name = "vnc",
@ -3150,9 +3144,7 @@ void vnc_display_init(const char *id)
QTAILQ_INSERT_TAIL(&vnc_displays, vs, next); QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
vs->lsock = -1; vs->lsock = -1;
#ifdef CONFIG_VNC_WS
vs->lwebsock = -1; vs->lwebsock = -1;
#endif
QTAILQ_INIT(&vs->clients); QTAILQ_INIT(&vs->clients);
vs->expires = TIME_MAX; vs->expires = TIME_MAX;
@ -3186,14 +3178,12 @@ static void vnc_display_close(VncDisplay *vs)
close(vs->lsock); close(vs->lsock);
vs->lsock = -1; vs->lsock = -1;
} }
#ifdef CONFIG_VNC_WS
vs->ws_enabled = false; vs->ws_enabled = false;
if (vs->lwebsock != -1) { if (vs->lwebsock != -1) {
qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL); qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
close(vs->lwebsock); close(vs->lwebsock);
vs->lwebsock = -1; vs->lwebsock = -1;
} }
#endif /* CONFIG_VNC_WS */
vs->auth = VNC_AUTH_INVALID; vs->auth = VNC_AUTH_INVALID;
vs->subauth = VNC_AUTH_INVALID; vs->subauth = VNC_AUTH_INVALID;
#ifdef CONFIG_VNC_TLS #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); password = qemu_opt_get_bool(opts, "password", false);
if (password && fips_get_state()) { if (password) {
error_setg(errp, if (fips_get_state()) {
"VNC password auth disabled due to FIPS mode, " error_setg(errp,
"consider using the VeNCrypt or SASL authentication " "VNC password auth disabled due to FIPS mode, "
"methods as an alternative"); "consider using the VeNCrypt or SASL authentication "
goto fail; "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); 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"); websocket = qemu_opt_get(opts, "websocket");
if (websocket) { if (websocket) {
#ifdef CONFIG_VNC_WS
vs->ws_enabled = true; vs->ws_enabled = true;
qemu_opt_set(wsopts, "port", websocket, &error_abort); qemu_opt_set(wsopts, "port", websocket, &error_abort);
#else /* ! CONFIG_VNC_WS */ if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
error_setg(errp, "Websockets protocol requires gnutls support"); error_setg(errp, "SHA1 hash support is required for websockets");
goto fail; goto fail;
#endif /* ! CONFIG_VNC_WS */ }
} }
#ifdef CONFIG_VNC_JPEG #ifdef CONFIG_VNC_JPEG
@ -3668,9 +3665,7 @@ void vnc_display_open(const char *id, Error **errp)
/* connect to viewer */ /* connect to viewer */
int csock; int csock;
vs->lsock = -1; vs->lsock = -1;
#ifdef CONFIG_VNC_WS
vs->lwebsock = -1; vs->lwebsock = -1;
#endif
if (strncmp(vnc, "unix:", 5) == 0) { if (strncmp(vnc, "unix:", 5) == 0) {
csock = unix_connect(vnc+5, errp); csock = unix_connect(vnc+5, errp);
} else { } else {
@ -3693,7 +3688,6 @@ void vnc_display_open(const char *id, Error **errp)
if (vs->lsock < 0) { if (vs->lsock < 0) {
goto fail; goto fail;
} }
#ifdef CONFIG_VNC_WS
if (vs->ws_enabled) { if (vs->ws_enabled) {
vs->lwebsock = inet_listen_opts(wsopts, 0, errp); vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
if (vs->lwebsock < 0) { if (vs->lwebsock < 0) {
@ -3704,16 +3698,13 @@ void vnc_display_open(const char *id, Error **errp)
goto fail; goto fail;
} }
} }
#endif /* CONFIG_VNC_WS */
} }
vs->enabled = true; vs->enabled = true;
qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs); qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
#ifdef CONFIG_VNC_WS
if (vs->ws_enabled) { if (vs->ws_enabled) {
qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read, qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
NULL, vs); NULL, vs);
} }
#endif /* CONFIG_VNC_WS */
} }
qemu_opts_del(sopts); qemu_opts_del(sopts);
qemu_opts_del(wsopts); qemu_opts_del(wsopts);
@ -3723,9 +3714,7 @@ fail:
qemu_opts_del(sopts); qemu_opts_del(sopts);
qemu_opts_del(wsopts); qemu_opts_del(wsopts);
vs->enabled = false; vs->enabled = false;
#ifdef CONFIG_VNC_WS
vs->ws_enabled = false; vs->ws_enabled = false;
#endif /* CONFIG_VNC_WS */
} }
void vnc_display_add_client(const char *id, int csock, bool skipauth) void vnc_display_add_client(const char *id, int csock, bool skipauth)

View File

@ -108,9 +108,7 @@ typedef struct VncDisplay VncDisplay;
#ifdef CONFIG_VNC_SASL #ifdef CONFIG_VNC_SASL
#include "vnc-auth-sasl.h" #include "vnc-auth-sasl.h"
#endif #endif
#ifdef CONFIG_VNC_WS
#include "vnc-ws.h" #include "vnc-ws.h"
#endif
struct VncRectStat struct VncRectStat
{ {
@ -156,10 +154,8 @@ struct VncDisplay
int connections_limit; int connections_limit;
VncSharePolicy share_policy; VncSharePolicy share_policy;
int lsock; int lsock;
#ifdef CONFIG_VNC_WS
int lwebsock; int lwebsock;
bool ws_enabled; bool ws_enabled;
#endif
DisplaySurface *ds; DisplaySurface *ds;
DisplayChangeListener dcl; DisplayChangeListener dcl;
kbd_layout_t *kbd_layout; kbd_layout_t *kbd_layout;
@ -294,21 +290,17 @@ struct VncState
#ifdef CONFIG_VNC_SASL #ifdef CONFIG_VNC_SASL
VncStateSASL sasl; VncStateSASL sasl;
#endif #endif
#ifdef CONFIG_VNC_WS
bool encode_ws; bool encode_ws;
bool websocket; bool websocket;
#endif /* CONFIG_VNC_WS */
VncClientInfo *info; VncClientInfo *info;
Buffer output; Buffer output;
Buffer input; Buffer input;
#ifdef CONFIG_VNC_WS
Buffer ws_input; Buffer ws_input;
Buffer ws_output; Buffer ws_output;
size_t ws_payload_remain; size_t ws_payload_remain;
WsMask ws_payload_mask; WsMask ws_payload_mask;
#endif
/* current output mode information */ /* current output mode information */
VncWritePixels *write_pixels; VncWritePixels *write_pixels;
PixelFormat client_pf; PixelFormat client_pf;

View File

@ -9,7 +9,7 @@ util-obj-y += acl.o
util-obj-y += error.o qemu-error.o util-obj-y += error.o qemu-error.o
util-obj-$(CONFIG_POSIX) += compatfd.o util-obj-$(CONFIG_POSIX) += compatfd.o
util-obj-y += id.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 += qemu-option.o qemu-progress.o
util-obj-y += hexdump.o util-obj-y += hexdump.o
util-obj-y += crc32c.o util-obj-y += crc32c.o

18
vl.c
View File

@ -121,6 +121,7 @@ int main(int argc, char **argv)
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "qapi-event.h" #include "qapi-event.h"
#include "exec/semihost.h" #include "exec/semihost.h"
#include "crypto/init.h"
#define MAX_VIRTIO_CONSOLES 1 #define MAX_VIRTIO_CONSOLES 1
#define MAX_SCLP_CONSOLES 1 #define MAX_SCLP_CONSOLES 1
@ -2976,6 +2977,7 @@ int main(int argc, char **argv, char **envp)
uint64_t ram_slots = 0; uint64_t ram_slots = 0;
FILE *vmstate_dump_file = NULL; FILE *vmstate_dump_file = NULL;
Error *main_loop_err = NULL; Error *main_loop_err = NULL;
Error *err = NULL;
qemu_init_cpu_loop(); qemu_init_cpu_loop();
qemu_mutex_lock_iothread(); qemu_mutex_lock_iothread();
@ -3019,6 +3021,11 @@ int main(int argc, char **argv, char **envp)
runstate_init(); 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; rtc_clock = QEMU_CLOCK_HOST;
QLIST_INIT (&vm_change_state_head); QLIST_INIT (&vm_change_state_head);
@ -4597,18 +4604,15 @@ int main(int argc, char **argv, char **envp)
qdev_machine_creation_done(); 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 /* TODO: once all bus devices are qdevified, this should be done
* when bus is created by qdev.c */ * when bus is created by qdev.c */
qemu_register_reset(qbus_reset_all_fn, sysbus_get_default()); qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());
qemu_run_machine_init_done_notifiers(); qemu_run_machine_init_done_notifiers();
/* Done notifiers can load ROMs */ if (rom_check_and_register_reset() != 0) {
rom_load_done(); fprintf(stderr, "rom check and register reset failed\n");
exit(1);
}
qemu_system_reset(VMRESET_SILENT); qemu_system_reset(VMRESET_SILENT);
if (loadvm) { if (loadvm) {