mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 19:49:43 +00:00
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:
commit
acf7b7fdf3
@ -1052,6 +1052,13 @@ S: Supported
|
||||
F: qemu-seccomp.c
|
||||
F: include/sysemu/seccomp.h
|
||||
|
||||
Cryptography
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
S: Maintained
|
||||
F: crypto/
|
||||
F: include/crypto/
|
||||
F: tests/test-crypto-*
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
|
@ -2,6 +2,7 @@
|
||||
# Common libraries for tools and emulators
|
||||
stub-obj-y = stubs/
|
||||
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
|
||||
util-obj-y += crypto/
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
@ -853,6 +853,7 @@ static void *oss_audio_init (void)
|
||||
|
||||
if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
|
||||
access(conf->devpath_out, R_OK | W_OK) < 0) {
|
||||
g_free(conf);
|
||||
return NULL;
|
||||
}
|
||||
return conf;
|
||||
|
@ -3,7 +3,7 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c
|
||||
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-y += qed-check.o
|
||||
block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
|
||||
block-obj-$(CONFIG_QUORUM) += quorum.o
|
||||
block-obj-y += quorum.o
|
||||
block-obj-y += parallels.o blkdebug.o blkverify.o
|
||||
block-obj-y += block-backend.o snapshot.o qapi.o
|
||||
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||
|
102
block/qcow.c
102
block/qcow.c
@ -26,7 +26,7 @@
|
||||
#include "qemu/module.h"
|
||||
#include <zlib.h>
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/aes.h"
|
||||
#include "crypto/cipher.h"
|
||||
#include "migration/migration.h"
|
||||
|
||||
/**************************************************************/
|
||||
@ -72,10 +72,8 @@ typedef struct BDRVQcowState {
|
||||
uint8_t *cluster_cache;
|
||||
uint8_t *cluster_data;
|
||||
uint64_t cluster_cache_offset;
|
||||
uint32_t crypt_method; /* current crypt method, 0 if no key yet */
|
||||
QCryptoCipher *cipher; /* NULL if no key yet */
|
||||
uint32_t crypt_method_header;
|
||||
AES_KEY aes_encrypt_key;
|
||||
AES_KEY aes_decrypt_key;
|
||||
CoMutex lock;
|
||||
Error *migration_blocker;
|
||||
} BDRVQcowState;
|
||||
@ -154,6 +152,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
|
||||
error_setg(errp, "AES cipher not available");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
s->crypt_method_header = header.crypt_method;
|
||||
if (s->crypt_method_header) {
|
||||
bs->encrypted = 1;
|
||||
@ -260,6 +263,7 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint8_t keybuf[16];
|
||||
int len, i;
|
||||
Error *err;
|
||||
|
||||
memset(keybuf, 0, 16);
|
||||
len = strlen(key);
|
||||
@ -271,38 +275,67 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
|
||||
keybuf[i] = key[i];
|
||||
}
|
||||
assert(bs->encrypted);
|
||||
s->crypt_method = s->crypt_method_header;
|
||||
|
||||
if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
|
||||
return -1;
|
||||
if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
|
||||
qcrypto_cipher_free(s->cipher);
|
||||
s->cipher = qcrypto_cipher_new(
|
||||
QCRYPTO_CIPHER_ALG_AES_128,
|
||||
QCRYPTO_CIPHER_MODE_CBC,
|
||||
keybuf, G_N_ELEMENTS(keybuf),
|
||||
&err);
|
||||
|
||||
if (!s->cipher) {
|
||||
/* XXX would be nice if errors in this method could
|
||||
* be properly propagate to the caller. Would need
|
||||
* the bdrv_set_key() API signature to be fixed. */
|
||||
error_free(err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The crypt function is compatible with the linux cryptoloop
|
||||
algorithm for < 4 GB images. NOTE: out_buf == in_buf is
|
||||
supported */
|
||||
static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, int enc,
|
||||
const AES_KEY *key)
|
||||
static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, bool enc, Error **errp)
|
||||
{
|
||||
union {
|
||||
uint64_t ll[2];
|
||||
uint8_t b[16];
|
||||
} ivec;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for(i = 0; i < nb_sectors; i++) {
|
||||
ivec.ll[0] = cpu_to_le64(sector_num);
|
||||
ivec.ll[1] = 0;
|
||||
AES_cbc_encrypt(in_buf, out_buf, 512, key,
|
||||
ivec.b, enc);
|
||||
if (qcrypto_cipher_setiv(s->cipher,
|
||||
ivec.b, G_N_ELEMENTS(ivec.b),
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (enc) {
|
||||
ret = qcrypto_cipher_encrypt(s->cipher,
|
||||
in_buf,
|
||||
out_buf,
|
||||
512,
|
||||
errp);
|
||||
} else {
|
||||
ret = qcrypto_cipher_decrypt(s->cipher,
|
||||
in_buf,
|
||||
out_buf,
|
||||
512,
|
||||
errp);
|
||||
}
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
sector_num++;
|
||||
in_buf += 512;
|
||||
out_buf += 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 'allocate' is:
|
||||
@ -416,15 +449,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
if (bs->encrypted &&
|
||||
(n_end - n_start) < s->cluster_sectors) {
|
||||
uint64_t start_sect;
|
||||
assert(s->crypt_method);
|
||||
assert(s->cipher);
|
||||
start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
|
||||
memset(s->cluster_data + 512, 0x00, 512);
|
||||
for(i = 0; i < s->cluster_sectors; i++) {
|
||||
if (i < n_start || i >= n_end) {
|
||||
encrypt_sectors(s, start_sect + i,
|
||||
s->cluster_data,
|
||||
s->cluster_data + 512, 1, 1,
|
||||
&s->aes_encrypt_key);
|
||||
Error *err = NULL;
|
||||
if (encrypt_sectors(s, start_sect + i,
|
||||
s->cluster_data,
|
||||
s->cluster_data + 512, 1,
|
||||
true, &err) < 0) {
|
||||
error_free(err);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
|
||||
s->cluster_data, 512) != 512)
|
||||
return -1;
|
||||
@ -464,7 +502,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
|
||||
if (!cluster_offset) {
|
||||
return 0;
|
||||
}
|
||||
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) {
|
||||
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) {
|
||||
return BDRV_BLOCK_DATA;
|
||||
}
|
||||
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
|
||||
@ -531,6 +569,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector hd_qiov;
|
||||
uint8_t *buf;
|
||||
void *orig_buf;
|
||||
Error *err = NULL;
|
||||
|
||||
if (qiov->niov > 1) {
|
||||
buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
|
||||
@ -594,10 +633,11 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
break;
|
||||
}
|
||||
if (bs->encrypted) {
|
||||
assert(s->crypt_method);
|
||||
encrypt_sectors(s, sector_num, buf, buf,
|
||||
n, 0,
|
||||
&s->aes_decrypt_key);
|
||||
assert(s->cipher);
|
||||
if (encrypt_sectors(s, sector_num, buf, buf,
|
||||
n, false, &err) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
@ -618,6 +658,7 @@ done:
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
error_free(err);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
@ -666,12 +707,17 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
break;
|
||||
}
|
||||
if (bs->encrypted) {
|
||||
assert(s->crypt_method);
|
||||
Error *err = NULL;
|
||||
assert(s->cipher);
|
||||
if (!cluster_data) {
|
||||
cluster_data = g_malloc0(s->cluster_size);
|
||||
}
|
||||
encrypt_sectors(s, sector_num, cluster_data, buf,
|
||||
n, 1, &s->aes_encrypt_key);
|
||||
if (encrypt_sectors(s, sector_num, cluster_data, buf,
|
||||
n, true, &err) < 0) {
|
||||
error_free(err);
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
src_buf = cluster_data;
|
||||
} else {
|
||||
src_buf = buf;
|
||||
@ -708,6 +754,8 @@ static void qcow_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
qcrypto_cipher_free(s->cipher);
|
||||
s->cipher = NULL;
|
||||
g_free(s->l1_table);
|
||||
qemu_vfree(s->l2_cache);
|
||||
g_free(s->cluster_cache);
|
||||
|
@ -339,26 +339,47 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab
|
||||
/* The crypt function is compatible with the linux cryptoloop
|
||||
algorithm for < 4 GB images. NOTE: out_buf == in_buf is
|
||||
supported */
|
||||
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, int enc,
|
||||
const AES_KEY *key)
|
||||
int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, bool enc,
|
||||
Error **errp)
|
||||
{
|
||||
union {
|
||||
uint64_t ll[2];
|
||||
uint8_t b[16];
|
||||
} ivec;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for(i = 0; i < nb_sectors; i++) {
|
||||
ivec.ll[0] = cpu_to_le64(sector_num);
|
||||
ivec.ll[1] = 0;
|
||||
AES_cbc_encrypt(in_buf, out_buf, 512, key,
|
||||
ivec.b, enc);
|
||||
if (qcrypto_cipher_setiv(s->cipher,
|
||||
ivec.b, G_N_ELEMENTS(ivec.b),
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (enc) {
|
||||
ret = qcrypto_cipher_encrypt(s->cipher,
|
||||
in_buf,
|
||||
out_buf,
|
||||
512,
|
||||
errp);
|
||||
} else {
|
||||
ret = qcrypto_cipher_decrypt(s->cipher,
|
||||
in_buf,
|
||||
out_buf,
|
||||
512,
|
||||
errp);
|
||||
}
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
sector_num++;
|
||||
in_buf += 512;
|
||||
out_buf += 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||
@ -401,10 +422,15 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
if (bs->encrypted) {
|
||||
assert(s->crypt_method);
|
||||
qcow2_encrypt_sectors(s, start_sect + n_start,
|
||||
iov.iov_base, iov.iov_base, n, 1,
|
||||
&s->aes_encrypt_key);
|
||||
Error *err = NULL;
|
||||
assert(s->cipher);
|
||||
if (qcow2_encrypt_sectors(s, start_sect + n_start,
|
||||
iov.iov_base, iov.iov_base, n,
|
||||
true, &err) < 0) {
|
||||
ret = -EIO;
|
||||
error_free(err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0,
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include <zlib.h>
|
||||
#include "qemu/aes.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
@ -699,6 +698,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
|
||||
error_setg(errp, "AES cipher not available");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
s->crypt_method_header = header.crypt_method;
|
||||
if (s->crypt_method_header) {
|
||||
bs->encrypted = 1;
|
||||
@ -1032,6 +1036,7 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint8_t keybuf[16];
|
||||
int len, i;
|
||||
Error *err = NULL;
|
||||
|
||||
memset(keybuf, 0, 16);
|
||||
len = strlen(key);
|
||||
@ -1043,30 +1048,21 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
||||
keybuf[i] = key[i];
|
||||
}
|
||||
assert(bs->encrypted);
|
||||
s->crypt_method = s->crypt_method_header;
|
||||
|
||||
if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
|
||||
qcrypto_cipher_free(s->cipher);
|
||||
s->cipher = qcrypto_cipher_new(
|
||||
QCRYPTO_CIPHER_ALG_AES_128,
|
||||
QCRYPTO_CIPHER_MODE_CBC,
|
||||
keybuf, G_N_ELEMENTS(keybuf),
|
||||
&err);
|
||||
|
||||
if (!s->cipher) {
|
||||
/* XXX would be nice if errors in this method could
|
||||
* be properly propagate to the caller. Would need
|
||||
* the bdrv_set_key() API signature to be fixed. */
|
||||
error_free(err);
|
||||
return -1;
|
||||
if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
|
||||
return -1;
|
||||
#if 0
|
||||
/* test */
|
||||
{
|
||||
uint8_t in[16];
|
||||
uint8_t out[16];
|
||||
uint8_t tmp[16];
|
||||
for(i=0;i<16;i++)
|
||||
in[i] = i;
|
||||
AES_encrypt(in, tmp, &s->aes_encrypt_key);
|
||||
AES_decrypt(tmp, out, &s->aes_decrypt_key);
|
||||
for(i = 0; i < 16; i++)
|
||||
printf(" %02x", tmp[i]);
|
||||
printf("\n");
|
||||
for(i = 0; i < 16; i++)
|
||||
printf(" %02x", out[i]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1109,7 +1105,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
|
||||
!s->crypt_method) {
|
||||
!s->cipher) {
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
|
||||
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
||||
@ -1159,7 +1155,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
/* prepare next request */
|
||||
cur_nr_sectors = remaining_sectors;
|
||||
if (s->crypt_method) {
|
||||
if (s->cipher) {
|
||||
cur_nr_sectors = MIN(cur_nr_sectors,
|
||||
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
|
||||
}
|
||||
@ -1231,7 +1227,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
|
||||
if (bs->encrypted) {
|
||||
assert(s->crypt_method);
|
||||
assert(s->cipher);
|
||||
|
||||
/*
|
||||
* For encrypted images, read everything into a temporary
|
||||
@ -1264,9 +1260,15 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
goto fail;
|
||||
}
|
||||
if (bs->encrypted) {
|
||||
assert(s->crypt_method);
|
||||
qcow2_encrypt_sectors(s, sector_num, cluster_data,
|
||||
cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key);
|
||||
assert(s->cipher);
|
||||
Error *err = NULL;
|
||||
if (qcow2_encrypt_sectors(s, sector_num, cluster_data,
|
||||
cluster_data, cur_nr_sectors, false,
|
||||
&err) < 0) {
|
||||
error_free(err);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
qemu_iovec_from_buf(qiov, bytes_done,
|
||||
cluster_data, 512 * cur_nr_sectors);
|
||||
}
|
||||
@ -1344,7 +1346,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
cur_nr_sectors * 512);
|
||||
|
||||
if (bs->encrypted) {
|
||||
assert(s->crypt_method);
|
||||
Error *err = NULL;
|
||||
assert(s->cipher);
|
||||
if (!cluster_data) {
|
||||
cluster_data = qemu_try_blockalign(bs->file,
|
||||
QCOW_MAX_CRYPT_CLUSTERS
|
||||
@ -1359,8 +1362,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
|
||||
qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
|
||||
|
||||
qcow2_encrypt_sectors(s, sector_num, cluster_data,
|
||||
cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key);
|
||||
if (qcow2_encrypt_sectors(s, sector_num, cluster_data,
|
||||
cluster_data, cur_nr_sectors,
|
||||
true, &err) < 0) {
|
||||
error_free(err);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
qemu_iovec_add(&hd_qiov, cluster_data,
|
||||
@ -1466,6 +1474,9 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
qcow2_cache_destroy(bs, s->l2_table_cache);
|
||||
qcow2_cache_destroy(bs, s->refcount_block_cache);
|
||||
|
||||
qcrypto_cipher_free(s->cipher);
|
||||
s->cipher = NULL;
|
||||
|
||||
g_free(s->unknown_header_fields);
|
||||
cleanup_unknown_header_ext(bs);
|
||||
|
||||
@ -1482,9 +1493,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int flags = s->flags;
|
||||
AES_KEY aes_encrypt_key;
|
||||
AES_KEY aes_decrypt_key;
|
||||
uint32_t crypt_method = 0;
|
||||
QCryptoCipher *cipher = NULL;
|
||||
QDict *options;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
@ -1494,12 +1503,8 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
* that means we don't have to worry about reopening them here.
|
||||
*/
|
||||
|
||||
if (bs->encrypted) {
|
||||
assert(s->crypt_method);
|
||||
crypt_method = s->crypt_method;
|
||||
memcpy(&aes_encrypt_key, &s->aes_encrypt_key, sizeof(aes_encrypt_key));
|
||||
memcpy(&aes_decrypt_key, &s->aes_decrypt_key, sizeof(aes_decrypt_key));
|
||||
}
|
||||
cipher = s->cipher;
|
||||
s->cipher = NULL;
|
||||
|
||||
qcow2_close(bs);
|
||||
|
||||
@ -1524,11 +1529,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (bs->encrypted) {
|
||||
s->crypt_method = crypt_method;
|
||||
memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
|
||||
memcpy(&s->aes_decrypt_key, &aes_decrypt_key, sizeof(aes_decrypt_key));
|
||||
}
|
||||
s->cipher = cipher;
|
||||
}
|
||||
|
||||
static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
|
||||
@ -2729,8 +2730,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
|
||||
encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
|
||||
s->crypt_method);
|
||||
if (encrypt != !!s->crypt_method) {
|
||||
!!s->cipher);
|
||||
|
||||
if (encrypt != !!s->cipher) {
|
||||
fprintf(stderr, "Changing the encryption flag is not "
|
||||
"supported.\n");
|
||||
return -ENOTSUP;
|
||||
|
@ -25,7 +25,7 @@
|
||||
#ifndef BLOCK_QCOW2_H
|
||||
#define BLOCK_QCOW2_H
|
||||
|
||||
#include "qemu/aes.h"
|
||||
#include "crypto/cipher.h"
|
||||
#include "block/coroutine.h"
|
||||
|
||||
//#define DEBUG_ALLOC
|
||||
@ -253,10 +253,8 @@ typedef struct BDRVQcowState {
|
||||
|
||||
CoMutex lock;
|
||||
|
||||
uint32_t crypt_method; /* current crypt method, 0 if no key yet */
|
||||
QCryptoCipher *cipher; /* current cipher, NULL if no key yet */
|
||||
uint32_t crypt_method_header;
|
||||
AES_KEY aes_encrypt_key;
|
||||
AES_KEY aes_decrypt_key;
|
||||
uint64_t snapshots_offset;
|
||||
int snapshots_size;
|
||||
unsigned int nb_snapshots;
|
||||
@ -536,10 +534,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
|
||||
void qcow2_l2_cache_reset(BlockDriverState *bs);
|
||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
||||
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, int enc,
|
||||
const AES_KEY *key);
|
||||
int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, bool enc, Error **errp);
|
||||
|
||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *cluster_offset);
|
||||
|
@ -13,8 +13,6 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
@ -24,6 +22,7 @@
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi-event.h"
|
||||
#include "crypto/hash.h"
|
||||
|
||||
#define HASH_LENGTH 32
|
||||
|
||||
@ -34,7 +33,7 @@
|
||||
|
||||
/* This union holds a vote hash value */
|
||||
typedef union QuorumVoteValue {
|
||||
char h[HASH_LENGTH]; /* SHA-256 hash */
|
||||
uint8_t h[HASH_LENGTH]; /* SHA-256 hash */
|
||||
int64_t l; /* simpler 64 bits hash */
|
||||
} QuorumVoteValue;
|
||||
|
||||
@ -428,25 +427,21 @@ static void quorum_free_vote_list(QuorumVotes *votes)
|
||||
|
||||
static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash)
|
||||
{
|
||||
int j, ret;
|
||||
gnutls_hash_hd_t dig;
|
||||
QEMUIOVector *qiov = &acb->qcrs[i].qiov;
|
||||
size_t len = sizeof(hash->h);
|
||||
uint8_t *data = hash->h;
|
||||
|
||||
ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
/* XXX - would be nice if we could pass in the Error **
|
||||
* and propagate that back, but this quorum code is
|
||||
* restricted to just errno values currently */
|
||||
if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256,
|
||||
qiov->iov, qiov->niov,
|
||||
&data, &len,
|
||||
NULL) < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (j = 0; j < qiov->niov; j++) {
|
||||
ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_hash_deinit(dig, (void *) hash);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes)
|
||||
@ -870,6 +865,12 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA256)) {
|
||||
error_setg(errp,
|
||||
"SHA256 hash support is required for quorum device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qdict_flatten(options);
|
||||
|
||||
/* count how many different children are present */
|
||||
|
160
configure
vendored
160
configure
vendored
@ -246,7 +246,6 @@ vnc_tls=""
|
||||
vnc_sasl=""
|
||||
vnc_jpeg=""
|
||||
vnc_png=""
|
||||
vnc_ws=""
|
||||
xen=""
|
||||
xen_ctrl_version=""
|
||||
xen_pci_passthrough=""
|
||||
@ -331,11 +330,12 @@ glusterfs_zerofill="no"
|
||||
archipelago="no"
|
||||
gtk=""
|
||||
gtkabi=""
|
||||
gnutls=""
|
||||
gnutls_hash=""
|
||||
vte=""
|
||||
tpm="yes"
|
||||
libssh2=""
|
||||
vhdx=""
|
||||
quorum=""
|
||||
numa=""
|
||||
tcmalloc="no"
|
||||
|
||||
@ -896,10 +896,6 @@ for opt do
|
||||
;;
|
||||
--enable-vnc-png) vnc_png="yes"
|
||||
;;
|
||||
--disable-vnc-ws) vnc_ws="no"
|
||||
;;
|
||||
--enable-vnc-ws) vnc_ws="yes"
|
||||
;;
|
||||
--disable-slirp) slirp="no"
|
||||
;;
|
||||
--disable-uuid) uuid="no"
|
||||
@ -1119,6 +1115,10 @@ for opt do
|
||||
;;
|
||||
--enable-gtk) gtk="yes"
|
||||
;;
|
||||
--disable-gnutls) gnutls="no"
|
||||
;;
|
||||
--enable-gnutls) gnutls="yes"
|
||||
;;
|
||||
--enable-rdma) rdma="yes"
|
||||
;;
|
||||
--disable-rdma) rdma="no"
|
||||
@ -1141,10 +1141,6 @@ for opt do
|
||||
;;
|
||||
--disable-vhdx) vhdx="no"
|
||||
;;
|
||||
--disable-quorum) quorum="no"
|
||||
;;
|
||||
--enable-quorum) quorum="yes"
|
||||
;;
|
||||
--disable-numa) numa="no"
|
||||
;;
|
||||
--enable-numa) numa="yes"
|
||||
@ -1329,6 +1325,7 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
debug-info debugging information
|
||||
sparse sparse checker
|
||||
|
||||
gnutls GNUTLS cryptography support
|
||||
sdl SDL UI
|
||||
--with-sdlabi select preferred SDL ABI 1.2 or 2.0
|
||||
gtk gtk UI
|
||||
@ -1376,7 +1373,6 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
tpm TPM support
|
||||
libssh2 ssh block device support
|
||||
vhdx support for the Microsoft VHDX image format
|
||||
quorum quorum block filter support
|
||||
numa libnuma support
|
||||
tcmalloc tcmalloc support
|
||||
|
||||
@ -2116,6 +2112,86 @@ if test "$gtk" != "no"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
##########################################
|
||||
# GNUTLS probe
|
||||
|
||||
gnutls_gcrypt=no
|
||||
gnutls_nettle=no
|
||||
if test "$gnutls" != "no"; then
|
||||
if $pkg_config --exists "gnutls"; then
|
||||
gnutls_cflags=`$pkg_config --cflags gnutls`
|
||||
gnutls_libs=`$pkg_config --libs gnutls`
|
||||
libs_softmmu="$gnutls_libs $libs_softmmu"
|
||||
libs_tools="$gnutls_libs $libs_tools"
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags"
|
||||
gnutls="yes"
|
||||
|
||||
# gnutls_hash_init requires >= 2.9.10
|
||||
if $pkg_config --exists "gnutls >= 2.9.10"; then
|
||||
gnutls_hash="yes"
|
||||
else
|
||||
gnutls_hash="no"
|
||||
fi
|
||||
|
||||
if $pkg_config --exists 'gnutls >= 3.0'; then
|
||||
gnutls_gcrypt=no
|
||||
gnutls_nettle=yes
|
||||
elif $pkg_config --exists 'gnutls >= 2.12'; then
|
||||
case `$pkg_config --libs --static gnutls` in
|
||||
*gcrypt*)
|
||||
gnutls_gcrypt=yes
|
||||
gnutls_nettle=no
|
||||
;;
|
||||
*nettle*)
|
||||
gnutls_gcrypt=no
|
||||
gnutls_nettle=yes
|
||||
;;
|
||||
*)
|
||||
gnutls_gcrypt=yes
|
||||
gnutls_nettle=no
|
||||
;;
|
||||
esac
|
||||
else
|
||||
gnutls_gcrypt=yes
|
||||
gnutls_nettle=no
|
||||
fi
|
||||
elif test "$gnutls" = "yes"; then
|
||||
feature_not_found "gnutls" "Install gnutls devel"
|
||||
else
|
||||
gnutls="no"
|
||||
gnutls_hash="no"
|
||||
fi
|
||||
else
|
||||
gnutls_hash="no"
|
||||
fi
|
||||
|
||||
if test "$gnutls_gcrypt" != "no"; then
|
||||
if has "libgcrypt-config"; then
|
||||
gcrypt_cflags=`libgcrypt-config --cflags`
|
||||
gcrypt_libs=`libgcrypt-config --libs`
|
||||
libs_softmmu="$gcrypt_libs $libs_softmmu"
|
||||
libs_tools="$gcrypt_libs $libs_tools"
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $gcrypt_cflags"
|
||||
else
|
||||
feature_not_found "gcrypt" "Install gcrypt devel"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if test "$gnutls_nettle" != "no"; then
|
||||
if $pkg_config --exists "nettle"; then
|
||||
nettle_cflags=`$pkg_config --cflags nettle`
|
||||
nettle_libs=`$pkg_config --libs nettle`
|
||||
libs_softmmu="$nettle_libs $libs_softmmu"
|
||||
libs_tools="$nettle_libs $libs_tools"
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags"
|
||||
else
|
||||
feature_not_found "nettle" "Install nettle devel"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
##########################################
|
||||
# VTE probe
|
||||
|
||||
@ -2263,7 +2339,7 @@ fi
|
||||
|
||||
##########################################
|
||||
# VNC TLS/WS detection
|
||||
if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then
|
||||
if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then
|
||||
cat > $TMPC <<EOF
|
||||
#include <gnutls/gnutls.h>
|
||||
int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
|
||||
@ -2274,50 +2350,16 @@ EOF
|
||||
if test "$vnc_tls" != "no" ; then
|
||||
vnc_tls=yes
|
||||
fi
|
||||
if test "$vnc_ws" != "no" ; then
|
||||
vnc_ws=yes
|
||||
fi
|
||||
libs_softmmu="$vnc_tls_libs $libs_softmmu"
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
|
||||
else
|
||||
if test "$vnc_tls" = "yes" ; then
|
||||
feature_not_found "vnc-tls" "Install gnutls devel"
|
||||
fi
|
||||
if test "$vnc_ws" = "yes" ; then
|
||||
feature_not_found "vnc-ws" "Install gnutls devel"
|
||||
fi
|
||||
vnc_tls=no
|
||||
vnc_ws=no
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# Quorum probe (check for gnutls)
|
||||
if test "$quorum" != "no" ; then
|
||||
cat > $TMPC <<EOF
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
int main(void) {char data[4096], digest[32];
|
||||
gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
|
||||
quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
|
||||
if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then
|
||||
qcow_tls=yes
|
||||
libs_softmmu="$quorum_tls_libs $libs_softmmu"
|
||||
libs_tools="$quorum_tls_libs $libs_softmmu"
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags"
|
||||
quorum="yes"
|
||||
else
|
||||
if test "$quorum" = "yes"; then
|
||||
feature_not_found "gnutls" "gnutls > 2.10.0 required to compile Quorum"
|
||||
fi
|
||||
quorum="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# VNC SASL detection
|
||||
if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then
|
||||
@ -4445,6 +4487,10 @@ fi
|
||||
echo "pixman $pixman"
|
||||
echo "SDL support $sdl"
|
||||
echo "GTK support $gtk"
|
||||
echo "GNUTLS support $gnutls"
|
||||
echo "GNUTLS hash $gnutls_hash"
|
||||
echo "GNUTLS gcrypt $gnutls_gcrypt"
|
||||
echo "GNUTLS nettle $gnutls_nettle"
|
||||
echo "VTE support $vte"
|
||||
echo "curses support $curses"
|
||||
echo "curl support $curl"
|
||||
@ -4459,7 +4505,6 @@ if test "$vnc" = "yes" ; then
|
||||
echo "VNC SASL support $vnc_sasl"
|
||||
echo "VNC JPEG support $vnc_jpeg"
|
||||
echo "VNC PNG support $vnc_png"
|
||||
echo "VNC WS support $vnc_ws"
|
||||
fi
|
||||
if test -n "$sparc_cpu"; then
|
||||
echo "Target Sparc Arch $sparc_cpu"
|
||||
@ -4523,7 +4568,6 @@ echo "libssh2 support $libssh2"
|
||||
echo "TPM passthrough $tpm_passthrough"
|
||||
echo "QOM debugging $qom_cast_debug"
|
||||
echo "vhdx $vhdx"
|
||||
echo "Quorum $quorum"
|
||||
echo "lzo support $lzo"
|
||||
echo "snappy support $snappy"
|
||||
echo "bzip2 support $bzip2"
|
||||
@ -4676,10 +4720,6 @@ fi
|
||||
if test "$vnc_png" = "yes" ; then
|
||||
echo "CONFIG_VNC_PNG=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vnc_ws" = "yes" ; then
|
||||
echo "CONFIG_VNC_WS=y" >> $config_host_mak
|
||||
echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak
|
||||
fi
|
||||
if test "$fnmatch" = "yes" ; then
|
||||
echo "CONFIG_FNMATCH=y" >> $config_host_mak
|
||||
fi
|
||||
@ -4807,6 +4847,18 @@ if test "$gtk" = "yes" ; then
|
||||
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
|
||||
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
|
||||
fi
|
||||
if test "$gnutls" = "yes" ; then
|
||||
echo "CONFIG_GNUTLS=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$gnutls_hash" = "yes" ; then
|
||||
echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$gnutls_gcrypt" = "yes" ; then
|
||||
echo "CONFIG_GNUTLS_GCRYPT=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$gnutls_nettle" = "yes" ; then
|
||||
echo "CONFIG_GNUTLS_NETTLE=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vte" = "yes" ; then
|
||||
echo "CONFIG_VTE=y" >> $config_host_mak
|
||||
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
|
||||
@ -4996,10 +5048,6 @@ if test "$libssh2" = "yes" ; then
|
||||
echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$quorum" = "yes" ; then
|
||||
echo "CONFIG_QUORUM=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$vhdx" = "yes" ; then
|
||||
echo "CONFIG_VHDX=y" >> $config_host_mak
|
||||
fi
|
||||
|
5
crypto/Makefile.objs
Normal file
5
crypto/Makefile.objs
Normal 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
|
@ -28,7 +28,7 @@
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/aes.h"
|
||||
#include "crypto/aes.h"
|
||||
|
||||
typedef uint32_t u32;
|
||||
typedef uint8_t u8;
|
398
crypto/cipher-builtin.c
Normal file
398
crypto/cipher-builtin.c
Normal 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
195
crypto/cipher-gcrypt.c
Normal 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
206
crypto/cipher-nettle.c
Normal 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
74
crypto/cipher.c
Normal 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
|
@ -26,7 +26,7 @@
|
||||
* (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
|
||||
*/
|
||||
|
||||
#include "d3des.h"
|
||||
#include "crypto/desrfb.h"
|
||||
|
||||
static void scrunch(unsigned char *, unsigned long *);
|
||||
static void unscrun(unsigned long *, unsigned char *);
|
200
crypto/hash.c
Normal file
200
crypto/hash.c
Normal 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
150
crypto/init.c
Normal 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 */
|
@ -933,7 +933,7 @@ static void rom_reset(void *unused)
|
||||
}
|
||||
}
|
||||
|
||||
int rom_load_all(void)
|
||||
int rom_check_and_register_reset(void)
|
||||
{
|
||||
hwaddr addr = 0;
|
||||
MemoryRegionSection section;
|
||||
@ -957,12 +957,8 @@ int rom_load_all(void)
|
||||
memory_region_unref(section.mr);
|
||||
}
|
||||
qemu_register_reset(rom_reset, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rom_load_done(void)
|
||||
{
|
||||
roms_loaded = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rom_set_fw(FWCfgState *f)
|
||||
|
210
include/crypto/cipher.h
Normal file
210
include/crypto/cipher.h
Normal 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
189
include/crypto/hash.h
Normal 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
29
include/crypto/init.h
Normal 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__ */
|
@ -75,8 +75,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
|
||||
void *callback_opaque);
|
||||
int rom_add_elf_program(const char *name, void *data, size_t datasize,
|
||||
size_t romsize, hwaddr addr);
|
||||
int rom_load_all(void);
|
||||
void rom_load_done(void);
|
||||
int rom_check_and_register_reset(void);
|
||||
void rom_set_fw(FWCfgState *f);
|
||||
int rom_copy(uint8_t *dest, hwaddr addr, size_t size);
|
||||
void *rom_ptr(hwaddr addr);
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/aes.h"
|
||||
#include "crypto/aes.h"
|
||||
|
||||
union CRYPTO_STATE {
|
||||
uint8_t bytes[16];
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <math.h>
|
||||
#include "cpu.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/aes.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/aes.h"
|
||||
#include "crypto/aes.h"
|
||||
|
||||
#if SHIFT == 0
|
||||
#define Reg MMXReg
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "cpu.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/aes.h"
|
||||
#include "crypto/aes.h"
|
||||
|
||||
#include "helper_regs.h"
|
||||
/*****************************************************************************/
|
||||
|
2
tests/.gitignore
vendored
2
tests/.gitignore
vendored
@ -10,6 +10,8 @@ rcutorture
|
||||
test-aio
|
||||
test-bitops
|
||||
test-coroutine
|
||||
test-crypto-cipher
|
||||
test-crypto-hash
|
||||
test-cutils
|
||||
test-hbitmap
|
||||
test-int128
|
||||
|
@ -74,6 +74,8 @@ check-unit-y += tests/test-qemu-opts$(EXESUF)
|
||||
gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c
|
||||
check-unit-y += tests/test-write-threshold$(EXESUF)
|
||||
gcov-files-test-write-threshold-y = block/write-threshold.c
|
||||
check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF)
|
||||
check-unit-y += tests/test-crypto-cipher$(EXESUF)
|
||||
|
||||
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
|
||||
|
||||
@ -343,6 +345,8 @@ tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) l
|
||||
|
||||
tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
|
||||
tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a
|
||||
tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a
|
||||
tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a
|
||||
|
||||
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
|
||||
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
|
||||
|
290
tests/test-crypto-cipher.c
Normal file
290
tests/test-crypto-cipher.c
Normal 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
209
tests/test-crypto-hash.c
Normal 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();
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
vnc-obj-y += vnc.o d3des.o
|
||||
vnc-obj-y += vnc.o
|
||||
vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
|
||||
vnc-obj-y += vnc-enc-tight.o vnc-palette.o
|
||||
vnc-obj-y += vnc-enc-zrle.o
|
||||
vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
|
||||
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
|
||||
vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o
|
||||
vnc-obj-y += vnc-ws.o
|
||||
vnc-obj-y += vnc-jobs.o
|
||||
|
||||
common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
|
||||
|
22
ui/vnc-ws.c
22
ui/vnc-ws.c
@ -20,6 +20,7 @@
|
||||
|
||||
#include "vnc.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "crypto/hash.h"
|
||||
|
||||
#ifdef CONFIG_VNC_TLS
|
||||
#include "qemu/sockets.h"
|
||||
@ -203,24 +204,21 @@ static char *vncws_extract_handshake_entry(const char *handshake,
|
||||
static void vncws_send_handshake_response(VncState *vs, const char* key)
|
||||
{
|
||||
char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1];
|
||||
unsigned char hash[SHA1_DIGEST_LEN];
|
||||
size_t hash_size = sizeof(hash);
|
||||
char *accept = NULL, *response = NULL;
|
||||
gnutls_datum_t in;
|
||||
int ret;
|
||||
Error *err = NULL;
|
||||
|
||||
g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1);
|
||||
g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1);
|
||||
|
||||
/* hash and encode it */
|
||||
in.data = (void *)combined_key;
|
||||
in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN;
|
||||
ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size);
|
||||
if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) {
|
||||
accept = g_base64_encode(hash, hash_size);
|
||||
}
|
||||
if (accept == NULL) {
|
||||
VNC_DEBUG("Hashing Websocket combined key failed\n");
|
||||
if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
|
||||
combined_key,
|
||||
WS_CLIENT_KEY_LEN + WS_GUID_LEN,
|
||||
&accept,
|
||||
&err) < 0) {
|
||||
VNC_DEBUG("Hashing Websocket combined key failed %s\n",
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
vnc_client_error(vs);
|
||||
return;
|
||||
}
|
||||
|
@ -21,8 +21,6 @@
|
||||
#ifndef __QEMU_UI_VNC_WS_H
|
||||
#define __QEMU_UI_VNC_WS_H
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
|
||||
#define SHA1_DIGEST_LEN 20
|
||||
|
||||
|
119
ui/vnc.c
119
ui/vnc.c
@ -40,6 +40,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "ui/input.h"
|
||||
#include "qapi-event.h"
|
||||
#include "crypto/hash.h"
|
||||
|
||||
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
|
||||
#define VNC_REFRESH_INTERVAL_INC 50
|
||||
@ -48,7 +49,7 @@ static const struct timeval VNC_REFRESH_STATS = { 0, 500000 };
|
||||
static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
|
||||
|
||||
#include "vnc_keysym.h"
|
||||
#include "d3des.h"
|
||||
#include "crypto/cipher.h"
|
||||
|
||||
static QTAILQ_HEAD(, VncDisplay) vnc_displays =
|
||||
QTAILQ_HEAD_INITIALIZER(vnc_displays);
|
||||
@ -355,9 +356,7 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
|
||||
info->base->host = g_strdup(host);
|
||||
info->base->service = g_strdup(serv);
|
||||
info->base->family = inet_netfamily(sa.ss_family);
|
||||
#ifdef CONFIG_VNC_WS
|
||||
info->base->websocket = client->websocket;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VNC_TLS
|
||||
if (client->tls.session && client->tls.dname) {
|
||||
@ -582,12 +581,10 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
|
||||
info->server = qmp_query_server_entry(vd->lsock, false,
|
||||
info->server);
|
||||
}
|
||||
#ifdef CONFIG_VNC_WS
|
||||
if (vd->lwebsock != -1) {
|
||||
info->server = qmp_query_server_entry(vd->lwebsock, true,
|
||||
info->server);
|
||||
}
|
||||
#endif
|
||||
|
||||
item = g_new0(VncInfo2List, 1);
|
||||
item->value = info;
|
||||
@ -1231,10 +1228,8 @@ void vnc_disconnect_finish(VncState *vs)
|
||||
|
||||
buffer_free(&vs->input);
|
||||
buffer_free(&vs->output);
|
||||
#ifdef CONFIG_VNC_WS
|
||||
buffer_free(&vs->ws_input);
|
||||
buffer_free(&vs->ws_output);
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
|
||||
qapi_free_VncClientInfo(vs->info);
|
||||
|
||||
@ -1413,12 +1408,9 @@ static void vnc_client_write_locked(void *opaque)
|
||||
} else
|
||||
#endif /* CONFIG_VNC_SASL */
|
||||
{
|
||||
#ifdef CONFIG_VNC_WS
|
||||
if (vs->encode_ws) {
|
||||
vnc_client_write_ws(vs);
|
||||
} else
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
{
|
||||
} else {
|
||||
vnc_client_write_plain(vs);
|
||||
}
|
||||
}
|
||||
@ -1429,11 +1421,7 @@ void vnc_client_write(void *opaque)
|
||||
VncState *vs = opaque;
|
||||
|
||||
vnc_lock_output(vs);
|
||||
if (vs->output.offset
|
||||
#ifdef CONFIG_VNC_WS
|
||||
|| vs->ws_output.offset
|
||||
#endif
|
||||
) {
|
||||
if (vs->output.offset || vs->ws_output.offset) {
|
||||
vnc_client_write_locked(opaque);
|
||||
} else if (vs->csock != -1) {
|
||||
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
|
||||
@ -1539,7 +1527,6 @@ void vnc_client_read(void *opaque)
|
||||
ret = vnc_client_read_sasl(vs);
|
||||
else
|
||||
#endif /* CONFIG_VNC_SASL */
|
||||
#ifdef CONFIG_VNC_WS
|
||||
if (vs->encode_ws) {
|
||||
ret = vnc_client_read_ws(vs);
|
||||
if (ret == -1) {
|
||||
@ -1549,10 +1536,8 @@ void vnc_client_read(void *opaque)
|
||||
vnc_client_error(vs);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
{
|
||||
ret = vnc_client_read_plain(vs);
|
||||
} else {
|
||||
ret = vnc_client_read_plain(vs);
|
||||
}
|
||||
if (!ret) {
|
||||
if (vs->csock == -1)
|
||||
@ -1624,11 +1609,8 @@ void vnc_write_u8(VncState *vs, uint8_t value)
|
||||
void vnc_flush(VncState *vs)
|
||||
{
|
||||
vnc_lock_output(vs);
|
||||
if (vs->csock != -1 && (vs->output.offset
|
||||
#ifdef CONFIG_VNC_WS
|
||||
|| vs->ws_output.offset
|
||||
#endif
|
||||
)) {
|
||||
if (vs->csock != -1 && (vs->output.offset ||
|
||||
vs->ws_output.offset)) {
|
||||
vnc_client_write_locked(vs);
|
||||
}
|
||||
vnc_unlock_output(vs);
|
||||
@ -2535,9 +2517,11 @@ static void make_challenge(VncState *vs)
|
||||
static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
|
||||
{
|
||||
unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
|
||||
int i, j, pwlen;
|
||||
size_t i, pwlen;
|
||||
unsigned char key[8];
|
||||
time_t now = time(NULL);
|
||||
QCryptoCipher *cipher;
|
||||
Error *err = NULL;
|
||||
|
||||
if (!vs->vd->password) {
|
||||
VNC_DEBUG("No password configured on server");
|
||||
@ -2554,9 +2538,29 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
|
||||
pwlen = strlen(vs->vd->password);
|
||||
for (i=0; i<sizeof(key); i++)
|
||||
key[i] = i<pwlen ? vs->vd->password[i] : 0;
|
||||
deskey(key, EN0);
|
||||
for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
|
||||
des(response+j, response+j);
|
||||
|
||||
cipher = qcrypto_cipher_new(
|
||||
QCRYPTO_CIPHER_ALG_DES_RFB,
|
||||
QCRYPTO_CIPHER_MODE_ECB,
|
||||
key, G_N_ELEMENTS(key),
|
||||
&err);
|
||||
if (!cipher) {
|
||||
VNC_DEBUG("Cannot initialize cipher %s",
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
goto reject;
|
||||
}
|
||||
|
||||
if (qcrypto_cipher_decrypt(cipher,
|
||||
vs->challenge,
|
||||
response,
|
||||
VNC_AUTH_CHALLENGE_SIZE,
|
||||
&err) < 0) {
|
||||
VNC_DEBUG("Cannot encrypt challenge %s",
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
goto reject;
|
||||
}
|
||||
|
||||
/* Compare expected vs actual challenge response */
|
||||
if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
|
||||
@ -3019,7 +3023,6 @@ static void vnc_connect(VncDisplay *vd, int csock,
|
||||
VNC_DEBUG("New client on socket %d\n", csock);
|
||||
update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
|
||||
qemu_set_nonblock(vs->csock);
|
||||
#ifdef CONFIG_VNC_WS
|
||||
if (websocket) {
|
||||
vs->websocket = 1;
|
||||
#ifdef CONFIG_VNC_TLS
|
||||
@ -3031,7 +3034,6 @@ static void vnc_connect(VncDisplay *vd, int csock,
|
||||
qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
|
||||
}
|
||||
} else
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
{
|
||||
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
|
||||
}
|
||||
@ -3040,10 +3042,7 @@ static void vnc_connect(VncDisplay *vd, int csock,
|
||||
vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
|
||||
vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
|
||||
|
||||
#ifdef CONFIG_VNC_WS
|
||||
if (!vs->websocket)
|
||||
#endif
|
||||
{
|
||||
if (!vs->websocket) {
|
||||
vnc_init_state(vs);
|
||||
}
|
||||
|
||||
@ -3099,12 +3098,9 @@ static void vnc_listen_read(void *opaque, bool websocket)
|
||||
|
||||
/* Catch-up */
|
||||
graphic_hw_update(vs->dcl.con);
|
||||
#ifdef CONFIG_VNC_WS
|
||||
if (websocket) {
|
||||
csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
|
||||
} else
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
{
|
||||
} else {
|
||||
csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
|
||||
}
|
||||
|
||||
@ -3119,12 +3115,10 @@ static void vnc_listen_regular_read(void *opaque)
|
||||
vnc_listen_read(opaque, false);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VNC_WS
|
||||
static void vnc_listen_websocket_read(void *opaque)
|
||||
{
|
||||
vnc_listen_read(opaque, true);
|
||||
}
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
|
||||
static const DisplayChangeListenerOps dcl_ops = {
|
||||
.dpy_name = "vnc",
|
||||
@ -3150,9 +3144,7 @@ void vnc_display_init(const char *id)
|
||||
QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
|
||||
|
||||
vs->lsock = -1;
|
||||
#ifdef CONFIG_VNC_WS
|
||||
vs->lwebsock = -1;
|
||||
#endif
|
||||
|
||||
QTAILQ_INIT(&vs->clients);
|
||||
vs->expires = TIME_MAX;
|
||||
@ -3186,14 +3178,12 @@ static void vnc_display_close(VncDisplay *vs)
|
||||
close(vs->lsock);
|
||||
vs->lsock = -1;
|
||||
}
|
||||
#ifdef CONFIG_VNC_WS
|
||||
vs->ws_enabled = false;
|
||||
if (vs->lwebsock != -1) {
|
||||
qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
|
||||
close(vs->lwebsock);
|
||||
vs->lwebsock = -1;
|
||||
}
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
vs->auth = VNC_AUTH_INVALID;
|
||||
vs->subauth = VNC_AUTH_INVALID;
|
||||
#ifdef CONFIG_VNC_TLS
|
||||
@ -3516,12 +3506,20 @@ void vnc_display_open(const char *id, Error **errp)
|
||||
}
|
||||
|
||||
password = qemu_opt_get_bool(opts, "password", false);
|
||||
if (password && fips_get_state()) {
|
||||
error_setg(errp,
|
||||
"VNC password auth disabled due to FIPS mode, "
|
||||
"consider using the VeNCrypt or SASL authentication "
|
||||
"methods as an alternative");
|
||||
goto fail;
|
||||
if (password) {
|
||||
if (fips_get_state()) {
|
||||
error_setg(errp,
|
||||
"VNC password auth disabled due to FIPS mode, "
|
||||
"consider using the VeNCrypt or SASL authentication "
|
||||
"methods as an alternative");
|
||||
goto fail;
|
||||
}
|
||||
if (!qcrypto_cipher_supports(
|
||||
QCRYPTO_CIPHER_ALG_DES_RFB)) {
|
||||
error_setg(errp,
|
||||
"Cipher backend does not support DES RFB algorithm");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
reverse = qemu_opt_get_bool(opts, "reverse", false);
|
||||
@ -3579,13 +3577,12 @@ void vnc_display_open(const char *id, Error **errp)
|
||||
|
||||
websocket = qemu_opt_get(opts, "websocket");
|
||||
if (websocket) {
|
||||
#ifdef CONFIG_VNC_WS
|
||||
vs->ws_enabled = true;
|
||||
qemu_opt_set(wsopts, "port", websocket, &error_abort);
|
||||
#else /* ! CONFIG_VNC_WS */
|
||||
error_setg(errp, "Websockets protocol requires gnutls support");
|
||||
goto fail;
|
||||
#endif /* ! CONFIG_VNC_WS */
|
||||
if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
|
||||
error_setg(errp, "SHA1 hash support is required for websockets");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VNC_JPEG
|
||||
@ -3668,9 +3665,7 @@ void vnc_display_open(const char *id, Error **errp)
|
||||
/* connect to viewer */
|
||||
int csock;
|
||||
vs->lsock = -1;
|
||||
#ifdef CONFIG_VNC_WS
|
||||
vs->lwebsock = -1;
|
||||
#endif
|
||||
if (strncmp(vnc, "unix:", 5) == 0) {
|
||||
csock = unix_connect(vnc+5, errp);
|
||||
} else {
|
||||
@ -3693,7 +3688,6 @@ void vnc_display_open(const char *id, Error **errp)
|
||||
if (vs->lsock < 0) {
|
||||
goto fail;
|
||||
}
|
||||
#ifdef CONFIG_VNC_WS
|
||||
if (vs->ws_enabled) {
|
||||
vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
|
||||
if (vs->lwebsock < 0) {
|
||||
@ -3704,16 +3698,13 @@ void vnc_display_open(const char *id, Error **errp)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
}
|
||||
vs->enabled = true;
|
||||
qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
|
||||
#ifdef CONFIG_VNC_WS
|
||||
if (vs->ws_enabled) {
|
||||
qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
|
||||
NULL, vs);
|
||||
}
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
}
|
||||
qemu_opts_del(sopts);
|
||||
qemu_opts_del(wsopts);
|
||||
@ -3723,9 +3714,7 @@ fail:
|
||||
qemu_opts_del(sopts);
|
||||
qemu_opts_del(wsopts);
|
||||
vs->enabled = false;
|
||||
#ifdef CONFIG_VNC_WS
|
||||
vs->ws_enabled = false;
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
}
|
||||
|
||||
void vnc_display_add_client(const char *id, int csock, bool skipauth)
|
||||
|
8
ui/vnc.h
8
ui/vnc.h
@ -108,9 +108,7 @@ typedef struct VncDisplay VncDisplay;
|
||||
#ifdef CONFIG_VNC_SASL
|
||||
#include "vnc-auth-sasl.h"
|
||||
#endif
|
||||
#ifdef CONFIG_VNC_WS
|
||||
#include "vnc-ws.h"
|
||||
#endif
|
||||
|
||||
struct VncRectStat
|
||||
{
|
||||
@ -156,10 +154,8 @@ struct VncDisplay
|
||||
int connections_limit;
|
||||
VncSharePolicy share_policy;
|
||||
int lsock;
|
||||
#ifdef CONFIG_VNC_WS
|
||||
int lwebsock;
|
||||
bool ws_enabled;
|
||||
#endif
|
||||
DisplaySurface *ds;
|
||||
DisplayChangeListener dcl;
|
||||
kbd_layout_t *kbd_layout;
|
||||
@ -294,21 +290,17 @@ struct VncState
|
||||
#ifdef CONFIG_VNC_SASL
|
||||
VncStateSASL sasl;
|
||||
#endif
|
||||
#ifdef CONFIG_VNC_WS
|
||||
bool encode_ws;
|
||||
bool websocket;
|
||||
#endif /* CONFIG_VNC_WS */
|
||||
|
||||
VncClientInfo *info;
|
||||
|
||||
Buffer output;
|
||||
Buffer input;
|
||||
#ifdef CONFIG_VNC_WS
|
||||
Buffer ws_input;
|
||||
Buffer ws_output;
|
||||
size_t ws_payload_remain;
|
||||
WsMask ws_payload_mask;
|
||||
#endif
|
||||
/* current output mode information */
|
||||
VncWritePixels *write_pixels;
|
||||
PixelFormat client_pf;
|
||||
|
@ -9,7 +9,7 @@ util-obj-y += acl.o
|
||||
util-obj-y += error.o qemu-error.o
|
||||
util-obj-$(CONFIG_POSIX) += compatfd.o
|
||||
util-obj-y += id.o
|
||||
util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o uri.o notify.o
|
||||
util-obj-y += iov.o qemu-config.o qemu-sockets.o uri.o notify.o
|
||||
util-obj-y += qemu-option.o qemu-progress.o
|
||||
util-obj-y += hexdump.o
|
||||
util-obj-y += crc32c.o
|
||||
|
18
vl.c
18
vl.c
@ -121,6 +121,7 @@ int main(int argc, char **argv)
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi-event.h"
|
||||
#include "exec/semihost.h"
|
||||
#include "crypto/init.h"
|
||||
|
||||
#define MAX_VIRTIO_CONSOLES 1
|
||||
#define MAX_SCLP_CONSOLES 1
|
||||
@ -2976,6 +2977,7 @@ int main(int argc, char **argv, char **envp)
|
||||
uint64_t ram_slots = 0;
|
||||
FILE *vmstate_dump_file = NULL;
|
||||
Error *main_loop_err = NULL;
|
||||
Error *err = NULL;
|
||||
|
||||
qemu_init_cpu_loop();
|
||||
qemu_mutex_lock_iothread();
|
||||
@ -3019,6 +3021,11 @@ int main(int argc, char **argv, char **envp)
|
||||
|
||||
runstate_init();
|
||||
|
||||
if (qcrypto_init(&err) < 0) {
|
||||
fprintf(stderr, "Cannot initialize crypto: %s\n",
|
||||
error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
rtc_clock = QEMU_CLOCK_HOST;
|
||||
|
||||
QLIST_INIT (&vm_change_state_head);
|
||||
@ -4597,18 +4604,15 @@ int main(int argc, char **argv, char **envp)
|
||||
|
||||
qdev_machine_creation_done();
|
||||
|
||||
if (rom_load_all() != 0) {
|
||||
fprintf(stderr, "rom loading failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* TODO: once all bus devices are qdevified, this should be done
|
||||
* when bus is created by qdev.c */
|
||||
qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());
|
||||
qemu_run_machine_init_done_notifiers();
|
||||
|
||||
/* Done notifiers can load ROMs */
|
||||
rom_load_done();
|
||||
if (rom_check_and_register_reset() != 0) {
|
||||
fprintf(stderr, "rom check and register reset failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
qemu_system_reset(VMRESET_SILENT);
|
||||
if (loadvm) {
|
||||
|
Loading…
Reference in New Issue
Block a user