diff mbox series

[07/13] qcow2: extend qemu-img amend interface with crypto options

Message ID 20200114193350.10830-8-mlevitsk@redhat.com
State New
Headers show
Series LUKS: encryption slot management using amend interface | expand

Commit Message

Maxim Levitsky Jan. 14, 2020, 7:33 p.m. UTC
Now that we have all the infrastructure in place,
wire it in the qcow2 driver and expose this to the user.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 block/qcow2.c | 101 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 79 insertions(+), 22 deletions(-)

Comments

Daniel P. Berrangé Jan. 28, 2020, 5:30 p.m. UTC | #1
On Tue, Jan 14, 2020 at 09:33:44PM +0200, Maxim Levitsky wrote:
> Now that we have all the infrastructure in place,
> wire it in the qcow2 driver and expose this to the user.
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  block/qcow2.c | 101 +++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 79 insertions(+), 22 deletions(-)
> 
> diff --git a/block/qcow2.c b/block/qcow2.c
> index c6c2deee75..1b01174aed 100644
> --- a/block/qcow2.c
> +++ b/block/qcow2.c
> @@ -173,6 +173,19 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset,
>      return ret;
>  }
>  
> +static QDict*
> +qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp)
> +{
> +    QDict *cryptoopts_qdict;
> +    QDict *opts_qdict;
> +
> +    /* Extract "encrypt." options into a qdict */
> +    opts_qdict = qemu_opts_to_qdict(opts, NULL);
> +    qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
> +    qobject_unref(opts_qdict);
> +    qdict_put_str(cryptoopts_qdict, "format", "luks");
> +    return cryptoopts_qdict;
> +}
>  
>  /* 
>   * read qcow2 extension and fill bs
> @@ -4631,20 +4644,18 @@ static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block,
>  static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len,
>                                           Error **errp)
>  {
> -    QDict *opts_qdict;
> -    QDict *cryptoopts_qdict;
>      QCryptoBlockCreateOptions *cryptoopts;
> +    QDict* crypto_opts_dict;
>      QCryptoBlock *crypto;
>  
> -    /* Extract "encrypt." options into a qdict */
> -    opts_qdict = qemu_opts_to_qdict(opts, NULL);
> -    qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
> -    qobject_unref(opts_qdict);
> +    crypto_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp);
> +    if (!crypto_opts_dict) {
> +        return false;
> +    }
> +
> +    cryptoopts = block_crypto_create_opts_init(crypto_opts_dict, errp);
> +    qobject_unref(crypto_opts_dict);
>  
> -    /* Build QCryptoBlockCreateOptions object from qdict */
> -    qdict_put_str(cryptoopts_qdict, "format", "luks");
> -    cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
> -    qobject_unref(cryptoopts_qdict);
>      if (!cryptoopts) {
>          return false;
>      }
> @@ -5083,6 +5094,7 @@ typedef enum Qcow2AmendOperation {
>      QCOW2_NO_OPERATION = 0,
>  
>      QCOW2_UPGRADING,
> +    QCOW2_UPDATING_ENCRYPTION,
>      QCOW2_CHANGING_REFCOUNT_ORDER,
>      QCOW2_DOWNGRADING,
>  } Qcow2AmendOperation;
> @@ -5167,6 +5179,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>      int ret;
>      QemuOptDesc *desc = opts->list->desc;
>      Qcow2AmendHelperCBInfo helper_cb_info;
> +    bool encryption_update = false;
>  
>      while (desc && desc->name) {
>          if (!qemu_opt_find(opts, desc->name)) {
> @@ -5215,9 +5228,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>                  return -ENOTSUP;
>              }
>          } else if (g_str_has_prefix(desc->name, "encrypt.")) {
> -            error_setg(errp,
> -                       "Changing the encryption parameters is not supported");
> -            return -ENOTSUP;
> +            if (!s->crypto) {
> +                error_setg(errp,
> +                           "Can't amend encryption options - encryption not present");
> +                return -EINVAL;
> +            }
> +            if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
> +                error_setg(errp,
> +                           "Only LUKS encryption options can be amended");
> +                return -ENOTSUP;
> +            }
> +            encryption_update = true;
>          } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
>              cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
>                                               cluster_size);
> @@ -5267,7 +5288,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>          .original_status_cb = status_cb,
>          .original_cb_opaque = cb_opaque,
>          .total_operations = (new_version != old_version)
> -                          + (s->refcount_bits != refcount_bits)
> +                          + (s->refcount_bits != refcount_bits) +
> +                            (encryption_update == true)
>      };
>  
>      /* Upgrade first (some features may require compat=1.1) */
> @@ -5280,6 +5302,33 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>          }
>      }
>  
> +    if (encryption_update) {
> +        QDict *amend_opts_dict;
> +        QCryptoBlockAmendOptions *amend_opts;
> +
> +        helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION;
> +        amend_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp);
> +        if (!amend_opts_dict) {
> +            return -EINVAL;
> +        }
> +        amend_opts = block_crypto_amend_opts_init(amend_opts_dict, errp);
> +        qobject_unref(amend_opts_dict);
> +        if (!amend_opts) {
> +            return -EINVAL;
> +        }
> +        ret = qcrypto_block_amend_options(s->crypto,
> +                                          qcow2_crypto_hdr_read_func,
> +                                          qcow2_crypto_hdr_write_func,
> +                                          bs,
> +                                          amend_opts,
> +                                          force,
> +                                          errp);
> +        qapi_free_QCryptoBlockAmendOptions(amend_opts);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +    }
> +
>      if (s->refcount_bits != refcount_bits) {
>          int refcount_order = ctz32(refcount_bits);
>  
> @@ -5488,14 +5537,6 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
>          .type = QEMU_OPT_STRING,                                    \
>          .help = "Encrypt the image, format choices: 'aes', 'luks'", \
>      },                                                              \
> -    BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.",                     \
> -        "ID of secret providing qcow AES key or LUKS passphrase"),  \
> -    BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."),               \
> -    BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."),              \
> -    BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."),                \
> -    BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),           \
> -    BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),                 \
> -    BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),                \
>      {                                                               \
>          .name = BLOCK_OPT_CLUSTER_SIZE,                             \
>          .type = QEMU_OPT_SIZE,                                      \
> @@ -5526,6 +5567,14 @@ static QemuOptsList qcow2_create_opts = {
>      .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
>      .desc = {
>          QCOW_COMMON_OPTIONS,
> +        BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.",
> +            "ID of secret providing qcow AES key or LUKS passphrase"),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),
>          { /* end of list */ }
>      }

These two chunks should habe been in the earlier patch IMHO.

>  };
> @@ -5535,6 +5584,14 @@ static QemuOptsList qcow2_amend_opts = {
>      .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head),
>      .desc = {
>          QCOW_COMMON_OPTIONS,
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.0."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.1."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.2."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.3."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.4."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.5."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.6."),
> +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.7."),
>          { /* end of list */ }

Same naming idea about  "encrypt.key.0" or "encrypt.keyslot.0"

>      }
>  };
> -- 
> 2.17.2
> 

Regards,
Daniel
Maxim Levitsky Jan. 30, 2020, 4:09 p.m. UTC | #2
On Tue, 2020-01-28 at 17:30 +0000, Daniel P. Berrangé wrote:
> On Tue, Jan 14, 2020 at 09:33:44PM +0200, Maxim Levitsky wrote:
> > Now that we have all the infrastructure in place,
> > wire it in the qcow2 driver and expose this to the user.
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  block/qcow2.c | 101 +++++++++++++++++++++++++++++++++++++++-----------
> >  1 file changed, 79 insertions(+), 22 deletions(-)
> > 
> > diff --git a/block/qcow2.c b/block/qcow2.c
> > index c6c2deee75..1b01174aed 100644
> > --- a/block/qcow2.c
> > +++ b/block/qcow2.c
> > @@ -173,6 +173,19 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset,
> >      return ret;
> >  }
> >  
> > +static QDict*
> > +qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp)
> > +{
> > +    QDict *cryptoopts_qdict;
> > +    QDict *opts_qdict;
> > +
> > +    /* Extract "encrypt." options into a qdict */
> > +    opts_qdict = qemu_opts_to_qdict(opts, NULL);
> > +    qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
> > +    qobject_unref(opts_qdict);
> > +    qdict_put_str(cryptoopts_qdict, "format", "luks");
> > +    return cryptoopts_qdict;
> > +}
> >  
> >  /* 
> >   * read qcow2 extension and fill bs
> > @@ -4631,20 +4644,18 @@ static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block,
> >  static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len,
> >                                           Error **errp)
> >  {
> > -    QDict *opts_qdict;
> > -    QDict *cryptoopts_qdict;
> >      QCryptoBlockCreateOptions *cryptoopts;
> > +    QDict* crypto_opts_dict;
> >      QCryptoBlock *crypto;
> >  
> > -    /* Extract "encrypt." options into a qdict */
> > -    opts_qdict = qemu_opts_to_qdict(opts, NULL);
> > -    qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
> > -    qobject_unref(opts_qdict);
> > +    crypto_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp);
> > +    if (!crypto_opts_dict) {
> > +        return false;
> > +    }
> > +
> > +    cryptoopts = block_crypto_create_opts_init(crypto_opts_dict, errp);
> > +    qobject_unref(crypto_opts_dict);
> >  
> > -    /* Build QCryptoBlockCreateOptions object from qdict */
> > -    qdict_put_str(cryptoopts_qdict, "format", "luks");
> > -    cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
> > -    qobject_unref(cryptoopts_qdict);
> >      if (!cryptoopts) {
> >          return false;
> >      }
> > @@ -5083,6 +5094,7 @@ typedef enum Qcow2AmendOperation {
> >      QCOW2_NO_OPERATION = 0,
> >  
> >      QCOW2_UPGRADING,
> > +    QCOW2_UPDATING_ENCRYPTION,
> >      QCOW2_CHANGING_REFCOUNT_ORDER,
> >      QCOW2_DOWNGRADING,
> >  } Qcow2AmendOperation;
> > @@ -5167,6 +5179,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
> >      int ret;
> >      QemuOptDesc *desc = opts->list->desc;
> >      Qcow2AmendHelperCBInfo helper_cb_info;
> > +    bool encryption_update = false;
> >  
> >      while (desc && desc->name) {
> >          if (!qemu_opt_find(opts, desc->name)) {
> > @@ -5215,9 +5228,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
> >                  return -ENOTSUP;
> >              }
> >          } else if (g_str_has_prefix(desc->name, "encrypt.")) {
> > -            error_setg(errp,
> > -                       "Changing the encryption parameters is not supported");
> > -            return -ENOTSUP;
> > +            if (!s->crypto) {
> > +                error_setg(errp,
> > +                           "Can't amend encryption options - encryption not present");
> > +                return -EINVAL;
> > +            }
> > +            if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
> > +                error_setg(errp,
> > +                           "Only LUKS encryption options can be amended");
> > +                return -ENOTSUP;
> > +            }
> > +            encryption_update = true;
> >          } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
> >              cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
> >                                               cluster_size);
> > @@ -5267,7 +5288,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
> >          .original_status_cb = status_cb,
> >          .original_cb_opaque = cb_opaque,
> >          .total_operations = (new_version != old_version)
> > -                          + (s->refcount_bits != refcount_bits)
> > +                          + (s->refcount_bits != refcount_bits) +
> > +                            (encryption_update == true)
> >      };
> >  
> >      /* Upgrade first (some features may require compat=1.1) */
> > @@ -5280,6 +5302,33 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
> >          }
> >      }
> >  
> > +    if (encryption_update) {
> > +        QDict *amend_opts_dict;
> > +        QCryptoBlockAmendOptions *amend_opts;
> > +
> > +        helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION;
> > +        amend_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp);
> > +        if (!amend_opts_dict) {
> > +            return -EINVAL;
> > +        }
> > +        amend_opts = block_crypto_amend_opts_init(amend_opts_dict, errp);
> > +        qobject_unref(amend_opts_dict);
> > +        if (!amend_opts) {
> > +            return -EINVAL;
> > +        }
> > +        ret = qcrypto_block_amend_options(s->crypto,
> > +                                          qcow2_crypto_hdr_read_func,
> > +                                          qcow2_crypto_hdr_write_func,
> > +                                          bs,
> > +                                          amend_opts,
> > +                                          force,
> > +                                          errp);
> > +        qapi_free_QCryptoBlockAmendOptions(amend_opts);
> > +        if (ret < 0) {
> > +            return ret;
> > +        }
> > +    }
> > +
> >      if (s->refcount_bits != refcount_bits) {
> >          int refcount_order = ctz32(refcount_bits);
> >  
> > @@ -5488,14 +5537,6 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
> >          .type = QEMU_OPT_STRING,                                    \
> >          .help = "Encrypt the image, format choices: 'aes', 'luks'", \
> >      },                                                              \
> > -    BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.",                     \
> > -        "ID of secret providing qcow AES key or LUKS passphrase"),  \
> > -    BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."),               \
> > -    BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."),              \
> > -    BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."),                \
> > -    BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),           \
> > -    BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),                 \
> > -    BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),                \
> >      {                                                               \
> >          .name = BLOCK_OPT_CLUSTER_SIZE,                             \
> >          .type = QEMU_OPT_SIZE,                                      \
> > @@ -5526,6 +5567,14 @@ static QemuOptsList qcow2_create_opts = {
> >      .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
> >      .desc = {
> >          QCOW_COMMON_OPTIONS,
> > +        BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.",
> > +            "ID of secret providing qcow AES key or LUKS passphrase"),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),
> >          { /* end of list */ }
> >      }
> 
> These two chunks should habe been in the earlier patch IMHO.
Yep, these are gone from this patch in the next version I'll post soon.

> 
> >  };
> > @@ -5535,6 +5584,14 @@ static QemuOptsList qcow2_amend_opts = {
> >      .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head),
> >      .desc = {
> >          QCOW_COMMON_OPTIONS,
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.0."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.1."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.2."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.3."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.4."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.5."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.6."),
> > +        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.7."),
> >          { /* end of list */ }
> 
> Same naming idea about  "encrypt.key.0" or "encrypt.keyslot.0"
Yep, same applies as I said in comment in the other patch.

> 
> >      }
> >  };
> > -- 
> > 2.17.2
> > 
> 
> Regards,
> Daniel

Thanks for the review,
Best regards,
	Maxim Levitsky
diff mbox series

Patch

diff --git a/block/qcow2.c b/block/qcow2.c
index c6c2deee75..1b01174aed 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -173,6 +173,19 @@  static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset,
     return ret;
 }
 
+static QDict*
+qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp)
+{
+    QDict *cryptoopts_qdict;
+    QDict *opts_qdict;
+
+    /* Extract "encrypt." options into a qdict */
+    opts_qdict = qemu_opts_to_qdict(opts, NULL);
+    qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
+    qobject_unref(opts_qdict);
+    qdict_put_str(cryptoopts_qdict, "format", "luks");
+    return cryptoopts_qdict;
+}
 
 /* 
  * read qcow2 extension and fill bs
@@ -4631,20 +4644,18 @@  static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block,
 static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len,
                                          Error **errp)
 {
-    QDict *opts_qdict;
-    QDict *cryptoopts_qdict;
     QCryptoBlockCreateOptions *cryptoopts;
+    QDict* crypto_opts_dict;
     QCryptoBlock *crypto;
 
-    /* Extract "encrypt." options into a qdict */
-    opts_qdict = qemu_opts_to_qdict(opts, NULL);
-    qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
-    qobject_unref(opts_qdict);
+    crypto_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp);
+    if (!crypto_opts_dict) {
+        return false;
+    }
+
+    cryptoopts = block_crypto_create_opts_init(crypto_opts_dict, errp);
+    qobject_unref(crypto_opts_dict);
 
-    /* Build QCryptoBlockCreateOptions object from qdict */
-    qdict_put_str(cryptoopts_qdict, "format", "luks");
-    cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
-    qobject_unref(cryptoopts_qdict);
     if (!cryptoopts) {
         return false;
     }
@@ -5083,6 +5094,7 @@  typedef enum Qcow2AmendOperation {
     QCOW2_NO_OPERATION = 0,
 
     QCOW2_UPGRADING,
+    QCOW2_UPDATING_ENCRYPTION,
     QCOW2_CHANGING_REFCOUNT_ORDER,
     QCOW2_DOWNGRADING,
 } Qcow2AmendOperation;
@@ -5167,6 +5179,7 @@  static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
     int ret;
     QemuOptDesc *desc = opts->list->desc;
     Qcow2AmendHelperCBInfo helper_cb_info;
+    bool encryption_update = false;
 
     while (desc && desc->name) {
         if (!qemu_opt_find(opts, desc->name)) {
@@ -5215,9 +5228,17 @@  static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
                 return -ENOTSUP;
             }
         } else if (g_str_has_prefix(desc->name, "encrypt.")) {
-            error_setg(errp,
-                       "Changing the encryption parameters is not supported");
-            return -ENOTSUP;
+            if (!s->crypto) {
+                error_setg(errp,
+                           "Can't amend encryption options - encryption not present");
+                return -EINVAL;
+            }
+            if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
+                error_setg(errp,
+                           "Only LUKS encryption options can be amended");
+                return -ENOTSUP;
+            }
+            encryption_update = true;
         } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
             cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
                                              cluster_size);
@@ -5267,7 +5288,8 @@  static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
         .original_status_cb = status_cb,
         .original_cb_opaque = cb_opaque,
         .total_operations = (new_version != old_version)
-                          + (s->refcount_bits != refcount_bits)
+                          + (s->refcount_bits != refcount_bits) +
+                            (encryption_update == true)
     };
 
     /* Upgrade first (some features may require compat=1.1) */
@@ -5280,6 +5302,33 @@  static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
         }
     }
 
+    if (encryption_update) {
+        QDict *amend_opts_dict;
+        QCryptoBlockAmendOptions *amend_opts;
+
+        helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION;
+        amend_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp);
+        if (!amend_opts_dict) {
+            return -EINVAL;
+        }
+        amend_opts = block_crypto_amend_opts_init(amend_opts_dict, errp);
+        qobject_unref(amend_opts_dict);
+        if (!amend_opts) {
+            return -EINVAL;
+        }
+        ret = qcrypto_block_amend_options(s->crypto,
+                                          qcow2_crypto_hdr_read_func,
+                                          qcow2_crypto_hdr_write_func,
+                                          bs,
+                                          amend_opts,
+                                          force,
+                                          errp);
+        qapi_free_QCryptoBlockAmendOptions(amend_opts);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
     if (s->refcount_bits != refcount_bits) {
         int refcount_order = ctz32(refcount_bits);
 
@@ -5488,14 +5537,6 @@  void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
         .type = QEMU_OPT_STRING,                                    \
         .help = "Encrypt the image, format choices: 'aes', 'luks'", \
     },                                                              \
-    BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.",                     \
-        "ID of secret providing qcow AES key or LUKS passphrase"),  \
-    BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."),               \
-    BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."),              \
-    BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."),                \
-    BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),           \
-    BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),                 \
-    BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),                \
     {                                                               \
         .name = BLOCK_OPT_CLUSTER_SIZE,                             \
         .type = QEMU_OPT_SIZE,                                      \
@@ -5526,6 +5567,14 @@  static QemuOptsList qcow2_create_opts = {
     .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
     .desc = {
         QCOW_COMMON_OPTIONS,
+        BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.",
+            "ID of secret providing qcow AES key or LUKS passphrase"),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),
         { /* end of list */ }
     }
 };
@@ -5535,6 +5584,14 @@  static QemuOptsList qcow2_amend_opts = {
     .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head),
     .desc = {
         QCOW_COMMON_OPTIONS,
+        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.0."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.1."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.2."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.3."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.4."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.5."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.6."),
+        BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT_UPDATE("encrypt.keys.7."),
         { /* end of list */ }
     }
 };