From b917da4cbd13dae4cda3852d5bdf3725202103ab Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Sat, 31 Oct 2015 14:39:52 +0900 Subject: crypto: add cryptographic random byte source There are three backend impls provided. The preferred is gnutls, which is backed by nettle in modern distros. The gcrypt impl is provided for cases where QEMU build against gnutls is disabled, but crypto is still desired. No nettle impl is provided, since it is non-trivial to use the nettle APIs for random numbers. Users of nettle should ensure gnutls is enabled for QEMU. Reviewed-by: Fam Zheng Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrange --- include/crypto/random.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 include/crypto/random.h (limited to 'include') diff --git a/include/crypto/random.h b/include/crypto/random.h new file mode 100644 index 0000000..b3021c4 --- /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 . + * + */ + +#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__ */ -- cgit v1.1 From 37788f253a4a9ad5f27dae68aee261c784e1fa17 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 14 Oct 2015 13:14:04 +0100 Subject: crypto: add support for PBKDF2 algorithm The LUKS data format includes use of PBKDF2 (Password-Based Key Derivation Function). The Nettle library can provide an implementation of this, but we don't want code directly depending on a specific crypto library backend. Introduce a new include/crypto/pbkdf.h header which defines a QEMU API for invoking PBKDK2. The initial implementations are backed by nettle & gcrypt, which are commonly available with distros shipping GNUTLS. The test suite data is taken from the cryptsetup codebase under the LGPLv2.1+ license. This merely aims to verify that whatever backend we provide for this function in QEMU will comply with the spec. Reviewed-by: Fam Zheng Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrange --- include/crypto/pbkdf.h | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 include/crypto/pbkdf.h (limited to 'include') diff --git a/include/crypto/pbkdf.h b/include/crypto/pbkdf.h new file mode 100644 index 0000000..58a1fe6 --- /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 . + * + */ + +#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 + * + * + * Generating an AES encryption key from a user password + * + * #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); + * + * + * + */ + +/** + * 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__ */ -- cgit v1.1 From cb730894ae284965e03a40eabbf623b87206777b 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 --- include/crypto/ivgen.h | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 include/crypto/ivgen.h (limited to 'include') 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 . + * + */ + +#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. + * + * + * Encrypting block data with initialiation vectors + * + * 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; + * + * + */ + +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__ */ -- cgit v1.1 From 5a95e0fccdad951d8779fca459c20649c8b0cbb4 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Fri, 23 Oct 2015 16:14:25 +0100 Subject: crypto: add support for anti-forensic split algorithm The LUKS format specifies an anti-forensic split algorithm which is used to artificially expand the size of the key material on disk. This is an implementation of that algorithm. Reviewed-by: Fam Zheng Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrange --- include/crypto/afsplit.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 include/crypto/afsplit.h (limited to 'include') diff --git a/include/crypto/afsplit.h b/include/crypto/afsplit.h new file mode 100644 index 0000000..4cc4ca4 --- /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 . + * + */ + +#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. + * + * + * Creating a large split key for storage + * + * 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); + * + * + * + * + * Retrieving a master key from storage + * + * 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); + * + * + */ + +/** + * 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__ */ -- cgit v1.1 From 84f7f180b034b2a08b558bd3fbb5be1ae88f74c1 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 11 Feb 2016 14:00:17 +0000 Subject: crypto: import an implementation of the XTS cipher mode The XTS (XEX with tweaked-codebook and ciphertext stealing) cipher mode is commonly used in full disk encryption. There is unfortunately no implementation of it in either libgcrypt or nettle, so we need to provide our own. The libtomcrypt project provides a repository of crypto algorithms under a choice of either "public domain" or the "what the fuck public license". So this impl is taken from the libtomcrypt GIT repo and adapted to be compatible with the way we need to call ciphers provided by nettle/gcrypt. Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrange --- include/crypto/xts.h | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 include/crypto/xts.h (limited to 'include') diff --git a/include/crypto/xts.h b/include/crypto/xts.h new file mode 100644 index 0000000..c2924d8 --- /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 . + * + * 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_ */ -- cgit v1.1 From 7d9690148ac25bc755a759adab7d63f517d092b1 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Sat, 24 Oct 2015 11:44:13 +0100 Subject: crypto: add block encryption framework Add a generic framework for supporting different block encryption formats. Upon instantiating a QCryptoBlock object, it will read the encryption header and extract the encryption keys. It is then possible to call methods to encrypt/decrypt data buffers. There is also a mode whereby it will create/initialize a new encryption header on a previously unformatted volume. The initial framework comes with support for the legacy QCow AES based encryption. This enables code in the QCow driver to be consolidated later. Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrange --- include/crypto/block.h | 232 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 include/crypto/block.h (limited to 'include') diff --git a/include/crypto/block.h b/include/crypto/block.h new file mode 100644 index 0000000..a21e11f --- /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 . + * + */ + +#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__ */ -- cgit v1.1