From 7581bdf62a71adc80d959526bdf192390b2d35b7 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 15 Oct 2015 12:35:28 +0100 Subject: 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 Reviewed-by: Fam Zheng Signed-off-by: Daniel P. Berrange --- 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 ++++++++++++++++++++ 9 files changed, 472 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 (limited to 'crypto') 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 . + * + */ + +#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 . + * + */ + +#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 . + * + */ + +#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 . + * + */ + +#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 . + * + */ + +#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 . + * + */ + +#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 . + * + */ + +#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 . + * + */ + +#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__ */ -- cgit v1.1