From nobody Wed Apr 16 07:40:18 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1499449188024240.0295726197545; Fri, 7 Jul 2017 10:39:48 -0700 (PDT) Received: from localhost ([::1]:57855 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dTXEH-0006qb-7b for importer@patchew.org; Fri, 07 Jul 2017 13:39:45 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42819) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dTWm5-000651-Gl for qemu-devel@nongnu.org; Fri, 07 Jul 2017 13:10:42 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dTWlx-0000C5-2p for qemu-devel@nongnu.org; Fri, 07 Jul 2017 13:10:37 -0400 Received: from mx1.redhat.com ([209.132.183.28]:56936) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dTWll-00005X-Uk; Fri, 07 Jul 2017 13:10:18 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EF639804F0; Fri, 7 Jul 2017 17:10:16 +0000 (UTC) Received: from noname.redhat.com (ovpn-117-34.ams2.redhat.com [10.36.117.34]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0EF4161F23; Fri, 7 Jul 2017 17:10:15 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com EF639804F0 Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=kwolf@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com EF639804F0 From: Kevin Wolf To: qemu-block@nongnu.org Date: Fri, 7 Jul 2017 19:08:06 +0200 Message-Id: <1499447335-6125-52-git-send-email-kwolf@redhat.com> In-Reply-To: <1499447335-6125-1-git-send-email-kwolf@redhat.com> References: <1499447335-6125-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Fri, 07 Jul 2017 17:10:17 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PULL 051/100] qcow2: convert QCow2 to use QCryptoBlock for encryption X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: "Daniel P. Berrange" This converts the qcow2 driver to make use of the QCryptoBlock APIs for encrypting image content, using the legacy QCow2 AES scheme. With this change it is now required to use the QCryptoSecret object for providing passwords, instead of the current block password APIs / interactive prompting. $QEMU \ -object secret,id=3Dsec0,file=3D/home/berrange/encrypted.pw \ -drive file=3D/home/berrange/encrypted.qcow2,encrypt.key-secret=3Dsec0 The test 087 could be simplified since there is no longer a difference in behaviour when using blockdev_add with encrypted images for the running vs stopped CPU state. Signed-off-by: Daniel P. Berrange Message-id: 20170623162419.26068-12-berrange@redhat.com Reviewed-by: Alberto Garcia Signed-off-by: Max Reitz --- block/qcow2-cluster.c | 47 +--------- block/qcow2.c | 226 ++++++++++++++++++++++++++++++-----------= ---- block/qcow2.h | 5 +- qapi/block-core.json | 27 +++++- tests/qemu-iotests/049 | 2 +- tests/qemu-iotests/049.out | 4 +- tests/qemu-iotests/082.out | 27 ++++++ tests/qemu-iotests/087 | 28 +++--- tests/qemu-iotests/087.out | 12 +-- tests/qemu-iotests/134 | 18 +++- tests/qemu-iotests/134.out | 10 +- tests/qemu-iotests/158 | 19 ++-- tests/qemu-iotests/158.out | 14 +-- tests/qemu-iotests/common | 10 +- 14 files changed, 263 insertions(+), 186 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index a570929..71a5e0d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -357,47 +357,6 @@ static int count_contiguous_clusters_unallocated(int n= b_clusters, return i; } =20 -/* The crypt function is compatible with the linux cryptoloop - algorithm for < 4 GB images. */ -int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, - uint8_t *buf, int nb_sectors, bool enc, - Error **errp) -{ - union { - uint64_t ll[2]; - uint8_t b[16]; - } ivec; - int i; - int ret; - - for(i =3D 0; i < nb_sectors; i++) { - ivec.ll[0] =3D cpu_to_le64(sector_num); - ivec.ll[1] =3D 0; - if (qcrypto_cipher_setiv(s->cipher, - ivec.b, G_N_ELEMENTS(ivec.b), - errp) < 0) { - return -1; - } - if (enc) { - ret =3D qcrypto_cipher_encrypt(s->cipher, - buf, buf, - 512, - errp); - } else { - ret =3D qcrypto_cipher_decrypt(s->cipher, - buf, buf, - 512, - errp); - } - if (ret < 0) { - return -1; - } - sector_num++; - buf +=3D 512; - } - return 0; -} - static int coroutine_fn do_perform_cow_read(BlockDriverState *bs, uint64_t src_cluster_offset, unsigned offset_in_cluster, @@ -438,11 +397,11 @@ static bool coroutine_fn do_perform_cow_encrypt(Block= DriverState *bs, BDRVQcow2State *s =3D bs->opaque; int64_t sector =3D (src_cluster_offset + offset_in_cluster) >> BDRV_SECTOR_BITS; - assert(s->cipher); assert((offset_in_cluster & ~BDRV_SECTOR_MASK) =3D=3D 0); assert((bytes & ~BDRV_SECTOR_MASK) =3D=3D 0); - if (qcow2_encrypt_sectors(s, sector, buffer, - bytes >> BDRV_SECTOR_BITS, true, NULL) <= 0) { + assert(s->crypto); + if (qcrypto_block_encrypt(s->crypto, sector, buffer, + bytes, NULL) < 0) { return false; } } diff --git a/block/qcow2.c b/block/qcow2.c index df9caa8..1c66ec1 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -37,6 +37,9 @@ #include "qemu/option_int.h" #include "qemu/cutils.h" #include "qemu/bswap.h" +#include "qapi/opts-visitor.h" +#include "qapi-visit.h" +#include "block/crypto.h" =20 /* Differences with QCOW: @@ -461,6 +464,7 @@ static QemuOptsList qcow2_runtime_opts =3D { .type =3D QEMU_OPT_NUMBER, .help =3D "Clean unused cache entries after this time (in seco= nds)", }, + BLOCK_CRYPTO_OPT_DEF_QCOW_KEY_SECRET("encrypt."), { /* end of list */ } }, }; @@ -585,6 +589,7 @@ typedef struct Qcow2ReopenState { int overlap_check; bool discard_passthrough[QCOW2_DISCARD_MAX]; uint64_t cache_clean_interval; + QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime optio= ns */ } Qcow2ReopenState; =20 static int qcow2_update_options_prepare(BlockDriverState *bs, @@ -598,9 +603,14 @@ static int qcow2_update_options_prepare(BlockDriverSta= te *bs, int overlap_check_template =3D 0; uint64_t l2_cache_size, refcount_cache_size; int i; + const char *encryptfmt; + QDict *encryptopts =3D NULL; Error *local_err =3D NULL; int ret; =20 + qdict_extract_subqdict(options, &encryptopts, "encrypt."); + encryptfmt =3D qdict_get_try_str(encryptopts, "format"); + opts =3D qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { @@ -751,8 +761,42 @@ static int qcow2_update_options_prepare(BlockDriverSta= te *bs, r->discard_passthrough[QCOW2_DISCARD_OTHER] =3D qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); =20 + switch (s->crypt_method_header) { + case QCOW_CRYPT_NONE: + if (encryptfmt) { + error_setg(errp, "No encryption in image header, but options " + "specified format '%s'", encryptfmt); + ret =3D -EINVAL; + goto fail; + } + break; + + case QCOW_CRYPT_AES: + if (encryptfmt && !g_str_equal(encryptfmt, "aes")) { + error_setg(errp, + "Header reported 'aes' encryption format but " + "options specify '%s'", encryptfmt); + ret =3D -EINVAL; + goto fail; + } + qdict_del(encryptopts, "format"); + r->crypto_opts =3D block_crypto_open_opts_init( + Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); + break; + + default: + error_setg(errp, "Unsupported encryption method %d", + s->crypt_method_header); + break; + } + if (s->crypt_method_header !=3D QCOW_CRYPT_NONE && !r->crypto_opts) { + ret =3D -EINVAL; + goto fail; + } + ret =3D 0; fail: + QDECREF(encryptopts); qemu_opts_del(opts); opts =3D NULL; return ret; @@ -785,6 +829,9 @@ static void qcow2_update_options_commit(BlockDriverStat= e *bs, s->cache_clean_interval =3D r->cache_clean_interval; cache_clean_timer_init(bs, bdrv_get_aio_context(bs)); } + + qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); + s->crypto_opts =3D r->crypto_opts; } =20 static void qcow2_update_options_abort(BlockDriverState *bs, @@ -796,6 +843,7 @@ static void qcow2_update_options_abort(BlockDriverState= *bs, if (r->refcount_block_cache) { qcow2_cache_destroy(bs, r->refcount_block_cache); } + qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); } =20 static int qcow2_update_options(BlockDriverState *bs, QDict *options, @@ -967,12 +1015,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict = *options, int flags, ret =3D -EINVAL; goto fail; } - if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128, - QCRYPTO_CIPHER_MODE_CBC)) { - error_setg(errp, "AES cipher not available"); - ret =3D -EINVAL; - goto fail; - } s->crypt_method_header =3D header.crypt_method; if (s->crypt_method_header) { if (bdrv_uses_whitelist() && @@ -990,6 +1032,7 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *= options, int flags, } =20 bs->encrypted =3D true; + bs->valid_key =3D true; } =20 s->l2_bits =3D s->cluster_bits - 3; /* L2 is always one cluster */ @@ -1122,6 +1165,19 @@ static int qcow2_do_open(BlockDriverState *bs, QDict= *options, int flags, goto fail; } =20 + if (s->crypt_method_header =3D=3D QCOW_CRYPT_AES) { + unsigned int cflags =3D 0; + if (flags & BDRV_O_NO_IO) { + cflags |=3D QCRYPTO_BLOCK_OPEN_NO_IO; + } + s->crypto =3D qcrypto_block_open(s->crypto_opts, NULL, NULL, + cflags, errp); + if (!s->crypto) { + ret =3D -EINVAL; + goto fail; + } + } + /* read the backing file name */ if (header.backing_file_offset !=3D 0) { len =3D header.backing_file_size; @@ -1202,6 +1258,8 @@ static int qcow2_do_open(BlockDriverState *bs, QDict = *options, int flags, } g_free(s->cluster_cache); qemu_vfree(s->cluster_data); + qcrypto_block_free(s->crypto); + qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); return ret; } =20 @@ -1229,41 +1287,6 @@ static void qcow2_refresh_limits(BlockDriverState *b= s, Error **errp) bs->bl.pdiscard_alignment =3D s->cluster_size; } =20 -static int qcow2_set_key(BlockDriverState *bs, const char *key) -{ - BDRVQcow2State *s =3D bs->opaque; - uint8_t keybuf[16]; - int len, i; - Error *err =3D NULL; - - memset(keybuf, 0, 16); - len =3D strlen(key); - if (len > 16) - len =3D 16; - /* XXX: we could compress the chars to 7 bits to increase - entropy */ - for(i =3D 0;i < len;i++) { - keybuf[i] =3D key[i]; - } - assert(bs->encrypted); - - qcrypto_cipher_free(s->cipher); - s->cipher =3D qcrypto_cipher_new( - QCRYPTO_CIPHER_ALG_AES_128, - QCRYPTO_CIPHER_MODE_CBC, - keybuf, G_N_ELEMENTS(keybuf), - &err); - - if (!s->cipher) { - /* XXX would be nice if errors in this method could - * be properly propagate to the caller. Would need - * the bdrv_set_key() API signature to be fixed. */ - error_free(err); - return -1; - } - return 0; -} - static int qcow2_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { @@ -1379,7 +1402,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status= (BlockDriverState *bs, *pnum =3D bytes >> BDRV_SECTOR_BITS; =20 if (cluster_offset !=3D 0 && ret !=3D QCOW2_CLUSTER_COMPRESSED && - !s->cipher) { + !s->crypto) { index_in_cluster =3D sector_num & (s->cluster_sectors - 1); cluster_offset |=3D (index_in_cluster << BDRV_SECTOR_BITS); *file =3D bs->file->bs; @@ -1436,7 +1459,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverSt= ate *bs, uint64_t offset, =20 /* prepare next request */ cur_bytes =3D MIN(bytes, INT_MAX); - if (s->cipher) { + if (s->crypto) { cur_bytes =3D MIN(cur_bytes, QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); } @@ -1506,7 +1529,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverSt= ate *bs, uint64_t offset, } =20 if (bs->encrypted) { - assert(s->cipher); + assert(s->crypto); =20 /* * For encrypted images, read everything into a temporary @@ -1538,14 +1561,15 @@ static coroutine_fn int qcow2_co_preadv(BlockDriver= State *bs, uint64_t offset, goto fail; } if (bs->encrypted) { - assert(s->cipher); + assert(s->crypto); assert((offset & (BDRV_SECTOR_SIZE - 1)) =3D=3D 0); assert((cur_bytes & (BDRV_SECTOR_SIZE - 1)) =3D=3D 0); Error *err =3D NULL; - if (qcow2_encrypt_sectors(s, offset >> BDRV_SECTOR_BITS, + if (qcrypto_block_decrypt(s->crypto, + offset >> BDRV_SECTOR_BITS, cluster_data, - cur_bytes >> BDRV_SECTOR_BITS, - false, &err) < 0) { + cur_bytes, + &err) < 0) { error_free(err); ret =3D -EIO; goto fail; @@ -1661,7 +1685,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverS= tate *bs, uint64_t offset, =20 if (bs->encrypted) { Error *err =3D NULL; - assert(s->cipher); + assert(s->crypto); if (!cluster_data) { cluster_data =3D qemu_try_blockalign(bs->file->bs, QCOW_MAX_CRYPT_CLUSTERS @@ -1676,10 +1700,9 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriver= State *bs, uint64_t offset, QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size); =20 - if (qcow2_encrypt_sectors(s, offset >> BDRV_SECTOR_BITS, + if (qcrypto_block_encrypt(s->crypto, offset >> BDRV_SECTOR_BIT= S, cluster_data, - cur_bytes >>BDRV_SECTOR_BITS, - true, &err) < 0) { + cur_bytes, &err) < 0) { error_free(err); ret =3D -EIO; goto fail; @@ -1804,8 +1827,8 @@ static void qcow2_close(BlockDriverState *bs) qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(bs, s->refcount_block_cache); =20 - qcrypto_cipher_free(s->cipher); - s->cipher =3D NULL; + qcrypto_block_free(s->crypto); + s->crypto =3D NULL; =20 g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); @@ -1823,7 +1846,7 @@ static void qcow2_invalidate_cache(BlockDriverState *= bs, Error **errp) { BDRVQcow2State *s =3D bs->opaque; int flags =3D s->flags; - QCryptoCipher *cipher =3D NULL; + QCryptoBlock *crypto =3D NULL; QDict *options; Error *local_err =3D NULL; int ret; @@ -1833,8 +1856,8 @@ static void qcow2_invalidate_cache(BlockDriverState *= bs, Error **errp) * that means we don't have to worry about reopening them here. */ =20 - cipher =3D s->cipher; - s->cipher =3D NULL; + crypto =3D s->crypto; + s->crypto =3D NULL; =20 qcow2_close(bs); =20 @@ -1855,7 +1878,7 @@ static void qcow2_invalidate_cache(BlockDriverState *= bs, Error **errp) return; } =20 - s->cipher =3D cipher; + s->crypto =3D crypto; } =20 static size_t header_ext_add(char *buf, uint32_t magic, const void *s, @@ -2079,6 +2102,56 @@ static int qcow2_change_backing_file(BlockDriverStat= e *bs, return qcow2_update_header(bs); } =20 + +static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encry= ptfmt, + QemuOpts *opts, Error **errp) +{ + BDRVQcow2State *s =3D bs->opaque; + QCryptoBlockCreateOptions *cryptoopts =3D NULL; + QCryptoBlock *crypto =3D NULL; + int ret =3D -EINVAL; + QDict *options, *encryptopts; + + options =3D qemu_opts_to_qdict(opts, NULL); + qdict_extract_subqdict(options, &encryptopts, "encrypt."); + QDECREF(options); + + if (!g_str_equal(encryptfmt, "aes")) { + error_setg(errp, "Unknown encryption format '%s', expected 'aes'", + encryptfmt); + ret =3D -EINVAL; + goto out; + } + cryptoopts =3D block_crypto_create_opts_init( + Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); + if (!cryptoopts) { + ret =3D -EINVAL; + goto out; + } + s->crypt_method_header =3D QCOW_CRYPT_AES; + + crypto =3D qcrypto_block_create(cryptoopts, + NULL, NULL, + bs, errp); + if (!crypto) { + ret =3D -EINVAL; + goto out; + } + + ret =3D qcow2_update_header(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write encryption header"); + goto out; + } + + out: + QDECREF(encryptopts); + qcrypto_block_free(crypto); + qapi_free_QCryptoBlockCreateOptions(cryptoopts); + return ret; +} + + static int preallocate(BlockDriverState *bs) { uint64_t bytes; @@ -2273,17 +2346,8 @@ static int qcow2_create2(const char *filename, int64= _t total_size, .header_length =3D cpu_to_be32(sizeof(*header)), }; =20 - if (encryptfmt) { - if (!g_str_equal(encryptfmt, "aes")) { - error_setg(errp, "Unknown encryption format '%s', expected 'ae= s'", - encryptfmt); - ret =3D -EINVAL; - goto out; - } - header->crypt_method =3D cpu_to_be32(QCOW_CRYPT_AES); - } else { - header->crypt_method =3D cpu_to_be32(QCOW_CRYPT_NONE); - } + /* We'll update this to correct value later */ + header->crypt_method =3D cpu_to_be32(QCOW_CRYPT_NONE); =20 if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) { header->compatible_features |=3D @@ -2362,6 +2426,14 @@ static int qcow2_create2(const char *filename, int64= _t total_size, } } =20 + /* Want encryption? There you go. */ + if (encryptfmt) { + ret =3D qcow2_set_up_encryption(blk_bs(blk), encryptfmt, opts, err= p); + if (ret < 0) { + goto out; + } + } + /* And if we're supposed to preallocate metadata, do that now */ if (prealloc !=3D PREALLOC_MODE_OFF) { BDRVQcow2State *s =3D blk_bs(blk)->opaque; @@ -2377,11 +2449,17 @@ static int qcow2_create2(const char *filename, int6= 4_t total_size, blk_unref(blk); blk =3D NULL; =20 - /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returni= ng */ + /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returni= ng. + * Using BDRV_O_NO_IO, since encryption is now setup we don't want to + * have to setup decryption context. We're not doing any I/O on the top + * level BlockDriverState, only lower layers, where BDRV_O_NO_IO does + * not have effect. + */ options =3D qdict_new(); qdict_put_str(options, "driver", "qcow2"); blk =3D blk_new_open(filename, NULL, options, - BDRV_O_RDWR | BDRV_O_NO_BACKING, &local_err); + BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO, + &local_err); if (blk =3D=3D NULL) { error_propagate(errp, local_err); ret =3D -EIO; @@ -3226,9 +3304,9 @@ static int qcow2_amend_options(BlockDriverState *bs, = QemuOpts *opts, backing_format =3D qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) { encrypt =3D qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, - !!s->cipher); + !!s->crypto); =20 - if (encrypt !=3D !!s->cipher) { + if (encrypt !=3D !!s->crypto) { error_report("Changing the encryption flag is not supporte= d"); return -ENOTSUP; } @@ -3454,6 +3532,7 @@ static QemuOptsList qcow2_create_opts =3D { .type =3D QEMU_OPT_STRING, .help =3D "Encrypt the image, format choices: 'aes'", }, + BLOCK_CRYPTO_OPT_DEF_QCOW_KEY_SECRET("encrypt."), { .name =3D BLOCK_OPT_CLUSTER_SIZE, .type =3D QEMU_OPT_SIZE, @@ -3496,7 +3575,6 @@ BlockDriver bdrv_qcow2 =3D { .bdrv_create =3D qcow2_create, .bdrv_has_zero_init =3D bdrv_has_zero_init_1, .bdrv_co_get_block_status =3D qcow2_co_get_block_status, - .bdrv_set_key =3D qcow2_set_key, =20 .bdrv_co_preadv =3D qcow2_co_preadv, .bdrv_co_pwritev =3D qcow2_co_pwritev, diff --git a/block/qcow2.h b/block/qcow2.h index 5a3f07e..4b89610 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -25,7 +25,7 @@ #ifndef BLOCK_QCOW2_H #define BLOCK_QCOW2_H =20 -#include "crypto/cipher.h" +#include "crypto/block.h" #include "qemu/coroutine.h" =20 //#define DEBUG_ALLOC @@ -257,7 +257,8 @@ typedef struct BDRVQcow2State { =20 CoMutex lock; =20 - QCryptoCipher *cipher; /* current cipher, NULL if no key yet */ + QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime optio= ns */ + QCryptoBlock *crypto; /* Disk encryption format driver */ uint32_t crypt_method_header; uint64_t snapshots_offset; int snapshots_size; diff --git a/qapi/block-core.json b/qapi/block-core.json index c4b7e6e..1f268ee 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2317,6 +2317,26 @@ 'data': { '*encrypt': 'BlockdevQcowEncryption' } } =20 =20 + +## +# @BlockdevQcow2EncryptionFormat: +# @aes: AES-CBC with plain64 initialization venctors +# +# Since: 2.10 +## +{ 'enum': 'BlockdevQcow2EncryptionFormat', + 'data': [ 'aes' ] } + +## +# @BlockdevQcow2Encryption: +# +# Since: 2.10 +## +{ 'union': 'BlockdevQcow2Encryption', + 'base': { 'format': 'BlockdevQcow2EncryptionFormat' }, + 'discriminator': 'format', + 'data': { 'aes': 'QCryptoBlockOptionsQCow' } } + ## # @BlockdevOptionsQcow2: # @@ -2351,6 +2371,9 @@ # @cache-clean-interval: clean unused entries in the L2 and refcount # caches. The interval is in seconds. The default = value # is 0 and it disables this feature (since 2.5) +# @encrypt: Image decryption options. Mandatory for +# encrypted images, except when doing a metadata-o= nly +# probe of the image. (since 2.10) # # Since: 2.9 ## @@ -2364,8 +2387,8 @@ '*cache-size': 'int', '*l2-cache-size': 'int', '*refcount-cache-size': 'int', - '*cache-clean-interval': 'int' } } - + '*cache-clean-interval': 'int', + '*encrypt': 'BlockdevQcow2Encryption' } } =20 ## # @BlockdevOptionsSsh: diff --git a/tests/qemu-iotests/049 b/tests/qemu-iotests/049 index fff0760..df35b6d 100755 --- a/tests/qemu-iotests/049 +++ b/tests/qemu-iotests/049 @@ -106,7 +106,7 @@ test_qemu_img create -f $IMGFMT -o preallocation=3D1234= "$TEST_IMG" 64M echo "=3D=3D Check encryption option =3D=3D" echo test_qemu_img create -f $IMGFMT -o encryption=3Doff "$TEST_IMG" 64M -test_qemu_img create -f $IMGFMT -o encryption=3Don "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT --object secret,id=3Dsec0,data=3D123456 -o= encryption=3Don,encrypt.key-secret=3Dsec0 "$TEST_IMG" 64M =20 echo "=3D=3D Check lazy_refcounts option (only with v3) =3D=3D" echo diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 50e91c8..0032470 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -190,8 +190,8 @@ Formatting 'TEST_DIR/t.qcow2', fmt=3Dqcow2 size=3D67108= 864 cluster_size=3D65536 preall qemu-img create -f qcow2 -o encryption=3Doff TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=3Dqcow2 size=3D67108864 encryption=3Dof= f cluster_size=3D65536 lazy_refcounts=3Doff refcount_bits=3D16 =20 -qemu-img create -f qcow2 -o encryption=3Don TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=3Dqcow2 size=3D67108864 encryption=3Don= cluster_size=3D65536 lazy_refcounts=3Doff refcount_bits=3D16 +qemu-img create -f qcow2 --object secret,id=3Dsec0,data=3D123456 -o encryp= tion=3Don,encrypt.key-secret=3Dsec0 TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=3Dqcow2 size=3D67108864 encryption=3Don= encrypt.key-secret=3Dsec0 cluster_size=3D65536 lazy_refcounts=3Doff refcou= nt_bits=3D16 =20 =3D=3D Check lazy_refcounts option (only with v3) =3D=3D =20 diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index 621386b..3978db5 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -50,6 +50,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -64,6 +65,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -78,6 +80,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -92,6 +95,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -106,6 +110,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -120,6 +125,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -134,6 +140,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -148,6 +155,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -177,6 +185,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -240,6 +249,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -254,6 +264,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -268,6 +279,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -282,6 +294,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -296,6 +309,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -310,6 +324,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -324,6 +339,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -338,6 +354,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -367,6 +384,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -427,6 +445,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -441,6 +460,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -455,6 +475,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -469,6 +490,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -483,6 +505,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -497,6 +520,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -511,6 +535,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -525,6 +550,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates @@ -556,6 +582,7 @@ backing_file File name of a base image backing_fmt Image format of the base image encryption Encrypt the image with format 'aes'. (Deprecated in favor= of encrypt.format=3Daes) encrypt.format Encrypt the image, format choices: 'aes' +encrypt.key-secret ID of the secret that provides the AES encryption key cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc= , full) lazy_refcounts Postpone refcount updates diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index 6d52f7d..1d595b2 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -122,24 +122,18 @@ echo echo =3D=3D=3D Encrypted image =3D=3D=3D echo =20 -_make_test_img -o encryption=3Don $size -run_qemu -S <