diff options
45 files changed, 6488 insertions, 109 deletions
diff --git a/Makefile.objs b/Makefile.objs index fbcaa7471f..8f705f6202 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -1,6 +1,6 @@ ####################################################################### # Common libraries for tools and emulators -stub-obj-y = stubs/ +stub-obj-y = stubs/ crypto/ util-obj-y = util/ qobject/ qapi/ util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o @@ -306,8 +306,10 @@ gtkabi="" gtk_gl="no" gnutls="" gnutls_hash="" +gnutls_rnd="" nettle="" gcrypt="" +gcrypt_kdf="no" vte="" virglrenderer="" tpm="yes" @@ -2201,6 +2203,13 @@ if test "$gnutls" != "no"; then gnutls_hash="no" fi + # gnutls_rnd requires >= 2.11.0 + if $pkg_config --exists "gnutls >= 2.11.0"; then + gnutls_rnd="yes" + else + gnutls_rnd="no" + fi + if $pkg_config --exists 'gnutls >= 3.0'; then gnutls_gcrypt=no gnutls_nettle=yes @@ -2228,9 +2237,11 @@ if test "$gnutls" != "no"; then else gnutls="no" gnutls_hash="no" + gnutls_rnd="no" fi else gnutls_hash="no" + gnutls_rnd="no" fi @@ -2292,6 +2303,19 @@ if test "$gcrypt" != "no"; then if test -z "$nettle"; then nettle="no" fi + + cat > $TMPC << EOF +#include <gcrypt.h> +int main(void) { + gcry_kdf_derive(NULL, 0, GCRY_KDF_PBKDF2, + GCRY_MD_SHA256, + NULL, 0, 0, 0, NULL); + return 0; +} +EOF + if compile_prog "$gcrypt_cflags" "$gcrypt_libs" ; then + gcrypt_kdf=yes + fi else if test "$gcrypt" = "yes"; then feature_not_found "gcrypt" "Install gcrypt devel" @@ -4714,7 +4738,9 @@ echo "GTK support $gtk" echo "GTK GL support $gtk_gl" echo "GNUTLS support $gnutls" echo "GNUTLS hash $gnutls_hash" +echo "GNUTLS rnd $gnutls_rnd" echo "libgcrypt $gcrypt" +echo "libgcrypt kdf $gcrypt_kdf" if test "$nettle" = "yes"; then echo "nettle $nettle ($nettle_version)" else @@ -5092,8 +5118,14 @@ fi if test "$gnutls_hash" = "yes" ; then echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak fi +if test "$gnutls_rnd" = "yes" ; then + echo "CONFIG_GNUTLS_RND=y" >> $config_host_mak +fi if test "$gcrypt" = "yes" ; then echo "CONFIG_GCRYPT=y" >> $config_host_mak + if test "$gcrypt_kdf" = "yes" ; then + echo "CONFIG_GCRYPT_KDF=y" >> $config_host_mak + fi fi if test "$nettle" = "yes" ; then echo "CONFIG_NETTLE=y" >> $config_host_mak diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index a3135f1ddf..9f2c87eafa 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -8,6 +8,23 @@ 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) += pbkdf-nettle.o +crypto-obj-$(if $(CONFIG_NETTLE),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/crypto/afsplit.c b/crypto/afsplit.c new file mode 100644 index 0000000000..8074913cdd --- /dev/null +++ b/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/crypto/block-luks.c b/crypto/block-luks.c new file mode 100644 index 0000000000..58c1b940e1 --- /dev/null +++ b/crypto/block-luks.c @@ -0,0 +1,1328 @@ +/* + * 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 "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/crypto/block-luks.h b/crypto/block-luks.h new file mode 100644 index 0000000000..0934138aaa --- /dev/null +++ b/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/crypto/block-qcow.c b/crypto/block-qcow.c new file mode 100644 index 0000000000..9f378e8635 --- /dev/null +++ b/crypto/block-qcow.c @@ -0,0 +1,173 @@ +/* + * 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 "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/crypto/block-qcow.h b/crypto/block-qcow.h new file mode 100644 index 0000000000..569f836100 --- /dev/null +++ b/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/crypto/block.c b/crypto/block.c new file mode 100644 index 0000000000..524ed91db8 --- /dev/null +++ b/crypto/block.c @@ -0,0 +1,260 @@ +/* + * 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 "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/crypto/blockpriv.h b/crypto/blockpriv.h new file mode 100644 index 0000000000..62970859d0 --- /dev/null +++ b/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/crypto/cipher-builtin.c b/crypto/cipher-builtin.c index 138b7a005a..88963f65c8 100644 --- a/crypto/cipher-builtin.c +++ b/crypto/cipher-builtin.c @@ -21,11 +21,17 @@ #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; + QCryptoCipherBuiltinAESContext key; + QCryptoCipherBuiltinAESContext key_tweak; uint8_t iv[AES_BLOCK_SIZE]; }; typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB; @@ -67,6 +73,82 @@ static void qcrypto_cipher_free_aes(QCryptoCipher *cipher) } +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; @@ -166,21 +242,46 @@ 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; @@ -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; } diff --git a/crypto/cipher-gcrypt.c b/crypto/cipher-gcrypt.c index 56d4c9d10b..ede2f70df8 100644 --- a/crypto/cipher-gcrypt.c +++ b/crypto/cipher-gcrypt.c @@ -19,6 +19,8 @@ */ #include "qemu/osdep.h" +#include "crypto/xts.h" + #include <gcrypt.h> @@ -29,6 +31,12 @@ 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; @@ -38,7 +46,9 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg) 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, @@ -53,6 +63,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: + case QCRYPTO_CIPHER_MODE_XTS: gcrymode = GCRY_CIPHER_MODE_ECB; break; case QCRYPTO_CIPHER_MODE_CBC: @@ -63,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; } @@ -84,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; @@ -101,6 +136,14 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, 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 @@ -112,13 +155,44 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, g_free(rfbkey); ctx->blocksize = 8; } else { - err = gcry_cipher_setkey(ctx->handle, key, nkey); - ctx->blocksize = 16; + 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 = ctx; @@ -126,6 +200,9 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, error: 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; @@ -140,11 +217,35 @@ void qcrypto_cipher_free(QCryptoCipher *cipher) } 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, @@ -160,13 +261,20 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher, return -1; } - 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; + 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; @@ -188,13 +296,20 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, return -1; } - 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; + 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; @@ -213,12 +328,16 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher, return -1; } - gcry_cipher_reset(ctx->handle); - err = gcry_cipher_setiv(ctx->handle, iv, niv); - if (err != 0) { - error_setg(errp, "Cannot set IV: %s", + 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 -1; + } } return 0; diff --git a/crypto/cipher-nettle.c b/crypto/cipher-nettle.c index cd2675c0bc..3c982e483a 100644 --- a/crypto/cipher-nettle.c +++ b/crypto/cipher-nettle.c @@ -19,10 +19,15 @@ */ #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; @@ -39,16 +44,23 @@ 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_wrapper(cipher_ctx_t ctx, cipher_length_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, 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, @@ -63,12 +75,52 @@ static void des_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, des_decrypt(ctx, length, dst, src); } +static void cast128_encrypt_wrapper(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_wrapper(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_wrapper(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_encrypt(ctx, length, dst, src); +} + +static void serpent_decrypt_wrapper(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_wrapper(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_wrapper(cipher_ctx_t ctx, cipher_length_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; + /* Primary cipher context for all modes */ + void *ctx; + /* Second cipher context for XTS mode only */ + void *ctx_tweak; + /* Cipher callbacks for both contexts */ nettle_cipher_func *alg_encrypt; nettle_cipher_func *alg_decrypt; + uint8_t *iv; size_t blocksize; }; @@ -80,6 +132,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; @@ -99,13 +158,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; } @@ -117,10 +177,9 @@ 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; @@ -132,17 +191,95 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_ALG_AES_128: case QCRYPTO_CIPHER_ALG_AES_192: case QCRYPTO_CIPHER_ALG_AES_256: - ctx->ctx_encrypt = g_new0(struct aes_ctx, 1); - ctx->ctx_decrypt = g_new0(struct aes_ctx, 1); - - aes_set_encrypt_key(ctx->ctx_encrypt, nkey, key); - aes_set_decrypt_key(ctx->ctx_decrypt, nkey, key); + ctx->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 = aes_encrypt_wrapper; ctx->alg_decrypt = 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); + + 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 = cast128_encrypt_wrapper; + ctx->alg_decrypt = cast128_decrypt_wrapper; + + 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 = serpent_encrypt_wrapper; + ctx->alg_decrypt = 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 = twofish_encrypt_wrapper; + ctx->alg_decrypt = twofish_decrypt_wrapper; + + ctx->blocksize = TWOFISH_BLOCK_SIZE; + break; + default: error_setg(errp, "Unsupported cipher algorithm %d", alg); goto error; @@ -170,8 +307,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); } @@ -193,14 +330,21 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher, switch (cipher->mode) { case QCRYPTO_CIPHER_MODE_ECB: - ctx->alg_encrypt(ctx->ctx_encrypt, len, out, in); + ctx->alg_encrypt(ctx->ctx, len, out, in); break; case QCRYPTO_CIPHER_MODE_CBC: - cbc_encrypt(ctx->ctx_encrypt, ctx->alg_encrypt, + cbc_encrypt(ctx->ctx, ctx->alg_encrypt, ctx->blocksize, ctx->iv, len, out, in); break; + + case QCRYPTO_CIPHER_MODE_XTS: + xts_encrypt(ctx->ctx, ctx->ctx_tweak, + ctx->alg_encrypt, ctx->alg_encrypt, + ctx->iv, len, out, in); + break; + default: error_setg(errp, "Unsupported cipher algorithm %d", cipher->alg); @@ -226,15 +370,26 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, 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(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->blocksize, ctx->iv, + cbc_decrypt(ctx->ctx, ctx->alg_decrypt, + 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, ctx->alg_decrypt, + ctx->iv, len, out, in); + break; + default: error_setg(errp, "Unsupported cipher algorithm %d", cipher->alg); diff --git a/crypto/cipher.c b/crypto/cipher.c index 076dff023c..5402d18525 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -27,6 +27,13 @@ static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { [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] = { @@ -34,11 +41,19 @@ static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { [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, }; @@ -79,6 +94,7 @@ size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg, static bool qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, size_t nkey, Error **errp) { @@ -88,10 +104,27 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, return false; } - if (alg_key_len[alg] != nkey) { - error_setg(errp, "Cipher key length %zu should be %zu", - nkey, alg_key_len[alg]); - 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; } diff --git a/crypto/ivgen-essiv.c b/crypto/ivgen-essiv.c new file mode 100644 index 0000000000..5649c01b85 --- /dev/null +++ b/crypto/ivgen-essiv.c @@ -0,0 +1,118 @@ +/* + * 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 "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 *)§or, 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/crypto/ivgen-essiv.h b/crypto/ivgen-essiv.h new file mode 100644 index 0000000000..4a00af849a --- /dev/null +++ b/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/crypto/ivgen-plain.c b/crypto/ivgen-plain.c new file mode 100644 index 0000000000..6a85256cac --- /dev/null +++ b/crypto/ivgen-plain.c @@ -0,0 +1,59 @@ +/* + * 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 "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/crypto/ivgen-plain.h b/crypto/ivgen-plain.h new file mode 100644 index 0000000000..0fe8835c3e --- /dev/null +++ b/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/crypto/ivgen-plain64.c b/crypto/ivgen-plain64.c new file mode 100644 index 0000000000..9ca6db9df2 --- /dev/null +++ b/crypto/ivgen-plain64.c @@ -0,0 +1,59 @@ +/* + * 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 "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, §or, 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/crypto/ivgen-plain64.h b/crypto/ivgen-plain64.h new file mode 100644 index 0000000000..c4104459b5 --- /dev/null +++ b/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/crypto/ivgen.c b/crypto/ivgen.c new file mode 100644 index 0000000000..4ffc1eb886 --- /dev/null +++ b/crypto/ivgen.c @@ -0,0 +1,99 @@ +/* + * 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 "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/crypto/ivgenpriv.h b/crypto/ivgenpriv.h new file mode 100644 index 0000000000..7b87e02ea7 --- /dev/null +++ b/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/crypto/pbkdf-gcrypt.c b/crypto/pbkdf-gcrypt.c new file mode 100644 index 0000000000..885614dfcc --- /dev/null +++ b/crypto/pbkdf-gcrypt.c @@ -0,0 +1,68 @@ +/* + * 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 "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/crypto/pbkdf-nettle.c b/crypto/pbkdf-nettle.c new file mode 100644 index 0000000000..1aa7395ea5 --- /dev/null +++ b/crypto/pbkdf-nettle.c @@ -0,0 +1,65 @@ +/* + * 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 "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/crypto/pbkdf-stub.c b/crypto/pbkdf-stub.c new file mode 100644 index 0000000000..cfc30d3c2a --- /dev/null +++ b/crypto/pbkdf-stub.c @@ -0,0 +1,42 @@ +/* + * 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 "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/crypto/pbkdf.c b/crypto/pbkdf.c new file mode 100644 index 0000000000..90721d85d6 --- /dev/null +++ b/crypto/pbkdf.c @@ -0,0 +1,109 @@ +/* + * 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 "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/crypto/random-gcrypt.c b/crypto/random-gcrypt.c new file mode 100644 index 0000000000..0de9a096df --- /dev/null +++ b/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/crypto/random-gnutls.c b/crypto/random-gnutls.c new file mode 100644 index 0000000000..04b45a8f8f --- /dev/null +++ b/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/crypto/random-stub.c b/crypto/random-stub.c new file mode 100644 index 0000000000..63bbf41473 --- /dev/null +++ b/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/crypto/xts.c b/crypto/xts.c new file mode 100644 index 0000000000..95212341f6 --- /dev/null +++ b/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); +} diff --git a/include/crypto/afsplit.h b/include/crypto/afsplit.h new file mode 100644 index 0000000000..4cc4ca4b38 --- /dev/null +++ b/include/crypto/afsplit.h @@ -0,0 +1,135 @@ +/* + * QEMU Crypto anti forensic information splitter + * + * 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 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_AFSPLIT_H__ +#define QCRYPTO_AFSPLIT_H__ + +#include "crypto/hash.h" + +/** + * This module implements the anti-forensic splitter that is specified + * as part of the LUKS format: + * + * http://clemens.endorphin.org/cryptography + * http://clemens.endorphin.org/TKS1-draft.pdf + * + * The core idea is to take a short piece of data (key material) + * and process it to expand it to a much larger piece of data. + * The expansion process is reversible, to obtain the original + * short data. The key property of the expansion is that if any + * byte in the larger data set is changed / missing, it should be + * impossible to recreate the original short data. + * + * <example> + * <title>Creating a large split key for storage</title> + * <programlisting> + * size_t nkey = 32; + * uint32_t stripes = 32768; // To produce a 1 MB split key + * uint8_t *masterkey = ....a 32-byte AES key... + * uint8_t *splitkey; + * + * splitkey = g_new0(uint8_t, nkey * stripes); + * + * if (qcrypto_afsplit_encode(QCRYPTO_HASH_ALG_SHA256, + * nkey, stripes, + * masterkey, splitkey, errp) < 0) { + * g_free(splitkey); + * g_free(masterkey); + * return -1; + * } + * + * ...store splitkey somewhere... + * + * g_free(splitkey); + * g_free(masterkey); + * </programlisting> + * </example> + * + * <example> + * <title>Retrieving a master key from storage</title> + * <programlisting> + * size_t nkey = 32; + * uint32_t stripes = 32768; // To produce a 1 MB split key + * uint8_t *masterkey; + * uint8_t *splitkey = .... read in 1 MB of data... + * + * masterkey = g_new0(uint8_t, nkey); + * + * if (qcrypto_afsplit_decode(QCRYPTO_HASH_ALG_SHA256, + * nkey, stripes, + * splitkey, masterkey, errp) < 0) { + * g_free(splitkey); + * g_free(masterkey); + * return -1; + * } + * + * ..decrypt data with masterkey... + * + * g_free(splitkey); + * g_free(masterkey); + * </programlisting> + * </example> + */ + +/** + * qcrypto_afsplit_encode: + * @hash: the hash algorithm to use for data expansion + * @blocklen: the size of @in in bytes + * @stripes: the number of times to expand @in in size + * @in: the master key to be expanded in size + * @out: preallocated buffer to hold the split key + * @errp: pointer to a NULL-initialized error object + * + * Split the data in @in, which is @blocklen bytes in + * size, to form a larger piece of data @out, which is + * @blocklen * @stripes bytes in size. + * + * Returns: 0 on success, -1 on error; + */ +int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash, + size_t blocklen, + uint32_t stripes, + const uint8_t *in, + uint8_t *out, + Error **errp); + +/** + * qcrypto_afsplit_decode: + * @hash: the hash algorithm to use for data compression + * @blocklen: the size of @out in bytes + * @stripes: the number of times to decrease @in in size + * @in: the split key to be recombined + * @out: preallocated buffer to hold the master key + * @errp: pointer to a NULL-initialized error object + * + * Join the data in @in, which is @blocklen * @stripes + * bytes in size, to form the original small piece of + * data @out, which is @blocklen bytes in size. + * + * Returns: 0 on success, -1 on error; + */ +int qcrypto_afsplit_decode(QCryptoHashAlgorithm hash, + size_t blocklen, + uint32_t stripes, + const uint8_t *in, + uint8_t *out, + Error **errp); + +#endif /* QCRYPTO_AFSPLIT_H__ */ diff --git a/include/crypto/block.h b/include/crypto/block.h new file mode 100644 index 0000000000..a21e11ff86 --- /dev/null +++ b/include/crypto/block.h @@ -0,0 +1,232 @@ +/* + * 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_H__ +#define QCRYPTO_BLOCK_H__ + +#include "crypto/cipher.h" +#include "crypto/ivgen.h" + +typedef struct QCryptoBlock QCryptoBlock; + +/* See also QCryptoBlockFormat, QCryptoBlockCreateOptions + * and QCryptoBlockOpenOptions in qapi/crypto.json */ + +typedef ssize_t (*QCryptoBlockReadFunc)(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque); + +typedef ssize_t (*QCryptoBlockInitFunc)(QCryptoBlock *block, + size_t headerlen, + Error **errp, + void *opaque); + +typedef ssize_t (*QCryptoBlockWriteFunc)(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque); + +/** + * qcrypto_block_has_format: + * @format: the encryption format + * @buf: the data from head of the volume + * @len: the length of @buf in bytes + * + * Given @len bytes of data from the head of a storage volume + * in @buf, probe to determine if the volume has the encryption + * format specified in @format. + * + * Returns: true if the data in @buf matches @format + */ +bool qcrypto_block_has_format(QCryptoBlockFormat format, + const uint8_t *buf, + size_t buflen); + +typedef enum { + QCRYPTO_BLOCK_OPEN_NO_IO = (1 << 0), +} QCryptoBlockOpenFlags; + +/** + * qcrypto_block_open: + * @options: the encryption options + * @readfunc: callback for reading data from the volume + * @opaque: data to pass to @readfunc + * @flags: bitmask of QCryptoBlockOpenFlags values + * @errp: pointer to a NULL-initialized error object + * + * Create a new block encryption object for an existing + * storage volume encrypted with format identified by + * the parameters in @options. + * + * This will use @readfunc to initialize the encryption + * context based on the volume header(s), extracting the + * master key(s) as required. + * + * If @flags contains QCRYPTO_BLOCK_OPEN_NO_IO then + * the open process will be optimized to skip any parts + * that are only required to perform I/O. In particular + * this would usually avoid the need to decrypt any + * master keys. The only thing that can be done with + * the resulting QCryptoBlock object would be to query + * metadata such as the payload offset. There will be + * no cipher or ivgen objects available. + * + * If any part of initializing the encryption context + * fails an error will be returned. This could be due + * to the volume being in the wrong format, a cipher + * or IV generator algorithm that is not supported, + * or incorrect passphrases. + * + * Returns: a block encryption format, or NULL on error + */ +QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, + QCryptoBlockReadFunc readfunc, + void *opaque, + unsigned int flags, + Error **errp); + +/** + * qcrypto_block_create: + * @format: the encryption format + * @initfunc: callback for initializing volume header + * @writefunc: callback for writing data to the volume header + * @opaque: data to pass to @initfunc and @writefunc + * @errp: pointer to a NULL-initialized error object + * + * Create a new block encryption object for initializing + * a storage volume to be encrypted with format identified + * by the parameters in @options. + * + * This method will allocate space for a new volume header + * using @initfunc and then write header data using @writefunc, + * generating new master keys, etc as required. Any existing + * data present on the volume will be irrevocably destroyed. + * + * If any part of initializing the encryption context + * fails an error will be returned. This could be due + * to the volume being in the wrong format, a cipher + * or IV generator algorithm that is not supported, + * or incorrect passphrases. + * + * Returns: a block encryption format, or NULL on error + */ +QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, + QCryptoBlockInitFunc initfunc, + QCryptoBlockWriteFunc writefunc, + void *opaque, + Error **errp); + +/** + * @qcrypto_block_decrypt: + * @block: the block encryption object + * @startsector: the sector from which @buf was read + * @buf: the buffer to decrypt + * @len: the length of @buf in bytes + * @errp: pointer to a NULL-initialized error object + * + * Decrypt @len bytes of cipher text in @buf, writing + * plain text back into @buf + * + * Returns 0 on success, -1 on failure + */ +int qcrypto_block_decrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp); + +/** + * @qcrypto_block_encrypt: + * @block: the block encryption object + * @startsector: the sector to which @buf will be written + * @buf: the buffer to decrypt + * @len: the length of @buf in bytes + * @errp: pointer to a NULL-initialized error object + * + * Encrypt @len bytes of plain text in @buf, writing + * cipher text back into @buf + * + * Returns 0 on success, -1 on failure + */ +int qcrypto_block_encrypt(QCryptoBlock *block, + uint64_t startsector, + uint8_t *buf, + size_t len, + Error **errp); + +/** + * qcrypto_block_get_cipher: + * @block: the block encryption object + * + * Get the cipher to use for payload encryption + * + * Returns: the cipher object + */ +QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block); + +/** + * qcrypto_block_get_ivgen: + * @block: the block encryption object + * + * Get the initialization vector generator to use for + * payload encryption + * + * Returns: the IV generator object + */ +QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block); + + +/** + * qcrypto_block_get_kdf_hash: + * @block: the block encryption object + * + * Get the hash algorithm used with the key derivation + * function + * + * Returns: the hash algorithm + */ +QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block); + +/** + * qcrypto_block_get_payload_offset: + * @block: the block encryption object + * + * Get the offset to the payload indicated by the + * encryption header, in bytes. + * + * Returns: the payload offset in bytes + */ +uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block); + +/** + * qcrypto_block_free: + * @block: the block encryption object + * + * Release all resources associated with the encryption + * object + */ +void qcrypto_block_free(QCryptoBlock *block); + +#endif /* QCRYPTO_BLOCK_H__ */ diff --git a/include/crypto/ivgen.h b/include/crypto/ivgen.h new file mode 100644 index 0000000000..09cdb6fcd9 --- /dev/null +++ b/include/crypto/ivgen.h @@ -0,0 +1,206 @@ +/* + * 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_H__ +#define QCRYPTO_IVGEN_H__ + +#include "crypto/cipher.h" +#include "crypto/hash.h" + +/** + * This module provides a framework for generating initialization + * vectors for block encryption schemes using chained cipher modes + * CBC. The principle is that each disk sector is assigned a unique + * initialization vector for use for encryption of data in that + * sector. + * + * <example> + * <title>Encrypting block data with initialiation vectors</title> + * <programlisting> + * uint8_t *data = ....data to encrypt... + * size_t ndata = XXX; + * uint8_t *key = ....some encryption key... + * size_t nkey = XXX; + * uint8_t *iv; + * size_t niv; + * size_t sector = 0; + * + * g_assert((ndata % 512) == 0); + * + * QCryptoIVGen *ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_ESSIV, + * QCRYPTO_CIPHER_ALG_AES_128, + * QCRYPTO_HASH_ALG_SHA256, + * key, nkey, errp); + * if (!ivgen) { + * return -1; + * } + * + * QCryptoCipher *cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, + * QCRYPTO_CIPHER_MODE_CBC, + * key, nkey, errp); + * if (!cipher) { + * goto error; + * } + * + * niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128, + * QCRYPTO_CIPHER_MODE_CBC); + * iv = g_new0(uint8_t, niv); + * + * + * while (ndata) { + * if (qcrypto_ivgen_calculate(ivgen, sector, iv, niv, errp) < 0) { + * goto error; + * } + * if (qcrypto_cipher_setiv(cipher, iv, niv, errp) < 0) { + * goto error; + * } + * if (qcrypto_cipher_encrypt(cipher, + * data + (sector * 512), + * data + (sector * 512), + * 512, errp) < 0) { + * goto error; + * } + * sector++; + * ndata -= 512; + * } + * + * g_free(iv); + * qcrypto_ivgen_free(ivgen); + * qcrypto_cipher_free(cipher); + * return 0; + * + *error: + * g_free(iv); + * qcrypto_ivgen_free(ivgen); + * qcrypto_cipher_free(cipher); + * return -1; + * </programlisting> + * </example> + */ + +typedef struct QCryptoIVGen QCryptoIVGen; + +/* See also QCryptoIVGenAlgorithm enum in qapi/crypto.json */ + + +/** + * qcrypto_ivgen_new: + * @alg: the initialization vector generation algorithm + * @cipheralg: the cipher algorithm or 0 + * @hash: the hash algorithm or 0 + * @key: the encryption key or NULL + * @nkey: the size of @key in bytes + * + * Create a new initialization vector generator that uses + * the algorithm @alg. Whether the remaining parameters + * are required or not depends on the choice of @alg + * requested. + * + * - QCRYPTO_IVGEN_ALG_PLAIN + * + * The IVs are generated by the 32-bit truncated sector + * number. This should never be used for block devices + * that are larger than 2^32 sectors in size. + * All the other parameters are unused. + * + * - QCRYPTO_IVGEN_ALG_PLAIN64 + * + * The IVs are generated by the 64-bit sector number. + * All the other parameters are unused. + * + * - QCRYPTO_IVGEN_ALG_ESSIV: + * + * The IVs are generated by encrypting the 64-bit sector + * number with a hash of an encryption key. The @cipheralg, + * @hash, @key and @nkey parameters are all required. + * + * Returns: a new IV generator, or NULL on error + */ +QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, + QCryptoCipherAlgorithm cipheralg, + QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + Error **errp); + +/** + * qcrypto_ivgen_calculate: + * @ivgen: the IV generator object + * @sector: the 64-bit sector number + * @iv: a pre-allocated buffer to hold the generated IV + * @niv: the number of bytes in @iv + * @errp: pointer to a NULL-initialized error object + * + * Calculate a new initialiation vector for the data + * to be stored in sector @sector. The IV will be + * written into the buffer @iv of size @niv. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_ivgen_calculate(QCryptoIVGen *ivgen, + uint64_t sector, + uint8_t *iv, size_t niv, + Error **errp); + + +/** + * qcrypto_ivgen_get_algorithm: + * @ivgen: the IV generator object + * + * Get the algorithm used by this IV generator + * + * Returns: the IV generator algorithm + */ +QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen); + + +/** + * qcrypto_ivgen_get_cipher: + * @ivgen: the IV generator object + * + * Get the cipher algorithm used by this IV generator (if + * applicable) + * + * Returns: the cipher algorithm + */ +QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen); + + +/** + * qcrypto_ivgen_get_hash: + * @ivgen: the IV generator object + * + * Get the hash algorithm used by this IV generator (if + * applicable) + * + * Returns: the hash algorithm + */ +QCryptoHashAlgorithm qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen); + + +/** + * qcrypto_ivgen_free: + * @ivgen: the IV generator object + * + * Release all resources associated with @ivgen, or a no-op + * if @ivgen is NULL + */ +void qcrypto_ivgen_free(QCryptoIVGen *ivgen); + +#endif /* QCRYPTO_IVGEN_H__ */ diff --git a/include/crypto/pbkdf.h b/include/crypto/pbkdf.h new file mode 100644 index 0000000000..58a1fe62a1 --- /dev/null +++ b/include/crypto/pbkdf.h @@ -0,0 +1,152 @@ +/* + * 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/>. + * + */ + +#ifndef QCRYPTO_PBKDF_H__ +#define QCRYPTO_PBKDF_H__ + +#include "crypto/hash.h" + +/** + * This module provides an interface to the PBKDF2 algorithm + * + * https://en.wikipedia.org/wiki/PBKDF2 + * + * <example> + * <title>Generating an AES encryption key from a user password</title> + * <programlisting> + * #include "crypto/cipher.h" + * #include "crypto/random.h" + * #include "crypto/pbkdf.h" + * + * .... + * + * char *password = "a-typical-awful-user-password"; + * size_t nkey = qcrypto_cipher_get_key_len(QCRYPTO_CIPHER_ALG_AES_128); + * uint8_t *salt = g_new0(uint8_t, nkey); + * uint8_t *key = g_new0(uint8_t, nkey); + * int iterations; + * QCryptoCipher *cipher; + * + * if (qcrypto_random_bytes(salt, nkey, errp) < 0) { + * g_free(key); + * g_free(salt); + * return -1; + * } + * + * iterations = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, + * (const uint8_t *)password, + * strlen(password), + * salt, nkey, errp); + * if (iterations < 0) { + * g_free(key); + * g_free(salt); + * return -1; + * } + * + * if (qcrypto_pbkdf2(QCRYPTO_HASH_ALG_SHA256, + * (const uint8_t *)password, strlen(password), + * salt, nkey, iterations, key, nkey, errp) < 0) { + * g_free(key); + * g_free(salt); + * return -1; + * } + * + * g_free(salt); + * + * cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, + * QCRYPTO_CIPHER_MODE_ECB, + * key, nkey, errp); + * g_free(key); + * + * ....encrypt some data... + * + * qcrypto_cipher_free(cipher); + * </programlisting> + * </example> + * + */ + +/** + * qcrypto_pbkdf2_supports: + * @hash: the hash algorithm + * + * Determine if the current build supports the PBKDF2 algorithm + * in combination with the hash @hash. + * + * Returns true if supported, false otherwise + */ +bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash); + + +/** + * qcrypto_pbkdf2: + * @hash: the hash algorithm to use + * @key: the user password / key + * @nkey: the length of @key in bytes + * @salt: a random salt + * @nsalt: length of @salt in bytes + * @iterations: the number of iterations to compute + * @out: pointer to pre-allocated buffer to hold output + * @nout: length of @out in bytes + * @errp: pointer to a NULL-initialized error object + * + * Apply the PBKDF2 algorithm to derive an encryption + * key from a user password provided in @key. The + * @salt parameter is used to perturb the algorithm. + * The @iterations count determines how many times + * the hashing process is run, which influences how + * hard it is to crack the key. The number of @iterations + * should be large enough such that the algorithm takes + * 1 second or longer to derive a key. The derived key + * will be stored in the preallocated buffer @out. + * + * Returns: 0 on success, -1 on error + */ +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); + +/** + * qcrypto_pbkdf2_count_iters: + * @hash: the hash algorithm to use + * @key: the user password / key + * @nkey: the length of @key in bytes + * @salt: a random salt + * @nsalt: length of @salt in bytes + * @errp: pointer to a NULL-initialized error object + * + * Time the PBKDF2 algorithm to determine how many + * iterations are required to derive an encryption + * key from a user password provided in @key in 1 + * second of compute time. The result of this can + * be used as a the @iterations parameter of a later + * call to qcrypto_pbkdf2(). + * + * Returns: number of iterations in 1 second, -1 on error + */ +int qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + Error **errp); + +#endif /* QCRYPTO_PBKDF_H__ */ diff --git a/include/crypto/random.h b/include/crypto/random.h new file mode 100644 index 0000000000..b3021c4ce6 --- /dev/null +++ b/include/crypto/random.h @@ -0,0 +1,44 @@ +/* + * 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/>. + * + */ + +#ifndef QCRYPTO_RANDOM_H__ +#define QCRYPTO_RANDOM_H__ + +#include "qemu-common.h" +#include "qapi/error.h" + + +/** + * qcrypto_random_bytes: + * @buf: the buffer to fill + * @buflen: length of @buf in bytes + * @errp: pointer to a NULL-initialized error object + * + * Fill @buf with @buflen bytes of cryptographically strong + * random data + * + * Returns 0 on sucess, -1 on error + */ +int qcrypto_random_bytes(uint8_t *buf, + size_t buflen, + Error **errp); + + +#endif /* QCRYPTO_RANDOM_H__ */ diff --git a/include/crypto/xts.h b/include/crypto/xts.h new file mode 100644 index 0000000000..c2924d8ba0 --- /dev/null +++ b/include/crypto/xts.h @@ -0,0 +1,86 @@ +/* + * 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 + * + */ + + +#ifndef QCRYPTO_XTS_H_ +#define QCRYPTO_XTS_H_ + +#include "qemu-common.h" +#include "qapi/error.h" + + +#define XTS_BLOCK_SIZE 16 + +typedef void xts_cipher_func(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src); + +/** + * xts_decrypt: + * @datactx: the cipher context for data decryption + * @tweakctx: the cipher context for tweak decryption + * @encfunc: the cipher function for encryption + * @decfunc: the cipher function for decryption + * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes + * @length: the length of @dst and @src + * @dst: buffer to hold the decrypted plaintext + * @src: buffer providing the ciphertext + * + * Decrypts @src into @dst + */ +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); + +/** + * xts_decrypt: + * @datactx: the cipher context for data encryption + * @tweakctx: the cipher context for tweak encryption + * @encfunc: the cipher function for encryption + * @decfunc: the cipher function for decryption + * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes + * @length: the length of @dst and @src + * @dst: buffer to hold the encrypted ciphertext + * @src: buffer providing the plaintext + * + * Decrypts @src into @dst + */ +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); + + +#endif /* QCRYPTO_XTS_H_ */ diff --git a/qapi/crypto.json b/qapi/crypto.json index 4bd690fcfb..760d0c0577 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -59,11 +59,22 @@ # @aes-192: AES with 192 bit / 24 byte keys # @aes-256: AES with 256 bit / 32 byte keys # @des-rfb: RFB specific variant of single DES. Do not use except in VNC. +# @cast5-128: Cast5 with 128 bit / 16 byte keys +# @serpent-128: Serpent with 128 bit / 16 byte keys +# @serpent-192: Serpent with 192 bit / 24 byte keys +# @serpent-256: Serpent with 256 bit / 32 byte keys +# @twofish-128: Twofish with 128 bit / 16 byte keys +# @twofish-192: Twofish with 192 bit / 24 byte keys +# @twofish-256: Twofish with 256 bit / 32 byte keys # Since: 2.6 ## { 'enum': 'QCryptoCipherAlgorithm', 'prefix': 'QCRYPTO_CIPHER_ALG', - 'data': ['aes-128', 'aes-192', 'aes-256', 'des-rfb']} + 'data': ['aes-128', 'aes-192', 'aes-256', + 'des-rfb', + 'cast5-128', + 'serpent-128', 'serpent-192', 'serpent-256', + 'twofish-128', 'twofish-192', 'twofish-256']} ## @@ -73,8 +84,139 @@ # # @ecb: Electronic Code Book # @cbc: Cipher Block Chaining +# @xts: XEX with tweaked code book and ciphertext stealing # Since: 2.6 ## { 'enum': 'QCryptoCipherMode', 'prefix': 'QCRYPTO_CIPHER_MODE', - 'data': ['ecb', 'cbc']} + 'data': ['ecb', 'cbc', 'xts']} + + +## +# QCryptoIVGenAlgorithm: +# +# The supported algorithms for generating initialization +# vectors for full disk encryption. The 'plain' generator +# should not be used for disks with sector numbers larger +# than 2^32, except where compatibility with pre-existing +# Linux dm-crypt volumes is required. +# +# @plain: 64-bit sector number truncated to 32-bits +# @plain64: 64-bit sector number +# @essiv: 64-bit sector number encrypted with a hash of the encryption key +# Since: 2.6 +## +{ 'enum': 'QCryptoIVGenAlgorithm', + 'prefix': 'QCRYPTO_IVGEN_ALG', + 'data': ['plain', 'plain64', 'essiv']} + +## +# QCryptoBlockFormat: +# +# The supported full disk encryption formats +# +# @qcow: QCow/QCow2 built-in AES-CBC encryption. Use only +# for liberating data from old images. +# @luks: LUKS encryption format. Recommended for new images +# +# Since: 2.6 +## +{ 'enum': 'QCryptoBlockFormat', +# 'prefix': 'QCRYPTO_BLOCK_FORMAT', + 'data': ['qcow', 'luks']} + +## +# QCryptoBlockOptionsBase: +# +# The common options that apply to all full disk +# encryption formats +# +# @format: the encryption format +# +# Since: 2.6 +## +{ 'struct': 'QCryptoBlockOptionsBase', + 'data': { 'format': 'QCryptoBlockFormat' }} + +## +# QCryptoBlockOptionsQCow: +# +# The options that apply to QCow/QCow2 AES-CBC encryption format +# +# @key-secret: #optional the ID of a QCryptoSecret object providing the +# decryption key. Mandatory except when probing image for +# metadata only. +# +# Since: 2.6 +## +{ 'struct': 'QCryptoBlockOptionsQCow', + 'data': { '*key-secret': 'str' }} + +## +# QCryptoBlockOptionsLUKS: +# +# The options that apply to LUKS encryption format +# +# @key-secret: #optional the ID of a QCryptoSecret object providing the +# decryption key. Mandatory except when probing image for +# metadata only. +# Since: 2.6 +## +{ 'struct': 'QCryptoBlockOptionsLUKS', + 'data': { '*key-secret': 'str' }} + + +## +# QCryptoBlockCreateOptionsLUKS: +# +# The options that apply to LUKS encryption format initialization +# +# @cipher-alg: #optional the cipher algorithm for data encryption +# Currently defaults to 'aes'. +# @cipher-mode: #optional the cipher mode for data encryption +# Currently defaults to 'cbc' +# @ivgen-alg: #optional the initialization vector generator +# Currently defaults to 'essiv' +# @ivgen-hash-alg: #optional the initialization vector generator hash +# Currently defaults to 'sha256' +# @hash-alg: #optional the master key hash algorithm +# Currently defaults to 'sha256' +# Since: 2.6 +## +{ 'struct': 'QCryptoBlockCreateOptionsLUKS', + 'base': 'QCryptoBlockOptionsLUKS', + 'data': { '*cipher-alg': 'QCryptoCipherAlgorithm', + '*cipher-mode': 'QCryptoCipherMode', + '*ivgen-alg': 'QCryptoIVGenAlgorithm', + '*ivgen-hash-alg': 'QCryptoHashAlgorithm', + '*hash-alg': 'QCryptoHashAlgorithm'}} + + +## +# QCryptoBlockOpenOptions: +# +# The options that are available for all encryption formats +# when opening an existing volume +# +# Since: 2.6 +## +{ 'union': 'QCryptoBlockOpenOptions', + 'base': 'QCryptoBlockOptionsBase', + 'discriminator': 'format', + 'data': { 'qcow': 'QCryptoBlockOptionsQCow', + 'luks': 'QCryptoBlockOptionsLUKS' } } + + +## +# QCryptoBlockCreateOptions: +# +# The options that are available for all encryption formats +# when initializing a new volume +# +# Since: 2.6 +## +{ 'union': 'QCryptoBlockCreateOptions', + 'base': 'QCryptoBlockOptionsBase', + 'discriminator': 'format', + 'data': { 'qcow': 'QCryptoBlockOptionsQCow', + 'luks': 'QCryptoBlockCreateOptionsLUKS' } } diff --git a/tests/.gitignore b/tests/.gitignore index 787c95c73d..5f30cbea07 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -12,8 +12,12 @@ test-base64 test-bitops test-blockjob-txn test-coroutine +test-crypto-afsplit +test-crypto-block test-crypto-cipher test-crypto-hash +test-crypto-ivgen +test-crypto-pbkdf test-crypto-secret test-crypto-tlscredsx509 test-crypto-tlscredsx509-work/ @@ -22,6 +26,7 @@ test-crypto-tlssession test-crypto-tlssession-work/ test-crypto-tlssession-client/ test-crypto-tlssession-server/ +test-crypto-xts test-cutils test-hbitmap test-int128 diff --git a/tests/Makefile b/tests/Makefile index cd4bbd41ad..60371ca008 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -92,6 +92,11 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF) check-unit-y += tests/test-io-channel-command$(EXESUF) check-unit-y += tests/test-io-channel-buffer$(EXESUF) check-unit-y += tests/test-base64$(EXESUF) +check-unit-$(if $(CONFIG_NETTLE),y,$(CONFIG_GCRYPT_KDF)) += tests/test-crypto-pbkdf$(EXESUF) +check-unit-y += tests/test-crypto-ivgen$(EXESUF) +check-unit-y += tests/test-crypto-afsplit$(EXESUF) +check-unit-y += tests/test-crypto-xts$(EXESUF) +check-unit-y += tests/test-crypto-block$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -472,6 +477,7 @@ tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y) tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(test-crypto-obj-y) tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(test-crypto-obj-y) tests/test-crypto-secret$(EXESUF): tests/test-crypto-secret.o $(test-crypto-obj-y) +tests/test-crypto-xts$(EXESUF): tests/test-crypto-xts.o $(test-crypto-obj-y) tests/crypto-tls-x509-helpers.o-cflags := $(TASN1_CFLAGS) tests/crypto-tls-x509-helpers.o-libs := $(TASN1_LIBS) @@ -496,6 +502,10 @@ tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \ tests/io-channel-helpers.o $(test-io-obj-y) tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \ tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-crypto-pbkdf$(EXESUF): tests/test-crypto-pbkdf.o $(test-crypto-obj-y) +tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y) +tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y) +tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o diff --git a/tests/test-crypto-afsplit.c b/tests/test-crypto-afsplit.c new file mode 100644 index 0000000000..ceaac0a06d --- /dev/null +++ b/tests/test-crypto-afsplit.c @@ -0,0 +1,193 @@ +/* + * QEMU Crypto anti-forensic splitter + * + * 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/init.h" +#include "crypto/afsplit.h" + +typedef struct QCryptoAFSplitTestData QCryptoAFSplitTestData; +struct QCryptoAFSplitTestData { + const char *path; + QCryptoHashAlgorithm hash; + uint32_t stripes; + size_t blocklen; + const uint8_t *key; + const uint8_t *splitkey; +}; + +static QCryptoAFSplitTestData test_data[] = { + { + .path = "/crypto/afsplit/sha256/5", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 5, + .blocklen = 32, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + .splitkey = (const uint8_t *) + "\xfd\xd2\x73\xb1\x7d\x99\x93\x34" + "\x70\xde\xfa\x07\xc5\xac\x58\xd2" + "\x30\x67\x2f\x1a\x35\x43\x60\x7d" + "\x77\x02\xdb\x62\x3c\xcb\x2c\x33" + "\x48\x08\xb6\xf1\x7c\xa3\x20\xa0" + "\xad\x2d\x4c\xf3\xcd\x18\x6f\x53" + "\xf9\xe8\xe7\x59\x27\x3c\xa9\x54" + "\x61\x87\xb3\xaf\xf6\xf7\x7e\x64" + "\x86\xaa\x89\x7f\x1f\x9f\xdb\x86" + "\xf4\xa2\x16\xff\xa3\x4f\x8c\xa1" + "\x59\xc4\x23\x34\x28\xc4\x77\x71" + "\x83\xd4\xcd\x8e\x89\x1b\xc7\xc5" + "\xae\x4d\xa9\xcd\xc9\x72\x85\x70" + "\x13\x68\x52\x83\xfc\xb8\x11\x72" + "\xba\x3d\xc6\x4a\x28\xfa\xe2\x86" + "\x7b\x27\xab\x58\xe1\xa4\xca\xf6" + "\x9e\xbc\xfe\x0c\x92\x79\xb3\xec" + "\x1c\x5f\x79\x3b\x0d\x1e\xaa\x1a" + "\x77\x0f\x70\x19\x4b\xc8\x80\xee" + "\x27\x7c\x6e\x4a\x91\x96\x5c\xf4" + }, + { + .path = "/crypto/afsplit/sha256/5000", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 5000, + .blocklen = 16, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + }, + { + .path = "/crypto/afsplit/sha1/1000", + .hash = QCRYPTO_HASH_ALG_SHA1, + .stripes = 1000, + .blocklen = 32, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + }, + { + .path = "/crypto/afsplit/sha256/big", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 1000, + .blocklen = 64, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + }, +}; + + +static inline char hex(int i) +{ + if (i < 10) { + return '0' + i; + } + return 'a' + (i - 10); +} + +static char *hex_string(const uint8_t *bytes, + size_t len) +{ + char *hexstr = g_new0(char, len * 2 + 1); + size_t i; + + for (i = 0; i < len; i++) { + hexstr[i * 2] = hex((bytes[i] >> 4) & 0xf); + hexstr[i * 2 + 1] = hex(bytes[i] & 0xf); + } + hexstr[len * 2] = '\0'; + + return hexstr; +} + +static void test_afsplit(const void *opaque) +{ + const QCryptoAFSplitTestData *data = opaque; + size_t splitlen = data->blocklen * data->stripes; + uint8_t *splitkey = g_new0(uint8_t, splitlen); + uint8_t *key = g_new0(uint8_t, data->blocklen); + gchar *expect, *actual; + + /* First time we round-trip the key */ + qcrypto_afsplit_encode(data->hash, + data->blocklen, data->stripes, + data->key, splitkey, + &error_abort); + + qcrypto_afsplit_decode(data->hash, + data->blocklen, data->stripes, + splitkey, key, + &error_abort); + + expect = hex_string(data->key, data->blocklen); + actual = hex_string(key, data->blocklen); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + + /* Second time we merely try decoding a previous split */ + if (data->splitkey) { + memset(key, 0, data->blocklen); + + qcrypto_afsplit_decode(data->hash, + data->blocklen, data->stripes, + data->splitkey, key, + &error_abort); + + expect = hex_string(data->key, data->blocklen); + actual = hex_string(key, data->blocklen); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + } + + g_free(key); + g_free(splitkey); +} + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (!qcrypto_hash_supports(test_data[i].hash)) { + continue; + } + g_test_add_data_func(test_data[i].path, &test_data[i], test_afsplit); + } + return g_test_run(); +} diff --git a/tests/test-crypto-block.c b/tests/test-crypto-block.c new file mode 100644 index 0000000000..cdbe09d4ed --- /dev/null +++ b/tests/test-crypto-block.c @@ -0,0 +1,362 @@ +/* + * QEMU Crypto block encryption + * + * Copyright (c) 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/init.h" +#include "crypto/block.h" +#include "qemu/buffer.h" +#include "crypto/secret.h" +#ifndef _WIN32 +#include <sys/resource.h> +#endif + +#if defined(CONFIG_UUID) && (defined(_WIN32) || defined RUSAGE_THREAD) +#define TEST_LUKS +#else +#undef TEST_LUKS +#endif + +static QCryptoBlockCreateOptions qcow_create_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .u.qcow = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + +static QCryptoBlockOpenOptions qcow_open_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .u.qcow = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +#ifdef TEST_LUKS +static QCryptoBlockOpenOptions luks_open_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +/* Creation with all default values */ +static QCryptoBlockCreateOptions luks_create_opts_default = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +/* ...and with explicit values */ +static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + .has_cipher_alg = true, + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .has_cipher_mode = true, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .has_ivgen_alg = true, + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + }, +}; + + +static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + .has_cipher_alg = true, + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .has_cipher_mode = true, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .has_ivgen_alg = true, + .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, + .has_ivgen_hash_alg = true, + .ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256, + .has_hash_alg = true, + .hash_alg = QCRYPTO_HASH_ALG_SHA1, + }, +}; +#endif /* TEST_LUKS */ + + +static struct QCryptoBlockTestData { + const char *path; + QCryptoBlockCreateOptions *create_opts; + QCryptoBlockOpenOptions *open_opts; + + bool expect_header; + + QCryptoCipherAlgorithm cipher_alg; + QCryptoCipherMode cipher_mode; + QCryptoHashAlgorithm hash_alg; + + QCryptoIVGenAlgorithm ivgen_alg; + QCryptoHashAlgorithm ivgen_hash; + + bool slow; +} test_data[] = { + { + .path = "/crypto/block/qcow", + .create_opts = &qcow_create_opts, + .open_opts = &qcow_open_opts, + + .expect_header = false, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_128, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + }, +#ifdef TEST_LUKS + { + .path = "/crypto/block/luks/default", + .create_opts = &luks_create_opts_default, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_XTS, + .hash_alg = QCRYPTO_HASH_ALG_SHA256, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + + .slow = true, + }, + { + .path = "/crypto/block/luks/aes-256-cbc-plain64", + .create_opts = &luks_create_opts_aes256_cbc_plain64, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .hash_alg = QCRYPTO_HASH_ALG_SHA256, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + + .slow = true, + }, + { + .path = "/crypto/block/luks/aes-256-cbc-essiv", + .create_opts = &luks_create_opts_aes256_cbc_essiv, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .hash_alg = QCRYPTO_HASH_ALG_SHA1, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, + .ivgen_hash = QCRYPTO_HASH_ALG_SHA256, + + .slow = true, + }, +#endif +}; + + +static ssize_t test_block_read_func(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque) +{ + Buffer *header = opaque; + + g_assert_cmpint(offset + buflen, <=, header->capacity); + + memcpy(buf, header->buffer + offset, buflen); + + return buflen; +} + + +static ssize_t test_block_init_func(QCryptoBlock *block, + size_t headerlen, + Error **errp, + void *opaque) +{ + Buffer *header = opaque; + + g_assert_cmpint(header->capacity, ==, 0); + + buffer_reserve(header, headerlen); + + return headerlen; +} + + +static ssize_t test_block_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + Error **errp, + void *opaque) +{ + Buffer *header = opaque; + + g_assert_cmpint(buflen + offset, <=, header->capacity); + + memcpy(header->buffer + offset, buf, buflen); + header->offset = offset + buflen; + + return buflen; +} + + +static Object *test_block_secret(void) +{ + return object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + NULL); +} + +static void test_block_assert_setup(const struct QCryptoBlockTestData *data, + QCryptoBlock *blk) +{ + QCryptoIVGen *ivgen; + QCryptoCipher *cipher; + + ivgen = qcrypto_block_get_ivgen(blk); + cipher = qcrypto_block_get_cipher(blk); + + g_assert(ivgen); + g_assert(cipher); + + g_assert_cmpint(data->cipher_alg, ==, cipher->alg); + g_assert_cmpint(data->cipher_mode, ==, cipher->mode); + g_assert_cmpint(data->hash_alg, ==, + qcrypto_block_get_kdf_hash(blk)); + + g_assert_cmpint(data->ivgen_alg, ==, + qcrypto_ivgen_get_algorithm(ivgen)); + g_assert_cmpint(data->ivgen_hash, ==, + qcrypto_ivgen_get_hash(ivgen)); +} + + +static void test_block(gconstpointer opaque) +{ + const struct QCryptoBlockTestData *data = opaque; + QCryptoBlock *blk; + Buffer header; + Object *sec = test_block_secret(); + + memset(&header, 0, sizeof(header)); + buffer_init(&header, "header"); + + blk = qcrypto_block_create(data->create_opts, + test_block_init_func, + test_block_write_func, + &header, + &error_abort); + g_assert(blk); + + if (data->expect_header) { + g_assert_cmpint(header.capacity, >, 0); + } else { + g_assert_cmpint(header.capacity, ==, 0); + } + + test_block_assert_setup(data, blk); + + qcrypto_block_free(blk); + object_unparent(sec); + + /* Ensure we can't open without the secret */ + blk = qcrypto_block_open(data->open_opts, + test_block_read_func, + &header, + 0, + NULL); + g_assert(blk == NULL); + + /* Ensure we can't open without the secret, unless NO_IO */ + blk = qcrypto_block_open(data->open_opts, + test_block_read_func, + &header, + QCRYPTO_BLOCK_OPEN_NO_IO, + &error_abort); + + g_assert(qcrypto_block_get_cipher(blk) == NULL); + g_assert(qcrypto_block_get_ivgen(blk) == NULL); + + qcrypto_block_free(blk); + + + /* Now open for real with secret */ + sec = test_block_secret(); + blk = qcrypto_block_open(data->open_opts, + test_block_read_func, + &header, + 0, + &error_abort); + g_assert(blk); + + test_block_assert_setup(data, blk); + + qcrypto_block_free(blk); + + object_unparent(sec); + + buffer_free(&header); +} + + +int main(int argc, char **argv) +{ + gsize i; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (test_data[i].open_opts->format == Q_CRYPTO_BLOCK_FORMAT_LUKS && + !qcrypto_hash_supports(test_data[i].hash_alg)) { + continue; + } + if (!test_data[i].slow || + g_test_slow()) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_block); + } + } + + return g_test_run(); +} diff --git a/tests/test-crypto-cipher.c b/tests/test-crypto-cipher.c index 9f912eccb5..e52b741f87 100644 --- a/tests/test-crypto-cipher.c +++ b/tests/test-crypto-cipher.c @@ -165,6 +165,211 @@ static QCryptoCipherTestData test_data[] = { "ffd29f1bb5596ad94ea2d8e6196b7f09" "30d8ed0bf2773af36dd82a6280c20926", }, + { + /* RFC 2144, Appendix B.1 */ + .path = "/crypto/cipher/cast5-128", + .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "0123456712345678234567893456789A", + .plaintext = "0123456789abcdef", + .ciphertext = "238b4fe5847e44b2", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-128", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000", + .plaintext = "d29d576fcea3a3a7ed9099f29273d78e", + .ciphertext = "b2288b968ae8b08648d1ce9606fd992d", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-192", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "0000000000000000", + .plaintext = "d29d576fceaba3a7ed9899f2927bd78e", + .ciphertext = "130e353e1037c22405e8faefb2c3c3e9", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-256a", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "00000000000000000000000000000000", + .plaintext = "d095576fcea3e3a7ed98d9f29073d78e", + .ciphertext = "b90ee5862de69168f2bdd5125b45472b", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-256b", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "00000000000000000000000000000000", + .plaintext = "00000000010000000200000003000000", + .ciphertext = "2061a42782bd52ec691ec383b03ba77c", + }, + { + /* Twofish paper "Known Answer Test" */ + .path = "/crypto/cipher/twofish-128", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "d491db16e7b1c39e86cb086b789f5419", + .plaintext = "019f9809de1711858faac3a3ba20fbc3", + .ciphertext = "6363977de839486297e661c6c9d668eb", + }, + { + /* Twofish paper "Known Answer Test", I=3 */ + .path = "/crypto/cipher/twofish-192", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "88b2b2706b105e36b446bb6d731a1e88" + "efa71f788965bd44", + .plaintext = "39da69d6ba4997d585b6dc073ca341b2", + .ciphertext = "182b02d81497ea45f9daacdc29193a65", + }, + { + /* Twofish paper "Known Answer Test", I=4 */ + .path = "/crypto/cipher/twofish-256", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "d43bb7556ea32e46f2a282b7d45b4e0d" + "57ff739d4dc92c1bd7fc01700cc8216f", + .plaintext = "90afe91bb288544f2c32dc239b2635e6", + .ciphertext = "6cb4561c40bf0a9705931cb6d408e7fa", + }, + { + /* #1 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-1", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "00000000000000000000000000000000" + "00000000000000000000000000000000", + .iv = + "00000000000000000000000000000000", + .plaintext = + "00000000000000000000000000000000" + "00000000000000000000000000000000", + .ciphertext = + "917cf69ebd68b2ec9b9fe9a3eadda692" + "cd43d2f59598ed858c02c2652fbf922e", + }, + { + /* #2, 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-2", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "11111111111111111111111111111111" + "22222222222222222222222222222222", + .iv = + "33333333330000000000000000000000", + .plaintext = + "44444444444444444444444444444444" + "44444444444444444444444444444444", + .ciphertext = + "c454185e6a16936e39334038acef838b" + "fb186fff7480adc4289382ecd6d394f0", + }, + { + /* #5 from xts.7, 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-3", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0" + "bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0", + .iv = + "9a785634120000000000000000000000", + .plaintext = + "44444444444444444444444444444444" + "44444444444444444444444444444444", + .ciphertext = + "b01f86f8edc1863706fa8a4253e34f28" + "af319de38334870f4dd1f94cbe9832f1", + }, + { + /* #4, 32 byte key, 512 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-4", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "27182818284590452353602874713526" + "31415926535897932384626433832795", + .iv = + "00000000000000000000000000000000", + .plaintext = + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + .ciphertext = + "27a7479befa1d476489f308cd4cfa6e2" + "a96e4bbe3208ff25287dd3819616e89c" + "c78cf7f5e543445f8333d8fa7f560000" + "05279fa5d8b5e4ad40e736ddb4d35412" + "328063fd2aab53e5ea1e0a9f332500a5" + "df9487d07a5c92cc512c8866c7e860ce" + "93fdf166a24912b422976146ae20ce84" + "6bb7dc9ba94a767aaef20c0d61ad0265" + "5ea92dc4c4e41a8952c651d33174be51" + "a10c421110e6d81588ede82103a252d8" + "a750e8768defffed9122810aaeb99f91" + "72af82b604dc4b8e51bcb08235a6f434" + "1332e4ca60482a4ba1a03b3e65008fc5" + "da76b70bf1690db4eae29c5f1badd03c" + "5ccf2a55d705ddcd86d449511ceb7ec3" + "0bf12b1fa35b913f9f747a8afd1b130e" + "94bff94effd01a91735ca1726acd0b19" + "7c4e5b03393697e126826fb6bbde8ecc" + "1e08298516e2c9ed03ff3c1b7860f6de" + "76d4cecd94c8119855ef5297ca67e9f3" + "e7ff72b1e99785ca0a7e7720c5b36dc6" + "d72cac9574c8cbbc2f801e23e56fd344" + "b07f22154beba0f08ce8891e643ed995" + "c94d9a69c9f1b5f499027a78572aeebd" + "74d20cc39881c213ee770b1010e4bea7" + "18846977ae119f7a023ab58cca0ad752" + "afe656bb3c17256a9f6e9bf19fdd5a38" + "fc82bbe872c5539edb609ef4f79c203e" + "bb140f2e583cb2ad15b4aa5b655016a8" + "449277dbd477ef2c8d6c017db738b18d" + "eb4a427d1923ce3ff262735779a418f2" + "0a282df920147beabe421ee5319d0568", + }, }; @@ -251,7 +456,11 @@ static void test_cipher(const void *opaque) blocksize = qcrypto_cipher_get_block_len(data->alg); ivsize = qcrypto_cipher_get_iv_len(data->alg, data->mode); - g_assert_cmpint(keysize, ==, nkey); + if (data->mode == QCRYPTO_CIPHER_MODE_XTS) { + g_assert_cmpint(keysize * 2, ==, nkey); + } else { + g_assert_cmpint(keysize, ==, nkey); + } g_assert_cmpint(ivsize, ==, niv); if (niv) { g_assert_cmpint(blocksize, ==, niv); @@ -380,7 +589,9 @@ int main(int argc, char **argv) g_assert(qcrypto_init(NULL) == 0); for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); + if (qcrypto_cipher_supports(test_data[i].alg)) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); + } } g_test_add_func("/crypto/cipher/null-iv", diff --git a/tests/test-crypto-ivgen.c b/tests/test-crypto-ivgen.c new file mode 100644 index 0000000000..96129da367 --- /dev/null +++ b/tests/test-crypto-ivgen.c @@ -0,0 +1,173 @@ +/* + * QEMU Crypto IV generator algorithms + * + * 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/ivgen.h" + + +struct QCryptoIVGenTestData { + const char *path; + uint64_t sector; + QCryptoIVGenAlgorithm ivalg; + QCryptoHashAlgorithm hashalg; + QCryptoCipherAlgorithm cipheralg; + const uint8_t *key; + size_t nkey; + const uint8_t *iv; + size_t niv; +} test_data[] = { + /* Small */ + { + "/crypto/ivgen/plain/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/plain/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Truncation */ + { + "/crypto/ivgen/plain/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Small */ + { + "/crypto/ivgen/plain64/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/plain64/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* No Truncation */ + { + "/crypto/ivgen/plain64/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x4c\x3d\x2e\x1f" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Small */ + { + "/crypto/ivgen/essiv/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\xd4\x83\x71\xb2\xa1\x94\x53\x88" + "\x1c\x7a\x2d\06\x2d\x0b\x65\x46", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/essiv/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\x5d\x36\x09\x5d\xc6\x9e\x5e\xe9" + "\xe3\x02\x8d\xd8\x7a\x3d\xe7\x8f", + .niv = 16, + }, + /* No Truncation */ + { + "/crypto/ivgen/essiv/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\x58\xbb\x81\x94\x51\x83\x23\x23" + "\x7a\x08\x93\xa9\xdc\xd2\xd9\xab", + .niv = 16, + }, +}; + + +static void test_ivgen(const void *opaque) +{ + const struct QCryptoIVGenTestData *data = opaque; + uint8_t *iv = g_new0(uint8_t, data->niv); + QCryptoIVGen *ivgen = qcrypto_ivgen_new( + data->ivalg, + data->cipheralg, + data->hashalg, + data->key, + data->nkey, + &error_abort); + + qcrypto_ivgen_calculate(ivgen, + data->sector, + iv, + data->niv, + &error_abort); + + g_assert(memcmp(iv, data->iv, data->niv) == 0); + + qcrypto_ivgen_free(ivgen); + g_free(iv); +} + +int main(int argc, char **argv) +{ + size_t i; + g_test_init(&argc, &argv, NULL); + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (test_data[i].ivalg == QCRYPTO_IVGEN_ALG_ESSIV && + !qcrypto_hash_supports(test_data[i].hashalg)) { + continue; + } + g_test_add_data_func(test_data[i].path, + &(test_data[i]), + test_ivgen); + } + return g_test_run(); +} diff --git a/tests/test-crypto-pbkdf.c b/tests/test-crypto-pbkdf.c new file mode 100644 index 0000000000..bb9c14c110 --- /dev/null +++ b/tests/test-crypto-pbkdf.c @@ -0,0 +1,392 @@ +/* + * QEMU Crypto cipher algorithms + * + * 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/init.h" +#ifndef _WIN32 +#include <sys/resource.h> +#endif + +#if ((defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) && \ + (defined(_WIN32) || defined(RUSAGE_THREAD))) +#include "crypto/pbkdf.h" + +typedef struct QCryptoPbkdfTestData QCryptoPbkdfTestData; +struct QCryptoPbkdfTestData { + const char *path; + QCryptoHashAlgorithm hash; + unsigned int iterations; + const char *key; + size_t nkey; + const char *salt; + size_t nsalt; + const char *out; + size_t nout; + bool slow; +}; + +/* This test data comes from cryptsetup package + * + * $SRC/lib/crypto_backend/pbkdf2_generic.c + * + * under LGPLv2.1+ license + */ +static QCryptoPbkdfTestData test_data[] = { + /* RFC 3962 test data */ + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\xcd\xed\xb5\x28\x1b\xb2\xf8\x01" + "\x56\x5a\x11\x22\xb2\x56\x35\x15" + "\x0a\xd1\xf7\xa0\x4b\xb9\xf3\xa3" + "\x33\xec\xc0\xe2\xe1\xf7\x08\x37", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e" + "\x98\x8b\x62\xc7\x3c\xda\x93\x5d" + "\xa0\x53\x78\xb9\x32\x44\xec\x8f" + "\x48\xa9\x9e\x61\xad\x79\x9d\x86", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200a", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e" + "\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b" + "\xa7\xe5\x2d\xdb\xc5\xe5\x14\x2f" + "\x70\x8a\x31\xe2\xe6\x2b\x1e\x13", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter5", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 5, + .key = "password", + .nkey = 8, + .salt = "\0224VxxV4\022", /* "\x1234567878563412 */ + .nsalt = 8, + .out = "\xd1\xda\xa7\x86\x15\xf2\x87\xe6" + "\xa1\xc8\xb1\x20\xd7\x06\x2a\x49" + "\x3f\x98\xd2\x03\xe6\xbe\x49\xa6" + "\xad\xf4\xfa\x57\x4b\x6e\x64\xee", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200b", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 64, + .salt = "pass phrase equals block size", + .nsalt = 29, + .out = "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b" + "\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9" + "\xc5\xec\x59\xf1\xa4\x52\xf5\xcc" + "\x9a\xd9\x40\xfe\xa0\x59\x8e\xd1", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200c", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5" + "\x1b\x10\xe6\xa6\x87\x21\xbe\x61" + "\x1a\x8b\x4d\x28\x26\x01\xdb\x3b" + "\x36\xbe\x92\x46\x91\x5e\xc8\x2a", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter50", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 50, + .key = "\360\235\204\236", /* g-clef ("\xf09d849e) */ + .nkey = 4, + .salt = "EXAMPLE.COMpianist", + .nsalt = 18, + .out = "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43" + "\xa5\xb8\xbb\x27\x6a\x40\x3b\x39" + "\xe7\xfe\x37\xa0\xc4\x1e\x02\xc2" + "\x81\xff\x30\x69\xe1\xe9\x4f\x52", + .nout = 32 + }, + + /* RFC-6070 test data */ + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter1", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9" + "\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e" + "\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad" + "\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter16777216", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 16777216, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94" + "\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84", + .nout = 20, + .slow = true, + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096a", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "passwordPASSWORDpassword", + .nkey = 24, + .salt = "saltSALTsaltSALTsaltSALTsaltSALTsalt", + .nsalt = 36, + .out = "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" + "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" + "\x4c\xf2\xf0\x70\x38", + .nout = 25 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096b", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "pass\0word", + .nkey = 9, + .salt = "sa\0lt", + .nsalt = 5, + .out = "\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37" + "\xd7\xf0\x34\x25\xe0\xc3", + .nout = 16 + }, + + /* non-RFC misc test data */ +#ifdef CONFIG_NETTLE + { + /* empty password test. + * Broken with libgcrypt <= 1.5.0, hence CONFIG_NETTLE */ + .path = "/crypto/pbkdf/nonrfc/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "", + .nkey = 0, + .salt = "salt", + .nsalt = 4, + .out = "\x13\x3a\x4c\xe8\x37\xb4\xd2\x52\x1e\xe2" + "\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97", + .nout = 20 + }, +#endif + { + /* Password exceeds block size test */ + .path = "/crypto/pbkdf/nonrfc/sha256/iter1200", + .hash = QCRYPTO_HASH_ALG_SHA256, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x22\x34\x4b\xc4\xb6\xe3\x26\x75" + "\xa8\x09\x0f\x3e\xa8\x0b\xe0\x1d" + "\x5f\x95\x12\x6a\x2c\xdd\xc3\xfa" + "\xcc\x4a\x5e\x6d\xca\x04\xec\x58", + .nout = 32 + }, +#if 0 + { + .path = "/crypto/pbkdf/nonrfc/sha512/iter1200", + .hash = QCRYPTO_HASH_ALG_SHA512, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 129, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x0f\xb2\xed\x2c\x0e\x6e\xfb\x7d" + "\x7d\x8e\xdd\x58\x01\xb4\x59\x72" + "\x99\x92\x16\x30\x5e\xa4\x36\x8d" + "\x76\x14\x80\xf3\xe3\x7a\x22\xb9", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/nonrfc/whirlpool/iter1200", + .hash = QCRYPTO_HASH_ALG_WHIRLPOOL, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x9c\x1c\x74\xf5\x88\x26\xe7\x6a" + "\x53\x58\xf4\x0c\x39\xe7\x80\x89" + "\x07\xc0\x31\x19\x9a\x50\xa2\x48" + "\xf1\xd9\xfe\x78\x64\xe5\x84\x50", + .nout = 32 + } +#endif +}; + + +static inline char hex(int i) +{ + if (i < 10) { + return '0' + i; + } + return 'a' + (i - 10); +} + +static char *hex_string(const uint8_t *bytes, + size_t len) +{ + char *hexstr = g_new0(char, len * 2 + 1); + size_t i; + + for (i = 0; i < len; i++) { + hexstr[i * 2] = hex((bytes[i] >> 4) & 0xf); + hexstr[i * 2 + 1] = hex(bytes[i] & 0xf); + } + hexstr[len * 2] = '\0'; + + return hexstr; +} + +static void test_pbkdf(const void *opaque) +{ + const QCryptoPbkdfTestData *data = opaque; + size_t nout = data->nout; + uint8_t *out = g_new0(uint8_t, nout); + gchar *expect, *actual; + + qcrypto_pbkdf2(data->hash, + (uint8_t *)data->key, data->nkey, + (uint8_t *)data->salt, data->nsalt, + data->iterations, + (uint8_t *)out, nout, + &error_abort); + + expect = hex_string((const uint8_t *)data->out, data->nout); + actual = hex_string(out, nout); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + g_free(out); +} + + +static void test_pbkdf_timing(void) +{ + uint8_t key[32]; + uint8_t salt[32]; + int iters; + + memset(key, 0x5d, sizeof(key)); + memset(salt, 0x7c, sizeof(salt)); + + iters = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, + key, sizeof(key), + salt, sizeof(salt), + &error_abort); + + g_assert(iters >= (1 << 15)); +} + + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (!test_data[i].slow || + g_test_slow()) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_pbkdf); + } + } + + if (g_test_slow()) { + g_test_add_func("/crypt0/pbkdf/timing", test_pbkdf_timing); + } + + return g_test_run(); +} +#else +int main(int argc, char **argv) +{ + return 0; +} +#endif diff --git a/tests/test-crypto-xts.c b/tests/test-crypto-xts.c new file mode 100644 index 0000000000..7f68b063cd --- /dev/null +++ b/tests/test-crypto-xts.c @@ -0,0 +1,423 @@ +/* + * 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/init.h" +#include "crypto/xts.h" +#include "crypto/aes.h" + +typedef struct { + const char *path; + int keylen; + unsigned char key1[32]; + unsigned char key2[32]; + uint64_t seqnum; + unsigned long PTLEN; + unsigned char PTX[512], CTX[512]; +} QCryptoXTSTestData; + +static const QCryptoXTSTestData test_data[] = { + /* #1 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-1-key-32-ptx-32", + 32, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + 0, + 32, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x91, 0x7c, 0xf6, 0x9e, 0xbd, 0x68, 0xb2, 0xec, + 0x9b, 0x9f, 0xe9, 0xa3, 0xea, 0xdd, 0xa6, 0x92, + 0xcd, 0x43, 0xd2, 0xf5, 0x95, 0x98, 0xed, 0x85, + 0x8c, 0x02, 0xc2, 0x65, 0x2f, 0xbf, 0x92, 0x2e }, + }, + + /* #2, 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-2-key-32-ptx-32", + 32, + { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 }, + { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }, + 0x3333333333LL, + 32, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, + { 0xc4, 0x54, 0x18, 0x5e, 0x6a, 0x16, 0x93, 0x6e, + 0x39, 0x33, 0x40, 0x38, 0xac, 0xef, 0x83, 0x8b, + 0xfb, 0x18, 0x6f, 0xff, 0x74, 0x80, 0xad, 0xc4, + 0x28, 0x93, 0x82, 0xec, 0xd6, 0xd3, 0x94, 0xf0 }, + }, + + /* #5 from xts.7, 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-5-key-32-ptx-32", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 32, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, + { 0xb0, 0x1f, 0x86, 0xf8, 0xed, 0xc1, 0x86, 0x37, + 0x06, 0xfa, 0x8a, 0x42, 0x53, 0xe3, 0x4f, 0x28, + 0xaf, 0x31, 0x9d, 0xe3, 0x83, 0x34, 0x87, 0x0f, + 0x4d, 0xd1, 0xf9, 0x4c, 0xbe, 0x98, 0x32, 0xf1 }, + }, + + /* #4, 32 byte key, 512 byte PTX */ + { + "/crypto/xts/t-4-key-32-ptx-512", + 32, + { 0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45, + 0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26 }, + { 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, + 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95 }, + 0, + 512, + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + }, + { + 0x27, 0xa7, 0x47, 0x9b, 0xef, 0xa1, 0xd4, 0x76, + 0x48, 0x9f, 0x30, 0x8c, 0xd4, 0xcf, 0xa6, 0xe2, + 0xa9, 0x6e, 0x4b, 0xbe, 0x32, 0x08, 0xff, 0x25, + 0x28, 0x7d, 0xd3, 0x81, 0x96, 0x16, 0xe8, 0x9c, + 0xc7, 0x8c, 0xf7, 0xf5, 0xe5, 0x43, 0x44, 0x5f, + 0x83, 0x33, 0xd8, 0xfa, 0x7f, 0x56, 0x00, 0x00, + 0x05, 0x27, 0x9f, 0xa5, 0xd8, 0xb5, 0xe4, 0xad, + 0x40, 0xe7, 0x36, 0xdd, 0xb4, 0xd3, 0x54, 0x12, + 0x32, 0x80, 0x63, 0xfd, 0x2a, 0xab, 0x53, 0xe5, + 0xea, 0x1e, 0x0a, 0x9f, 0x33, 0x25, 0x00, 0xa5, + 0xdf, 0x94, 0x87, 0xd0, 0x7a, 0x5c, 0x92, 0xcc, + 0x51, 0x2c, 0x88, 0x66, 0xc7, 0xe8, 0x60, 0xce, + 0x93, 0xfd, 0xf1, 0x66, 0xa2, 0x49, 0x12, 0xb4, + 0x22, 0x97, 0x61, 0x46, 0xae, 0x20, 0xce, 0x84, + 0x6b, 0xb7, 0xdc, 0x9b, 0xa9, 0x4a, 0x76, 0x7a, + 0xae, 0xf2, 0x0c, 0x0d, 0x61, 0xad, 0x02, 0x65, + 0x5e, 0xa9, 0x2d, 0xc4, 0xc4, 0xe4, 0x1a, 0x89, + 0x52, 0xc6, 0x51, 0xd3, 0x31, 0x74, 0xbe, 0x51, + 0xa1, 0x0c, 0x42, 0x11, 0x10, 0xe6, 0xd8, 0x15, + 0x88, 0xed, 0xe8, 0x21, 0x03, 0xa2, 0x52, 0xd8, + 0xa7, 0x50, 0xe8, 0x76, 0x8d, 0xef, 0xff, 0xed, + 0x91, 0x22, 0x81, 0x0a, 0xae, 0xb9, 0x9f, 0x91, + 0x72, 0xaf, 0x82, 0xb6, 0x04, 0xdc, 0x4b, 0x8e, + 0x51, 0xbc, 0xb0, 0x82, 0x35, 0xa6, 0xf4, 0x34, + 0x13, 0x32, 0xe4, 0xca, 0x60, 0x48, 0x2a, 0x4b, + 0xa1, 0xa0, 0x3b, 0x3e, 0x65, 0x00, 0x8f, 0xc5, + 0xda, 0x76, 0xb7, 0x0b, 0xf1, 0x69, 0x0d, 0xb4, + 0xea, 0xe2, 0x9c, 0x5f, 0x1b, 0xad, 0xd0, 0x3c, + 0x5c, 0xcf, 0x2a, 0x55, 0xd7, 0x05, 0xdd, 0xcd, + 0x86, 0xd4, 0x49, 0x51, 0x1c, 0xeb, 0x7e, 0xc3, + 0x0b, 0xf1, 0x2b, 0x1f, 0xa3, 0x5b, 0x91, 0x3f, + 0x9f, 0x74, 0x7a, 0x8a, 0xfd, 0x1b, 0x13, 0x0e, + 0x94, 0xbf, 0xf9, 0x4e, 0xff, 0xd0, 0x1a, 0x91, + 0x73, 0x5c, 0xa1, 0x72, 0x6a, 0xcd, 0x0b, 0x19, + 0x7c, 0x4e, 0x5b, 0x03, 0x39, 0x36, 0x97, 0xe1, + 0x26, 0x82, 0x6f, 0xb6, 0xbb, 0xde, 0x8e, 0xcc, + 0x1e, 0x08, 0x29, 0x85, 0x16, 0xe2, 0xc9, 0xed, + 0x03, 0xff, 0x3c, 0x1b, 0x78, 0x60, 0xf6, 0xde, + 0x76, 0xd4, 0xce, 0xcd, 0x94, 0xc8, 0x11, 0x98, + 0x55, 0xef, 0x52, 0x97, 0xca, 0x67, 0xe9, 0xf3, + 0xe7, 0xff, 0x72, 0xb1, 0xe9, 0x97, 0x85, 0xca, + 0x0a, 0x7e, 0x77, 0x20, 0xc5, 0xb3, 0x6d, 0xc6, + 0xd7, 0x2c, 0xac, 0x95, 0x74, 0xc8, 0xcb, 0xbc, + 0x2f, 0x80, 0x1e, 0x23, 0xe5, 0x6f, 0xd3, 0x44, + 0xb0, 0x7f, 0x22, 0x15, 0x4b, 0xeb, 0xa0, 0xf0, + 0x8c, 0xe8, 0x89, 0x1e, 0x64, 0x3e, 0xd9, 0x95, + 0xc9, 0x4d, 0x9a, 0x69, 0xc9, 0xf1, 0xb5, 0xf4, + 0x99, 0x02, 0x7a, 0x78, 0x57, 0x2a, 0xee, 0xbd, + 0x74, 0xd2, 0x0c, 0xc3, 0x98, 0x81, 0xc2, 0x13, + 0xee, 0x77, 0x0b, 0x10, 0x10, 0xe4, 0xbe, 0xa7, + 0x18, 0x84, 0x69, 0x77, 0xae, 0x11, 0x9f, 0x7a, + 0x02, 0x3a, 0xb5, 0x8c, 0xca, 0x0a, 0xd7, 0x52, + 0xaf, 0xe6, 0x56, 0xbb, 0x3c, 0x17, 0x25, 0x6a, + 0x9f, 0x6e, 0x9b, 0xf1, 0x9f, 0xdd, 0x5a, 0x38, + 0xfc, 0x82, 0xbb, 0xe8, 0x72, 0xc5, 0x53, 0x9e, + 0xdb, 0x60, 0x9e, 0xf4, 0xf7, 0x9c, 0x20, 0x3e, + 0xbb, 0x14, 0x0f, 0x2e, 0x58, 0x3c, 0xb2, 0xad, + 0x15, 0xb4, 0xaa, 0x5b, 0x65, 0x50, 0x16, 0xa8, + 0x44, 0x92, 0x77, 0xdb, 0xd4, 0x77, 0xef, 0x2c, + 0x8d, 0x6c, 0x01, 0x7d, 0xb7, 0x38, 0xb1, 0x8d, + 0xeb, 0x4a, 0x42, 0x7d, 0x19, 0x23, 0xce, 0x3f, + 0xf2, 0x62, 0x73, 0x57, 0x79, 0xa4, 0x18, 0xf2, + 0x0a, 0x28, 0x2d, 0xf9, 0x20, 0x14, 0x7b, 0xea, + 0xbe, 0x42, 0x1e, 0xe5, 0x31, 0x9d, 0x05, 0x68, + } + }, + + /* #7, 32 byte key, 17 byte PTX */ + { + "/crypto/xts/t-7-key-32-ptx-17", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 17, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, + { 0x6c, 0x16, 0x25, 0xdb, 0x46, 0x71, 0x52, 0x2d, + 0x3d, 0x75, 0x99, 0x60, 0x1d, 0xe7, 0xca, 0x09, 0xed }, + }, + + /* #15, 32 byte key, 25 byte PTX */ + { + "/crypto/xts/t-15-key-32-ptx-25", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 25, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }, + { 0x8f, 0x4d, 0xcb, 0xad, 0x55, 0x55, 0x8d, 0x7b, + 0x4e, 0x01, 0xd9, 0x37, 0x9c, 0xd4, 0xea, 0x22, + 0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, 0x73 }, + }, + + /* #21, 32 byte key, 31 byte PTX */ + { + "/crypto/xts/t-21-key-32-ptx-31", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 31, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e }, + { 0xd0, 0x5b, 0xc0, 0x90, 0xa8, 0xe0, 0x4f, 0x1b, + 0x3d, 0x3e, 0xcd, 0xd5, 0xba, 0xec, 0x0f, 0xd4, + 0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, + 0x73, 0x06, 0xe6, 0x4b, 0xe5, 0xdd, 0x82 }, + }, +}; + +#define STORE64L(x, y) \ + do { \ + (y)[7] = (unsigned char)(((x) >> 56) & 255); \ + (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); \ + (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); \ + (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); \ + (y)[0] = (unsigned char)((x) & 255); \ + } while (0) + +struct TestAES { + AES_KEY enc; + AES_KEY dec; +}; + +static void test_xts_aes_encrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const struct TestAES *aesctx = ctx; + + AES_encrypt(src, dst, &aesctx->enc); +} + + +static void test_xts_aes_decrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const struct TestAES *aesctx = ctx; + + AES_decrypt(src, dst, &aesctx->dec); +} + + +static void test_xts(const void *opaque) +{ + const QCryptoXTSTestData *data = opaque; + unsigned char OUT[512], Torg[16], T[16]; + uint64_t seq; + int j; + unsigned long len; + struct TestAES aesdata; + struct TestAES aestweak; + + for (j = 0; j < 2; j++) { + /* skip the cases where + * the length is smaller than 2*blocklen + * or the length is not a multiple of 32 + */ + if ((j == 1) && ((data->PTLEN < 32) || (data->PTLEN % 32))) { + continue; + } + len = data->PTLEN / 2; + + AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); + AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); + AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); + AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); + + seq = data->seqnum; + STORE64L(seq, Torg); + memset(Torg + 8, 0, 8); + + memcpy(T, Torg, sizeof(T)); + if (j == 0) { + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, OUT, data->PTX); + } else { + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, OUT, data->PTX); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, &OUT[len], &data->PTX[len]); + } + + g_assert(memcmp(OUT, data->CTX, data->PTLEN) == 0); + + memcpy(T, Torg, sizeof(T)); + if (j == 0) { + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, OUT, data->CTX); + } else { + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, OUT, data->CTX); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, &OUT[len], &data->CTX[len]); + } + + g_assert(memcmp(OUT, data->PTX, data->PTLEN) == 0); + } +} + + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_xts); + } + + return g_test_run(); +} |