summaryrefslogtreecommitdiffstats
path: root/qemu/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/crypto')
-rw-r--r--qemu/crypto/Makefile.objs35
-rw-r--r--qemu/crypto/aes.c1
-rw-r--r--qemu/crypto/afsplit.c158
-rw-r--r--qemu/crypto/block-luks.c1329
-rw-r--r--qemu/crypto/block-luks.h28
-rw-r--r--qemu/crypto/block-qcow.c174
-rw-r--r--qemu/crypto/block-qcow.h28
-rw-r--r--qemu/crypto/block.c261
-rw-r--r--qemu/crypto/blockpriv.h92
-rw-r--r--qemu/crypto/cipher-builtin.c237
-rw-r--r--qemu/crypto/cipher-gcrypt.c217
-rw-r--r--qemu/crypto/cipher-nettle.c330
-rw-r--r--qemu/crypto/cipher.c103
-rw-r--r--qemu/crypto/desrfb.c1
-rw-r--r--qemu/crypto/hash.c23
-rw-r--r--qemu/crypto/init.c28
-rw-r--r--qemu/crypto/ivgen-essiv.c120
-rw-r--r--qemu/crypto/ivgen-essiv.h28
-rw-r--r--qemu/crypto/ivgen-plain.c61
-rw-r--r--qemu/crypto/ivgen-plain.h28
-rw-r--r--qemu/crypto/ivgen-plain64.c61
-rw-r--r--qemu/crypto/ivgen-plain64.h28
-rw-r--r--qemu/crypto/ivgen.c101
-rw-r--r--qemu/crypto/ivgenpriv.h49
-rw-r--r--qemu/crypto/pbkdf-gcrypt.c69
-rw-r--r--qemu/crypto/pbkdf-nettle.c66
-rw-r--r--qemu/crypto/pbkdf-stub.c43
-rw-r--r--qemu/crypto/pbkdf.c110
-rw-r--r--qemu/crypto/random-gcrypt.c33
-rw-r--r--qemu/crypto/random-gnutls.c43
-rw-r--r--qemu/crypto/random-stub.c31
-rw-r--r--qemu/crypto/secret.c509
-rw-r--r--qemu/crypto/tlscreds.c259
-rw-r--r--qemu/crypto/tlscredsanon.c219
-rw-r--r--qemu/crypto/tlscredspriv.h42
-rw-r--r--qemu/crypto/tlscredsx509.c865
-rw-r--r--qemu/crypto/tlssession.c576
-rw-r--r--qemu/crypto/xts.c230
38 files changed, 6447 insertions, 169 deletions
diff --git a/qemu/crypto/Makefile.objs b/qemu/crypto/Makefile.objs
index b05013831..0737f4811 100644
--- a/qemu/crypto/Makefile.objs
+++ b/qemu/crypto/Makefile.objs
@@ -1,5 +1,30 @@
-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
+crypto-obj-y = init.o
+crypto-obj-y += hash.o
+crypto-obj-y += aes.o
+crypto-obj-y += desrfb.o
+crypto-obj-y += cipher.o
+crypto-obj-y += tlscreds.o
+crypto-obj-y += tlscredsanon.o
+crypto-obj-y += tlscredsx509.o
+crypto-obj-y += tlssession.o
+crypto-obj-y += secret.o
+crypto-obj-$(CONFIG_GCRYPT) += random-gcrypt.o
+crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS_RND)) += random-gnutls.o
+crypto-obj-y += pbkdf.o
+crypto-obj-$(CONFIG_NETTLE_KDF) += pbkdf-nettle.o
+crypto-obj-$(if $(CONFIG_NETTLE_KDF),n,$(CONFIG_GCRYPT_KDF)) += pbkdf-gcrypt.o
+crypto-obj-y += ivgen.o
+crypto-obj-y += ivgen-essiv.o
+crypto-obj-y += ivgen-plain.o
+crypto-obj-y += ivgen-plain64.o
+crypto-obj-y += afsplit.o
+crypto-obj-y += xts.o
+crypto-obj-y += block.o
+crypto-obj-y += block-qcow.o
+crypto-obj-y += block-luks.o
+
+# Let the userspace emulators avoid linking gnutls/etc
+crypto-aes-obj-y = aes.o
+
+stub-obj-y += random-stub.o
+stub-obj-y += pbkdf-stub.o
diff --git a/qemu/crypto/aes.c b/qemu/crypto/aes.c
index 244a388eb..3456eacd0 100644
--- a/qemu/crypto/aes.c
+++ b/qemu/crypto/aes.c
@@ -27,6 +27,7 @@
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "crypto/aes.h"
diff --git a/qemu/crypto/afsplit.c b/qemu/crypto/afsplit.c
new file mode 100644
index 000000000..8074913cd
--- /dev/null
+++ b/qemu/crypto/afsplit.c
@@ -0,0 +1,158 @@
+/*
+ * QEMU Crypto anti forensic information splitter
+ *
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * Derived from cryptsetup package lib/luks1/af.c
+ *
+ * Copyright (C) 2004, Clemens Fruhwirth <clemens@endorphin.org>
+ * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 "qemu/osdep.h"
+#include "crypto/afsplit.h"
+#include "crypto/random.h"
+
+
+static void qcrypto_afsplit_xor(size_t blocklen,
+ const uint8_t *in1,
+ const uint8_t *in2,
+ uint8_t *out)
+{
+ size_t i;
+ for (i = 0; i < blocklen; i++) {
+ out[i] = in1[i] ^ in2[i];
+ }
+}
+
+
+static int qcrypto_afsplit_hash(QCryptoHashAlgorithm hash,
+ size_t blocklen,
+ uint8_t *block,
+ Error **errp)
+{
+ size_t digestlen = qcrypto_hash_digest_len(hash);
+
+ size_t hashcount = blocklen / digestlen;
+ size_t finallen = blocklen % digestlen;
+ uint32_t i;
+
+ if (finallen) {
+ hashcount++;
+ } else {
+ finallen = digestlen;
+ }
+
+ for (i = 0; i < hashcount; i++) {
+ uint8_t *out = NULL;
+ size_t outlen = 0;
+ uint32_t iv = cpu_to_be32(i);
+ struct iovec in[] = {
+ { .iov_base = &iv,
+ .iov_len = sizeof(iv) },
+ { .iov_base = block + (i * digestlen),
+ .iov_len = (i == (hashcount - 1)) ? finallen : digestlen },
+ };
+
+ if (qcrypto_hash_bytesv(hash,
+ in,
+ G_N_ELEMENTS(in),
+ &out, &outlen,
+ errp) < 0) {
+ return -1;
+ }
+
+ assert(outlen == digestlen);
+ memcpy(block + (i * digestlen), out,
+ (i == (hashcount - 1)) ? finallen : digestlen);
+ g_free(out);
+ }
+
+ return 0;
+}
+
+
+int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash,
+ size_t blocklen,
+ uint32_t stripes,
+ const uint8_t *in,
+ uint8_t *out,
+ Error **errp)
+{
+ uint8_t *block = g_new0(uint8_t, blocklen);
+ size_t i;
+ int ret = -1;
+
+ for (i = 0; i < (stripes - 1); i++) {
+ if (qcrypto_random_bytes(out + (i * blocklen), blocklen, errp) < 0) {
+ goto cleanup;
+ }
+
+ qcrypto_afsplit_xor(blocklen,
+ out + (i * blocklen),
+ block,
+ block);
+ if (qcrypto_afsplit_hash(hash, blocklen, block,
+ errp) < 0) {
+ goto cleanup;
+ }
+ }
+ qcrypto_afsplit_xor(blocklen,
+ in,
+ block,
+ out + (i * blocklen));
+ ret = 0;
+
+ cleanup:
+ g_free(block);
+ return ret;
+}
+
+
+int qcrypto_afsplit_decode(QCryptoHashAlgorithm hash,
+ size_t blocklen,
+ uint32_t stripes,
+ const uint8_t *in,
+ uint8_t *out,
+ Error **errp)
+{
+ uint8_t *block = g_new0(uint8_t, blocklen);
+ size_t i;
+ int ret = -1;
+
+ for (i = 0; i < (stripes - 1); i++) {
+ qcrypto_afsplit_xor(blocklen,
+ in + (i * blocklen),
+ block,
+ block);
+ if (qcrypto_afsplit_hash(hash, blocklen, block,
+ errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ qcrypto_afsplit_xor(blocklen,
+ in + (i * blocklen),
+ block,
+ out);
+
+ ret = 0;
+
+ cleanup:
+ g_free(block);
+ return ret;
+}
diff --git a/qemu/crypto/block-luks.c b/qemu/crypto/block-luks.c
new file mode 100644
index 000000000..439f89230
--- /dev/null
+++ b/qemu/crypto/block-luks.c
@@ -0,0 +1,1329 @@
+/*
+ * QEMU Crypto block device encryption LUKS format
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+
+#include "crypto/block-luks.h"
+
+#include "crypto/hash.h"
+#include "crypto/afsplit.h"
+#include "crypto/pbkdf.h"
+#include "crypto/secret.h"
+#include "crypto/random.h"
+
+#ifdef CONFIG_UUID
+#include <uuid/uuid.h>
+#endif
+
+#include "qemu/coroutine.h"
+
+/*
+ * Reference for the LUKS format implemented here is
+ *
+ * docs/on-disk-format.pdf
+ *
+ * in 'cryptsetup' package source code
+ *
+ * This file implements the 1.2.1 specification, dated
+ * Oct 16, 2011.
+ */
+
+typedef struct QCryptoBlockLUKS QCryptoBlockLUKS;
+typedef struct QCryptoBlockLUKSHeader QCryptoBlockLUKSHeader;
+typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
+
+
+/* The following constants are all defined by the LUKS spec */
+#define QCRYPTO_BLOCK_LUKS_VERSION 1
+
+#define QCRYPTO_BLOCK_LUKS_MAGIC_LEN 6
+#define QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN 32
+#define QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN 32
+#define QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN 32
+#define QCRYPTO_BLOCK_LUKS_DIGEST_LEN 20
+#define QCRYPTO_BLOCK_LUKS_SALT_LEN 32
+#define QCRYPTO_BLOCK_LUKS_UUID_LEN 40
+#define QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS 8
+#define QCRYPTO_BLOCK_LUKS_STRIPES 4000
+#define QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS 1000
+#define QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS 1000
+#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET 4096
+
+#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED 0x0000DEAD
+#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED 0x00AC71F3
+
+#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
+
+static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
+ 'L', 'U', 'K', 'S', 0xBA, 0xBE
+};
+
+typedef struct QCryptoBlockLUKSNameMap QCryptoBlockLUKSNameMap;
+struct QCryptoBlockLUKSNameMap {
+ const char *name;
+ int id;
+};
+
+typedef struct QCryptoBlockLUKSCipherSizeMap QCryptoBlockLUKSCipherSizeMap;
+struct QCryptoBlockLUKSCipherSizeMap {
+ uint32_t key_bytes;
+ int id;
+};
+typedef struct QCryptoBlockLUKSCipherNameMap QCryptoBlockLUKSCipherNameMap;
+struct QCryptoBlockLUKSCipherNameMap {
+ const char *name;
+ const QCryptoBlockLUKSCipherSizeMap *sizes;
+};
+
+
+static const QCryptoBlockLUKSCipherSizeMap
+qcrypto_block_luks_cipher_size_map_aes[] = {
+ { 16, QCRYPTO_CIPHER_ALG_AES_128 },
+ { 24, QCRYPTO_CIPHER_ALG_AES_192 },
+ { 32, QCRYPTO_CIPHER_ALG_AES_256 },
+ { 0, 0 },
+};
+
+static const QCryptoBlockLUKSCipherSizeMap
+qcrypto_block_luks_cipher_size_map_cast5[] = {
+ { 16, QCRYPTO_CIPHER_ALG_CAST5_128 },
+ { 0, 0 },
+};
+
+static const QCryptoBlockLUKSCipherSizeMap
+qcrypto_block_luks_cipher_size_map_serpent[] = {
+ { 16, QCRYPTO_CIPHER_ALG_SERPENT_128 },
+ { 24, QCRYPTO_CIPHER_ALG_SERPENT_192 },
+ { 32, QCRYPTO_CIPHER_ALG_SERPENT_256 },
+ { 0, 0 },
+};
+
+static const QCryptoBlockLUKSCipherSizeMap
+qcrypto_block_luks_cipher_size_map_twofish[] = {
+ { 16, QCRYPTO_CIPHER_ALG_TWOFISH_128 },
+ { 24, QCRYPTO_CIPHER_ALG_TWOFISH_192 },
+ { 32, QCRYPTO_CIPHER_ALG_TWOFISH_256 },
+ { 0, 0 },
+};
+
+static const QCryptoBlockLUKSCipherNameMap
+qcrypto_block_luks_cipher_name_map[] = {
+ { "aes", qcrypto_block_luks_cipher_size_map_aes },
+ { "cast5", qcrypto_block_luks_cipher_size_map_cast5 },
+ { "serpent", qcrypto_block_luks_cipher_size_map_serpent },
+ { "twofish", qcrypto_block_luks_cipher_size_map_twofish },
+};
+
+
+/*
+ * This struct is written to disk in big-endian format,
+ * but operated upon in native-endian format.
+ */
+struct QCryptoBlockLUKSKeySlot {
+ /* state of keyslot, enabled/disable */
+ uint32_t active;
+ /* iterations for PBKDF2 */
+ uint32_t iterations;
+ /* salt for PBKDF2 */
+ uint8_t salt[QCRYPTO_BLOCK_LUKS_SALT_LEN];
+ /* start sector of key material */
+ uint32_t key_offset;
+ /* number of anti-forensic stripes */
+ uint32_t stripes;
+} QEMU_PACKED;
+
+QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSKeySlot) != 48);
+
+
+/*
+ * This struct is written to disk in big-endian format,
+ * but operated upon in native-endian format.
+ */
+struct QCryptoBlockLUKSHeader {
+ /* 'L', 'U', 'K', 'S', '0xBA', '0xBE' */
+ char magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN];
+
+ /* LUKS version, currently 1 */
+ uint16_t version;
+
+ /* cipher name specification (aes, etc) */
+ char cipher_name[QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN];
+
+ /* cipher mode specification (cbc-plain, xts-essiv:sha256, etc) */
+ char cipher_mode[QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN];
+
+ /* hash specification (sha256, etc) */
+ char hash_spec[QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN];
+
+ /* start offset of the volume data (in 512 byte sectors) */
+ uint32_t payload_offset;
+
+ /* Number of key bytes */
+ uint32_t key_bytes;
+
+ /* master key checksum after PBKDF2 */
+ uint8_t master_key_digest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN];
+
+ /* salt for master key PBKDF2 */
+ uint8_t master_key_salt[QCRYPTO_BLOCK_LUKS_SALT_LEN];
+
+ /* iterations for master key PBKDF2 */
+ uint32_t master_key_iterations;
+
+ /* UUID of the partition in standard ASCII representation */
+ uint8_t uuid[QCRYPTO_BLOCK_LUKS_UUID_LEN];
+
+ /* key slots */
+ QCryptoBlockLUKSKeySlot key_slots[QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS];
+} QEMU_PACKED;
+
+QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592);
+
+
+struct QCryptoBlockLUKS {
+ QCryptoBlockLUKSHeader header;
+};
+
+
+static int qcrypto_block_luks_cipher_name_lookup(const char *name,
+ QCryptoCipherMode mode,
+ uint32_t key_bytes,
+ Error **errp)
+{
+ const QCryptoBlockLUKSCipherNameMap *map =
+ qcrypto_block_luks_cipher_name_map;
+ size_t maplen = G_N_ELEMENTS(qcrypto_block_luks_cipher_name_map);
+ size_t i, j;
+
+ if (mode == QCRYPTO_CIPHER_MODE_XTS) {
+ key_bytes /= 2;
+ }
+
+ for (i = 0; i < maplen; i++) {
+ if (!g_str_equal(map[i].name, name)) {
+ continue;
+ }
+ for (j = 0; j < map[i].sizes[j].key_bytes; j++) {
+ if (map[i].sizes[j].key_bytes == key_bytes) {
+ return map[i].sizes[j].id;
+ }
+ }
+ }
+
+ error_setg(errp, "Algorithm %s with key size %d bytes not supported",
+ name, key_bytes);
+ return 0;
+}
+
+static const char *
+qcrypto_block_luks_cipher_alg_lookup(QCryptoCipherAlgorithm alg,
+ Error **errp)
+{
+ const QCryptoBlockLUKSCipherNameMap *map =
+ qcrypto_block_luks_cipher_name_map;
+ size_t maplen = G_N_ELEMENTS(qcrypto_block_luks_cipher_name_map);
+ size_t i, j;
+ for (i = 0; i < maplen; i++) {
+ for (j = 0; j < map[i].sizes[j].key_bytes; j++) {
+ if (map[i].sizes[j].id == alg) {
+ return map[i].name;
+ }
+ }
+ }
+
+ error_setg(errp, "Algorithm '%s' not supported",
+ QCryptoCipherAlgorithm_lookup[alg]);
+ return NULL;
+}
+
+/* XXX replace with qapi_enum_parse() in future, when we can
+ * make that function emit a more friendly error message */
+static int qcrypto_block_luks_name_lookup(const char *name,
+ const char *const *map,
+ size_t maplen,
+ const char *type,
+ Error **errp)
+{
+ size_t i;
+ for (i = 0; i < maplen; i++) {
+ if (g_str_equal(map[i], name)) {
+ return i;
+ }
+ }
+
+ error_setg(errp, "%s %s not supported", type, name);
+ return 0;
+}
+
+#define qcrypto_block_luks_cipher_mode_lookup(name, errp) \
+ qcrypto_block_luks_name_lookup(name, \
+ QCryptoCipherMode_lookup, \
+ QCRYPTO_CIPHER_MODE__MAX, \
+ "Cipher mode", \
+ errp)
+
+#define qcrypto_block_luks_hash_name_lookup(name, errp) \
+ qcrypto_block_luks_name_lookup(name, \
+ QCryptoHashAlgorithm_lookup, \
+ QCRYPTO_HASH_ALG__MAX, \
+ "Hash algorithm", \
+ errp)
+
+#define qcrypto_block_luks_ivgen_name_lookup(name, errp) \
+ qcrypto_block_luks_name_lookup(name, \
+ QCryptoIVGenAlgorithm_lookup, \
+ QCRYPTO_IVGEN_ALG__MAX, \
+ "IV generator", \
+ errp)
+
+
+static bool
+qcrypto_block_luks_has_format(const uint8_t *buf,
+ size_t buf_size)
+{
+ const QCryptoBlockLUKSHeader *luks_header = (const void *)buf;
+
+ if (buf_size >= offsetof(QCryptoBlockLUKSHeader, cipher_name) &&
+ memcmp(luks_header->magic, qcrypto_block_luks_magic,
+ QCRYPTO_BLOCK_LUKS_MAGIC_LEN) == 0 &&
+ be16_to_cpu(luks_header->version) == QCRYPTO_BLOCK_LUKS_VERSION) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+/**
+ * Deal with a quirk of dm-crypt usage of ESSIV.
+ *
+ * When calculating ESSIV IVs, the cipher length used by ESSIV
+ * may be different from the cipher length used for the block
+ * encryption, becauses dm-crypt uses the hash digest length
+ * as the key size. ie, if you have AES 128 as the block cipher
+ * and SHA 256 as ESSIV hash, then ESSIV will use AES 256 as
+ * the cipher since that gets a key length matching the digest
+ * size, not AES 128 with truncated digest as might be imagined
+ */
+static QCryptoCipherAlgorithm
+qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
+ QCryptoHashAlgorithm hash,
+ Error **errp)
+{
+ size_t digestlen = qcrypto_hash_digest_len(hash);
+ size_t keylen = qcrypto_cipher_get_key_len(cipher);
+ if (digestlen == keylen) {
+ return cipher;
+ }
+
+ switch (cipher) {
+ case QCRYPTO_CIPHER_ALG_AES_128:
+ case QCRYPTO_CIPHER_ALG_AES_192:
+ case QCRYPTO_CIPHER_ALG_AES_256:
+ if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_AES_128)) {
+ return QCRYPTO_CIPHER_ALG_AES_128;
+ } else if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_AES_192)) {
+ return QCRYPTO_CIPHER_ALG_AES_192;
+ } else if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_AES_256)) {
+ return QCRYPTO_CIPHER_ALG_AES_256;
+ } else {
+ error_setg(errp, "No AES cipher with key size %zu available",
+ digestlen);
+ return 0;
+ }
+ break;
+ case QCRYPTO_CIPHER_ALG_SERPENT_128:
+ case QCRYPTO_CIPHER_ALG_SERPENT_192:
+ case QCRYPTO_CIPHER_ALG_SERPENT_256:
+ if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_SERPENT_128)) {
+ return QCRYPTO_CIPHER_ALG_SERPENT_128;
+ } else if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_SERPENT_192)) {
+ return QCRYPTO_CIPHER_ALG_SERPENT_192;
+ } else if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_SERPENT_256)) {
+ return QCRYPTO_CIPHER_ALG_SERPENT_256;
+ } else {
+ error_setg(errp, "No Serpent cipher with key size %zu available",
+ digestlen);
+ return 0;
+ }
+ break;
+ case QCRYPTO_CIPHER_ALG_TWOFISH_128:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_192:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_256:
+ if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_TWOFISH_128)) {
+ return QCRYPTO_CIPHER_ALG_TWOFISH_128;
+ } else if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_TWOFISH_192)) {
+ return QCRYPTO_CIPHER_ALG_TWOFISH_192;
+ } else if (digestlen == qcrypto_cipher_get_key_len(
+ QCRYPTO_CIPHER_ALG_TWOFISH_256)) {
+ return QCRYPTO_CIPHER_ALG_TWOFISH_256;
+ } else {
+ error_setg(errp, "No Twofish cipher with key size %zu available",
+ digestlen);
+ return 0;
+ }
+ break;
+ default:
+ error_setg(errp, "Cipher %s not supported with essiv",
+ QCryptoCipherAlgorithm_lookup[cipher]);
+ return 0;
+ }
+}
+
+/*
+ * Given a key slot, and user password, this will attempt to unlock
+ * the master encryption key from the key slot.
+ *
+ * Returns:
+ * 0 if the key slot is disabled, or key could not be decrypted
+ * with the provided password
+ * 1 if the key slot is enabled, and key decrypted successfully
+ * with the provided password
+ * -1 if a fatal error occurred loading the key
+ */
+static int
+qcrypto_block_luks_load_key(QCryptoBlock *block,
+ QCryptoBlockLUKSKeySlot *slot,
+ const char *password,
+ QCryptoCipherAlgorithm cipheralg,
+ QCryptoCipherMode ciphermode,
+ QCryptoHashAlgorithm hash,
+ QCryptoIVGenAlgorithm ivalg,
+ QCryptoCipherAlgorithm ivcipheralg,
+ QCryptoHashAlgorithm ivhash,
+ uint8_t *masterkey,
+ size_t masterkeylen,
+ QCryptoBlockReadFunc readfunc,
+ void *opaque,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ uint8_t *splitkey;
+ size_t splitkeylen;
+ uint8_t *possiblekey;
+ int ret = -1;
+ ssize_t rv;
+ QCryptoCipher *cipher = NULL;
+ uint8_t keydigest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN];
+ QCryptoIVGen *ivgen = NULL;
+ size_t niv;
+
+ if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
+ return 0;
+ }
+
+ splitkeylen = masterkeylen * slot->stripes;
+ splitkey = g_new0(uint8_t, splitkeylen);
+ possiblekey = g_new0(uint8_t, masterkeylen);
+
+ /*
+ * The user password is used to generate a (possible)
+ * decryption key. This may or may not successfully
+ * decrypt the master key - we just blindly assume
+ * the key is correct and validate the results of
+ * decryption later.
+ */
+ if (qcrypto_pbkdf2(hash,
+ (const uint8_t *)password, strlen(password),
+ slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ slot->iterations,
+ possiblekey, masterkeylen,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ /*
+ * We need to read the master key material from the
+ * LUKS key material header. What we're reading is
+ * not the raw master key, but rather the data after
+ * it has been passed through AFSplit and the result
+ * then encrypted.
+ */
+ rv = readfunc(block,
+ slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ splitkey, splitkeylen,
+ errp,
+ opaque);
+ if (rv < 0) {
+ goto cleanup;
+ }
+
+
+ /* Setup the cipher/ivgen that we'll use to try to decrypt
+ * the split master key material */
+ cipher = qcrypto_cipher_new(cipheralg, ciphermode,
+ possiblekey, masterkeylen,
+ errp);
+ if (!cipher) {
+ goto cleanup;
+ }
+
+ niv = qcrypto_cipher_get_iv_len(cipheralg,
+ ciphermode);
+ ivgen = qcrypto_ivgen_new(ivalg,
+ ivcipheralg,
+ ivhash,
+ possiblekey, masterkeylen,
+ errp);
+ if (!ivgen) {
+ goto cleanup;
+ }
+
+
+ /*
+ * The master key needs to be decrypted in the same
+ * way that the block device payload will be decrypted
+ * later. In particular we'll be using the IV generator
+ * to reset the encryption cipher every time the master
+ * key crosses a sector boundary.
+ */
+ if (qcrypto_block_decrypt_helper(cipher,
+ niv,
+ ivgen,
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ 0,
+ splitkey,
+ splitkeylen,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ /*
+ * Now we've decrypted the split master key, join
+ * it back together to get the actual master key.
+ */
+ if (qcrypto_afsplit_decode(hash,
+ masterkeylen,
+ slot->stripes,
+ splitkey,
+ masterkey,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+
+ /*
+ * We still don't know that the masterkey we got is valid,
+ * because we just blindly assumed the user's password
+ * was correct. This is where we now verify it. We are
+ * creating a hash of the master key using PBKDF and
+ * then comparing that to the hash stored in the key slot
+ * header
+ */
+ if (qcrypto_pbkdf2(hash,
+ masterkey, masterkeylen,
+ luks->header.master_key_salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ luks->header.master_key_iterations,
+ keydigest, G_N_ELEMENTS(keydigest),
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ if (memcmp(keydigest, luks->header.master_key_digest,
+ QCRYPTO_BLOCK_LUKS_DIGEST_LEN) == 0) {
+ /* Success, we got the right master key */
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* Fail, user's password was not valid for this key slot,
+ * tell caller to try another slot */
+ ret = 0;
+
+ cleanup:
+ qcrypto_ivgen_free(ivgen);
+ qcrypto_cipher_free(cipher);
+ g_free(splitkey);
+ g_free(possiblekey);
+ return ret;
+}
+
+
+/*
+ * Given a user password, this will iterate over all key
+ * slots and try to unlock each active key slot using the
+ * password until it successfully obtains a master key.
+ *
+ * Returns 0 if a key was loaded, -1 if no keys could be loaded
+ */
+static int
+qcrypto_block_luks_find_key(QCryptoBlock *block,
+ const char *password,
+ QCryptoCipherAlgorithm cipheralg,
+ QCryptoCipherMode ciphermode,
+ QCryptoHashAlgorithm hash,
+ QCryptoIVGenAlgorithm ivalg,
+ QCryptoCipherAlgorithm ivcipheralg,
+ QCryptoHashAlgorithm ivhash,
+ uint8_t **masterkey,
+ size_t *masterkeylen,
+ QCryptoBlockReadFunc readfunc,
+ void *opaque,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ size_t i;
+ int rv;
+
+ *masterkey = g_new0(uint8_t, luks->header.key_bytes);
+ *masterkeylen = luks->header.key_bytes;
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ rv = qcrypto_block_luks_load_key(block,
+ &luks->header.key_slots[i],
+ password,
+ cipheralg,
+ ciphermode,
+ hash,
+ ivalg,
+ ivcipheralg,
+ ivhash,
+ *masterkey,
+ *masterkeylen,
+ readfunc,
+ opaque,
+ errp);
+ if (rv < 0) {
+ goto error;
+ }
+ if (rv == 1) {
+ return 0;
+ }
+ }
+
+ error_setg(errp, "Invalid password, cannot unlock any keyslot");
+
+ error:
+ g_free(*masterkey);
+ *masterkey = NULL;
+ *masterkeylen = 0;
+ return -1;
+}
+
+
+static int
+qcrypto_block_luks_open(QCryptoBlock *block,
+ QCryptoBlockOpenOptions *options,
+ QCryptoBlockReadFunc readfunc,
+ void *opaque,
+ unsigned int flags,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks;
+ Error *local_err = NULL;
+ int ret = 0;
+ size_t i;
+ ssize_t rv;
+ uint8_t *masterkey = NULL;
+ size_t masterkeylen;
+ char *ivgen_name, *ivhash_name;
+ QCryptoCipherMode ciphermode;
+ QCryptoCipherAlgorithm cipheralg;
+ QCryptoIVGenAlgorithm ivalg;
+ QCryptoCipherAlgorithm ivcipheralg;
+ QCryptoHashAlgorithm hash;
+ QCryptoHashAlgorithm ivhash;
+ char *password = NULL;
+
+ if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
+ if (!options->u.luks.key_secret) {
+ error_setg(errp, "Parameter 'key-secret' is required for cipher");
+ return -1;
+ }
+ password = qcrypto_secret_lookup_as_utf8(
+ options->u.luks.key_secret, errp);
+ if (!password) {
+ return -1;
+ }
+ }
+
+ luks = g_new0(QCryptoBlockLUKS, 1);
+ block->opaque = luks;
+
+ /* Read the entire LUKS header, minus the key material from
+ * the underlying device */
+ rv = readfunc(block, 0,
+ (uint8_t *)&luks->header,
+ sizeof(luks->header),
+ errp,
+ opaque);
+ if (rv < 0) {
+ ret = rv;
+ goto fail;
+ }
+
+ /* The header is always stored in big-endian format, so
+ * convert everything to native */
+ be16_to_cpus(&luks->header.version);
+ be32_to_cpus(&luks->header.payload_offset);
+ be32_to_cpus(&luks->header.key_bytes);
+ be32_to_cpus(&luks->header.master_key_iterations);
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ be32_to_cpus(&luks->header.key_slots[i].active);
+ be32_to_cpus(&luks->header.key_slots[i].iterations);
+ be32_to_cpus(&luks->header.key_slots[i].key_offset);
+ be32_to_cpus(&luks->header.key_slots[i].stripes);
+ }
+
+ if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
+ QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
+ error_setg(errp, "Volume is not in LUKS format");
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
+ error_setg(errp, "LUKS version %" PRIu32 " is not supported",
+ luks->header.version);
+ ret = -ENOTSUP;
+ goto fail;
+ }
+
+ /*
+ * The cipher_mode header contains a string that we have
+ * to further parse, of the format
+ *
+ * <cipher-mode>-<iv-generator>[:<iv-hash>]
+ *
+ * eg cbc-essiv:sha256, cbc-plain64
+ */
+ ivgen_name = strchr(luks->header.cipher_mode, '-');
+ if (!ivgen_name) {
+ ret = -EINVAL;
+ error_setg(errp, "Unexpected cipher mode string format %s",
+ luks->header.cipher_mode);
+ goto fail;
+ }
+ *ivgen_name = '\0';
+ ivgen_name++;
+
+ ivhash_name = strchr(ivgen_name, ':');
+ if (!ivhash_name) {
+ ivhash = 0;
+ } else {
+ *ivhash_name = '\0';
+ ivhash_name++;
+
+ ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name,
+ &local_err);
+ if (local_err) {
+ ret = -ENOTSUP;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+ }
+
+ ciphermode = qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode,
+ &local_err);
+ if (local_err) {
+ ret = -ENOTSUP;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
+ ciphermode,
+ luks->header.key_bytes,
+ &local_err);
+ if (local_err) {
+ ret = -ENOTSUP;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
+ &local_err);
+ if (local_err) {
+ ret = -ENOTSUP;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ ivalg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
+ &local_err);
+ if (local_err) {
+ ret = -ENOTSUP;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ if (ivalg == QCRYPTO_IVGEN_ALG_ESSIV) {
+ ivcipheralg = qcrypto_block_luks_essiv_cipher(cipheralg,
+ ivhash,
+ &local_err);
+ if (local_err) {
+ ret = -ENOTSUP;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+ } else {
+ ivcipheralg = cipheralg;
+ }
+
+ if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
+ /* Try to find which key slot our password is valid for
+ * and unlock the master key from that slot.
+ */
+ if (qcrypto_block_luks_find_key(block,
+ password,
+ cipheralg, ciphermode,
+ hash,
+ ivalg,
+ ivcipheralg,
+ ivhash,
+ &masterkey, &masterkeylen,
+ readfunc, opaque,
+ errp) < 0) {
+ ret = -EACCES;
+ goto fail;
+ }
+
+ /* We have a valid master key now, so can setup the
+ * block device payload decryption objects
+ */
+ block->kdfhash = hash;
+ block->niv = qcrypto_cipher_get_iv_len(cipheralg,
+ ciphermode);
+ block->ivgen = qcrypto_ivgen_new(ivalg,
+ ivcipheralg,
+ ivhash,
+ masterkey, masterkeylen,
+ errp);
+ if (!block->ivgen) {
+ ret = -ENOTSUP;
+ goto fail;
+ }
+
+ block->cipher = qcrypto_cipher_new(cipheralg,
+ ciphermode,
+ masterkey, masterkeylen,
+ errp);
+ if (!block->cipher) {
+ ret = -ENOTSUP;
+ goto fail;
+ }
+ }
+
+ block->payload_offset = luks->header.payload_offset *
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+
+ g_free(masterkey);
+ g_free(password);
+
+ return 0;
+
+ fail:
+ g_free(masterkey);
+ qcrypto_cipher_free(block->cipher);
+ qcrypto_ivgen_free(block->ivgen);
+ g_free(luks);
+ g_free(password);
+ return ret;
+}
+
+
+static int
+qcrypto_block_luks_uuid_gen(uint8_t *uuidstr, Error **errp)
+{
+#ifdef CONFIG_UUID
+ uuid_t uuid;
+ uuid_generate(uuid);
+ uuid_unparse(uuid, (char *)uuidstr);
+ return 0;
+#else
+ error_setg(errp, "Unable to generate uuids on this platform");
+ return -1;
+#endif
+}
+
+static int
+qcrypto_block_luks_create(QCryptoBlock *block,
+ QCryptoBlockCreateOptions *options,
+ QCryptoBlockInitFunc initfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks;
+ QCryptoBlockCreateOptionsLUKS luks_opts;
+ Error *local_err = NULL;
+ uint8_t *masterkey = NULL;
+ uint8_t *slotkey = NULL;
+ uint8_t *splitkey = NULL;
+ size_t splitkeylen = 0;
+ size_t i;
+ QCryptoCipher *cipher = NULL;
+ QCryptoIVGen *ivgen = NULL;
+ char *password;
+ const char *cipher_alg;
+ const char *cipher_mode;
+ const char *ivgen_alg;
+ const char *ivgen_hash_alg = NULL;
+ const char *hash_alg;
+ char *cipher_mode_spec = NULL;
+ QCryptoCipherAlgorithm ivcipheralg = 0;
+
+ memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
+ if (!luks_opts.has_cipher_alg) {
+ luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
+ }
+ if (!luks_opts.has_cipher_mode) {
+ luks_opts.cipher_mode = QCRYPTO_CIPHER_MODE_XTS;
+ }
+ if (!luks_opts.has_ivgen_alg) {
+ luks_opts.ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64;
+ }
+ if (!luks_opts.has_hash_alg) {
+ luks_opts.hash_alg = QCRYPTO_HASH_ALG_SHA256;
+ }
+
+ if (!options->u.luks.key_secret) {
+ error_setg(errp, "Parameter 'key-secret' is required for cipher");
+ return -1;
+ }
+ password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
+ if (!password) {
+ return -1;
+ }
+
+ luks = g_new0(QCryptoBlockLUKS, 1);
+ block->opaque = luks;
+
+ memcpy(luks->header.magic, qcrypto_block_luks_magic,
+ QCRYPTO_BLOCK_LUKS_MAGIC_LEN);
+
+ /* We populate the header in native endianness initially and
+ * then convert everything to big endian just before writing
+ * it out to disk
+ */
+ luks->header.version = QCRYPTO_BLOCK_LUKS_VERSION;
+ if (qcrypto_block_luks_uuid_gen(luks->header.uuid,
+ errp) < 0) {
+ goto error;
+ }
+
+ cipher_alg = qcrypto_block_luks_cipher_alg_lookup(luks_opts.cipher_alg,
+ errp);
+ if (!cipher_alg) {
+ goto error;
+ }
+
+ cipher_mode = QCryptoCipherMode_lookup[luks_opts.cipher_mode];
+ ivgen_alg = QCryptoIVGenAlgorithm_lookup[luks_opts.ivgen_alg];
+ if (luks_opts.has_ivgen_hash_alg) {
+ ivgen_hash_alg = QCryptoHashAlgorithm_lookup[luks_opts.ivgen_hash_alg];
+ cipher_mode_spec = g_strdup_printf("%s-%s:%s", cipher_mode, ivgen_alg,
+ ivgen_hash_alg);
+ } else {
+ cipher_mode_spec = g_strdup_printf("%s-%s", cipher_mode, ivgen_alg);
+ }
+ hash_alg = QCryptoHashAlgorithm_lookup[luks_opts.hash_alg];
+
+
+ if (strlen(cipher_alg) >= QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN) {
+ error_setg(errp, "Cipher name '%s' is too long for LUKS header",
+ cipher_alg);
+ goto error;
+ }
+ if (strlen(cipher_mode_spec) >= QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN) {
+ error_setg(errp, "Cipher mode '%s' is too long for LUKS header",
+ cipher_mode_spec);
+ goto error;
+ }
+ if (strlen(hash_alg) >= QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN) {
+ error_setg(errp, "Hash name '%s' is too long for LUKS header",
+ hash_alg);
+ goto error;
+ }
+
+ if (luks_opts.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
+ ivcipheralg = qcrypto_block_luks_essiv_cipher(luks_opts.cipher_alg,
+ luks_opts.ivgen_hash_alg,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto error;
+ }
+ } else {
+ ivcipheralg = luks_opts.cipher_alg;
+ }
+
+ strcpy(luks->header.cipher_name, cipher_alg);
+ strcpy(luks->header.cipher_mode, cipher_mode_spec);
+ strcpy(luks->header.hash_spec, hash_alg);
+
+ luks->header.key_bytes = qcrypto_cipher_get_key_len(luks_opts.cipher_alg);
+ if (luks_opts.cipher_mode == QCRYPTO_CIPHER_MODE_XTS) {
+ luks->header.key_bytes *= 2;
+ }
+
+ /* Generate the salt used for hashing the master key
+ * with PBKDF later
+ */
+ if (qcrypto_random_bytes(luks->header.master_key_salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ errp) < 0) {
+ goto error;
+ }
+
+ /* Generate random master key */
+ masterkey = g_new0(uint8_t, luks->header.key_bytes);
+ if (qcrypto_random_bytes(masterkey,
+ luks->header.key_bytes, errp) < 0) {
+ goto error;
+ }
+
+
+ /* Setup the block device payload encryption objects */
+ block->cipher = qcrypto_cipher_new(luks_opts.cipher_alg,
+ luks_opts.cipher_mode,
+ masterkey, luks->header.key_bytes,
+ errp);
+ if (!block->cipher) {
+ goto error;
+ }
+
+ block->kdfhash = luks_opts.hash_alg;
+ block->niv = qcrypto_cipher_get_iv_len(luks_opts.cipher_alg,
+ luks_opts.cipher_mode);
+ block->ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg,
+ ivcipheralg,
+ luks_opts.ivgen_hash_alg,
+ masterkey, luks->header.key_bytes,
+ errp);
+
+ if (!block->ivgen) {
+ goto error;
+ }
+
+
+ /* Determine how many iterations we need to hash the master
+ * key, in order to have 1 second of compute time used
+ */
+ luks->header.master_key_iterations =
+ qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
+ masterkey, luks->header.key_bytes,
+ luks->header.master_key_salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto error;
+ }
+
+ /* Why /= 8 ? That matches cryptsetup, but there's no
+ * explanation why they chose /= 8... Probably so that
+ * if all 8 keyslots are active we only spend 1 second
+ * in total time to check all keys */
+ luks->header.master_key_iterations /= 8;
+ luks->header.master_key_iterations = MAX(
+ luks->header.master_key_iterations,
+ QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS);
+
+
+ /* Hash the master key, saving the result in the LUKS
+ * header. This hash is used when opening the encrypted
+ * device to verify that the user password unlocked a
+ * valid master key
+ */
+ if (qcrypto_pbkdf2(luks_opts.hash_alg,
+ masterkey, luks->header.key_bytes,
+ luks->header.master_key_salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ luks->header.master_key_iterations,
+ luks->header.master_key_digest,
+ QCRYPTO_BLOCK_LUKS_DIGEST_LEN,
+ errp) < 0) {
+ goto error;
+ }
+
+
+ /* Although LUKS has multiple key slots, we're just going
+ * to use the first key slot */
+ splitkeylen = luks->header.key_bytes * QCRYPTO_BLOCK_LUKS_STRIPES;
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ luks->header.key_slots[i].active = i == 0 ?
+ QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED :
+ QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
+ luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
+
+ /* This calculation doesn't match that shown in the spec,
+ * but instead follows the cryptsetup implementation.
+ */
+ luks->header.key_slots[i].key_offset =
+ (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
+ (ROUND_UP(((splitkeylen + (QCRYPTO_BLOCK_LUKS_SECTOR_SIZE - 1)) /
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
+ (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
+ }
+
+ if (qcrypto_random_bytes(luks->header.key_slots[0].salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ errp) < 0) {
+ goto error;
+ }
+
+ /* Again we determine how many iterations are required to
+ * hash the user password while consuming 1 second of compute
+ * time */
+ luks->header.key_slots[0].iterations =
+ qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
+ (uint8_t *)password, strlen(password),
+ luks->header.key_slots[0].salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto error;
+ }
+ /* Why /= 2 ? That matches cryptsetup, but there's no
+ * explanation why they chose /= 2... */
+ luks->header.key_slots[0].iterations /= 2;
+ luks->header.key_slots[0].iterations = MAX(
+ luks->header.key_slots[0].iterations,
+ QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
+
+
+ /* Generate a key that we'll use to encrypt the master
+ * key, from the user's password
+ */
+ slotkey = g_new0(uint8_t, luks->header.key_bytes);
+ if (qcrypto_pbkdf2(luks_opts.hash_alg,
+ (uint8_t *)password, strlen(password),
+ luks->header.key_slots[0].salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ luks->header.key_slots[0].iterations,
+ slotkey, luks->header.key_bytes,
+ errp) < 0) {
+ goto error;
+ }
+
+
+ /* Setup the encryption objects needed to encrypt the
+ * master key material
+ */
+ cipher = qcrypto_cipher_new(luks_opts.cipher_alg,
+ luks_opts.cipher_mode,
+ slotkey, luks->header.key_bytes,
+ errp);
+ if (!cipher) {
+ goto error;
+ }
+
+ ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg,
+ ivcipheralg,
+ luks_opts.ivgen_hash_alg,
+ slotkey, luks->header.key_bytes,
+ errp);
+ if (!ivgen) {
+ goto error;
+ }
+
+ /* Before storing the master key, we need to vastly
+ * increase its size, as protection against forensic
+ * disk data recovery */
+ splitkey = g_new0(uint8_t, splitkeylen);
+
+ if (qcrypto_afsplit_encode(luks_opts.hash_alg,
+ luks->header.key_bytes,
+ luks->header.key_slots[0].stripes,
+ masterkey,
+ splitkey,
+ errp) < 0) {
+ goto error;
+ }
+
+ /* Now we encrypt the split master key with the key generated
+ * from the user's password, before storing it */
+ if (qcrypto_block_encrypt_helper(cipher, block->niv, ivgen,
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ 0,
+ splitkey,
+ splitkeylen,
+ errp) < 0) {
+ goto error;
+ }
+
+
+ /* The total size of the LUKS headers is the partition header + key
+ * slot headers, rounded up to the nearest sector, combined with
+ * the size of each master key material region, also rounded up
+ * to the nearest sector */
+ luks->header.payload_offset =
+ (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
+ (ROUND_UP(((splitkeylen + (QCRYPTO_BLOCK_LUKS_SECTOR_SIZE - 1)) /
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
+ (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) *
+ QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+
+ block->payload_offset = luks->header.payload_offset *
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+
+ /* Reserve header space to match payload offset */
+ initfunc(block, block->payload_offset, &local_err, opaque);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto error;
+ }
+
+ /* Everything on disk uses Big Endian, so flip header fields
+ * before writing them */
+ cpu_to_be16s(&luks->header.version);
+ cpu_to_be32s(&luks->header.payload_offset);
+ cpu_to_be32s(&luks->header.key_bytes);
+ cpu_to_be32s(&luks->header.master_key_iterations);
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ cpu_to_be32s(&luks->header.key_slots[i].active);
+ cpu_to_be32s(&luks->header.key_slots[i].iterations);
+ cpu_to_be32s(&luks->header.key_slots[i].key_offset);
+ cpu_to_be32s(&luks->header.key_slots[i].stripes);
+ }
+
+
+ /* Write out the partition header and key slot headers */
+ writefunc(block, 0,
+ (const uint8_t *)&luks->header,
+ sizeof(luks->header),
+ &local_err,
+ opaque);
+
+ /* Delay checking local_err until we've byte-swapped */
+
+ /* Byte swap the header back to native, in case we need
+ * to read it again later */
+ be16_to_cpus(&luks->header.version);
+ be32_to_cpus(&luks->header.payload_offset);
+ be32_to_cpus(&luks->header.key_bytes);
+ be32_to_cpus(&luks->header.master_key_iterations);
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ be32_to_cpus(&luks->header.key_slots[i].active);
+ be32_to_cpus(&luks->header.key_slots[i].iterations);
+ be32_to_cpus(&luks->header.key_slots[i].key_offset);
+ be32_to_cpus(&luks->header.key_slots[i].stripes);
+ }
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto error;
+ }
+
+ /* Write out the master key material, starting at the
+ * sector immediately following the partition header. */
+ if (writefunc(block,
+ luks->header.key_slots[0].key_offset *
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ splitkey, splitkeylen,
+ errp,
+ opaque) != splitkeylen) {
+ goto error;
+ }
+
+ memset(masterkey, 0, luks->header.key_bytes);
+ g_free(masterkey);
+ memset(slotkey, 0, luks->header.key_bytes);
+ g_free(slotkey);
+ g_free(splitkey);
+ g_free(password);
+ g_free(cipher_mode_spec);
+
+ qcrypto_ivgen_free(ivgen);
+ qcrypto_cipher_free(cipher);
+
+ return 0;
+
+ error:
+ if (masterkey) {
+ memset(masterkey, 0, luks->header.key_bytes);
+ }
+ g_free(masterkey);
+ if (slotkey) {
+ memset(slotkey, 0, luks->header.key_bytes);
+ }
+ g_free(slotkey);
+ g_free(splitkey);
+ g_free(password);
+ g_free(cipher_mode_spec);
+
+ qcrypto_ivgen_free(ivgen);
+ qcrypto_cipher_free(cipher);
+
+ g_free(luks);
+ return -1;
+}
+
+
+static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
+{
+ g_free(block->opaque);
+}
+
+
+static int
+qcrypto_block_luks_decrypt(QCryptoBlock *block,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp)
+{
+ return qcrypto_block_decrypt_helper(block->cipher,
+ block->niv, block->ivgen,
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ startsector, buf, len, errp);
+}
+
+
+static int
+qcrypto_block_luks_encrypt(QCryptoBlock *block,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp)
+{
+ return qcrypto_block_encrypt_helper(block->cipher,
+ block->niv, block->ivgen,
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ startsector, buf, len, errp);
+}
+
+
+const QCryptoBlockDriver qcrypto_block_driver_luks = {
+ .open = qcrypto_block_luks_open,
+ .create = qcrypto_block_luks_create,
+ .cleanup = qcrypto_block_luks_cleanup,
+ .decrypt = qcrypto_block_luks_decrypt,
+ .encrypt = qcrypto_block_luks_encrypt,
+ .has_format = qcrypto_block_luks_has_format,
+};
diff --git a/qemu/crypto/block-luks.h b/qemu/crypto/block-luks.h
new file mode 100644
index 000000000..0934138aa
--- /dev/null
+++ b/qemu/crypto/block-luks.h
@@ -0,0 +1,28 @@
+/*
+ * QEMU Crypto block device encryption LUKS format
+ *
+ * Copyright (c) 2015-2016 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_BLOCK_LUKS_H__
+#define QCRYPTO_BLOCK_LUKS_H__
+
+#include "crypto/blockpriv.h"
+
+extern const QCryptoBlockDriver qcrypto_block_driver_luks;
+
+#endif /* QCRYPTO_BLOCK_LUKS_H__ */
diff --git a/qemu/crypto/block-qcow.c b/qemu/crypto/block-qcow.c
new file mode 100644
index 000000000..be88c6f0e
--- /dev/null
+++ b/qemu/crypto/block-qcow.c
@@ -0,0 +1,174 @@
+/*
+ * QEMU Crypto block device encryption QCow/QCow2 AES-CBC format
+ *
+ * Copyright (c) 2015-2016 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/>.
+ *
+ */
+
+/*
+ * Note that the block encryption implemented in this file is broken
+ * by design. This exists only to allow data to be liberated from
+ * existing qcow[2] images and should not be used in any new areas.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+
+#include "crypto/block-qcow.h"
+#include "crypto/secret.h"
+
+#define QCRYPTO_BLOCK_QCOW_SECTOR_SIZE 512
+
+
+static bool
+qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED,
+ size_t buf_size G_GNUC_UNUSED)
+{
+ return false;
+}
+
+
+static int
+qcrypto_block_qcow_init(QCryptoBlock *block,
+ const char *keysecret,
+ Error **errp)
+{
+ char *password;
+ int ret;
+ uint8_t keybuf[16];
+ int len;
+
+ memset(keybuf, 0, 16);
+
+ password = qcrypto_secret_lookup_as_utf8(keysecret, errp);
+ if (!password) {
+ return -1;
+ }
+
+ len = strlen(password);
+ memcpy(keybuf, password, MIN(len, sizeof(keybuf)));
+ g_free(password);
+
+ block->niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128,
+ QCRYPTO_CIPHER_MODE_CBC);
+ block->ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_PLAIN64,
+ 0, 0, NULL, 0, errp);
+ if (!block->ivgen) {
+ ret = -ENOTSUP;
+ goto fail;
+ }
+
+ block->cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128,
+ QCRYPTO_CIPHER_MODE_CBC,
+ keybuf, G_N_ELEMENTS(keybuf),
+ errp);
+ if (!block->cipher) {
+ ret = -ENOTSUP;
+ goto fail;
+ }
+
+ block->payload_offset = 0;
+
+ return 0;
+
+ fail:
+ qcrypto_cipher_free(block->cipher);
+ qcrypto_ivgen_free(block->ivgen);
+ return ret;
+}
+
+
+static int
+qcrypto_block_qcow_open(QCryptoBlock *block,
+ QCryptoBlockOpenOptions *options,
+ QCryptoBlockReadFunc readfunc G_GNUC_UNUSED,
+ void *opaque G_GNUC_UNUSED,
+ unsigned int flags,
+ Error **errp)
+{
+ if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) {
+ return 0;
+ } else {
+ if (!options->u.qcow.key_secret) {
+ error_setg(errp,
+ "Parameter 'key-secret' is required for cipher");
+ return -1;
+ }
+ return qcrypto_block_qcow_init(block,
+ options->u.qcow.key_secret, errp);
+ }
+}
+
+
+static int
+qcrypto_block_qcow_create(QCryptoBlock *block,
+ QCryptoBlockCreateOptions *options,
+ QCryptoBlockInitFunc initfunc G_GNUC_UNUSED,
+ QCryptoBlockWriteFunc writefunc G_GNUC_UNUSED,
+ void *opaque G_GNUC_UNUSED,
+ Error **errp)
+{
+ if (!options->u.qcow.key_secret) {
+ error_setg(errp, "Parameter 'key-secret' is required for cipher");
+ return -1;
+ }
+ /* QCow2 has no special header, since everything is hardwired */
+ return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp);
+}
+
+
+static void
+qcrypto_block_qcow_cleanup(QCryptoBlock *block)
+{
+}
+
+
+static int
+qcrypto_block_qcow_decrypt(QCryptoBlock *block,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp)
+{
+ return qcrypto_block_decrypt_helper(block->cipher,
+ block->niv, block->ivgen,
+ QCRYPTO_BLOCK_QCOW_SECTOR_SIZE,
+ startsector, buf, len, errp);
+}
+
+
+static int
+qcrypto_block_qcow_encrypt(QCryptoBlock *block,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp)
+{
+ return qcrypto_block_encrypt_helper(block->cipher,
+ block->niv, block->ivgen,
+ QCRYPTO_BLOCK_QCOW_SECTOR_SIZE,
+ startsector, buf, len, errp);
+}
+
+
+const QCryptoBlockDriver qcrypto_block_driver_qcow = {
+ .open = qcrypto_block_qcow_open,
+ .create = qcrypto_block_qcow_create,
+ .cleanup = qcrypto_block_qcow_cleanup,
+ .decrypt = qcrypto_block_qcow_decrypt,
+ .encrypt = qcrypto_block_qcow_encrypt,
+ .has_format = qcrypto_block_qcow_has_format,
+};
diff --git a/qemu/crypto/block-qcow.h b/qemu/crypto/block-qcow.h
new file mode 100644
index 000000000..569f83610
--- /dev/null
+++ b/qemu/crypto/block-qcow.h
@@ -0,0 +1,28 @@
+/*
+ * QEMU Crypto block device encryption QCow/QCow2 AES-CBC format
+ *
+ * Copyright (c) 2015-2016 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_BLOCK_QCOW_H__
+#define QCRYPTO_BLOCK_QCOW_H__
+
+#include "crypto/blockpriv.h"
+
+extern const QCryptoBlockDriver qcrypto_block_driver_qcow;
+
+#endif /* QCRYPTO_BLOCK_QCOW_H__ */
diff --git a/qemu/crypto/block.c b/qemu/crypto/block.c
new file mode 100644
index 000000000..da60eba85
--- /dev/null
+++ b/qemu/crypto/block.c
@@ -0,0 +1,261 @@
+/*
+ * QEMU Crypto block device encryption
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/blockpriv.h"
+#include "crypto/block-qcow.h"
+#include "crypto/block-luks.h"
+
+static const QCryptoBlockDriver *qcrypto_block_drivers[] = {
+ [Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow,
+ [Q_CRYPTO_BLOCK_FORMAT_LUKS] = &qcrypto_block_driver_luks,
+};
+
+
+bool qcrypto_block_has_format(QCryptoBlockFormat format,
+ const uint8_t *buf,
+ size_t len)
+{
+ const QCryptoBlockDriver *driver;
+
+ if (format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
+ !qcrypto_block_drivers[format]) {
+ return false;
+ }
+
+ driver = qcrypto_block_drivers[format];
+
+ return driver->has_format(buf, len);
+}
+
+
+QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options,
+ QCryptoBlockReadFunc readfunc,
+ void *opaque,
+ unsigned int flags,
+ Error **errp)
+{
+ QCryptoBlock *block = g_new0(QCryptoBlock, 1);
+
+ block->format = options->format;
+
+ if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
+ !qcrypto_block_drivers[options->format]) {
+ error_setg(errp, "Unsupported block driver %d", options->format);
+ g_free(block);
+ return NULL;
+ }
+
+ block->driver = qcrypto_block_drivers[options->format];
+
+ if (block->driver->open(block, options,
+ readfunc, opaque, flags, errp) < 0) {
+ g_free(block);
+ return NULL;
+ }
+
+ return block;
+}
+
+
+QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
+ QCryptoBlockInitFunc initfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp)
+{
+ QCryptoBlock *block = g_new0(QCryptoBlock, 1);
+
+ block->format = options->format;
+
+ if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
+ !qcrypto_block_drivers[options->format]) {
+ error_setg(errp, "Unsupported block driver %d", options->format);
+ g_free(block);
+ return NULL;
+ }
+
+ block->driver = qcrypto_block_drivers[options->format];
+
+ if (block->driver->create(block, options, initfunc,
+ writefunc, opaque, errp) < 0) {
+ g_free(block);
+ return NULL;
+ }
+
+ return block;
+}
+
+
+int qcrypto_block_decrypt(QCryptoBlock *block,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp)
+{
+ return block->driver->decrypt(block, startsector, buf, len, errp);
+}
+
+
+int qcrypto_block_encrypt(QCryptoBlock *block,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp)
+{
+ return block->driver->encrypt(block, startsector, buf, len, errp);
+}
+
+
+QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block)
+{
+ return block->cipher;
+}
+
+
+QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block)
+{
+ return block->ivgen;
+}
+
+
+QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block)
+{
+ return block->kdfhash;
+}
+
+
+uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block)
+{
+ return block->payload_offset;
+}
+
+
+void qcrypto_block_free(QCryptoBlock *block)
+{
+ if (!block) {
+ return;
+ }
+
+ block->driver->cleanup(block);
+
+ qcrypto_cipher_free(block->cipher);
+ qcrypto_ivgen_free(block->ivgen);
+ g_free(block);
+}
+
+
+int qcrypto_block_decrypt_helper(QCryptoCipher *cipher,
+ size_t niv,
+ QCryptoIVGen *ivgen,
+ int sectorsize,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp)
+{
+ uint8_t *iv;
+ int ret = -1;
+
+ iv = niv ? g_new0(uint8_t, niv) : NULL;
+
+ while (len > 0) {
+ size_t nbytes;
+ if (niv) {
+ if (qcrypto_ivgen_calculate(ivgen,
+ startsector,
+ iv, niv,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ if (qcrypto_cipher_setiv(cipher,
+ iv, niv,
+ errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ nbytes = len > sectorsize ? sectorsize : len;
+ if (qcrypto_cipher_decrypt(cipher, buf, buf,
+ nbytes, errp) < 0) {
+ goto cleanup;
+ }
+
+ startsector++;
+ buf += nbytes;
+ len -= nbytes;
+ }
+
+ ret = 0;
+ cleanup:
+ g_free(iv);
+ return ret;
+}
+
+
+int qcrypto_block_encrypt_helper(QCryptoCipher *cipher,
+ size_t niv,
+ QCryptoIVGen *ivgen,
+ int sectorsize,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp)
+{
+ uint8_t *iv;
+ int ret = -1;
+
+ iv = niv ? g_new0(uint8_t, niv) : NULL;
+
+ while (len > 0) {
+ size_t nbytes;
+ if (niv) {
+ if (qcrypto_ivgen_calculate(ivgen,
+ startsector,
+ iv, niv,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ if (qcrypto_cipher_setiv(cipher,
+ iv, niv,
+ errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ nbytes = len > sectorsize ? sectorsize : len;
+ if (qcrypto_cipher_encrypt(cipher, buf, buf,
+ nbytes, errp) < 0) {
+ goto cleanup;
+ }
+
+ startsector++;
+ buf += nbytes;
+ len -= nbytes;
+ }
+
+ ret = 0;
+ cleanup:
+ g_free(iv);
+ return ret;
+}
diff --git a/qemu/crypto/blockpriv.h b/qemu/crypto/blockpriv.h
new file mode 100644
index 000000000..62970859d
--- /dev/null
+++ b/qemu/crypto/blockpriv.h
@@ -0,0 +1,92 @@
+/*
+ * QEMU Crypto block device encryption
+ *
+ * Copyright (c) 2015-2016 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_BLOCK_PRIV_H__
+#define QCRYPTO_BLOCK_PRIV_H__
+
+#include "crypto/block.h"
+
+typedef struct QCryptoBlockDriver QCryptoBlockDriver;
+
+struct QCryptoBlock {
+ QCryptoBlockFormat format;
+
+ const QCryptoBlockDriver *driver;
+ void *opaque;
+
+ QCryptoCipher *cipher;
+ QCryptoIVGen *ivgen;
+ QCryptoHashAlgorithm kdfhash;
+ size_t niv;
+ uint64_t payload_offset; /* In bytes */
+};
+
+struct QCryptoBlockDriver {
+ int (*open)(QCryptoBlock *block,
+ QCryptoBlockOpenOptions *options,
+ QCryptoBlockReadFunc readfunc,
+ void *opaque,
+ unsigned int flags,
+ Error **errp);
+
+ int (*create)(QCryptoBlock *block,
+ QCryptoBlockCreateOptions *options,
+ QCryptoBlockInitFunc initfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp);
+
+ void (*cleanup)(QCryptoBlock *block);
+
+ int (*encrypt)(QCryptoBlock *block,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp);
+ int (*decrypt)(QCryptoBlock *block,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp);
+
+ bool (*has_format)(const uint8_t *buf,
+ size_t buflen);
+};
+
+
+int qcrypto_block_decrypt_helper(QCryptoCipher *cipher,
+ size_t niv,
+ QCryptoIVGen *ivgen,
+ int sectorsize,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp);
+
+int qcrypto_block_encrypt_helper(QCryptoCipher *cipher,
+ size_t niv,
+ QCryptoIVGen *ivgen,
+ int sectorsize,
+ uint64_t startsector,
+ uint8_t *buf,
+ size_t len,
+ Error **errp);
+
+#endif /* QCRYPTO_BLOCK_PRIV_H__ */
diff --git a/qemu/crypto/cipher-builtin.c b/qemu/crypto/cipher-builtin.c
index 30f4853c8..88963f65c 100644
--- a/qemu/crypto/cipher-builtin.c
+++ b/qemu/crypto/cipher-builtin.c
@@ -18,15 +18,21 @@
*
*/
+#include "qemu/osdep.h"
#include "crypto/aes.h"
#include "crypto/desrfb.h"
+#include "crypto/xts.h"
+typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext;
+struct QCryptoCipherBuiltinAESContext {
+ AES_KEY enc;
+ AES_KEY dec;
+};
typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
struct QCryptoCipherBuiltinAES {
- AES_KEY encrypt_key;
- AES_KEY decrypt_key;
- uint8_t *iv;
- size_t niv;
+ QCryptoCipherBuiltinAESContext key;
+ QCryptoCipherBuiltinAESContext key_tweak;
+ uint8_t iv[AES_BLOCK_SIZE];
};
typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB;
struct QCryptoCipherBuiltinDESRFB {
@@ -40,6 +46,7 @@ struct QCryptoCipherBuiltin {
QCryptoCipherBuiltinAES aes;
QCryptoCipherBuiltinDESRFB desrfb;
} state;
+ size_t blocksize;
void (*free)(QCryptoCipher *cipher);
int (*setiv)(QCryptoCipher *cipher,
const uint8_t *iv, size_t niv,
@@ -61,12 +68,87 @@ 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 void qcrypto_cipher_aes_ecb_encrypt(AES_KEY *key,
+ const void *in,
+ void *out,
+ size_t len)
+{
+ const uint8_t *inptr = in;
+ uint8_t *outptr = out;
+ while (len) {
+ if (len > AES_BLOCK_SIZE) {
+ AES_encrypt(inptr, outptr, 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, key);
+ memcpy(outptr, tmp2, len);
+ len = 0;
+ }
+ }
+}
+
+
+static void qcrypto_cipher_aes_ecb_decrypt(AES_KEY *key,
+ const void *in,
+ void *out,
+ size_t len)
+{
+ const uint8_t *inptr = in;
+ uint8_t *outptr = out;
+ while (len) {
+ if (len > AES_BLOCK_SIZE) {
+ AES_decrypt(inptr, outptr, 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, key);
+ memcpy(outptr, tmp2, len);
+ len = 0;
+ }
+ }
+}
+
+
+static void qcrypto_cipher_aes_xts_encrypt(const void *ctx,
+ size_t length,
+ uint8_t *dst,
+ const uint8_t *src)
+{
+ const QCryptoCipherBuiltinAESContext *aesctx = ctx;
+
+ qcrypto_cipher_aes_ecb_encrypt((AES_KEY *)&aesctx->enc,
+ src, dst, length);
+}
+
+
+static void qcrypto_cipher_aes_xts_decrypt(const void *ctx,
+ size_t length,
+ uint8_t *dst,
+ const uint8_t *src)
+{
+ const QCryptoCipherBuiltinAESContext *aesctx = ctx;
+
+ qcrypto_cipher_aes_ecb_decrypt((AES_KEY *)&aesctx->dec,
+ src, dst, length);
+}
+
+
static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher,
const void *in,
void *out,
@@ -75,29 +157,26 @@ static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher,
{
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 {
+ switch (cipher->mode) {
+ case QCRYPTO_CIPHER_MODE_ECB:
+ qcrypto_cipher_aes_ecb_encrypt(&ctxt->state.aes.key.enc,
+ in, out, len);
+ break;
+ case QCRYPTO_CIPHER_MODE_CBC:
AES_cbc_encrypt(in, out, len,
- &ctxt->state.aes.encrypt_key,
+ &ctxt->state.aes.key.enc,
ctxt->state.aes.iv, 1);
+ break;
+ case QCRYPTO_CIPHER_MODE_XTS:
+ xts_encrypt(&ctxt->state.aes.key,
+ &ctxt->state.aes.key_tweak,
+ qcrypto_cipher_aes_xts_encrypt,
+ qcrypto_cipher_aes_xts_decrypt,
+ ctxt->state.aes.iv,
+ len, out, in);
+ break;
+ default:
+ g_assert_not_reached();
}
return 0;
@@ -112,29 +191,26 @@ static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher,
{
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.decrypt_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.decrypt_key);
- memcpy(outptr, tmp2, len);
- len = 0;
- }
- }
- } else {
+ switch (cipher->mode) {
+ case QCRYPTO_CIPHER_MODE_ECB:
+ qcrypto_cipher_aes_ecb_decrypt(&ctxt->state.aes.key.dec,
+ in, out, len);
+ break;
+ case QCRYPTO_CIPHER_MODE_CBC:
AES_cbc_encrypt(in, out, len,
- &ctxt->state.aes.decrypt_key,
+ &ctxt->state.aes.key.dec,
ctxt->state.aes.iv, 0);
+ break;
+ case QCRYPTO_CIPHER_MODE_XTS:
+ xts_decrypt(&ctxt->state.aes.key,
+ &ctxt->state.aes.key_tweak,
+ qcrypto_cipher_aes_xts_encrypt,
+ qcrypto_cipher_aes_xts_decrypt,
+ ctxt->state.aes.iv,
+ len, out, in);
+ break;
+ default:
+ g_assert_not_reached();
}
return 0;
@@ -145,15 +221,13 @@ static int qcrypto_cipher_setiv_aes(QCryptoCipher *cipher,
Error **errp)
{
QCryptoCipherBuiltin *ctxt = cipher->opaque;
- if (niv != 16) {
- error_setg(errp, "IV must be 16 bytes not %zu", niv);
+ if (niv != AES_BLOCK_SIZE) {
+ error_setg(errp, "IV must be %d bytes not %zu",
+ AES_BLOCK_SIZE, 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;
+ memcpy(ctxt->state.aes.iv, iv, AES_BLOCK_SIZE);
return 0;
}
@@ -168,23 +242,49 @@ static int qcrypto_cipher_init_aes(QCryptoCipher *cipher,
QCryptoCipherBuiltin *ctxt;
if (cipher->mode != QCRYPTO_CIPHER_MODE_CBC &&
- cipher->mode != QCRYPTO_CIPHER_MODE_ECB) {
+ cipher->mode != QCRYPTO_CIPHER_MODE_ECB &&
+ cipher->mode != QCRYPTO_CIPHER_MODE_XTS) {
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 (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
+ if (AES_set_encrypt_key(key, nkey * 4, &ctxt->state.aes.key.enc) != 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;
+ if (AES_set_decrypt_key(key, nkey * 4, &ctxt->state.aes.key.dec) != 0) {
+ error_setg(errp, "Failed to set decryption key");
+ goto error;
+ }
+
+ if (AES_set_encrypt_key(key + (nkey / 2), nkey * 4,
+ &ctxt->state.aes.key_tweak.enc) != 0) {
+ error_setg(errp, "Failed to set encryption key");
+ goto error;
+ }
+
+ if (AES_set_decrypt_key(key + (nkey / 2), nkey * 4,
+ &ctxt->state.aes.key_tweak.dec) != 0) {
+ error_setg(errp, "Failed to set decryption key");
+ goto error;
+ }
+ } else {
+ if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.key.enc) != 0) {
+ error_setg(errp, "Failed to set encryption key");
+ goto error;
+ }
+
+ if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.key.dec) != 0) {
+ error_setg(errp, "Failed to set decryption key");
+ goto error;
+ }
}
+ ctxt->blocksize = AES_BLOCK_SIZE;
ctxt->free = qcrypto_cipher_free_aes;
ctxt->setiv = qcrypto_cipher_setiv_aes;
ctxt->encrypt = qcrypto_cipher_encrypt_aes;
@@ -286,6 +386,7 @@ static int qcrypto_cipher_init_des_rfb(QCryptoCipher *cipher,
memcpy(ctxt->state.desrfb.key, key, nkey);
ctxt->state.desrfb.nkey = nkey;
+ ctxt->blocksize = 8;
ctxt->free = qcrypto_cipher_free_des_rfb;
ctxt->setiv = qcrypto_cipher_setiv_des_rfb;
ctxt->encrypt = qcrypto_cipher_encrypt_des_rfb;
@@ -322,7 +423,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
cipher->alg = alg;
cipher->mode = mode;
- if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
+ if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
goto error;
}
@@ -374,6 +475,12 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
{
QCryptoCipherBuiltin *ctxt = cipher->opaque;
+ if (len % ctxt->blocksize) {
+ error_setg(errp, "Length %zu must be a multiple of block size %zu",
+ len, ctxt->blocksize);
+ return -1;
+ }
+
return ctxt->encrypt(cipher, in, out, len, errp);
}
@@ -386,6 +493,12 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
{
QCryptoCipherBuiltin *ctxt = cipher->opaque;
+ if (len % ctxt->blocksize) {
+ error_setg(errp, "Length %zu must be a multiple of block size %zu",
+ len, ctxt->blocksize);
+ return -1;
+ }
+
return ctxt->decrypt(cipher, in, out, len, errp);
}
diff --git a/qemu/crypto/cipher-gcrypt.c b/qemu/crypto/cipher-gcrypt.c
index 8cfc56250..ede2f70df 100644
--- a/qemu/crypto/cipher-gcrypt.c
+++ b/qemu/crypto/cipher-gcrypt.c
@@ -18,6 +18,9 @@
*
*/
+#include "qemu/osdep.h"
+#include "crypto/xts.h"
+
#include <gcrypt.h>
@@ -28,12 +31,25 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
case QCRYPTO_CIPHER_ALG_AES_128:
case QCRYPTO_CIPHER_ALG_AES_192:
case QCRYPTO_CIPHER_ALG_AES_256:
+ case QCRYPTO_CIPHER_ALG_CAST5_128:
+ case QCRYPTO_CIPHER_ALG_SERPENT_128:
+ case QCRYPTO_CIPHER_ALG_SERPENT_192:
+ case QCRYPTO_CIPHER_ALG_SERPENT_256:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_128:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_256:
return true;
default:
return false;
}
}
+typedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt;
+struct QCryptoCipherGcrypt {
+ gcry_cipher_hd_t handle;
+ gcry_cipher_hd_t tweakhandle;
+ size_t blocksize;
+ uint8_t *iv;
+};
QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
QCryptoCipherMode mode,
@@ -41,12 +57,13 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
Error **errp)
{
QCryptoCipher *cipher;
- gcry_cipher_hd_t handle;
+ QCryptoCipherGcrypt *ctx;
gcry_error_t err;
int gcryalg, gcrymode;
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
+ case QCRYPTO_CIPHER_MODE_XTS:
gcrymode = GCRY_CIPHER_MODE_ECB;
break;
case QCRYPTO_CIPHER_MODE_CBC:
@@ -57,7 +74,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
return NULL;
}
- if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
+ if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
return NULL;
}
@@ -78,6 +95,30 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
gcryalg = GCRY_CIPHER_AES256;
break;
+ case QCRYPTO_CIPHER_ALG_CAST5_128:
+ gcryalg = GCRY_CIPHER_CAST5;
+ break;
+
+ case QCRYPTO_CIPHER_ALG_SERPENT_128:
+ gcryalg = GCRY_CIPHER_SERPENT128;
+ break;
+
+ case QCRYPTO_CIPHER_ALG_SERPENT_192:
+ gcryalg = GCRY_CIPHER_SERPENT192;
+ break;
+
+ case QCRYPTO_CIPHER_ALG_SERPENT_256:
+ gcryalg = GCRY_CIPHER_SERPENT256;
+ break;
+
+ case QCRYPTO_CIPHER_ALG_TWOFISH_128:
+ gcryalg = GCRY_CIPHER_TWOFISH128;
+ break;
+
+ case QCRYPTO_CIPHER_ALG_TWOFISH_256:
+ gcryalg = GCRY_CIPHER_TWOFISH;
+ break;
+
default:
error_setg(errp, "Unsupported cipher algorithm %d", alg);
return NULL;
@@ -87,12 +128,22 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
cipher->alg = alg;
cipher->mode = mode;
- err = gcry_cipher_open(&handle, gcryalg, gcrymode, 0);
+ ctx = g_new0(QCryptoCipherGcrypt, 1);
+
+ err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0);
if (err != 0) {
error_setg(errp, "Cannot initialize cipher: %s",
gcry_strerror(err));
goto error;
}
+ if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
+ err = gcry_cipher_open(&ctx->tweakhandle, 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
@@ -100,22 +151,59 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
* bizarre RFB variant of DES :-)
*/
uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
- err = gcry_cipher_setkey(handle, rfbkey, nkey);
+ err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey);
g_free(rfbkey);
+ ctx->blocksize = 8;
} else {
- err = gcry_cipher_setkey(handle, key, nkey);
+ if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
+ nkey /= 2;
+ err = gcry_cipher_setkey(ctx->handle, key, nkey);
+ if (err != 0) {
+ error_setg(errp, "Cannot set key: %s",
+ gcry_strerror(err));
+ goto error;
+ }
+ err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
+ } else {
+ err = gcry_cipher_setkey(ctx->handle, key, nkey);
+ }
+ if (err != 0) {
+ error_setg(errp, "Cannot set key: %s",
+ gcry_strerror(err));
+ goto error;
+ }
+ switch (cipher->alg) {
+ case QCRYPTO_CIPHER_ALG_AES_128:
+ case QCRYPTO_CIPHER_ALG_AES_192:
+ case QCRYPTO_CIPHER_ALG_AES_256:
+ case QCRYPTO_CIPHER_ALG_SERPENT_128:
+ case QCRYPTO_CIPHER_ALG_SERPENT_192:
+ case QCRYPTO_CIPHER_ALG_SERPENT_256:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_128:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_256:
+ ctx->blocksize = 16;
+ break;
+ case QCRYPTO_CIPHER_ALG_CAST5_128:
+ ctx->blocksize = 8;
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
- if (err != 0) {
- error_setg(errp, "Cannot set key: %s",
- gcry_strerror(err));
- goto error;
+
+ if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
+ ctx->iv = g_new0(uint8_t, ctx->blocksize);
}
- cipher->opaque = handle;
+ cipher->opaque = ctx;
return cipher;
error:
- gcry_cipher_close(handle);
+ gcry_cipher_close(ctx->handle);
+ if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
+ gcry_cipher_close(ctx->tweakhandle);
+ }
+ g_free(ctx);
g_free(cipher);
return NULL;
}
@@ -123,34 +211,72 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
void qcrypto_cipher_free(QCryptoCipher *cipher)
{
- gcry_cipher_hd_t handle;
+ QCryptoCipherGcrypt *ctx;
if (!cipher) {
return;
}
- handle = cipher->opaque;
- gcry_cipher_close(handle);
+ ctx = cipher->opaque;
+ gcry_cipher_close(ctx->handle);
+ if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
+ gcry_cipher_close(ctx->tweakhandle);
+ }
+ g_free(ctx->iv);
+ g_free(ctx);
g_free(cipher);
}
+static void qcrypto_gcrypt_xts_encrypt(const void *ctx,
+ size_t length,
+ uint8_t *dst,
+ const uint8_t *src)
+{
+ gcry_error_t err;
+ err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
+ g_assert(err == 0);
+}
+
+static void qcrypto_gcrypt_xts_decrypt(const void *ctx,
+ size_t length,
+ uint8_t *dst,
+ const uint8_t *src)
+{
+ gcry_error_t err;
+ err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
+ g_assert(err == 0);
+}
+
int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
const void *in,
void *out,
size_t len,
Error **errp)
{
- gcry_cipher_hd_t handle = cipher->opaque;
+ QCryptoCipherGcrypt *ctx = 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));
+ if (len % ctx->blocksize) {
+ error_setg(errp, "Length %zu must be a multiple of block size %zu",
+ len, ctx->blocksize);
return -1;
}
+ if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
+ xts_encrypt(ctx->handle, ctx->tweakhandle,
+ qcrypto_gcrypt_xts_encrypt,
+ qcrypto_gcrypt_xts_decrypt,
+ ctx->iv, len, out, in);
+ } else {
+ err = gcry_cipher_encrypt(ctx->handle,
+ out, len,
+ in, len);
+ if (err != 0) {
+ error_setg(errp, "Cannot encrypt data: %s",
+ gcry_strerror(err));
+ return -1;
+ }
+ }
+
return 0;
}
@@ -161,18 +287,31 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
size_t len,
Error **errp)
{
- gcry_cipher_hd_t handle = cipher->opaque;
+ QCryptoCipherGcrypt *ctx = 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));
+ if (len % ctx->blocksize) {
+ error_setg(errp, "Length %zu must be a multiple of block size %zu",
+ len, ctx->blocksize);
return -1;
}
+ if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
+ xts_decrypt(ctx->handle, ctx->tweakhandle,
+ qcrypto_gcrypt_xts_encrypt,
+ qcrypto_gcrypt_xts_decrypt,
+ ctx->iv, len, out, in);
+ } else {
+ err = gcry_cipher_decrypt(ctx->handle,
+ out, len,
+ in, len);
+ if (err != 0) {
+ error_setg(errp, "Cannot decrypt data: %s",
+ gcry_strerror(err));
+ return -1;
+ }
+ }
+
return 0;
}
@@ -180,16 +319,26 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher,
const uint8_t *iv, size_t niv,
Error **errp)
{
- gcry_cipher_hd_t handle = cipher->opaque;
+ QCryptoCipherGcrypt *ctx = 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));
+ if (niv != ctx->blocksize) {
+ error_setg(errp, "Expected IV size %zu not %zu",
+ ctx->blocksize, niv);
return -1;
}
+ if (ctx->iv) {
+ memcpy(ctx->iv, iv, niv);
+ } else {
+ gcry_cipher_reset(ctx->handle);
+ err = gcry_cipher_setiv(ctx->handle, iv, niv);
+ if (err != 0) {
+ error_setg(errp, "Cannot set IV: %s",
+ gcry_strerror(err));
+ return -1;
+ }
+ }
+
return 0;
}
diff --git a/qemu/crypto/cipher-nettle.c b/qemu/crypto/cipher-nettle.c
index b01cb1c85..70909fb7f 100644
--- a/qemu/crypto/cipher-nettle.c
+++ b/qemu/crypto/cipher-nettle.c
@@ -18,58 +18,177 @@
*
*/
+#include "qemu/osdep.h"
+#include "crypto/xts.h"
+
#include <nettle/nettle-types.h>
#include <nettle/aes.h>
#include <nettle/des.h>
#include <nettle/cbc.h>
+#include <nettle/cast128.h>
+#include <nettle/serpent.h>
+#include <nettle/twofish.h>
-#if CONFIG_NETTLE_VERSION_MAJOR < 3
-typedef nettle_crypt_func nettle_cipher_func;
+typedef void (*QCryptoCipherNettleFuncWrapper)(const void *ctx,
+ size_t length,
+ uint8_t *dst,
+ const uint8_t *src);
+#if CONFIG_NETTLE_VERSION_MAJOR < 3
+typedef nettle_crypt_func * QCryptoCipherNettleFuncNative;
typedef void * cipher_ctx_t;
typedef unsigned cipher_length_t;
+
+#define cast5_set_key cast128_set_key
#else
+typedef nettle_cipher_func * QCryptoCipherNettleFuncNative;
typedef const void * cipher_ctx_t;
typedef size_t cipher_length_t;
#endif
-static nettle_cipher_func aes_encrypt_wrapper;
-static nettle_cipher_func aes_decrypt_wrapper;
-static nettle_cipher_func des_encrypt_wrapper;
-static nettle_cipher_func des_decrypt_wrapper;
+typedef struct QCryptoNettleAES {
+ struct aes_ctx enc;
+ struct aes_ctx dec;
+} QCryptoNettleAES;
+
+static void aes_encrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ const QCryptoNettleAES *aesctx = ctx;
+ aes_encrypt(&aesctx->enc, length, dst, src);
+}
+
+static void aes_decrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ const QCryptoNettleAES *aesctx = ctx;
+ aes_decrypt(&aesctx->dec, length, dst, src);
+}
+
+static void des_encrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ des_encrypt(ctx, length, dst, src);
+}
+
+static void des_decrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ des_decrypt(ctx, length, dst, src);
+}
+
+static void cast128_encrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ cast128_encrypt(ctx, length, dst, src);
+}
+
+static void cast128_decrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ cast128_decrypt(ctx, length, dst, src);
+}
+
+static void serpent_encrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ serpent_encrypt(ctx, length, dst, src);
+}
-static void aes_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
+static void serpent_decrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ serpent_decrypt(ctx, length, dst, src);
+}
+
+static void twofish_encrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ twofish_encrypt(ctx, length, dst, src);
+}
+
+static void twofish_decrypt_native(cipher_ctx_t ctx, cipher_length_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ twofish_decrypt(ctx, length, dst, src);
+}
+
+static void aes_encrypt_wrapper(const void *ctx, size_t length,
uint8_t *dst, const uint8_t *src)
{
- aes_encrypt(ctx, length, dst, src);
+ const QCryptoNettleAES *aesctx = ctx;
+ aes_encrypt(&aesctx->enc, length, dst, src);
}
-static void aes_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
+static void aes_decrypt_wrapper(const void *ctx, size_t length,
uint8_t *dst, const uint8_t *src)
{
- aes_decrypt(ctx, length, dst, src);
+ const QCryptoNettleAES *aesctx = ctx;
+ aes_decrypt(&aesctx->dec, length, dst, src);
}
-static void des_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
+static void des_encrypt_wrapper(const void *ctx, size_t length,
uint8_t *dst, const uint8_t *src)
{
des_encrypt(ctx, length, dst, src);
}
-static void des_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
+static void des_decrypt_wrapper(const void *ctx, size_t length,
uint8_t *dst, const uint8_t *src)
{
des_decrypt(ctx, length, dst, src);
}
+static void cast128_encrypt_wrapper(const void *ctx, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ cast128_encrypt(ctx, length, dst, src);
+}
+
+static void cast128_decrypt_wrapper(const void *ctx, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ cast128_decrypt(ctx, length, dst, src);
+}
+
+static void serpent_encrypt_wrapper(const void *ctx, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ serpent_encrypt(ctx, length, dst, src);
+}
+
+static void serpent_decrypt_wrapper(const void *ctx, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ serpent_decrypt(ctx, length, dst, src);
+}
+
+static void twofish_encrypt_wrapper(const void *ctx, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ twofish_encrypt(ctx, length, dst, src);
+}
+
+static void twofish_decrypt_wrapper(const void *ctx, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ twofish_decrypt(ctx, length, dst, src);
+}
+
typedef struct QCryptoCipherNettle QCryptoCipherNettle;
struct QCryptoCipherNettle {
- void *ctx_encrypt;
- void *ctx_decrypt;
- nettle_cipher_func *alg_encrypt;
- nettle_cipher_func *alg_decrypt;
+ /* Primary cipher context for all modes */
+ void *ctx;
+ /* Second cipher context for XTS mode only */
+ void *ctx_tweak;
+ /* Cipher callbacks for both contexts */
+ QCryptoCipherNettleFuncNative alg_encrypt_native;
+ QCryptoCipherNettleFuncNative alg_decrypt_native;
+ QCryptoCipherNettleFuncWrapper alg_encrypt_wrapper;
+ QCryptoCipherNettleFuncWrapper alg_decrypt_wrapper;
+
uint8_t *iv;
- size_t niv;
+ size_t blocksize;
};
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
@@ -79,6 +198,13 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
case QCRYPTO_CIPHER_ALG_AES_128:
case QCRYPTO_CIPHER_ALG_AES_192:
case QCRYPTO_CIPHER_ALG_AES_256:
+ case QCRYPTO_CIPHER_ALG_CAST5_128:
+ case QCRYPTO_CIPHER_ALG_SERPENT_128:
+ case QCRYPTO_CIPHER_ALG_SERPENT_192:
+ case QCRYPTO_CIPHER_ALG_SERPENT_256:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_128:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_192:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_256:
return true;
default:
return false;
@@ -98,13 +224,14 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
case QCRYPTO_CIPHER_MODE_CBC:
+ case QCRYPTO_CIPHER_MODE_XTS:
break;
default:
error_setg(errp, "Unsupported cipher mode %d", mode);
return NULL;
}
- if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
+ if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
return NULL;
}
@@ -116,38 +243,125 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
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 */
+ ctx->ctx = g_new0(struct des_ctx, 1);
rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
- des_set_key(ctx->ctx_encrypt, rfbkey);
+ des_set_key(ctx->ctx, rfbkey);
g_free(rfbkey);
- ctx->alg_encrypt = des_encrypt_wrapper;
- ctx->alg_decrypt = des_decrypt_wrapper;
+ ctx->alg_encrypt_native = des_encrypt_native;
+ ctx->alg_decrypt_native = des_decrypt_native;
+ ctx->alg_encrypt_wrapper = des_encrypt_wrapper;
+ ctx->alg_decrypt_wrapper = des_decrypt_wrapper;
- ctx->niv = DES_BLOCK_SIZE;
+ ctx->blocksize = 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);
+ ctx->ctx = g_new0(QCryptoNettleAES, 1);
+
+ if (mode == QCRYPTO_CIPHER_MODE_XTS) {
+ ctx->ctx_tweak = g_new0(QCryptoNettleAES, 1);
+
+ nkey /= 2;
+ aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx)->enc,
+ nkey, key);
+ aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx)->dec,
+ nkey, key);
+
+ aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx_tweak)->enc,
+ nkey, key + nkey);
+ aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx_tweak)->dec,
+ nkey, key + nkey);
+ } else {
+ aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx)->enc,
+ nkey, key);
+ aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx)->dec,
+ nkey, key);
+ }
+
+ ctx->alg_encrypt_native = aes_encrypt_native;
+ ctx->alg_decrypt_native = aes_decrypt_native;
+ ctx->alg_encrypt_wrapper = aes_encrypt_wrapper;
+ ctx->alg_decrypt_wrapper = aes_decrypt_wrapper;
+
+ ctx->blocksize = AES_BLOCK_SIZE;
+ break;
+
+ case QCRYPTO_CIPHER_ALG_CAST5_128:
+ ctx->ctx = g_new0(struct cast128_ctx, 1);
+
+ if (mode == QCRYPTO_CIPHER_MODE_XTS) {
+ ctx->ctx_tweak = g_new0(struct cast128_ctx, 1);
- aes_set_encrypt_key(ctx->ctx_encrypt, nkey, key);
- aes_set_decrypt_key(ctx->ctx_decrypt, nkey, key);
+ nkey /= 2;
+ cast5_set_key(ctx->ctx, nkey, key);
+ cast5_set_key(ctx->ctx_tweak, nkey, key + nkey);
+ } else {
+ cast5_set_key(ctx->ctx, nkey, key);
+ }
- ctx->alg_encrypt = aes_encrypt_wrapper;
- ctx->alg_decrypt = aes_decrypt_wrapper;
+ ctx->alg_encrypt_native = cast128_encrypt_native;
+ ctx->alg_decrypt_native = cast128_decrypt_native;
+ ctx->alg_encrypt_wrapper = cast128_encrypt_wrapper;
+ ctx->alg_decrypt_wrapper = cast128_decrypt_wrapper;
- ctx->niv = AES_BLOCK_SIZE;
+ ctx->blocksize = CAST128_BLOCK_SIZE;
break;
+
+ case QCRYPTO_CIPHER_ALG_SERPENT_128:
+ case QCRYPTO_CIPHER_ALG_SERPENT_192:
+ case QCRYPTO_CIPHER_ALG_SERPENT_256:
+ ctx->ctx = g_new0(struct serpent_ctx, 1);
+
+ if (mode == QCRYPTO_CIPHER_MODE_XTS) {
+ ctx->ctx_tweak = g_new0(struct serpent_ctx, 1);
+
+ nkey /= 2;
+ serpent_set_key(ctx->ctx, nkey, key);
+ serpent_set_key(ctx->ctx_tweak, nkey, key + nkey);
+ } else {
+ serpent_set_key(ctx->ctx, nkey, key);
+ }
+
+ ctx->alg_encrypt_native = serpent_encrypt_native;
+ ctx->alg_decrypt_native = serpent_decrypt_native;
+ ctx->alg_encrypt_wrapper = serpent_encrypt_wrapper;
+ ctx->alg_decrypt_wrapper = serpent_decrypt_wrapper;
+
+ ctx->blocksize = SERPENT_BLOCK_SIZE;
+ break;
+
+ case QCRYPTO_CIPHER_ALG_TWOFISH_128:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_192:
+ case QCRYPTO_CIPHER_ALG_TWOFISH_256:
+ ctx->ctx = g_new0(struct twofish_ctx, 1);
+
+ if (mode == QCRYPTO_CIPHER_MODE_XTS) {
+ ctx->ctx_tweak = g_new0(struct twofish_ctx, 1);
+
+ nkey /= 2;
+ twofish_set_key(ctx->ctx, nkey, key);
+ twofish_set_key(ctx->ctx_tweak, nkey, key + nkey);
+ } else {
+ twofish_set_key(ctx->ctx, nkey, key);
+ }
+
+ ctx->alg_encrypt_native = twofish_encrypt_native;
+ ctx->alg_decrypt_native = twofish_decrypt_native;
+ ctx->alg_encrypt_wrapper = twofish_encrypt_wrapper;
+ ctx->alg_decrypt_wrapper = twofish_decrypt_wrapper;
+
+ ctx->blocksize = TWOFISH_BLOCK_SIZE;
+ break;
+
default:
error_setg(errp, "Unsupported cipher algorithm %d", alg);
goto error;
}
- ctx->iv = g_new0(uint8_t, ctx->niv);
+ ctx->iv = g_new0(uint8_t, ctx->blocksize);
cipher->opaque = ctx;
return cipher;
@@ -169,8 +383,8 @@ void qcrypto_cipher_free(QCryptoCipher *cipher)
ctx = cipher->opaque;
g_free(ctx->iv);
- g_free(ctx->ctx_encrypt);
- g_free(ctx->ctx_decrypt);
+ g_free(ctx->ctx);
+ g_free(ctx->ctx_tweak);
g_free(ctx);
g_free(cipher);
}
@@ -184,16 +398,29 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
{
QCryptoCipherNettle *ctx = cipher->opaque;
+ if (len % ctx->blocksize) {
+ error_setg(errp, "Length %zu must be a multiple of block size %zu",
+ len, ctx->blocksize);
+ return -1;
+ }
+
switch (cipher->mode) {
case QCRYPTO_CIPHER_MODE_ECB:
- ctx->alg_encrypt(ctx->ctx_encrypt, len, out, in);
+ ctx->alg_encrypt_wrapper(ctx->ctx, len, out, in);
break;
case QCRYPTO_CIPHER_MODE_CBC:
- cbc_encrypt(ctx->ctx_encrypt, ctx->alg_encrypt,
- ctx->niv, ctx->iv,
+ cbc_encrypt(ctx->ctx, ctx->alg_encrypt_native,
+ ctx->blocksize, ctx->iv,
len, out, in);
break;
+
+ case QCRYPTO_CIPHER_MODE_XTS:
+ xts_encrypt(ctx->ctx, ctx->ctx_tweak,
+ ctx->alg_encrypt_wrapper, ctx->alg_encrypt_wrapper,
+ ctx->iv, len, out, in);
+ break;
+
default:
error_setg(errp, "Unsupported cipher algorithm %d",
cipher->alg);
@@ -211,17 +438,34 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
{
QCryptoCipherNettle *ctx = cipher->opaque;
+ if (len % ctx->blocksize) {
+ error_setg(errp, "Length %zu must be a multiple of block size %zu",
+ len, ctx->blocksize);
+ return -1;
+ }
+
switch (cipher->mode) {
case QCRYPTO_CIPHER_MODE_ECB:
- ctx->alg_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt,
- len, out, in);
+ ctx->alg_decrypt_wrapper(ctx->ctx, 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,
+ cbc_decrypt(ctx->ctx, ctx->alg_decrypt_native,
+ ctx->blocksize, ctx->iv,
len, out, in);
break;
+
+ case QCRYPTO_CIPHER_MODE_XTS:
+ if (ctx->blocksize != XTS_BLOCK_SIZE) {
+ error_setg(errp, "Block size must be %d not %zu",
+ XTS_BLOCK_SIZE, ctx->blocksize);
+ return -1;
+ }
+ xts_decrypt(ctx->ctx, ctx->ctx_tweak,
+ ctx->alg_encrypt_wrapper, ctx->alg_decrypt_wrapper,
+ ctx->iv, len, out, in);
+ break;
+
default:
error_setg(errp, "Unsupported cipher algorithm %d",
cipher->alg);
@@ -235,9 +479,9 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher,
Error **errp)
{
QCryptoCipherNettle *ctx = cipher->opaque;
- if (niv != ctx->niv) {
+ if (niv != ctx->blocksize) {
error_setg(errp, "Expected IV size %zu not %zu",
- ctx->niv, niv);
+ ctx->blocksize, niv);
return -1;
}
memcpy(ctx->iv, iv, niv);
diff --git a/qemu/crypto/cipher.c b/qemu/crypto/cipher.c
index 024a00cb5..cafb45436 100644
--- a/qemu/crypto/cipher.c
+++ b/qemu/crypto/cipher.c
@@ -18,36 +18,119 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "crypto/cipher.h"
-static size_t alg_key_len[QCRYPTO_CIPHER_ALG_LAST] = {
+static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = {
[QCRYPTO_CIPHER_ALG_AES_128] = 16,
[QCRYPTO_CIPHER_ALG_AES_192] = 24,
[QCRYPTO_CIPHER_ALG_AES_256] = 32,
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
+ [QCRYPTO_CIPHER_ALG_CAST5_128] = 16,
+ [QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
+ [QCRYPTO_CIPHER_ALG_SERPENT_192] = 24,
+ [QCRYPTO_CIPHER_ALG_SERPENT_256] = 32,
+ [QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16,
+ [QCRYPTO_CIPHER_ALG_TWOFISH_192] = 24,
+ [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 32,
};
+static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {
+ [QCRYPTO_CIPHER_ALG_AES_128] = 16,
+ [QCRYPTO_CIPHER_ALG_AES_192] = 16,
+ [QCRYPTO_CIPHER_ALG_AES_256] = 16,
+ [QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
+ [QCRYPTO_CIPHER_ALG_CAST5_128] = 8,
+ [QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
+ [QCRYPTO_CIPHER_ALG_SERPENT_192] = 16,
+ [QCRYPTO_CIPHER_ALG_SERPENT_256] = 16,
+ [QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16,
+ [QCRYPTO_CIPHER_ALG_TWOFISH_192] = 16,
+ [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 16,
+};
+
+static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = {
+ [QCRYPTO_CIPHER_MODE_ECB] = false,
+ [QCRYPTO_CIPHER_MODE_CBC] = true,
+ [QCRYPTO_CIPHER_MODE_XTS] = true,
+};
+
+
+size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg)
+{
+ if (alg >= G_N_ELEMENTS(alg_key_len)) {
+ return 0;
+ }
+ return alg_block_len[alg];
+}
+
+
+size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg)
+{
+ if (alg >= G_N_ELEMENTS(alg_key_len)) {
+ return 0;
+ }
+ return alg_key_len[alg];
+}
+
+
+size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg,
+ QCryptoCipherMode mode)
+{
+ if (alg >= G_N_ELEMENTS(alg_block_len)) {
+ return 0;
+ }
+ if (mode >= G_N_ELEMENTS(mode_need_iv)) {
+ return 0;
+ }
+
+ if (mode_need_iv[mode]) {
+ return alg_block_len[alg];
+ }
+ return 0;
+}
+
+
static bool
qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
+ QCryptoCipherMode mode,
size_t nkey,
Error **errp)
{
- if ((unsigned)alg >= QCRYPTO_CIPHER_ALG_LAST) {
+ if ((unsigned)alg >= QCRYPTO_CIPHER_ALG__MAX) {
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;
+ if (mode == QCRYPTO_CIPHER_MODE_XTS) {
+ if (alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
+ error_setg(errp, "XTS mode not compatible with DES-RFB");
+ return false;
+ }
+ if (nkey % 2) {
+ error_setg(errp, "XTS cipher key length should be a multiple of 2");
+ return false;
+ }
+
+ if (alg_key_len[alg] != (nkey / 2)) {
+ error_setg(errp, "Cipher key length %zu should be %zu",
+ nkey, alg_key_len[alg] * 2);
+ return false;
+ }
+ } else {
+ if (alg_key_len[alg] != nkey) {
+ error_setg(errp, "Cipher key length %zu should be %zu",
+ nkey, alg_key_len[alg]);
+ return false;
+ }
}
return true;
}
-#if defined(CONFIG_GNUTLS_GCRYPT) || defined(CONFIG_GNUTLS_NETTLE)
+#if defined(CONFIG_GCRYPT) || defined(CONFIG_NETTLE)
static uint8_t *
qcrypto_cipher_munge_des_rfb_key(const uint8_t *key,
size_t nkey)
@@ -63,11 +146,11 @@ qcrypto_cipher_munge_des_rfb_key(const uint8_t *key,
}
return ret;
}
-#endif /* CONFIG_GNUTLS_GCRYPT || CONFIG_GNUTLS_NETTLE */
+#endif /* CONFIG_GCRYPT || CONFIG_NETTLE */
-#ifdef CONFIG_GNUTLS_GCRYPT
+#ifdef CONFIG_GCRYPT
#include "crypto/cipher-gcrypt.c"
-#elif defined CONFIG_GNUTLS_NETTLE
+#elif defined CONFIG_NETTLE
#include "crypto/cipher-nettle.c"
#else
#include "crypto/cipher-builtin.c"
diff --git a/qemu/crypto/desrfb.c b/qemu/crypto/desrfb.c
index fc20a30df..ec47dea3b 100644
--- a/qemu/crypto/desrfb.c
+++ b/qemu/crypto/desrfb.c
@@ -26,6 +26,7 @@
* (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
*/
+#include "qemu/osdep.h"
#include "crypto/desrfb.h"
static void scrunch(unsigned char *, unsigned long *);
diff --git a/qemu/crypto/hash.c b/qemu/crypto/hash.c
index 81e74de86..b90af3495 100644
--- a/qemu/crypto/hash.c
+++ b/qemu/crypto/hash.c
@@ -18,13 +18,33 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "crypto/hash.h"
#ifdef CONFIG_GNUTLS_HASH
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
+#endif
-static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = {
+
+static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = {
+ [QCRYPTO_HASH_ALG_MD5] = 16,
+ [QCRYPTO_HASH_ALG_SHA1] = 20,
+ [QCRYPTO_HASH_ALG_SHA256] = 32,
+};
+
+size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg)
+{
+ if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_size)) {
+ return 0;
+ }
+ return qcrypto_hash_alg_size[alg];
+}
+
+
+#ifdef CONFIG_GNUTLS_HASH
+static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
@@ -38,6 +58,7 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
return false;
}
+
int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
const struct iovec *iov,
size_t niov,
diff --git a/qemu/crypto/init.c b/qemu/crypto/init.c
index 7447882c7..1e564d949 100644
--- a/qemu/crypto/init.c
+++ b/qemu/crypto/init.c
@@ -18,14 +18,17 @@
*
*/
+#include "qemu/osdep.h"
#include "crypto/init.h"
+#include "qapi/error.h"
#include "qemu/thread.h"
#ifdef CONFIG_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
+#endif
-#ifdef CONFIG_GNUTLS_GCRYPT
+#ifdef CONFIG_GCRYPT
#include <gcrypt.h>
#endif
@@ -37,6 +40,7 @@
* - 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
+ * - When GNUTLS is disabled we must always initialize gcrypt threading
*
* But....
*
@@ -47,12 +51,15 @@
*
* - gcrypt < 1.6.0
* AND
- * - gnutls < 2.12
+ * - gnutls < 2.12
+ * OR
+ * - gnutls is disabled
*
*/
-#if (defined(CONFIG_GNUTLS_GCRYPT) && \
- (!defined(GNUTLS_VERSION_NUMBER) || \
+#if (defined(CONFIG_GCRYPT) && \
+ (!defined(CONFIG_GNUTLS) || \
+ !defined(GNUTLS_VERSION_NUMBER) || \
(GNUTLS_VERSION_NUMBER < 0x020c00)) && \
(!defined(GCRYPT_VERSION_NUMBER) || \
(GCRYPT_VERSION_NUMBER < 0x010600)))
@@ -113,6 +120,7 @@ static struct gcry_thread_cbs qcrypto_gcrypt_thread_impl = {
int qcrypto_init(Error **errp)
{
+#ifdef CONFIG_GNUTLS
int ret;
ret = gnutls_global_init();
if (ret < 0) {
@@ -125,8 +133,9 @@ int qcrypto_init(Error **errp)
gnutls_global_set_log_level(10);
gnutls_global_set_log_function(qcrypto_gnutls_log);
#endif
+#endif
-#ifdef CONFIG_GNUTLS_GCRYPT
+#ifdef CONFIG_GCRYPT
if (!gcry_check_version(GCRYPT_VERSION)) {
error_setg(errp, "Unable to initialize gcrypt");
return -1;
@@ -139,12 +148,3 @@ int qcrypto_init(Error **errp)
return 0;
}
-
-#else /* ! CONFIG_GNUTLS */
-
-int qcrypto_init(Error **errp G_GNUC_UNUSED)
-{
- return 0;
-}
-
-#endif /* ! CONFIG_GNUTLS */
diff --git a/qemu/crypto/ivgen-essiv.c b/qemu/crypto/ivgen-essiv.c
new file mode 100644
index 000000000..634de6333
--- /dev/null
+++ b/qemu/crypto/ivgen-essiv.c
@@ -0,0 +1,120 @@
+/*
+ * QEMU Crypto block IV generator - essiv
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/bswap.h"
+#include "crypto/ivgen-essiv.h"
+
+typedef struct QCryptoIVGenESSIV QCryptoIVGenESSIV;
+struct QCryptoIVGenESSIV {
+ QCryptoCipher *cipher;
+};
+
+static int qcrypto_ivgen_essiv_init(QCryptoIVGen *ivgen,
+ const uint8_t *key, size_t nkey,
+ Error **errp)
+{
+ uint8_t *salt;
+ size_t nhash;
+ size_t nsalt;
+ QCryptoIVGenESSIV *essiv = g_new0(QCryptoIVGenESSIV, 1);
+
+ /* Not necessarily the same as nkey */
+ nsalt = qcrypto_cipher_get_key_len(ivgen->cipher);
+
+ nhash = qcrypto_hash_digest_len(ivgen->hash);
+ /* Salt must be larger of hash size or key size */
+ salt = g_new0(uint8_t, MAX(nhash, nsalt));
+
+ if (qcrypto_hash_bytes(ivgen->hash, (const gchar *)key, nkey,
+ &salt, &nhash,
+ errp) < 0) {
+ g_free(essiv);
+ return -1;
+ }
+
+ /* Now potentially truncate salt to match cipher key len */
+ essiv->cipher = qcrypto_cipher_new(ivgen->cipher,
+ QCRYPTO_CIPHER_MODE_ECB,
+ salt, MIN(nhash, nsalt),
+ errp);
+ if (!essiv->cipher) {
+ g_free(essiv);
+ g_free(salt);
+ return -1;
+ }
+
+ g_free(salt);
+ ivgen->private = essiv;
+
+ return 0;
+}
+
+static int qcrypto_ivgen_essiv_calculate(QCryptoIVGen *ivgen,
+ uint64_t sector,
+ uint8_t *iv, size_t niv,
+ Error **errp)
+{
+ QCryptoIVGenESSIV *essiv = ivgen->private;
+ size_t ndata = qcrypto_cipher_get_block_len(ivgen->cipher);
+ uint8_t *data = g_new(uint8_t, ndata);
+
+ sector = cpu_to_le64(sector);
+ memcpy(data, (uint8_t *)&sector, ndata);
+ if (sizeof(sector) < ndata) {
+ memset(data + sizeof(sector), 0, ndata - sizeof(sector));
+ }
+
+ if (qcrypto_cipher_encrypt(essiv->cipher,
+ data,
+ data,
+ ndata,
+ errp) < 0) {
+ g_free(data);
+ return -1;
+ }
+
+ if (ndata > niv) {
+ ndata = niv;
+ }
+ memcpy(iv, data, ndata);
+ if (ndata < niv) {
+ memset(iv + ndata, 0, niv - ndata);
+ }
+ g_free(data);
+ return 0;
+}
+
+static void qcrypto_ivgen_essiv_cleanup(QCryptoIVGen *ivgen)
+{
+ QCryptoIVGenESSIV *essiv = ivgen->private;
+
+ qcrypto_cipher_free(essiv->cipher);
+ g_free(essiv);
+}
+
+
+struct QCryptoIVGenDriver qcrypto_ivgen_essiv = {
+ .init = qcrypto_ivgen_essiv_init,
+ .calculate = qcrypto_ivgen_essiv_calculate,
+ .cleanup = qcrypto_ivgen_essiv_cleanup,
+};
+
diff --git a/qemu/crypto/ivgen-essiv.h b/qemu/crypto/ivgen-essiv.h
new file mode 100644
index 000000000..4a00af849
--- /dev/null
+++ b/qemu/crypto/ivgen-essiv.h
@@ -0,0 +1,28 @@
+/*
+ * QEMU Crypto block IV generator - essiv
+ *
+ * Copyright (c) 2015-2016 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/ivgenpriv.h"
+
+#ifndef QCRYPTO_IVGEN_ESSIV_H__
+#define QCRYPTO_IVGEN_ESSIV_H__
+
+extern struct QCryptoIVGenDriver qcrypto_ivgen_essiv;
+
+#endif /* QCRYPTO_IVGEN_ESSIV_H__ */
diff --git a/qemu/crypto/ivgen-plain.c b/qemu/crypto/ivgen-plain.c
new file mode 100644
index 000000000..9b9b4ad0b
--- /dev/null
+++ b/qemu/crypto/ivgen-plain.c
@@ -0,0 +1,61 @@
+/*
+ * QEMU Crypto block IV generator - plain
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/bswap.h"
+#include "crypto/ivgen-plain.h"
+
+static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen,
+ const uint8_t *key, size_t nkey,
+ Error **errp)
+{
+ return 0;
+}
+
+static int qcrypto_ivgen_plain_calculate(QCryptoIVGen *ivgen,
+ uint64_t sector,
+ uint8_t *iv, size_t niv,
+ Error **errp)
+{
+ size_t ivprefix;
+ uint32_t shortsector = cpu_to_le32((sector & 0xffffffff));
+ ivprefix = sizeof(shortsector);
+ if (ivprefix > niv) {
+ ivprefix = niv;
+ }
+ memcpy(iv, &shortsector, ivprefix);
+ if (ivprefix < niv) {
+ memset(iv + ivprefix, 0, niv - ivprefix);
+ }
+ return 0;
+}
+
+static void qcrypto_ivgen_plain_cleanup(QCryptoIVGen *ivgen)
+{
+}
+
+
+struct QCryptoIVGenDriver qcrypto_ivgen_plain = {
+ .init = qcrypto_ivgen_plain_init,
+ .calculate = qcrypto_ivgen_plain_calculate,
+ .cleanup = qcrypto_ivgen_plain_cleanup,
+};
+
diff --git a/qemu/crypto/ivgen-plain.h b/qemu/crypto/ivgen-plain.h
new file mode 100644
index 000000000..0fe8835c3
--- /dev/null
+++ b/qemu/crypto/ivgen-plain.h
@@ -0,0 +1,28 @@
+/*
+ * QEMU Crypto block IV generator - plain
+ *
+ * Copyright (c) 2015-2016 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/ivgenpriv.h"
+
+#ifndef QCRYPTO_IVGEN_PLAIN_H__
+#define QCRYPTO_IVGEN_PLAIN_H__
+
+extern struct QCryptoIVGenDriver qcrypto_ivgen_plain;
+
+#endif /* QCRYPTO_IVGEN_PLAIN_H__ */
diff --git a/qemu/crypto/ivgen-plain64.c b/qemu/crypto/ivgen-plain64.c
new file mode 100644
index 000000000..6c6b1b44c
--- /dev/null
+++ b/qemu/crypto/ivgen-plain64.c
@@ -0,0 +1,61 @@
+/*
+ * QEMU Crypto block IV generator - plain
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/bswap.h"
+#include "crypto/ivgen-plain.h"
+
+static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen,
+ const uint8_t *key, size_t nkey,
+ Error **errp)
+{
+ return 0;
+}
+
+static int qcrypto_ivgen_plain_calculate(QCryptoIVGen *ivgen,
+ uint64_t sector,
+ uint8_t *iv, size_t niv,
+ Error **errp)
+{
+ size_t ivprefix;
+ ivprefix = sizeof(sector);
+ sector = cpu_to_le64(sector);
+ if (ivprefix > niv) {
+ ivprefix = niv;
+ }
+ memcpy(iv, &sector, ivprefix);
+ if (ivprefix < niv) {
+ memset(iv + ivprefix, 0, niv - ivprefix);
+ }
+ return 0;
+}
+
+static void qcrypto_ivgen_plain_cleanup(QCryptoIVGen *ivgen)
+{
+}
+
+
+struct QCryptoIVGenDriver qcrypto_ivgen_plain64 = {
+ .init = qcrypto_ivgen_plain_init,
+ .calculate = qcrypto_ivgen_plain_calculate,
+ .cleanup = qcrypto_ivgen_plain_cleanup,
+};
+
diff --git a/qemu/crypto/ivgen-plain64.h b/qemu/crypto/ivgen-plain64.h
new file mode 100644
index 000000000..c4104459b
--- /dev/null
+++ b/qemu/crypto/ivgen-plain64.h
@@ -0,0 +1,28 @@
+/*
+ * QEMU Crypto block IV generator - plain64
+ *
+ * Copyright (c) 2015-2016 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/ivgenpriv.h"
+
+#ifndef QCRYPTO_IVGEN_PLAIN64_H__
+#define QCRYPTO_IVGEN_PLAIN64_H__
+
+extern struct QCryptoIVGenDriver qcrypto_ivgen_plain64;
+
+#endif /* QCRYPTO_IVGEN_PLAIN64_H__ */
diff --git a/qemu/crypto/ivgen.c b/qemu/crypto/ivgen.c
new file mode 100644
index 000000000..f66435112
--- /dev/null
+++ b/qemu/crypto/ivgen.c
@@ -0,0 +1,101 @@
+/*
+ * QEMU Crypto block IV generator
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+
+#include "crypto/ivgenpriv.h"
+#include "crypto/ivgen-plain.h"
+#include "crypto/ivgen-plain64.h"
+#include "crypto/ivgen-essiv.h"
+
+
+QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg,
+ QCryptoCipherAlgorithm cipheralg,
+ QCryptoHashAlgorithm hash,
+ const uint8_t *key, size_t nkey,
+ Error **errp)
+{
+ QCryptoIVGen *ivgen = g_new0(QCryptoIVGen, 1);
+
+ ivgen->algorithm = alg;
+ ivgen->cipher = cipheralg;
+ ivgen->hash = hash;
+
+ switch (alg) {
+ case QCRYPTO_IVGEN_ALG_PLAIN:
+ ivgen->driver = &qcrypto_ivgen_plain;
+ break;
+ case QCRYPTO_IVGEN_ALG_PLAIN64:
+ ivgen->driver = &qcrypto_ivgen_plain64;
+ break;
+ case QCRYPTO_IVGEN_ALG_ESSIV:
+ ivgen->driver = &qcrypto_ivgen_essiv;
+ break;
+ default:
+ error_setg(errp, "Unknown block IV generator algorithm %d", alg);
+ g_free(ivgen);
+ return NULL;
+ }
+
+ if (ivgen->driver->init(ivgen, key, nkey, errp) < 0) {
+ g_free(ivgen);
+ return NULL;
+ }
+
+ return ivgen;
+}
+
+
+int qcrypto_ivgen_calculate(QCryptoIVGen *ivgen,
+ uint64_t sector,
+ uint8_t *iv, size_t niv,
+ Error **errp)
+{
+ return ivgen->driver->calculate(ivgen, sector, iv, niv, errp);
+}
+
+
+QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen)
+{
+ return ivgen->algorithm;
+}
+
+
+QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen)
+{
+ return ivgen->cipher;
+}
+
+
+QCryptoHashAlgorithm qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen)
+{
+ return ivgen->hash;
+}
+
+
+void qcrypto_ivgen_free(QCryptoIVGen *ivgen)
+{
+ if (!ivgen) {
+ return;
+ }
+ ivgen->driver->cleanup(ivgen);
+ g_free(ivgen);
+}
diff --git a/qemu/crypto/ivgenpriv.h b/qemu/crypto/ivgenpriv.h
new file mode 100644
index 000000000..7b87e02ea
--- /dev/null
+++ b/qemu/crypto/ivgenpriv.h
@@ -0,0 +1,49 @@
+/*
+ * QEMU Crypto block IV generator
+ *
+ * Copyright (c) 2015-2016 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_IVGEN_PRIV_H__
+#define QCRYPTO_IVGEN_PRIV_H__
+
+#include "crypto/ivgen.h"
+
+typedef struct QCryptoIVGenDriver QCryptoIVGenDriver;
+
+struct QCryptoIVGenDriver {
+ int (*init)(QCryptoIVGen *ivgen,
+ const uint8_t *key, size_t nkey,
+ Error **errp);
+ int (*calculate)(QCryptoIVGen *ivgen,
+ uint64_t sector,
+ uint8_t *iv, size_t niv,
+ Error **errp);
+ void (*cleanup)(QCryptoIVGen *ivgen);
+};
+
+struct QCryptoIVGen {
+ QCryptoIVGenDriver *driver;
+ void *private;
+
+ QCryptoIVGenAlgorithm algorithm;
+ QCryptoCipherAlgorithm cipher;
+ QCryptoHashAlgorithm hash;
+};
+
+
+#endif /* QCRYPTO_IVGEN_PRIV_H__ */
diff --git a/qemu/crypto/pbkdf-gcrypt.c b/qemu/crypto/pbkdf-gcrypt.c
new file mode 100644
index 000000000..997b311d8
--- /dev/null
+++ b/qemu/crypto/pbkdf-gcrypt.c
@@ -0,0 +1,69 @@
+/*
+ * QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/pbkdf.h"
+#include "gcrypt.h"
+
+bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash)
+{
+ switch (hash) {
+ case QCRYPTO_HASH_ALG_MD5:
+ case QCRYPTO_HASH_ALG_SHA1:
+ case QCRYPTO_HASH_ALG_SHA256:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int qcrypto_pbkdf2(QCryptoHashAlgorithm hash,
+ const uint8_t *key, size_t nkey,
+ const uint8_t *salt, size_t nsalt,
+ unsigned int iterations,
+ uint8_t *out, size_t nout,
+ Error **errp)
+{
+ static const int hash_map[QCRYPTO_HASH_ALG__MAX] = {
+ [QCRYPTO_HASH_ALG_MD5] = GCRY_MD_MD5,
+ [QCRYPTO_HASH_ALG_SHA1] = GCRY_MD_SHA1,
+ [QCRYPTO_HASH_ALG_SHA256] = GCRY_MD_SHA256,
+ };
+ int ret;
+
+ if (hash >= G_N_ELEMENTS(hash_map) ||
+ hash_map[hash] == GCRY_MD_NONE) {
+ error_setg(errp, "Unexpected hash algorithm %d", hash);
+ return -1;
+ }
+
+ ret = gcry_kdf_derive(key, nkey, GCRY_KDF_PBKDF2,
+ hash_map[hash],
+ salt, nsalt, iterations,
+ nout, out);
+ if (ret != 0) {
+ error_setg(errp, "Cannot derive password: %s",
+ gcry_strerror(ret));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/qemu/crypto/pbkdf-nettle.c b/qemu/crypto/pbkdf-nettle.c
new file mode 100644
index 000000000..db9fc1578
--- /dev/null
+++ b/qemu/crypto/pbkdf-nettle.c
@@ -0,0 +1,66 @@
+/*
+ * QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/pbkdf.h"
+#include "nettle/pbkdf2.h"
+
+
+bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash)
+{
+ switch (hash) {
+ case QCRYPTO_HASH_ALG_SHA1:
+ case QCRYPTO_HASH_ALG_SHA256:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int qcrypto_pbkdf2(QCryptoHashAlgorithm hash,
+ const uint8_t *key, size_t nkey,
+ const uint8_t *salt, size_t nsalt,
+ unsigned int iterations,
+ uint8_t *out, size_t nout,
+ Error **errp)
+{
+ switch (hash) {
+ case QCRYPTO_HASH_ALG_SHA1:
+ pbkdf2_hmac_sha1(nkey, key,
+ iterations,
+ nsalt, salt,
+ nout, out);
+ break;
+
+ case QCRYPTO_HASH_ALG_SHA256:
+ pbkdf2_hmac_sha256(nkey, key,
+ iterations,
+ nsalt, salt,
+ nout, out);
+ break;
+
+ default:
+ error_setg_errno(errp, ENOSYS,
+ "PBKDF does not support hash algorithm %d", hash);
+ return -1;
+ }
+ return 0;
+}
diff --git a/qemu/crypto/pbkdf-stub.c b/qemu/crypto/pbkdf-stub.c
new file mode 100644
index 000000000..266a5051b
--- /dev/null
+++ b/qemu/crypto/pbkdf-stub.c
@@ -0,0 +1,43 @@
+/*
+ * QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/pbkdf.h"
+
+bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash G_GNUC_UNUSED)
+{
+ return false;
+}
+
+int qcrypto_pbkdf2(QCryptoHashAlgorithm hash G_GNUC_UNUSED,
+ const uint8_t *key G_GNUC_UNUSED,
+ size_t nkey G_GNUC_UNUSED,
+ const uint8_t *salt G_GNUC_UNUSED,
+ size_t nsalt G_GNUC_UNUSED,
+ unsigned int iterations G_GNUC_UNUSED,
+ uint8_t *out G_GNUC_UNUSED,
+ size_t nout G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg_errno(errp, ENOSYS,
+ "No crypto library supporting PBKDF in this build");
+ return -1;
+}
diff --git a/qemu/crypto/pbkdf.c b/qemu/crypto/pbkdf.c
new file mode 100644
index 000000000..695cc35df
--- /dev/null
+++ b/qemu/crypto/pbkdf.c
@@ -0,0 +1,110 @@
+/*
+ * QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/pbkdf.h"
+#ifndef _WIN32
+#include <sys/resource.h>
+#endif
+
+
+static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms,
+ Error **errp)
+{
+#ifdef _WIN32
+ FILETIME creation_time, exit_time, kernel_time, user_time;
+ ULARGE_INTEGER thread_time;
+
+ if (!GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time,
+ &kernel_time, &user_time)) {
+ error_setg(errp, "Unable to get thread CPU usage");
+ return -1;
+ }
+
+ thread_time.LowPart = user_time.dwLowDateTime;
+ thread_time.HighPart = user_time.dwHighDateTime;
+
+ /* QuadPart is units of 100ns and we want ms as unit */
+ *val_ms = thread_time.QuadPart / 10000ll;
+ return 0;
+#elif defined(RUSAGE_THREAD)
+ struct rusage ru;
+ if (getrusage(RUSAGE_THREAD, &ru) < 0) {
+ error_setg_errno(errp, errno, "Unable to get thread CPU usage");
+ return -1;
+ }
+
+ *val_ms = ((ru.ru_utime.tv_sec * 1000ll) +
+ (ru.ru_utime.tv_usec / 1000));
+ return 0;
+#else
+ *val_ms = 0;
+ error_setg(errp, "Unable to calculate thread CPU usage on this platform");
+ return -1;
+#endif
+}
+
+int qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash,
+ const uint8_t *key, size_t nkey,
+ const uint8_t *salt, size_t nsalt,
+ Error **errp)
+{
+ uint8_t out[32];
+ long long int iterations = (1 << 15);
+ unsigned long long delta_ms, start_ms, end_ms;
+
+ while (1) {
+ if (qcrypto_pbkdf2_get_thread_cpu(&start_ms, errp) < 0) {
+ return -1;
+ }
+ if (qcrypto_pbkdf2(hash,
+ key, nkey,
+ salt, nsalt,
+ iterations,
+ out, sizeof(out),
+ errp) < 0) {
+ return -1;
+ }
+ if (qcrypto_pbkdf2_get_thread_cpu(&end_ms, errp) < 0) {
+ return -1;
+ }
+
+ delta_ms = end_ms - start_ms;
+
+ if (delta_ms > 500) {
+ break;
+ } else if (delta_ms < 100) {
+ iterations = iterations * 10;
+ } else {
+ iterations = (iterations * 1000 / delta_ms);
+ }
+ }
+
+ iterations = iterations * 1000 / delta_ms;
+
+ if (iterations > INT32_MAX) {
+ error_setg(errp, "Iterations %lld too large for a 32-bit int",
+ iterations);
+ return -1;
+ }
+
+ return iterations;
+}
diff --git a/qemu/crypto/random-gcrypt.c b/qemu/crypto/random-gcrypt.c
new file mode 100644
index 000000000..0de9a096d
--- /dev/null
+++ b/qemu/crypto/random-gcrypt.c
@@ -0,0 +1,33 @@
+/*
+ * QEMU Crypto random number provider
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+
+#include "crypto/random.h"
+
+#include <gcrypt.h>
+
+int qcrypto_random_bytes(uint8_t *buf,
+ size_t buflen,
+ Error **errp G_GNUC_UNUSED)
+{
+ gcry_randomize(buf, buflen, GCRY_STRONG_RANDOM);
+ return 0;
+}
diff --git a/qemu/crypto/random-gnutls.c b/qemu/crypto/random-gnutls.c
new file mode 100644
index 000000000..04b45a8f8
--- /dev/null
+++ b/qemu/crypto/random-gnutls.c
@@ -0,0 +1,43 @@
+/*
+ * QEMU Crypto random number provider
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+
+#include "crypto/random.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+int qcrypto_random_bytes(uint8_t *buf,
+ size_t buflen,
+ Error **errp)
+{
+ int ret;
+
+ ret = gnutls_rnd(GNUTLS_RND_RANDOM, buf, buflen);
+
+ if (ret < 0) {
+ error_setg(errp, "Cannot get random bytes: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/qemu/crypto/random-stub.c b/qemu/crypto/random-stub.c
new file mode 100644
index 000000000..63bbf4147
--- /dev/null
+++ b/qemu/crypto/random-stub.c
@@ -0,0 +1,31 @@
+/*
+ * QEMU Crypto random number provider
+ *
+ * Copyright (c) 2015-2016 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 "qemu/osdep.h"
+
+#include "crypto/random.h"
+
+int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
+ size_t buflen G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "No random byte source provided in this build");
+ return -1;
+}
diff --git a/qemu/crypto/secret.c b/qemu/crypto/secret.c
new file mode 100644
index 000000000..285ab7a63
--- /dev/null
+++ b/qemu/crypto/secret.c
@@ -0,0 +1,509 @@
+/*
+ * QEMU crypto secret support
+ *
+ * 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 "qemu/osdep.h"
+#include "crypto/secret.h"
+#include "crypto/cipher.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "qemu/base64.h"
+#include "trace.h"
+
+
+static void
+qcrypto_secret_load_data(QCryptoSecret *secret,
+ uint8_t **output,
+ size_t *outputlen,
+ Error **errp)
+{
+ char *data = NULL;
+ size_t length = 0;
+ GError *gerr = NULL;
+
+ *output = NULL;
+ *outputlen = 0;
+
+ if (secret->file) {
+ if (secret->data) {
+ error_setg(errp,
+ "'file' and 'data' are mutually exclusive");
+ return;
+ }
+ if (!g_file_get_contents(secret->file, &data, &length, &gerr)) {
+ error_setg(errp,
+ "Unable to read %s: %s",
+ secret->file, gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+ *output = (uint8_t *)data;
+ *outputlen = length;
+ } else if (secret->data) {
+ *outputlen = strlen(secret->data);
+ *output = (uint8_t *)g_strdup(secret->data);
+ } else {
+ error_setg(errp, "Either 'file' or 'data' must be provided");
+ }
+}
+
+
+static void qcrypto_secret_decrypt(QCryptoSecret *secret,
+ const uint8_t *input,
+ size_t inputlen,
+ uint8_t **output,
+ size_t *outputlen,
+ Error **errp)
+{
+ uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL;
+ size_t keylen, ciphertextlen, ivlen;
+ QCryptoCipher *aes = NULL;
+ uint8_t *plaintext = NULL;
+
+ *output = NULL;
+ *outputlen = 0;
+
+ if (qcrypto_secret_lookup(secret->keyid,
+ &key, &keylen,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ if (keylen != 32) {
+ error_setg(errp, "Key should be 32 bytes in length");
+ goto cleanup;
+ }
+
+ if (!secret->iv) {
+ error_setg(errp, "IV is required to decrypt secret");
+ goto cleanup;
+ }
+
+ iv = qbase64_decode(secret->iv, -1, &ivlen, errp);
+ if (!iv) {
+ goto cleanup;
+ }
+ if (ivlen != 16) {
+ error_setg(errp, "IV should be 16 bytes in length not %zu",
+ ivlen);
+ goto cleanup;
+ }
+
+ aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
+ QCRYPTO_CIPHER_MODE_CBC,
+ key, keylen,
+ errp);
+ if (!aes) {
+ goto cleanup;
+ }
+
+ if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
+ goto cleanup;
+ }
+
+ if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
+ ciphertext = qbase64_decode((const gchar*)input,
+ inputlen,
+ &ciphertextlen,
+ errp);
+ if (!ciphertext) {
+ goto cleanup;
+ }
+ plaintext = g_new0(uint8_t, ciphertextlen + 1);
+ } else {
+ ciphertextlen = inputlen;
+ plaintext = g_new0(uint8_t, inputlen + 1);
+ }
+ if (qcrypto_cipher_decrypt(aes,
+ ciphertext ? ciphertext : input,
+ plaintext,
+ ciphertextlen,
+ errp) < 0) {
+ plaintext = NULL;
+ goto cleanup;
+ }
+
+ if (plaintext[ciphertextlen - 1] > 16 ||
+ plaintext[ciphertextlen - 1] > ciphertextlen) {
+ error_setg(errp, "Incorrect number of padding bytes (%d) "
+ "found on decrypted data",
+ (int)plaintext[ciphertextlen - 1]);
+ g_free(plaintext);
+ plaintext = NULL;
+ goto cleanup;
+ }
+
+ /* Even though plaintext may contain arbitrary NUL
+ * ensure it is explicitly NUL terminated.
+ */
+ ciphertextlen -= plaintext[ciphertextlen - 1];
+ plaintext[ciphertextlen] = '\0';
+
+ *output = plaintext;
+ *outputlen = ciphertextlen;
+
+ cleanup:
+ g_free(ciphertext);
+ g_free(iv);
+ g_free(key);
+ qcrypto_cipher_free(aes);
+}
+
+
+static void qcrypto_secret_decode(const uint8_t *input,
+ size_t inputlen,
+ uint8_t **output,
+ size_t *outputlen,
+ Error **errp)
+{
+ *output = qbase64_decode((const gchar*)input,
+ inputlen,
+ outputlen,
+ errp);
+}
+
+
+static void
+qcrypto_secret_prop_set_loaded(Object *obj,
+ bool value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ if (value) {
+ Error *local_err = NULL;
+ uint8_t *input = NULL;
+ size_t inputlen = 0;
+ uint8_t *output = NULL;
+ size_t outputlen = 0;
+
+ qcrypto_secret_load_data(secret, &input, &inputlen, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (secret->keyid) {
+ qcrypto_secret_decrypt(secret, input, inputlen,
+ &output, &outputlen, &local_err);
+ g_free(input);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ input = output;
+ inputlen = outputlen;
+ } else {
+ if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) {
+ qcrypto_secret_decode(input, inputlen,
+ &output, &outputlen, &local_err);
+ g_free(input);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ input = output;
+ inputlen = outputlen;
+ }
+ }
+
+ secret->rawdata = input;
+ secret->rawlen = inputlen;
+ } else {
+ g_free(secret->rawdata);
+ secret->rawlen = 0;
+ }
+}
+
+
+static bool
+qcrypto_secret_prop_get_loaded(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return secret->data != NULL;
+}
+
+
+static void
+qcrypto_secret_prop_set_format(Object *obj,
+ int value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoSecret *creds = QCRYPTO_SECRET(obj);
+
+ creds->format = value;
+}
+
+
+static int
+qcrypto_secret_prop_get_format(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoSecret *creds = QCRYPTO_SECRET(obj);
+
+ return creds->format;
+}
+
+
+static void
+qcrypto_secret_prop_set_data(Object *obj,
+ const char *value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->data);
+ secret->data = g_strdup(value);
+}
+
+
+static char *
+qcrypto_secret_prop_get_data(Object *obj,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return g_strdup(secret->data);
+}
+
+
+static void
+qcrypto_secret_prop_set_file(Object *obj,
+ const char *value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->file);
+ secret->file = g_strdup(value);
+}
+
+
+static char *
+qcrypto_secret_prop_get_file(Object *obj,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return g_strdup(secret->file);
+}
+
+
+static void
+qcrypto_secret_prop_set_iv(Object *obj,
+ const char *value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->iv);
+ secret->iv = g_strdup(value);
+}
+
+
+static char *
+qcrypto_secret_prop_get_iv(Object *obj,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return g_strdup(secret->iv);
+}
+
+
+static void
+qcrypto_secret_prop_set_keyid(Object *obj,
+ const char *value,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->keyid);
+ secret->keyid = g_strdup(value);
+}
+
+
+static char *
+qcrypto_secret_prop_get_keyid(Object *obj,
+ Error **errp)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+ return g_strdup(secret->keyid);
+}
+
+
+static void
+qcrypto_secret_complete(UserCreatable *uc, Error **errp)
+{
+ object_property_set_bool(OBJECT(uc), true, "loaded", errp);
+}
+
+
+static void
+qcrypto_secret_finalize(Object *obj)
+{
+ QCryptoSecret *secret = QCRYPTO_SECRET(obj);
+
+ g_free(secret->iv);
+ g_free(secret->file);
+ g_free(secret->keyid);
+ g_free(secret->rawdata);
+ g_free(secret->data);
+}
+
+static void
+qcrypto_secret_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = qcrypto_secret_complete;
+
+ object_class_property_add_bool(oc, "loaded",
+ qcrypto_secret_prop_get_loaded,
+ qcrypto_secret_prop_set_loaded,
+ NULL);
+ object_class_property_add_enum(oc, "format",
+ "QCryptoSecretFormat",
+ QCryptoSecretFormat_lookup,
+ qcrypto_secret_prop_get_format,
+ qcrypto_secret_prop_set_format,
+ NULL);
+ object_class_property_add_str(oc, "data",
+ qcrypto_secret_prop_get_data,
+ qcrypto_secret_prop_set_data,
+ NULL);
+ object_class_property_add_str(oc, "file",
+ qcrypto_secret_prop_get_file,
+ qcrypto_secret_prop_set_file,
+ NULL);
+ object_class_property_add_str(oc, "keyid",
+ qcrypto_secret_prop_get_keyid,
+ qcrypto_secret_prop_set_keyid,
+ NULL);
+ object_class_property_add_str(oc, "iv",
+ qcrypto_secret_prop_get_iv,
+ qcrypto_secret_prop_set_iv,
+ NULL);
+}
+
+
+int qcrypto_secret_lookup(const char *secretid,
+ uint8_t **data,
+ size_t *datalen,
+ Error **errp)
+{
+ Object *obj;
+ QCryptoSecret *secret;
+
+ obj = object_resolve_path_component(
+ object_get_objects_root(), secretid);
+ if (!obj) {
+ error_setg(errp, "No secret with id '%s'", secretid);
+ return -1;
+ }
+
+ secret = (QCryptoSecret *)
+ object_dynamic_cast(obj,
+ TYPE_QCRYPTO_SECRET);
+ if (!secret) {
+ error_setg(errp, "Object with id '%s' is not a secret",
+ secretid);
+ return -1;
+ }
+
+ if (!secret->rawdata) {
+ error_setg(errp, "Secret with id '%s' has no data",
+ secretid);
+ return -1;
+ }
+
+ *data = g_new0(uint8_t, secret->rawlen + 1);
+ memcpy(*data, secret->rawdata, secret->rawlen);
+ (*data)[secret->rawlen] = '\0';
+ *datalen = secret->rawlen;
+
+ return 0;
+}
+
+
+char *qcrypto_secret_lookup_as_utf8(const char *secretid,
+ Error **errp)
+{
+ uint8_t *data;
+ size_t datalen;
+
+ if (qcrypto_secret_lookup(secretid,
+ &data,
+ &datalen,
+ errp) < 0) {
+ return NULL;
+ }
+
+ if (!g_utf8_validate((const gchar*)data, datalen, NULL)) {
+ error_setg(errp,
+ "Data from secret %s is not valid UTF-8",
+ secretid);
+ g_free(data);
+ return NULL;
+ }
+
+ return (char *)data;
+}
+
+
+char *qcrypto_secret_lookup_as_base64(const char *secretid,
+ Error **errp)
+{
+ uint8_t *data;
+ size_t datalen;
+ char *ret;
+
+ if (qcrypto_secret_lookup(secretid,
+ &data,
+ &datalen,
+ errp) < 0) {
+ return NULL;
+ }
+
+ ret = g_base64_encode(data, datalen);
+ g_free(data);
+ return ret;
+}
+
+
+static const TypeInfo qcrypto_secret_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QCRYPTO_SECRET,
+ .instance_size = sizeof(QCryptoSecret),
+ .instance_finalize = qcrypto_secret_finalize,
+ .class_size = sizeof(QCryptoSecretClass),
+ .class_init = qcrypto_secret_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qcrypto_secret_register_types(void)
+{
+ type_register_static(&qcrypto_secret_info);
+}
+
+
+type_init(qcrypto_secret_register_types);
diff --git a/qemu/crypto/tlscreds.c b/qemu/crypto/tlscreds.c
new file mode 100644
index 000000000..1620e126a
--- /dev/null
+++ b/qemu/crypto/tlscreds.c
@@ -0,0 +1,259 @@
+/*
+ * QEMU crypto TLS credential support
+ *
+ * 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/tlscredspriv.h"
+#include "trace.h"
+
+#define DH_BITS 2048
+
+#ifdef CONFIG_GNUTLS
+int
+qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
+ const char *filename,
+ gnutls_dh_params_t *dh_params,
+ Error **errp)
+{
+ int ret;
+
+ trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : "<generated>");
+
+ if (filename == NULL) {
+ ret = gnutls_dh_params_init(dh_params);
+ if (ret < 0) {
+ error_setg(errp, "Unable to initialize DH parameters: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+ ret = gnutls_dh_params_generate2(*dh_params, DH_BITS);
+ if (ret < 0) {
+ gnutls_dh_params_deinit(*dh_params);
+ *dh_params = NULL;
+ error_setg(errp, "Unable to generate DH parameters: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+ } else {
+ GError *gerr = NULL;
+ gchar *contents;
+ gsize len;
+ gnutls_datum_t data;
+ if (!g_file_get_contents(filename,
+ &contents,
+ &len,
+ &gerr)) {
+
+ error_setg(errp, "%s", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+ data.data = (unsigned char *)contents;
+ data.size = len;
+ ret = gnutls_dh_params_init(dh_params);
+ if (ret < 0) {
+ g_free(contents);
+ error_setg(errp, "Unable to initialize DH parameters: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+ ret = gnutls_dh_params_import_pkcs3(*dh_params,
+ &data,
+ GNUTLS_X509_FMT_PEM);
+ g_free(contents);
+ if (ret < 0) {
+ gnutls_dh_params_deinit(*dh_params);
+ *dh_params = NULL;
+ error_setg(errp, "Unable to load DH parameters from %s: %s",
+ filename, gnutls_strerror(ret));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
+ const char *filename,
+ bool required,
+ char **cred,
+ Error **errp)
+{
+ struct stat sb;
+ int ret = -1;
+
+ if (!creds->dir) {
+ if (required) {
+ error_setg(errp, "Missing 'dir' property value");
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ *cred = g_strdup_printf("%s/%s", creds->dir, filename);
+
+ if (stat(*cred, &sb) < 0) {
+ if (errno == ENOENT && !required) {
+ ret = 0;
+ } else {
+ error_setg_errno(errp, errno,
+ "Unable to access credentials %s",
+ *cred);
+ }
+ g_free(*cred);
+ *cred = NULL;
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ trace_qcrypto_tls_creds_get_path(creds, filename,
+ *cred ? *cred : "<none>");
+ return ret;
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_prop_set_verify(Object *obj,
+ bool value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->verifyPeer = value;
+}
+
+
+static bool
+qcrypto_tls_creds_prop_get_verify(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ return creds->verifyPeer;
+}
+
+
+static void
+qcrypto_tls_creds_prop_set_dir(Object *obj,
+ const char *value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->dir = g_strdup(value);
+}
+
+
+static char *
+qcrypto_tls_creds_prop_get_dir(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ return g_strdup(creds->dir);
+}
+
+
+static void
+qcrypto_tls_creds_prop_set_endpoint(Object *obj,
+ int value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->endpoint = value;
+}
+
+
+static int
+qcrypto_tls_creds_prop_get_endpoint(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ return creds->endpoint;
+}
+
+
+static void
+qcrypto_tls_creds_class_init(ObjectClass *oc, void *data)
+{
+ object_class_property_add_bool(oc, "verify-peer",
+ qcrypto_tls_creds_prop_get_verify,
+ qcrypto_tls_creds_prop_set_verify,
+ NULL);
+ object_class_property_add_str(oc, "dir",
+ qcrypto_tls_creds_prop_get_dir,
+ qcrypto_tls_creds_prop_set_dir,
+ NULL);
+ object_class_property_add_enum(oc, "endpoint",
+ "QCryptoTLSCredsEndpoint",
+ QCryptoTLSCredsEndpoint_lookup,
+ qcrypto_tls_creds_prop_get_endpoint,
+ qcrypto_tls_creds_prop_set_endpoint,
+ NULL);
+}
+
+
+static void
+qcrypto_tls_creds_init(Object *obj)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->verifyPeer = true;
+}
+
+
+static void
+qcrypto_tls_creds_finalize(Object *obj)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ g_free(creds->dir);
+}
+
+
+static const TypeInfo qcrypto_tls_creds_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QCRYPTO_TLS_CREDS,
+ .instance_size = sizeof(QCryptoTLSCreds),
+ .instance_init = qcrypto_tls_creds_init,
+ .instance_finalize = qcrypto_tls_creds_finalize,
+ .class_init = qcrypto_tls_creds_class_init,
+ .class_size = sizeof(QCryptoTLSCredsClass),
+ .abstract = true,
+};
+
+
+static void
+qcrypto_tls_creds_register_types(void)
+{
+ type_register_static(&qcrypto_tls_creds_info);
+}
+
+
+type_init(qcrypto_tls_creds_register_types);
diff --git a/qemu/crypto/tlscredsanon.c b/qemu/crypto/tlscredsanon.c
new file mode 100644
index 000000000..146422008
--- /dev/null
+++ b/qemu/crypto/tlscredsanon.c
@@ -0,0 +1,219 @@
+/*
+ * QEMU crypto TLS anonymous credential support
+ *
+ * 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 "qemu/osdep.h"
+#include "crypto/tlscredsanon.h"
+#include "crypto/tlscredspriv.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "trace.h"
+
+
+#ifdef CONFIG_GNUTLS
+
+
+static int
+qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
+ Error **errp)
+{
+ char *dhparams = NULL;
+ int ret;
+ int rv = -1;
+
+ trace_qcrypto_tls_creds_anon_load(creds,
+ creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_DH_PARAMS,
+ false, &dhparams, errp) < 0) {
+ goto cleanup;
+ }
+
+ ret = gnutls_anon_allocate_server_credentials(&creds->data.server);
+ if (ret < 0) {
+ error_setg(errp, "Cannot allocate credentials: %s",
+ gnutls_strerror(ret));
+ goto cleanup;
+ }
+
+ if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
+ &creds->parent_obj.dh_params,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ gnutls_anon_set_server_dh_params(creds->data.server,
+ creds->parent_obj.dh_params);
+ } else {
+ ret = gnutls_anon_allocate_client_credentials(&creds->data.client);
+ if (ret < 0) {
+ error_setg(errp, "Cannot allocate credentials: %s",
+ gnutls_strerror(ret));
+ goto cleanup;
+ }
+ }
+
+ rv = 0;
+ cleanup:
+ g_free(dhparams);
+ return rv;
+}
+
+
+static void
+qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds)
+{
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+ if (creds->data.client) {
+ gnutls_anon_free_client_credentials(creds->data.client);
+ creds->data.client = NULL;
+ }
+ } else {
+ if (creds->data.server) {
+ gnutls_anon_free_server_credentials(creds->data.server);
+ creds->data.server = NULL;
+ }
+ }
+ if (creds->parent_obj.dh_params) {
+ gnutls_dh_params_deinit(creds->parent_obj.dh_params);
+ creds->parent_obj.dh_params = NULL;
+ }
+}
+
+#else /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "TLS credentials support requires GNUTLS");
+}
+
+
+static void
+qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED)
+{
+ /* nada */
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_anon_prop_set_loaded(Object *obj,
+ bool value,
+ Error **errp)
+{
+ QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
+
+ if (value) {
+ qcrypto_tls_creds_anon_load(creds, errp);
+ } else {
+ qcrypto_tls_creds_anon_unload(creds);
+ }
+}
+
+
+#ifdef CONFIG_GNUTLS
+
+
+static bool
+qcrypto_tls_creds_anon_prop_get_loaded(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ return creds->data.server != NULL;
+ } else {
+ return creds->data.client != NULL;
+ }
+}
+
+
+#else /* ! CONFIG_GNUTLS */
+
+
+static bool
+qcrypto_tls_creds_anon_prop_get_loaded(Object *obj G_GNUC_UNUSED,
+ Error **errp G_GNUC_UNUSED)
+{
+ return false;
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_anon_complete(UserCreatable *uc, Error **errp)
+{
+ object_property_set_bool(OBJECT(uc), true, "loaded", errp);
+}
+
+
+static void
+qcrypto_tls_creds_anon_finalize(Object *obj)
+{
+ QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
+
+ qcrypto_tls_creds_anon_unload(creds);
+}
+
+
+static void
+qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = qcrypto_tls_creds_anon_complete;
+
+ object_class_property_add_bool(oc, "loaded",
+ qcrypto_tls_creds_anon_prop_get_loaded,
+ qcrypto_tls_creds_anon_prop_set_loaded,
+ NULL);
+}
+
+
+static const TypeInfo qcrypto_tls_creds_anon_info = {
+ .parent = TYPE_QCRYPTO_TLS_CREDS,
+ .name = TYPE_QCRYPTO_TLS_CREDS_ANON,
+ .instance_size = sizeof(QCryptoTLSCredsAnon),
+ .instance_finalize = qcrypto_tls_creds_anon_finalize,
+ .class_size = sizeof(QCryptoTLSCredsAnonClass),
+ .class_init = qcrypto_tls_creds_anon_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qcrypto_tls_creds_anon_register_types(void)
+{
+ type_register_static(&qcrypto_tls_creds_anon_info);
+}
+
+
+type_init(qcrypto_tls_creds_anon_register_types);
diff --git a/qemu/crypto/tlscredspriv.h b/qemu/crypto/tlscredspriv.h
new file mode 100644
index 000000000..9222be4a9
--- /dev/null
+++ b/qemu/crypto/tlscredspriv.h
@@ -0,0 +1,42 @@
+/*
+ * QEMU crypto TLS credential support private helpers
+ *
+ * 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_TLSCRED_PRIV_H__
+#define QCRYPTO_TLSCRED_PRIV_H__
+
+#include "crypto/tlscreds.h"
+
+#ifdef CONFIG_GNUTLS
+
+int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
+ const char *filename,
+ bool required,
+ char **cred,
+ Error **errp);
+
+int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
+ const char *filename,
+ gnutls_dh_params_t *dh_params,
+ Error **errp);
+
+#endif
+
+#endif /* QCRYPTO_TLSCRED_PRIV_H__ */
+
diff --git a/qemu/crypto/tlscredsx509.c b/qemu/crypto/tlscredsx509.c
new file mode 100644
index 000000000..6a0179c2e
--- /dev/null
+++ b/qemu/crypto/tlscredsx509.c
@@ -0,0 +1,865 @@
+/*
+ * QEMU crypto TLS x509 credential support
+ *
+ * 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 "qemu/osdep.h"
+#include "crypto/tlscredsx509.h"
+#include "crypto/tlscredspriv.h"
+#include "crypto/secret.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "trace.h"
+
+
+#ifdef CONFIG_GNUTLS
+
+#include <gnutls/x509.h>
+
+
+static int
+qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isServer,
+ bool isCA,
+ Error **errp)
+{
+ time_t now = time(NULL);
+
+ if (now == ((time_t)-1)) {
+ error_setg_errno(errp, errno, "cannot get current time");
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_expiration_time(cert) < now) {
+ error_setg(errp,
+ (isCA ?
+ "The CA certificate %s has expired" :
+ (isServer ?
+ "The server certificate %s has expired" :
+ "The client certificate %s has expired")),
+ certFile);
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_activation_time(cert) > now) {
+ error_setg(errp,
+ (isCA ?
+ "The CA certificate %s is not yet active" :
+ (isServer ?
+ "The server certificate %s is not yet active" :
+ "The client certificate %s is not yet active")),
+ certFile);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#if LIBGNUTLS_VERSION_NUMBER >= 2
+/*
+ * The gnutls_x509_crt_get_basic_constraints function isn't
+ * available in GNUTLS 1.0.x branches. This isn't critical
+ * though, since gnutls_certificate_verify_peers2 will do
+ * pretty much the same check at runtime, so we can just
+ * disable this code
+ */
+static int
+qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds,
+ gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isServer,
+ bool isCA,
+ Error **errp)
+{
+ int status;
+
+ status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL);
+ trace_qcrypto_tls_creds_x509_check_basic_constraints(
+ creds, certFile, status);
+
+ if (status > 0) { /* It is a CA cert */
+ if (!isCA) {
+ error_setg(errp, isServer ?
+ "The certificate %s basic constraints show a CA, "
+ "but we need one for a server" :
+ "The certificate %s basic constraints show a CA, "
+ "but we need one for a client",
+ certFile);
+ return -1;
+ }
+ } else if (status == 0) { /* It is not a CA cert */
+ if (isCA) {
+ error_setg(errp,
+ "The certificate %s basic constraints do not "
+ "show a CA",
+ certFile);
+ return -1;
+ }
+ } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ /* Missing basicConstraints */
+ if (isCA) {
+ error_setg(errp,
+ "The certificate %s is missing basic constraints "
+ "for a CA",
+ certFile);
+ return -1;
+ }
+ } else { /* General error */
+ error_setg(errp,
+ "Unable to query certificate %s basic constraints: %s",
+ certFile, gnutls_strerror(status));
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+
+static int
+qcrypto_tls_creds_check_cert_key_usage(QCryptoTLSCredsX509 *creds,
+ gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isCA,
+ Error **errp)
+{
+ int status;
+ unsigned int usage = 0;
+ unsigned int critical = 0;
+
+ status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical);
+ trace_qcrypto_tls_creds_x509_check_key_usage(
+ creds, certFile, status, usage, critical);
+
+ if (status < 0) {
+ if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN :
+ GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT;
+ } else {
+ error_setg(errp,
+ "Unable to query certificate %s key usage: %s",
+ certFile, gnutls_strerror(status));
+ return -1;
+ }
+ }
+
+ if (isCA) {
+ if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s usage does not permit "
+ "certificate signing", certFile);
+ return -1;
+ }
+ }
+ } else {
+ if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s usage does not permit digital "
+ "signature", certFile);
+ return -1;
+ }
+ }
+ if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s usage does not permit key "
+ "encipherment", certFile);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qcrypto_tls_creds_check_cert_key_purpose(QCryptoTLSCredsX509 *creds,
+ gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isServer,
+ Error **errp)
+{
+ int status;
+ size_t i;
+ unsigned int purposeCritical;
+ unsigned int critical;
+ char *buffer = NULL;
+ size_t size;
+ bool allowClient = false, allowServer = false;
+
+ critical = 0;
+ for (i = 0; ; i++) {
+ size = 0;
+ status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer,
+ &size, NULL);
+
+ if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+
+ /* If there is no data at all, then we must allow
+ client/server to pass */
+ if (i == 0) {
+ allowServer = allowClient = true;
+ }
+ break;
+ }
+ if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ error_setg(errp,
+ "Unable to query certificate %s key purpose: %s",
+ certFile, gnutls_strerror(status));
+ return -1;
+ }
+
+ buffer = g_new0(char, size);
+
+ status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer,
+ &size, &purposeCritical);
+
+ if (status < 0) {
+ trace_qcrypto_tls_creds_x509_check_key_purpose(
+ creds, certFile, status, "<none>", purposeCritical);
+ g_free(buffer);
+ error_setg(errp,
+ "Unable to query certificate %s key purpose: %s",
+ certFile, gnutls_strerror(status));
+ return -1;
+ }
+ trace_qcrypto_tls_creds_x509_check_key_purpose(
+ creds, certFile, status, buffer, purposeCritical);
+ if (purposeCritical) {
+ critical = true;
+ }
+
+ if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_SERVER)) {
+ allowServer = true;
+ } else if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) {
+ allowClient = true;
+ } else if (g_str_equal(buffer, GNUTLS_KP_ANY)) {
+ allowServer = allowClient = true;
+ }
+
+ g_free(buffer);
+ buffer = NULL;
+ }
+
+ if (isServer) {
+ if (!allowServer) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s purpose does not allow "
+ "use with a TLS server", certFile);
+ return -1;
+ }
+ }
+ } else {
+ if (!allowClient) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s purpose does not allow use "
+ "with a TLS client", certFile);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds,
+ gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isServer,
+ bool isCA,
+ Error **errp)
+{
+ if (qcrypto_tls_creds_check_cert_times(cert, certFile,
+ isServer, isCA,
+ errp) < 0) {
+ return -1;
+ }
+
+#if LIBGNUTLS_VERSION_NUMBER >= 2
+ if (qcrypto_tls_creds_check_cert_basic_constraints(creds,
+ cert, certFile,
+ isServer, isCA,
+ errp) < 0) {
+ return -1;
+ }
+#endif
+
+ if (qcrypto_tls_creds_check_cert_key_usage(creds,
+ cert, certFile,
+ isCA, errp) < 0) {
+ return -1;
+ }
+
+ if (!isCA &&
+ qcrypto_tls_creds_check_cert_key_purpose(creds,
+ cert, certFile,
+ isServer, errp) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t cert,
+ const char *certFile,
+ gnutls_x509_crt_t *cacerts,
+ size_t ncacerts,
+ const char *cacertFile,
+ bool isServer,
+ Error **errp)
+{
+ unsigned int status;
+
+ if (gnutls_x509_crt_list_verify(&cert, 1,
+ cacerts, ncacerts,
+ NULL, 0,
+ 0, &status) < 0) {
+ error_setg(errp, isServer ?
+ "Unable to verify server certificate %s against "
+ "CA certificate %s" :
+ "Unable to verify client certificate %s against "
+ "CA certificate %s",
+ certFile, cacertFile);
+ return -1;
+ }
+
+ if (status != 0) {
+ const char *reason = "Invalid certificate";
+
+ if (status & GNUTLS_CERT_INVALID) {
+ reason = "The certificate is not trusted";
+ }
+
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+ reason = "The certificate hasn't got a known issuer";
+ }
+
+ if (status & GNUTLS_CERT_REVOKED) {
+ reason = "The certificate has been revoked";
+ }
+
+#ifndef GNUTLS_1_0_COMPAT
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
+ reason = "The certificate uses an insecure algorithm";
+ }
+#endif
+
+ error_setg(errp,
+ "Our own certificate %s failed validation against %s: %s",
+ certFile, cacertFile, reason);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static gnutls_x509_crt_t
+qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds,
+ const char *certFile,
+ bool isServer,
+ Error **errp)
+{
+ gnutls_datum_t data;
+ gnutls_x509_crt_t cert = NULL;
+ char *buf = NULL;
+ gsize buflen;
+ GError *gerr;
+ int ret = -1;
+
+ trace_qcrypto_tls_creds_x509_load_cert(creds, isServer, certFile);
+
+ if (gnutls_x509_crt_init(&cert) < 0) {
+ error_setg(errp, "Unable to initialize certificate");
+ goto cleanup;
+ }
+
+ if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) {
+ error_setg(errp, "Cannot load CA cert list %s: %s",
+ certFile, gerr->message);
+ g_error_free(gerr);
+ goto cleanup;
+ }
+
+ data.data = (unsigned char *)buf;
+ data.size = strlen(buf);
+
+ if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) {
+ error_setg(errp, isServer ?
+ "Unable to import server certificate %s" :
+ "Unable to import client certificate %s",
+ certFile);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret != 0) {
+ gnutls_x509_crt_deinit(cert);
+ cert = NULL;
+ }
+ g_free(buf);
+ return cert;
+}
+
+
+static int
+qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds,
+ const char *certFile,
+ gnutls_x509_crt_t *certs,
+ unsigned int certMax,
+ size_t *ncerts,
+ Error **errp)
+{
+ gnutls_datum_t data;
+ char *buf = NULL;
+ gsize buflen;
+ int ret = -1;
+ GError *gerr = NULL;
+
+ *ncerts = 0;
+ trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile);
+
+ if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) {
+ error_setg(errp, "Cannot load CA cert list %s: %s",
+ certFile, gerr->message);
+ g_error_free(gerr);
+ goto cleanup;
+ }
+
+ data.data = (unsigned char *)buf;
+ data.size = strlen(buf);
+
+ if (gnutls_x509_crt_list_import(certs, &certMax, &data,
+ GNUTLS_X509_FMT_PEM, 0) < 0) {
+ error_setg(errp,
+ "Unable to import CA certificate list %s",
+ certFile);
+ goto cleanup;
+ }
+ *ncerts = certMax;
+
+ ret = 0;
+
+ cleanup:
+ g_free(buf);
+ return ret;
+}
+
+
+#define MAX_CERTS 16
+static int
+qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
+ bool isServer,
+ const char *cacertFile,
+ const char *certFile,
+ Error **errp)
+{
+ gnutls_x509_crt_t cert = NULL;
+ gnutls_x509_crt_t cacerts[MAX_CERTS];
+ size_t ncacerts = 0;
+ size_t i;
+ int ret = -1;
+
+ memset(cacerts, 0, sizeof(cacerts));
+ if (certFile &&
+ access(certFile, R_OK) == 0) {
+ cert = qcrypto_tls_creds_load_cert(creds,
+ certFile, isServer,
+ errp);
+ if (!cert) {
+ goto cleanup;
+ }
+ }
+ if (access(cacertFile, R_OK) == 0) {
+ if (qcrypto_tls_creds_load_ca_cert_list(creds,
+ cacertFile, cacerts,
+ MAX_CERTS, &ncacerts,
+ errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (cert &&
+ qcrypto_tls_creds_check_cert(creds,
+ cert, certFile, isServer,
+ false, errp) < 0) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < ncacerts; i++) {
+ if (qcrypto_tls_creds_check_cert(creds,
+ cacerts[i], cacertFile,
+ isServer, true, errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (cert && ncacerts &&
+ qcrypto_tls_creds_check_cert_pair(cert, certFile, cacerts,
+ ncacerts, cacertFile,
+ isServer, errp) < 0) {
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (cert) {
+ gnutls_x509_crt_deinit(cert);
+ }
+ for (i = 0; i < ncacerts; i++) {
+ gnutls_x509_crt_deinit(cacerts[i]);
+ }
+ return ret;
+}
+
+
+static int
+qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
+ Error **errp)
+{
+ char *cacert = NULL, *cacrl = NULL, *cert = NULL,
+ *key = NULL, *dhparams = NULL;
+ int ret;
+ int rv = -1;
+
+ trace_qcrypto_tls_creds_x509_load(creds,
+ creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CA_CERT,
+ true, &cacert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CA_CRL,
+ false, &cacrl, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
+ true, &cert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
+ true, &key, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_DH_PARAMS,
+ false, &dhparams, errp) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CA_CERT,
+ true, &cacert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
+ false, &cert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
+ false, &key, errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (creds->sanityCheck &&
+ qcrypto_tls_creds_x509_sanity_check(creds,
+ creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ cacert, cert, errp) < 0) {
+ goto cleanup;
+ }
+
+ ret = gnutls_certificate_allocate_credentials(&creds->data);
+ if (ret < 0) {
+ error_setg(errp, "Cannot allocate credentials: '%s'",
+ gnutls_strerror(ret));
+ goto cleanup;
+ }
+
+ ret = gnutls_certificate_set_x509_trust_file(creds->data,
+ cacert,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ error_setg(errp, "Cannot load CA certificate '%s': %s",
+ cacert, gnutls_strerror(ret));
+ goto cleanup;
+ }
+
+ if (cert != NULL && key != NULL) {
+#if GNUTLS_VERSION_NUMBER >= 0x030111
+ char *password = NULL;
+ if (creds->passwordid) {
+ password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
+ errp);
+ if (!password) {
+ goto cleanup;
+ }
+ }
+ ret = gnutls_certificate_set_x509_key_file2(creds->data,
+ cert, key,
+ GNUTLS_X509_FMT_PEM,
+ password,
+ 0);
+ g_free(password);
+#else /* GNUTLS_VERSION_NUMBER < 0x030111 */
+ if (creds->passwordid) {
+ error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11");
+ goto cleanup;
+ }
+ ret = gnutls_certificate_set_x509_key_file(creds->data,
+ cert, key,
+ GNUTLS_X509_FMT_PEM);
+#endif /* GNUTLS_VERSION_NUMBER < 0x030111 */
+ if (ret < 0) {
+ error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
+ cert, key, gnutls_strerror(ret));
+ goto cleanup;
+ }
+ }
+
+ if (cacrl != NULL) {
+ ret = gnutls_certificate_set_x509_crl_file(creds->data,
+ cacrl,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ error_setg(errp, "Cannot load CRL '%s': %s",
+ cacrl, gnutls_strerror(ret));
+ goto cleanup;
+ }
+ }
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
+ &creds->parent_obj.dh_params,
+ errp) < 0) {
+ goto cleanup;
+ }
+ gnutls_certificate_set_dh_params(creds->data,
+ creds->parent_obj.dh_params);
+ }
+
+ rv = 0;
+ cleanup:
+ g_free(cacert);
+ g_free(cacrl);
+ g_free(cert);
+ g_free(key);
+ g_free(dhparams);
+ return rv;
+}
+
+
+static void
+qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds)
+{
+ if (creds->data) {
+ gnutls_certificate_free_credentials(creds->data);
+ creds->data = NULL;
+ }
+ if (creds->parent_obj.dh_params) {
+ gnutls_dh_params_deinit(creds->parent_obj.dh_params);
+ creds->parent_obj.dh_params = NULL;
+ }
+}
+
+
+#else /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "TLS credentials support requires GNUTLS");
+}
+
+
+static void
+qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED)
+{
+ /* nada */
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_x509_prop_set_loaded(Object *obj,
+ bool value,
+ Error **errp)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ if (value) {
+ qcrypto_tls_creds_x509_load(creds, errp);
+ } else {
+ qcrypto_tls_creds_x509_unload(creds);
+ }
+}
+
+
+#ifdef CONFIG_GNUTLS
+
+
+static bool
+qcrypto_tls_creds_x509_prop_get_loaded(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ return creds->data != NULL;
+}
+
+
+#else /* ! CONFIG_GNUTLS */
+
+
+static bool
+qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED,
+ Error **errp G_GNUC_UNUSED)
+{
+ return false;
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_x509_prop_set_sanity(Object *obj,
+ bool value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ creds->sanityCheck = value;
+}
+
+
+static void
+qcrypto_tls_creds_x509_prop_set_passwordid(Object *obj,
+ const char *value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ creds->passwordid = g_strdup(value);
+}
+
+
+static char *
+qcrypto_tls_creds_x509_prop_get_passwordid(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ return g_strdup(creds->passwordid);
+}
+
+
+static bool
+qcrypto_tls_creds_x509_prop_get_sanity(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ return creds->sanityCheck;
+}
+
+
+static void
+qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp)
+{
+ object_property_set_bool(OBJECT(uc), true, "loaded", errp);
+}
+
+
+static void
+qcrypto_tls_creds_x509_init(Object *obj)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ creds->sanityCheck = true;
+}
+
+
+static void
+qcrypto_tls_creds_x509_finalize(Object *obj)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ g_free(creds->passwordid);
+ qcrypto_tls_creds_x509_unload(creds);
+}
+
+
+static void
+qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = qcrypto_tls_creds_x509_complete;
+
+ object_class_property_add_bool(oc, "loaded",
+ qcrypto_tls_creds_x509_prop_get_loaded,
+ qcrypto_tls_creds_x509_prop_set_loaded,
+ NULL);
+ object_class_property_add_bool(oc, "sanity-check",
+ qcrypto_tls_creds_x509_prop_get_sanity,
+ qcrypto_tls_creds_x509_prop_set_sanity,
+ NULL);
+ object_class_property_add_str(oc, "passwordid",
+ qcrypto_tls_creds_x509_prop_get_passwordid,
+ qcrypto_tls_creds_x509_prop_set_passwordid,
+ NULL);
+}
+
+
+static const TypeInfo qcrypto_tls_creds_x509_info = {
+ .parent = TYPE_QCRYPTO_TLS_CREDS,
+ .name = TYPE_QCRYPTO_TLS_CREDS_X509,
+ .instance_size = sizeof(QCryptoTLSCredsX509),
+ .instance_init = qcrypto_tls_creds_x509_init,
+ .instance_finalize = qcrypto_tls_creds_x509_finalize,
+ .class_size = sizeof(QCryptoTLSCredsX509Class),
+ .class_init = qcrypto_tls_creds_x509_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qcrypto_tls_creds_x509_register_types(void)
+{
+ type_register_static(&qcrypto_tls_creds_x509_info);
+}
+
+
+type_init(qcrypto_tls_creds_x509_register_types);
diff --git a/qemu/crypto/tlssession.c b/qemu/crypto/tlssession.c
new file mode 100644
index 000000000..a543e5a57
--- /dev/null
+++ b/qemu/crypto/tlssession.c
@@ -0,0 +1,576 @@
+/*
+ * QEMU crypto TLS session support
+ *
+ * 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 "qemu/osdep.h"
+#include "crypto/tlssession.h"
+#include "crypto/tlscredsanon.h"
+#include "crypto/tlscredsx509.h"
+#include "qapi/error.h"
+#include "qemu/acl.h"
+#include "trace.h"
+
+#ifdef CONFIG_GNUTLS
+
+
+#include <gnutls/x509.h>
+
+
+struct QCryptoTLSSession {
+ QCryptoTLSCreds *creds;
+ gnutls_session_t handle;
+ char *hostname;
+ char *aclname;
+ bool handshakeComplete;
+ QCryptoTLSSessionWriteFunc writeFunc;
+ QCryptoTLSSessionReadFunc readFunc;
+ void *opaque;
+ char *peername;
+};
+
+
+void
+qcrypto_tls_session_free(QCryptoTLSSession *session)
+{
+ if (!session) {
+ return;
+ }
+
+ gnutls_deinit(session->handle);
+ g_free(session->hostname);
+ g_free(session->peername);
+ g_free(session->aclname);
+ object_unref(OBJECT(session->creds));
+ g_free(session);
+}
+
+
+static ssize_t
+qcrypto_tls_session_push(void *opaque, const void *buf, size_t len)
+{
+ QCryptoTLSSession *session = opaque;
+
+ if (!session->writeFunc) {
+ errno = EIO;
+ return -1;
+ };
+
+ return session->writeFunc(buf, len, session->opaque);
+}
+
+
+static ssize_t
+qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
+{
+ QCryptoTLSSession *session = opaque;
+
+ if (!session->readFunc) {
+ errno = EIO;
+ return -1;
+ };
+
+ return session->readFunc(buf, len, session->opaque);
+}
+
+
+QCryptoTLSSession *
+qcrypto_tls_session_new(QCryptoTLSCreds *creds,
+ const char *hostname,
+ const char *aclname,
+ QCryptoTLSCredsEndpoint endpoint,
+ Error **errp)
+{
+ QCryptoTLSSession *session;
+ int ret;
+
+ session = g_new0(QCryptoTLSSession, 1);
+ trace_qcrypto_tls_session_new(
+ session, creds, hostname ? hostname : "<none>",
+ aclname ? aclname : "<none>", endpoint);
+
+ if (hostname) {
+ session->hostname = g_strdup(hostname);
+ }
+ if (aclname) {
+ session->aclname = g_strdup(aclname);
+ }
+ session->creds = creds;
+ object_ref(OBJECT(creds));
+
+ if (creds->endpoint != endpoint) {
+ error_setg(errp, "Credentials endpoint doesn't match session");
+ goto error;
+ }
+
+ if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ ret = gnutls_init(&session->handle, GNUTLS_SERVER);
+ } else {
+ ret = gnutls_init(&session->handle, GNUTLS_CLIENT);
+ }
+ if (ret < 0) {
+ error_setg(errp, "Cannot initialize TLS session: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+
+ if (object_dynamic_cast(OBJECT(creds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
+ QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds);
+
+ ret = gnutls_priority_set_direct(session->handle,
+ "NORMAL:+ANON-DH", NULL);
+ if (ret < 0) {
+ error_setg(errp, "Unable to set TLS session priority: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ ret = gnutls_credentials_set(session->handle,
+ GNUTLS_CRD_ANON,
+ acreds->data.server);
+ } else {
+ ret = gnutls_credentials_set(session->handle,
+ GNUTLS_CRD_ANON,
+ acreds->data.client);
+ }
+ if (ret < 0) {
+ error_setg(errp, "Cannot set session credentials: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+ } else if (object_dynamic_cast(OBJECT(creds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
+ QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds);
+
+ ret = gnutls_set_default_priority(session->handle);
+ if (ret < 0) {
+ error_setg(errp, "Cannot set default TLS session priority: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+ ret = gnutls_credentials_set(session->handle,
+ GNUTLS_CRD_CERTIFICATE,
+ tcreds->data);
+ if (ret < 0) {
+ error_setg(errp, "Cannot set session credentials: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+
+ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ /* This requests, but does not enforce a client cert.
+ * The cert checking code later does enforcement */
+ gnutls_certificate_server_set_request(session->handle,
+ GNUTLS_CERT_REQUEST);
+ }
+ } else {
+ error_setg(errp, "Unsupported TLS credentials type %s",
+ object_get_typename(OBJECT(creds)));
+ goto error;
+ }
+
+ gnutls_transport_set_ptr(session->handle, session);
+ gnutls_transport_set_push_function(session->handle,
+ qcrypto_tls_session_push);
+ gnutls_transport_set_pull_function(session->handle,
+ qcrypto_tls_session_pull);
+
+ return session;
+
+ error:
+ qcrypto_tls_session_free(session);
+ return NULL;
+}
+
+static int
+qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
+ Error **errp)
+{
+ int ret;
+ unsigned int status;
+ const gnutls_datum_t *certs;
+ unsigned int nCerts, i;
+ time_t now;
+ gnutls_x509_crt_t cert = NULL;
+
+ now = time(NULL);
+ if (now == ((time_t)-1)) {
+ error_setg_errno(errp, errno, "Cannot get current time");
+ return -1;
+ }
+
+ ret = gnutls_certificate_verify_peers2(session->handle, &status);
+ if (ret < 0) {
+ error_setg(errp, "Verify failed: %s", gnutls_strerror(ret));
+ return -1;
+ }
+
+ if (status != 0) {
+ const char *reason = "Invalid certificate";
+
+ if (status & GNUTLS_CERT_INVALID) {
+ reason = "The certificate is not trusted";
+ }
+
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+ reason = "The certificate hasn't got a known issuer";
+ }
+
+ if (status & GNUTLS_CERT_REVOKED) {
+ reason = "The certificate has been revoked";
+ }
+
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
+ reason = "The certificate uses an insecure algorithm";
+ }
+
+ error_setg(errp, "%s", reason);
+ return -1;
+ }
+
+ certs = gnutls_certificate_get_peers(session->handle, &nCerts);
+ if (!certs) {
+ error_setg(errp, "No certificate peers");
+ return -1;
+ }
+
+ for (i = 0; i < nCerts; i++) {
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0) {
+ error_setg(errp, "Cannot initialize certificate: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+
+ ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ error_setg(errp, "Cannot import certificate: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+
+ if (gnutls_x509_crt_get_expiration_time(cert) < now) {
+ error_setg(errp, "The certificate has expired");
+ goto error;
+ }
+
+ if (gnutls_x509_crt_get_activation_time(cert) > now) {
+ error_setg(errp, "The certificate is not yet activated");
+ goto error;
+ }
+
+ if (gnutls_x509_crt_get_activation_time(cert) > now) {
+ error_setg(errp, "The certificate is not yet activated");
+ goto error;
+ }
+
+ if (i == 0) {
+ size_t dnameSize = 1024;
+ session->peername = g_malloc(dnameSize);
+ requery:
+ ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ session->peername = g_realloc(session->peername,
+ dnameSize);
+ goto requery;
+ }
+ error_setg(errp, "Cannot get client distinguished name: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+ if (session->aclname) {
+ qemu_acl *acl = qemu_acl_find(session->aclname);
+ int allow;
+ if (!acl) {
+ error_setg(errp, "Cannot find ACL %s",
+ session->aclname);
+ goto error;
+ }
+
+ allow = qemu_acl_party_is_allowed(acl, session->peername);
+
+ if (!allow) {
+ error_setg(errp, "TLS x509 ACL check for %s is denied",
+ session->peername);
+ goto error;
+ }
+ }
+ if (session->hostname) {
+ if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) {
+ error_setg(errp,
+ "Certificate does not match the hostname %s",
+ session->hostname);
+ goto error;
+ }
+ }
+ }
+
+ gnutls_x509_crt_deinit(cert);
+ }
+
+ return 0;
+
+ error:
+ gnutls_x509_crt_deinit(cert);
+ return -1;
+}
+
+
+int
+qcrypto_tls_session_check_credentials(QCryptoTLSSession *session,
+ Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(session->creds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
+ return 0;
+ } else if (object_dynamic_cast(OBJECT(session->creds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
+ if (session->creds->verifyPeer) {
+ return qcrypto_tls_session_check_certificate(session,
+ errp);
+ } else {
+ return 0;
+ }
+ } else {
+ error_setg(errp, "Unexpected credential type %s",
+ object_get_typename(OBJECT(session->creds)));
+ return -1;
+ }
+}
+
+
+void
+qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session,
+ QCryptoTLSSessionWriteFunc writeFunc,
+ QCryptoTLSSessionReadFunc readFunc,
+ void *opaque)
+{
+ session->writeFunc = writeFunc;
+ session->readFunc = readFunc;
+ session->opaque = opaque;
+}
+
+
+ssize_t
+qcrypto_tls_session_write(QCryptoTLSSession *session,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret = gnutls_record_send(session->handle, buf, len);
+
+ if (ret < 0) {
+ switch (ret) {
+ case GNUTLS_E_AGAIN:
+ errno = EAGAIN;
+ break;
+ case GNUTLS_E_INTERRUPTED:
+ errno = EINTR;
+ break;
+ default:
+ errno = EIO;
+ break;
+ }
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+ssize_t
+qcrypto_tls_session_read(QCryptoTLSSession *session,
+ char *buf,
+ size_t len)
+{
+ ssize_t ret = gnutls_record_recv(session->handle, buf, len);
+
+ if (ret < 0) {
+ switch (ret) {
+ case GNUTLS_E_AGAIN:
+ errno = EAGAIN;
+ break;
+ case GNUTLS_E_INTERRUPTED:
+ errno = EINTR;
+ break;
+ default:
+ errno = EIO;
+ break;
+ }
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int
+qcrypto_tls_session_handshake(QCryptoTLSSession *session,
+ Error **errp)
+{
+ int ret = gnutls_handshake(session->handle);
+ if (ret == 0) {
+ session->handshakeComplete = true;
+ } else {
+ if (ret == GNUTLS_E_INTERRUPTED ||
+ ret == GNUTLS_E_AGAIN) {
+ ret = 1;
+ } else {
+ error_setg(errp, "TLS handshake failed: %s",
+ gnutls_strerror(ret));
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+
+QCryptoTLSSessionHandshakeStatus
+qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session)
+{
+ if (session->handshakeComplete) {
+ return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
+ } else if (gnutls_record_get_direction(session->handle) == 0) {
+ return QCRYPTO_TLS_HANDSHAKE_RECVING;
+ } else {
+ return QCRYPTO_TLS_HANDSHAKE_SENDING;
+ }
+}
+
+
+int
+qcrypto_tls_session_get_key_size(QCryptoTLSSession *session,
+ Error **errp)
+{
+ gnutls_cipher_algorithm_t cipher;
+ int ssf;
+
+ cipher = gnutls_cipher_get(session->handle);
+ ssf = gnutls_cipher_get_key_size(cipher);
+ if (!ssf) {
+ error_setg(errp, "Cannot get TLS cipher key size");
+ return -1;
+ }
+ return ssf;
+}
+
+
+char *
+qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session)
+{
+ if (session->peername) {
+ return g_strdup(session->peername);
+ }
+ return NULL;
+}
+
+
+#else /* ! CONFIG_GNUTLS */
+
+
+QCryptoTLSSession *
+qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED,
+ const char *hostname G_GNUC_UNUSED,
+ const char *aclname G_GNUC_UNUSED,
+ QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "TLS requires GNUTLS support");
+ return NULL;
+}
+
+
+void
+qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED)
+{
+}
+
+
+int
+qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "TLS requires GNUTLS support");
+ return -1;
+}
+
+
+void
+qcrypto_tls_session_set_callbacks(
+ QCryptoTLSSession *sess G_GNUC_UNUSED,
+ QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED,
+ QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED,
+ void *opaque G_GNUC_UNUSED)
+{
+}
+
+
+ssize_t
+qcrypto_tls_session_write(QCryptoTLSSession *sess,
+ const char *buf,
+ size_t len)
+{
+ errno = -EIO;
+ return -1;
+}
+
+
+ssize_t
+qcrypto_tls_session_read(QCryptoTLSSession *sess,
+ char *buf,
+ size_t len)
+{
+ errno = -EIO;
+ return -1;
+}
+
+
+int
+qcrypto_tls_session_handshake(QCryptoTLSSession *sess,
+ Error **errp)
+{
+ error_setg(errp, "TLS requires GNUTLS support");
+ return -1;
+}
+
+
+QCryptoTLSSessionHandshakeStatus
+qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess)
+{
+ return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
+}
+
+
+int
+qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess,
+ Error **errp)
+{
+ error_setg(errp, "TLS requires GNUTLS support");
+ return -1;
+}
+
+
+char *
+qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess)
+{
+ return NULL;
+}
+
+#endif
diff --git a/qemu/crypto/xts.c b/qemu/crypto/xts.c
new file mode 100644
index 000000000..95212341f
--- /dev/null
+++ b/qemu/crypto/xts.c
@@ -0,0 +1,230 @@
+/*
+ * QEMU Crypto XTS cipher mode
+ *
+ * Copyright (c) 2015-2016 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/>.
+ *
+ * This code is originally derived from public domain / WTFPL code in
+ * LibTomCrypt crytographic library http://libtom.org. The XTS code
+ * was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com)
+ * to the LibTom Projects
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/xts.h"
+
+static void xts_mult_x(uint8_t *I)
+{
+ int x;
+ uint8_t t, tt;
+
+ for (x = t = 0; x < 16; x++) {
+ tt = I[x] >> 7;
+ I[x] = ((I[x] << 1) | t) & 0xFF;
+ t = tt;
+ }
+ if (tt) {
+ I[0] ^= 0x87;
+ }
+}
+
+
+/**
+ * xts_tweak_uncrypt:
+ * @param ctxt: the cipher context
+ * @param func: the cipher function
+ * @src: buffer providing the cipher text of XTS_BLOCK_SIZE bytes
+ * @dst: buffer to output the plain text of XTS_BLOCK_SIZE bytes
+ * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
+ *
+ * Decrypt data with a tweak
+ */
+static void xts_tweak_decrypt(const void *ctx,
+ xts_cipher_func *func,
+ const uint8_t *src,
+ uint8_t *dst,
+ uint8_t *iv)
+{
+ unsigned long x;
+
+ /* tweak encrypt block i */
+ for (x = 0; x < XTS_BLOCK_SIZE; x++) {
+ dst[x] = src[x] ^ iv[x];
+ }
+
+ func(ctx, XTS_BLOCK_SIZE, dst, dst);
+
+ for (x = 0; x < XTS_BLOCK_SIZE; x++) {
+ dst[x] = dst[x] ^ iv[x];
+ }
+
+ /* LFSR the tweak */
+ xts_mult_x(iv);
+}
+
+
+void xts_decrypt(const void *datactx,
+ const void *tweakctx,
+ xts_cipher_func *encfunc,
+ xts_cipher_func *decfunc,
+ uint8_t *iv,
+ size_t length,
+ uint8_t *dst,
+ const uint8_t *src)
+{
+ uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE];
+ unsigned long i, m, mo, lim;
+
+ /* get number of blocks */
+ m = length >> 4;
+ mo = length & 15;
+
+ /* must have at least one full block */
+ g_assert(m != 0);
+
+ if (mo == 0) {
+ lim = m;
+ } else {
+ lim = m - 1;
+ }
+
+ /* encrypt the iv */
+ encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv);
+
+ for (i = 0; i < lim; i++) {
+ xts_tweak_decrypt(datactx, decfunc, src, dst, T);
+
+ src += XTS_BLOCK_SIZE;
+ dst += XTS_BLOCK_SIZE;
+ }
+
+ /* if length is not a multiple of XTS_BLOCK_SIZE then */
+ if (mo > 0) {
+ memcpy(CC, T, XTS_BLOCK_SIZE);
+ xts_mult_x(CC);
+
+ /* PP = tweak decrypt block m-1 */
+ xts_tweak_decrypt(datactx, decfunc, src, PP, CC);
+
+ /* Pm = first length % XTS_BLOCK_SIZE bytes of PP */
+ for (i = 0; i < mo; i++) {
+ CC[i] = src[XTS_BLOCK_SIZE + i];
+ dst[XTS_BLOCK_SIZE + i] = PP[i];
+ }
+ for (; i < XTS_BLOCK_SIZE; i++) {
+ CC[i] = PP[i];
+ }
+
+ /* Pm-1 = Tweak uncrypt CC */
+ xts_tweak_decrypt(datactx, decfunc, CC, dst, T);
+ }
+
+ /* Decrypt the iv back */
+ decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T);
+}
+
+
+/**
+ * xts_tweak_crypt:
+ * @param ctxt: the cipher context
+ * @param func: the cipher function
+ * @src: buffer providing the plain text of XTS_BLOCK_SIZE bytes
+ * @dst: buffer to output the cipher text of XTS_BLOCK_SIZE bytes
+ * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
+ *
+ * Encrypt data with a tweak
+ */
+static void xts_tweak_encrypt(const void *ctx,
+ xts_cipher_func *func,
+ const uint8_t *src,
+ uint8_t *dst,
+ uint8_t *iv)
+{
+ unsigned long x;
+
+ /* tweak encrypt block i */
+ for (x = 0; x < XTS_BLOCK_SIZE; x++) {
+ dst[x] = src[x] ^ iv[x];
+ }
+
+ func(ctx, XTS_BLOCK_SIZE, dst, dst);
+
+ for (x = 0; x < XTS_BLOCK_SIZE; x++) {
+ dst[x] = dst[x] ^ iv[x];
+ }
+
+ /* LFSR the tweak */
+ xts_mult_x(iv);
+}
+
+
+void xts_encrypt(const void *datactx,
+ const void *tweakctx,
+ xts_cipher_func *encfunc,
+ xts_cipher_func *decfunc,
+ uint8_t *iv,
+ size_t length,
+ uint8_t *dst,
+ const uint8_t *src)
+{
+ uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE];
+ unsigned long i, m, mo, lim;
+
+ /* get number of blocks */
+ m = length >> 4;
+ mo = length & 15;
+
+ /* must have at least one full block */
+ g_assert(m != 0);
+
+ if (mo == 0) {
+ lim = m;
+ } else {
+ lim = m - 1;
+ }
+
+ /* encrypt the iv */
+ encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv);
+
+ for (i = 0; i < lim; i++) {
+ xts_tweak_encrypt(datactx, encfunc, src, dst, T);
+
+ dst += XTS_BLOCK_SIZE;
+ src += XTS_BLOCK_SIZE;
+ }
+
+ /* if length is not a multiple of XTS_BLOCK_SIZE then */
+ if (mo > 0) {
+ /* CC = tweak encrypt block m-1 */
+ xts_tweak_encrypt(datactx, encfunc, src, CC, T);
+
+ /* Cm = first length % XTS_BLOCK_SIZE bytes of CC */
+ for (i = 0; i < mo; i++) {
+ PP[i] = src[XTS_BLOCK_SIZE + i];
+ dst[XTS_BLOCK_SIZE + i] = CC[i];
+ }
+
+ for (; i < XTS_BLOCK_SIZE; i++) {
+ PP[i] = CC[i];
+ }
+
+ /* Cm-1 = Tweak encrypt PP */
+ xts_tweak_encrypt(datactx, encfunc, PP, dst, T);
+ }
+
+ /* Decrypt the iv back */
+ decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T);
+}