aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2015-10-15 12:35:28 +0100
committerDaniel P. Berrange <berrange@redhat.com>2016-03-17 14:41:14 +0000
commitcb730894ae284965e03a40eabbf623b87206777b (patch)
tree9394f77b5d7f285918dc134d61da8630ba0fab68
parent37788f253a4a9ad5f27dae68aee261c784e1fa17 (diff)
crypto: add support for generating initialization vectors
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 <eblake@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
-rw-r--r--crypto/Makefile.objs4
-rw-r--r--crypto/ivgen-essiv.c118
-rw-r--r--crypto/ivgen-essiv.h28
-rw-r--r--crypto/ivgen-plain.c59
-rw-r--r--crypto/ivgen-plain.h28
-rw-r--r--crypto/ivgen-plain64.c59
-rw-r--r--crypto/ivgen-plain64.h28
-rw-r--r--crypto/ivgen.c99
-rw-r--r--crypto/ivgenpriv.h49
-rw-r--r--include/crypto/ivgen.h206
-rw-r--r--qapi/crypto.json19
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile2
-rw-r--r--tests/test-crypto-ivgen.c173
14 files changed, 873 insertions, 0 deletions
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index 7122cc8e54..f28b00e549 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 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 *)&sector, ndata);
+ if (sizeof(sector) < ndata) {
+ memset(data + sizeof(sector), 0, ndata - sizeof(sector));
+ }
+
+ if (qcrypto_cipher_encrypt(essiv->cipher,
+ data,
+ data,
+ ndata,
+ errp) < 0) {
+ g_free(data);
+ return -1;
+ }
+
+ if (ndata > niv) {
+ ndata = niv;
+ }
+ memcpy(iv, data, ndata);
+ if (ndata < niv) {
+ memset(iv + ndata, 0, niv - ndata);
+ }
+ g_free(data);
+ return 0;
+}
+
+static void qcrypto_ivgen_essiv_cleanup(QCryptoIVGen *ivgen)
+{
+ QCryptoIVGenESSIV *essiv = ivgen->private;
+
+ qcrypto_cipher_free(essiv->cipher);
+ g_free(essiv);
+}
+
+
+struct QCryptoIVGenDriver qcrypto_ivgen_essiv = {
+ .init = qcrypto_ivgen_essiv_init,
+ .calculate = qcrypto_ivgen_essiv_calculate,
+ .cleanup = qcrypto_ivgen_essiv_cleanup,
+};
+
diff --git a/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, &sector, ivprefix);
+ if (ivprefix < niv) {
+ memset(iv + ivprefix, 0, niv - ivprefix);
+ }
+ return 0;
+}
+
+static void qcrypto_ivgen_plain_cleanup(QCryptoIVGen *ivgen)
+{
+}
+
+
+struct QCryptoIVGenDriver qcrypto_ivgen_plain64 = {
+ .init = qcrypto_ivgen_plain_init,
+ .calculate = qcrypto_ivgen_plain_calculate,
+ .cleanup = qcrypto_ivgen_plain_cleanup,
+};
+
diff --git a/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/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/qapi/crypto.json b/qapi/crypto.json
index 4bd690fcfb..42b979a0d5 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 db6b9beea8..369f848036 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 6e3b344e7c..5ff0e728fb 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 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();
+}