123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- /*
- * 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.1 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 "qapi/error.h"
- #include "blockpriv.h"
- #include "block-qcow.h"
- #include "block-luks.h"
- static const QCryptoBlockDriver *qcrypto_block_drivers[] = {
- [Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow,
- [Q_CRYPTO_BLOCK_FORMAT_LUKS] = &qcrypto_block_driver_luks,
- };
- bool qcrypto_block_has_format(QCryptoBlockFormat format,
- const uint8_t *buf,
- size_t len)
- {
- const QCryptoBlockDriver *driver;
- if (format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
- !qcrypto_block_drivers[format]) {
- return false;
- }
- driver = qcrypto_block_drivers[format];
- return driver->has_format(buf, len);
- }
- QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options,
- const char *optprefix,
- QCryptoBlockReadFunc readfunc,
- void *opaque,
- unsigned int flags,
- size_t n_threads,
- Error **errp)
- {
- QCryptoBlock *block = g_new0(QCryptoBlock, 1);
- block->format = options->format;
- if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
- !qcrypto_block_drivers[options->format]) {
- error_setg(errp, "Unsupported block driver %s",
- QCryptoBlockFormat_str(options->format));
- g_free(block);
- return NULL;
- }
- block->driver = qcrypto_block_drivers[options->format];
- if (block->driver->open(block, options, optprefix,
- readfunc, opaque, flags, n_threads, errp) < 0)
- {
- g_free(block);
- return NULL;
- }
- qemu_mutex_init(&block->mutex);
- return block;
- }
- QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
- const char *optprefix,
- QCryptoBlockInitFunc initfunc,
- QCryptoBlockWriteFunc writefunc,
- void *opaque,
- Error **errp)
- {
- QCryptoBlock *block = g_new0(QCryptoBlock, 1);
- block->format = options->format;
- if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
- !qcrypto_block_drivers[options->format]) {
- error_setg(errp, "Unsupported block driver %s",
- QCryptoBlockFormat_str(options->format));
- g_free(block);
- return NULL;
- }
- block->driver = qcrypto_block_drivers[options->format];
- if (block->driver->create(block, options, optprefix, initfunc,
- writefunc, opaque, errp) < 0) {
- g_free(block);
- return NULL;
- }
- qemu_mutex_init(&block->mutex);
- return block;
- }
- static int qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block,
- size_t headerlen, void *opaque, Error **errp)
- {
- size_t *headerlenp = opaque;
- /* Stash away the payload size */
- *headerlenp = headerlen;
- return 0;
- }
- static int qcrypto_block_headerlen_hdr_write_func(QCryptoBlock *block,
- size_t offset, const uint8_t *buf, size_t buflen,
- void *opaque, Error **errp)
- {
- /* Discard the bytes, we're not actually writing to an image */
- return 0;
- }
- bool
- qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts,
- const char *optprefix,
- size_t *len,
- Error **errp)
- {
- /* Fake LUKS creation in order to determine the payload size */
- g_autoptr(QCryptoBlock) crypto =
- qcrypto_block_create(create_opts, optprefix,
- qcrypto_block_headerlen_hdr_init_func,
- qcrypto_block_headerlen_hdr_write_func,
- len, errp);
- return crypto != NULL;
- }
- int qcrypto_block_amend_options(QCryptoBlock *block,
- QCryptoBlockReadFunc readfunc,
- QCryptoBlockWriteFunc writefunc,
- void *opaque,
- QCryptoBlockAmendOptions *options,
- bool force,
- Error **errp)
- {
- if (options->format != block->format) {
- error_setg(errp,
- "Cannot amend encryption format");
- return -1;
- }
- if (!block->driver->amend) {
- error_setg(errp,
- "Crypto format %s doesn't support format options amendment",
- QCryptoBlockFormat_str(block->format));
- return -1;
- }
- return block->driver->amend(block,
- readfunc,
- writefunc,
- opaque,
- options,
- force,
- errp);
- }
- QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block,
- Error **errp)
- {
- QCryptoBlockInfo *info = g_new0(QCryptoBlockInfo, 1);
- info->format = block->format;
- if (block->driver->get_info &&
- block->driver->get_info(block, info, errp) < 0) {
- g_free(info);
- return NULL;
- }
- return info;
- }
- int qcrypto_block_decrypt(QCryptoBlock *block,
- uint64_t offset,
- uint8_t *buf,
- size_t len,
- Error **errp)
- {
- return block->driver->decrypt(block, offset, buf, len, errp);
- }
- int qcrypto_block_encrypt(QCryptoBlock *block,
- uint64_t offset,
- uint8_t *buf,
- size_t len,
- Error **errp)
- {
- return block->driver->encrypt(block, offset, buf, len, errp);
- }
- QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block)
- {
- /* Ciphers should be accessed through pop/push method to be thread-safe.
- * Better, they should not be accessed externally at all (note, that
- * pop/push are static functions)
- * This function is used only in test with one thread (it's safe to skip
- * pop/push interface), so it's enough to assert it here:
- */
- assert(block->n_ciphers <= 1);
- return block->ciphers ? block->ciphers[0] : NULL;
- }
- static QCryptoCipher *qcrypto_block_pop_cipher(QCryptoBlock *block)
- {
- QCryptoCipher *cipher;
- qemu_mutex_lock(&block->mutex);
- assert(block->n_free_ciphers > 0);
- block->n_free_ciphers--;
- cipher = block->ciphers[block->n_free_ciphers];
- qemu_mutex_unlock(&block->mutex);
- return cipher;
- }
- static void qcrypto_block_push_cipher(QCryptoBlock *block,
- QCryptoCipher *cipher)
- {
- qemu_mutex_lock(&block->mutex);
- assert(block->n_free_ciphers < block->n_ciphers);
- block->ciphers[block->n_free_ciphers] = cipher;
- block->n_free_ciphers++;
- qemu_mutex_unlock(&block->mutex);
- }
- int qcrypto_block_init_cipher(QCryptoBlock *block,
- QCryptoCipherAlgorithm alg,
- QCryptoCipherMode mode,
- const uint8_t *key, size_t nkey,
- size_t n_threads, Error **errp)
- {
- size_t i;
- assert(!block->ciphers && !block->n_ciphers && !block->n_free_ciphers);
- block->ciphers = g_new0(QCryptoCipher *, n_threads);
- for (i = 0; i < n_threads; i++) {
- block->ciphers[i] = qcrypto_cipher_new(alg, mode, key, nkey, errp);
- if (!block->ciphers[i]) {
- qcrypto_block_free_cipher(block);
- return -1;
- }
- block->n_ciphers++;
- block->n_free_ciphers++;
- }
- return 0;
- }
- void qcrypto_block_free_cipher(QCryptoBlock *block)
- {
- size_t i;
- if (!block->ciphers) {
- return;
- }
- assert(block->n_ciphers == block->n_free_ciphers);
- for (i = 0; i < block->n_ciphers; i++) {
- qcrypto_cipher_free(block->ciphers[i]);
- }
- g_free(block->ciphers);
- block->ciphers = NULL;
- block->n_ciphers = block->n_free_ciphers = 0;
- }
- QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block)
- {
- /* ivgen should be accessed under mutex. However, this function is used only
- * in test with one thread, so it's enough to assert it here:
- */
- assert(block->n_ciphers <= 1);
- return block->ivgen;
- }
- QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block)
- {
- return block->kdfhash;
- }
- uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block)
- {
- return block->payload_offset;
- }
- uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block)
- {
- return block->sector_size;
- }
- void qcrypto_block_free(QCryptoBlock *block)
- {
- if (!block) {
- return;
- }
- block->driver->cleanup(block);
- qcrypto_block_free_cipher(block);
- qcrypto_ivgen_free(block->ivgen);
- qemu_mutex_destroy(&block->mutex);
- g_free(block);
- }
- typedef int (*QCryptoCipherEncDecFunc)(QCryptoCipher *cipher,
- const void *in,
- void *out,
- size_t len,
- Error **errp);
- static int do_qcrypto_block_cipher_encdec(QCryptoCipher *cipher,
- size_t niv,
- QCryptoIVGen *ivgen,
- QemuMutex *ivgen_mutex,
- int sectorsize,
- uint64_t offset,
- uint8_t *buf,
- size_t len,
- QCryptoCipherEncDecFunc func,
- Error **errp)
- {
- g_autofree uint8_t *iv = niv ? g_new0(uint8_t, niv) : NULL;
- int ret = -1;
- uint64_t startsector = offset / sectorsize;
- assert(QEMU_IS_ALIGNED(offset, sectorsize));
- assert(QEMU_IS_ALIGNED(len, sectorsize));
- while (len > 0) {
- size_t nbytes;
- if (niv) {
- if (ivgen_mutex) {
- qemu_mutex_lock(ivgen_mutex);
- }
- ret = qcrypto_ivgen_calculate(ivgen, startsector, iv, niv, errp);
- if (ivgen_mutex) {
- qemu_mutex_unlock(ivgen_mutex);
- }
- if (ret < 0) {
- return -1;
- }
- if (qcrypto_cipher_setiv(cipher,
- iv, niv,
- errp) < 0) {
- return -1;
- }
- }
- nbytes = len > sectorsize ? sectorsize : len;
- if (func(cipher, buf, buf, nbytes, errp) < 0) {
- return -1;
- }
- startsector++;
- buf += nbytes;
- len -= nbytes;
- }
- return 0;
- }
- int qcrypto_block_cipher_decrypt_helper(QCryptoCipher *cipher,
- size_t niv,
- QCryptoIVGen *ivgen,
- int sectorsize,
- uint64_t offset,
- uint8_t *buf,
- size_t len,
- Error **errp)
- {
- return do_qcrypto_block_cipher_encdec(cipher, niv, ivgen, NULL, sectorsize,
- offset, buf, len,
- qcrypto_cipher_decrypt, errp);
- }
- int qcrypto_block_cipher_encrypt_helper(QCryptoCipher *cipher,
- size_t niv,
- QCryptoIVGen *ivgen,
- int sectorsize,
- uint64_t offset,
- uint8_t *buf,
- size_t len,
- Error **errp)
- {
- return do_qcrypto_block_cipher_encdec(cipher, niv, ivgen, NULL, sectorsize,
- offset, buf, len,
- qcrypto_cipher_encrypt, errp);
- }
- int qcrypto_block_decrypt_helper(QCryptoBlock *block,
- int sectorsize,
- uint64_t offset,
- uint8_t *buf,
- size_t len,
- Error **errp)
- {
- int ret;
- QCryptoCipher *cipher = qcrypto_block_pop_cipher(block);
- ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen,
- &block->mutex, sectorsize, offset, buf,
- len, qcrypto_cipher_decrypt, errp);
- qcrypto_block_push_cipher(block, cipher);
- return ret;
- }
- int qcrypto_block_encrypt_helper(QCryptoBlock *block,
- int sectorsize,
- uint64_t offset,
- uint8_t *buf,
- size_t len,
- Error **errp)
- {
- int ret;
- QCryptoCipher *cipher = qcrypto_block_pop_cipher(block);
- ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen,
- &block->mutex, sectorsize, offset, buf,
- len, qcrypto_cipher_encrypt, errp);
- qcrypto_block_push_cipher(block, cipher);
- return ret;
- }
|