There are a number of different algorithms that can be used to generate initialization vectors for disk encryption. This introduces a simple internal QCryptoBlockIV object to provide a consistent internal API to the different algorithms. The initially implemented algorithms are 'plain', 'plain64' and 'essiv', each matching the same named algorithm provided by the Linux kernel dm-crypt driver.
Reviewed-by: Eric Blake <ebl...@redhat.com> Reviewed-by: Fam Zheng <f...@redhat.com> Signed-off-by: Daniel P. Berrange <berra...@redhat.com> --- crypto/Makefile.objs | 4 + crypto/ivgen-essiv.c | 118 ++++++++++++++++++++++++++ crypto/ivgen-essiv.h | 28 +++++++ crypto/ivgen-plain.c | 59 +++++++++++++ crypto/ivgen-plain.h | 28 +++++++ crypto/ivgen-plain64.c | 59 +++++++++++++ crypto/ivgen-plain64.h | 28 +++++++ crypto/ivgen.c | 99 ++++++++++++++++++++++ crypto/ivgenpriv.h | 49 +++++++++++ include/crypto/ivgen.h | 206 ++++++++++++++++++++++++++++++++++++++++++++++ qapi/crypto.json | 19 +++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-ivgen.c | 173 ++++++++++++++++++++++++++++++++++++++ 14 files changed, 873 insertions(+) create mode 100644 crypto/ivgen-essiv.c create mode 100644 crypto/ivgen-essiv.h create mode 100644 crypto/ivgen-plain.c create mode 100644 crypto/ivgen-plain.h create mode 100644 crypto/ivgen-plain64.c create mode 100644 crypto/ivgen-plain64.h create mode 100644 crypto/ivgen.c create mode 100644 crypto/ivgenpriv.h create mode 100644 include/crypto/ivgen.h create mode 100644 tests/test-crypto-ivgen.c diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index 7122cc8..f28b00e 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -13,6 +13,10 @@ 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 # Let the userspace emulators avoid linking gnutls/etc crypto-aes-obj-y = aes.o diff --git a/crypto/ivgen-essiv.c b/crypto/ivgen-essiv.c new file mode 100644 index 0000000..5649c01 --- /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 0000000..4a00af8 --- /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 0000000..6a85256 --- /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 0000000..0fe8835 --- /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 0000000..9ca6db9 --- /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 0000000..c410445 --- /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 0000000..4ffc1eb --- /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 0000000..7b87e02 --- /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/include/crypto/ivgen.h b/include/crypto/ivgen.h new file mode 100644 index 0000000..09cdb6f --- /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/qapi/crypto.json b/qapi/crypto.json index 4bd690f..42b979a 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -78,3 +78,22 @@ { 'enum': 'QCryptoCipherMode', 'prefix': 'QCRYPTO_CIPHER_MODE', 'data': ['ecb', 'cbc']} + + +## +# 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']} diff --git a/tests/.gitignore b/tests/.gitignore index db6b9be..369f848 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -14,6 +14,7 @@ test-blockjob-txn test-coroutine test-crypto-cipher test-crypto-hash +test-crypto-ivgen test-crypto-pbkdf test-crypto-secret test-crypto-tlscredsx509 diff --git a/tests/Makefile b/tests/Makefile index 6e3b344..5ff0e72 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -93,6 +93,7 @@ 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-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -498,6 +499,7 @@ tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \ 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) 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-ivgen.c b/tests/test-crypto-ivgen.c new file mode 100644 index 0000000..96129da --- /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(); +} -- 2.5.0