qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
@ 2019-08-14 20:22 Maxim Levitsky
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring Maxim Levitsky
                   ` (15 more replies)
  0 siblings, 16 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

Hi!

This patch series implements key management for luks based encryption
It supports both raw luks images and qcow2 encrypted images.

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1731898

There are still several issues that need to be figured out,
on which the feedback is very welcome, but other than that the code mostly works.

The main issues are:

1. Instead of the proposed blockdev-update-encryption/blockdev-erase-encryption
interface, it is probably better to implement 'blockdev-amend-options' in qmp,
and use this both for offline and online key update (with some translation
layer to convert the qemu-img 'options' to qmp structures)

This interface already exists for offline qcow2 format options update/

This is an issue that was raised today on IRC with Kevin Wolf. Really thanks
for the idea!

We agreed that this new qmp interface should take the same options as
blockdev-create does, however since we want to be able to edit the encryption
slots separately, this implies that we sort of need to allow this on creation
time as well.

Also the BlockdevCreateOptions is a union, which is specialized by the driver name
which is great for creation, but for update, the driver name is already known,
and thus the user should not be forced to pass it again.
However qmp doesn't seem to support union type guessing based on actual fields
given (this might not be desired either), which complicates this somewhat.

2. 'crypto' driver (the raw luks block device/file) has special behavior for

share-rw=on. write sharing usually is only allowed for raw files, files that
qemu doesn't itself touch, but only guest does. For such files a well behaved guests can
share the storage.

On the other hand most of the format drivers need to store the metadata, and we don't
have any format driver which implements some kind of sync vs other users of the same
file, thus this is not allowed.

However since for luks which is technically a format driver, the metadata is readonly,
such write sharing was allowed till now, and due to backward compatibility should
still be allowed in the future.

This causes an issue with online updating of the keys, and the solution that was suggested
by Keven that I implemented was to request the exclusive write access only during the key
update.

Testing. This was lightly tested with manual testing and with few iotests that I prepared.
I haven't yet tested fully the write sharing behavior, nor did I run the whole iotests
suite to see if this code causes some regressions. Since I will need probably
to rewrite some chunks of it to change to 'amend' interface, I decided to post it now,
to see if you have other ideas/comments to add.

Best regards,
	Maxim Levitsky

Maxim Levitsky (13):
  block-crypto: misc refactoring
  qcrypto-luks: misc refactoring
  qcrypto-luks: refactoring: extract load/store/check/parse header
    functions
  qcrypto-luks: refactoring: simplify the math used for keyslot
    locations
  qcrypto-luks: clear the masterkey and password before freeing them
    always
  qcrypto-luks: implement more rigorous header checking
  block: add manage-encryption command (qmp and blockdev)
  qcrypto: add the plumbing for encryption management
  qcrypto-luks: implement the encryption key management
  block/crypto: implement the encryption key management
  block/qcow2: implement the encryption key managment
  qemu-img: implement key management
  iotests : add tests for encryption key management

 block/block-backend.c            |    9 +
 block/crypto.c                   |  127 ++-
 block/crypto.h                   |    3 +
 block/io.c                       |   24 +
 block/qcow2.c                    |   27 +
 blockdev.c                       |   40 +
 crypto/block-luks.c              | 1673 ++++++++++++++++++++----------
 crypto/block.c                   |   29 +
 crypto/blockpriv.h               |    9 +
 include/block/block.h            |   12 +
 include/block/block_int.h        |   11 +
 include/crypto/block.h           |   27 +
 include/sysemu/block-backend.h   |    7 +
 qapi/block-core.json             |   36 +
 qapi/crypto.json                 |   26 +
 qemu-img-cmds.hx                 |   13 +
 qemu-img.c                       |  140 +++
 tests/qemu-iotests/257           |  197 ++++
 tests/qemu-iotests/257.out       |   96 ++
 tests/qemu-iotests/258           |   95 ++
 tests/qemu-iotests/258.out       |   30 +
 tests/qemu-iotests/259           |  199 ++++
 tests/qemu-iotests/259.out       |    5 +
 tests/qemu-iotests/common.filter |    5 +-
 tests/qemu-iotests/group         |    3 +
 25 files changed, 2286 insertions(+), 557 deletions(-)
 create mode 100755 tests/qemu-iotests/257
 create mode 100644 tests/qemu-iotests/257.out
 create mode 100755 tests/qemu-iotests/258
 create mode 100644 tests/qemu-iotests/258.out
 create mode 100644 tests/qemu-iotests/259
 create mode 100644 tests/qemu-iotests/259.out

-- 
2.17.2



^ permalink raw reply	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-20 16:38   ` Max Reitz
  2019-08-21 15:39   ` Daniel P. Berrangé
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 02/13] qcrypto-luks: " Maxim Levitsky
                   ` (14 subsequent siblings)
  15 siblings, 2 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

* rename the write_func to create_write_func,
  and init_func to create_init_func
  this is  preparation for other write_func that will
  be used to update the encryption keys.

No functional changes

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 block/crypto.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index 8237424ae6..42a3f0898b 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -51,7 +51,6 @@ static int block_crypto_probe_generic(QCryptoBlockFormat format,
     }
 }
 
-
 static ssize_t block_crypto_read_func(QCryptoBlock *block,
                                       size_t offset,
                                       uint8_t *buf,
@@ -77,7 +76,7 @@ struct BlockCryptoCreateData {
 };
 
 
-static ssize_t block_crypto_write_func(QCryptoBlock *block,
+static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
                                        size_t offset,
                                        const uint8_t *buf,
                                        size_t buflen,
@@ -95,8 +94,7 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
     return ret;
 }
 
-
-static ssize_t block_crypto_init_func(QCryptoBlock *block,
+static ssize_t block_crypto_create_init_func(QCryptoBlock *block,
                                       size_t headerlen,
                                       void *opaque,
                                       Error **errp)
@@ -108,7 +106,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
         return -EFBIG;
     }
 
-    /* User provided size should reflect amount of space made
+    /*
+     * User provided size should reflect amount of space made
      * available to the guest, so we must take account of that
      * which will be used by the crypto header
      */
@@ -117,6 +116,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
 }
 
 
+
+
 static QemuOptsList block_crypto_runtime_opts_luks = {
     .name = "crypto",
     .head = QTAILQ_HEAD_INITIALIZER(block_crypto_runtime_opts_luks.head),
@@ -272,8 +273,8 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
     };
 
     crypto = qcrypto_block_create(opts, NULL,
-                                  block_crypto_init_func,
-                                  block_crypto_write_func,
+                                  block_crypto_create_init_func,
+                                  block_crypto_create_write_func,
                                   &data,
                                   errp);
 
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-15 21:40   ` [Qemu-devel] [Qemu-block] " John Snow
  2019-08-20 17:36   ` [Qemu-devel] " Max Reitz
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions Maxim Levitsky
                   ` (13 subsequent siblings)
  15 siblings, 2 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

This is also a preparation for key read/write/erase functions

* use master key len from the header
* prefer to use crypto params in the QCryptoBlockLUKS
  over passing them as function arguments
* define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
* Add comments to various crypto parameters in the QCryptoBlockLUKS

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 crypto/block-luks.c | 213 ++++++++++++++++++++++----------------------
 1 file changed, 105 insertions(+), 108 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 409ab50f20..48213abde7 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -70,6 +70,8 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
 
 #define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
 
+#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME 2000
+
 static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
     'L', 'U', 'K', 'S', 0xBA, 0xBE
 };
@@ -199,13 +201,25 @@ QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592);
 struct QCryptoBlockLUKS {
     QCryptoBlockLUKSHeader header;
 
-    /* Cache parsed versions of what's in header fields,
-     * as we can't rely on QCryptoBlock.cipher being
-     * non-NULL */
+    /* Main encryption algorithm used for encryption*/
     QCryptoCipherAlgorithm cipher_alg;
+
+    /* Mode of encryption for the selected encryption algorithm */
     QCryptoCipherMode cipher_mode;
+
+    /* Initialization vector generation algorithm */
     QCryptoIVGenAlgorithm ivgen_alg;
+
+    /* Hash algorithm used for IV generation*/
     QCryptoHashAlgorithm ivgen_hash_alg;
+
+    /*
+     * Encryption algorithm used for IV generation.
+     * Usually the same as main encryption algorithm
+     */
+    QCryptoCipherAlgorithm ivgen_cipher_alg;
+
+    /* Hash algorithm used in pbkdf2 function */
     QCryptoHashAlgorithm hash_alg;
 };
 
@@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
     }
 }
 
+static int masterkeylen(QCryptoBlockLUKS *luks)
+{
+    return luks->header.key_bytes;
+}
+
+
 /*
  * Given a key slot, and user password, this will attempt to unlock
  * the master encryption key from the key slot.
@@ -410,21 +430,15 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
  */
 static int
 qcrypto_block_luks_load_key(QCryptoBlock *block,
-                            QCryptoBlockLUKSKeySlot *slot,
+                            uint slot_idx,
                             const char *password,
-                            QCryptoCipherAlgorithm cipheralg,
-                            QCryptoCipherMode ciphermode,
-                            QCryptoHashAlgorithm hash,
-                            QCryptoIVGenAlgorithm ivalg,
-                            QCryptoCipherAlgorithm ivcipheralg,
-                            QCryptoHashAlgorithm ivhash,
                             uint8_t *masterkey,
-                            size_t masterkeylen,
                             QCryptoBlockReadFunc readfunc,
                             void *opaque,
                             Error **errp)
 {
     QCryptoBlockLUKS *luks = block->opaque;
+    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
     uint8_t *splitkey;
     size_t splitkeylen;
     uint8_t *possiblekey;
@@ -439,9 +453,9 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
         return 0;
     }
 
-    splitkeylen = masterkeylen * slot->stripes;
+    splitkeylen = masterkeylen(luks) * slot->stripes;
     splitkey = g_new0(uint8_t, splitkeylen);
-    possiblekey = g_new0(uint8_t, masterkeylen);
+    possiblekey = g_new0(uint8_t, masterkeylen(luks));
 
     /*
      * The user password is used to generate a (possible)
@@ -450,11 +464,11 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
      * the key is correct and validate the results of
      * decryption later.
      */
-    if (qcrypto_pbkdf2(hash,
+    if (qcrypto_pbkdf2(luks->hash_alg,
                        (const uint8_t *)password, strlen(password),
                        slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
                        slot->iterations,
-                       possiblekey, masterkeylen,
+                       possiblekey, masterkeylen(luks),
                        errp) < 0) {
         goto cleanup;
     }
@@ -478,19 +492,19 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 
     /* Setup the cipher/ivgen that we'll use to try to decrypt
      * the split master key material */
-    cipher = qcrypto_cipher_new(cipheralg, ciphermode,
-                                possiblekey, masterkeylen,
+    cipher = qcrypto_cipher_new(luks->cipher_alg, luks->cipher_mode,
+                                possiblekey, masterkeylen(luks),
                                 errp);
     if (!cipher) {
         goto cleanup;
     }
 
-    niv = qcrypto_cipher_get_iv_len(cipheralg,
-                                    ciphermode);
-    ivgen = qcrypto_ivgen_new(ivalg,
-                              ivcipheralg,
-                              ivhash,
-                              possiblekey, masterkeylen,
+    niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
+                                    luks->cipher_mode);
+    ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+                              luks->ivgen_cipher_alg,
+                              luks->ivgen_hash_alg,
+                              possiblekey, masterkeylen(luks),
                               errp);
     if (!ivgen) {
         goto cleanup;
@@ -519,8 +533,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
      * Now we've decrypted the split master key, join
      * it back together to get the actual master key.
      */
-    if (qcrypto_afsplit_decode(hash,
-                               masterkeylen,
+    if (qcrypto_afsplit_decode(luks->hash_alg,
+                               masterkeylen(luks),
                                slot->stripes,
                                splitkey,
                                masterkey,
@@ -537,8 +551,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
      * then comparing that to the hash stored in the key slot
      * header
      */
-    if (qcrypto_pbkdf2(hash,
-                       masterkey, masterkeylen,
+    if (qcrypto_pbkdf2(luks->hash_alg,
+                       masterkey, masterkeylen(luks),
                        luks->header.master_key_salt,
                        QCRYPTO_BLOCK_LUKS_SALT_LEN,
                        luks->header.master_key_iterations,
@@ -577,37 +591,19 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 static int
 qcrypto_block_luks_find_key(QCryptoBlock *block,
                             const char *password,
-                            QCryptoCipherAlgorithm cipheralg,
-                            QCryptoCipherMode ciphermode,
-                            QCryptoHashAlgorithm hash,
-                            QCryptoIVGenAlgorithm ivalg,
-                            QCryptoCipherAlgorithm ivcipheralg,
-                            QCryptoHashAlgorithm ivhash,
-                            uint8_t **masterkey,
-                            size_t *masterkeylen,
+                            uint8_t *masterkey,
                             QCryptoBlockReadFunc readfunc,
                             void *opaque,
                             Error **errp)
 {
-    QCryptoBlockLUKS *luks = block->opaque;
     size_t i;
     int rv;
 
-    *masterkey = g_new0(uint8_t, luks->header.key_bytes);
-    *masterkeylen = luks->header.key_bytes;
-
     for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
         rv = qcrypto_block_luks_load_key(block,
-                                         &luks->header.key_slots[i],
+                                         i,
                                          password,
-                                         cipheralg,
-                                         ciphermode,
-                                         hash,
-                                         ivalg,
-                                         ivcipheralg,
-                                         ivhash,
-                                         *masterkey,
-                                         *masterkeylen,
+                                         masterkey,
                                          readfunc,
                                          opaque,
                                          errp);
@@ -620,11 +616,7 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
     }
 
     error_setg(errp, "Invalid password, cannot unlock any keyslot");
-
  error:
-    g_free(*masterkey);
-    *masterkey = NULL;
-    *masterkeylen = 0;
     return -1;
 }
 
@@ -639,21 +631,15 @@ qcrypto_block_luks_open(QCryptoBlock *block,
                         size_t n_threads,
                         Error **errp)
 {
-    QCryptoBlockLUKS *luks;
+    QCryptoBlockLUKS *luks = NULL;
     Error *local_err = NULL;
     int ret = 0;
     size_t i;
     ssize_t rv;
     uint8_t *masterkey = NULL;
-    size_t masterkeylen;
     char *ivgen_name, *ivhash_name;
-    QCryptoCipherMode ciphermode;
-    QCryptoCipherAlgorithm cipheralg;
-    QCryptoIVGenAlgorithm ivalg;
-    QCryptoCipherAlgorithm ivcipheralg;
-    QCryptoHashAlgorithm hash;
-    QCryptoHashAlgorithm ivhash;
     char *password = NULL;
+    char *cipher_mode = NULL;
 
     if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
         if (!options->u.luks.key_secret) {
@@ -710,6 +696,8 @@ qcrypto_block_luks_open(QCryptoBlock *block,
         goto fail;
     }
 
+    cipher_mode = g_strdup(luks->header.cipher_mode);
+
     /*
      * The cipher_mode header contains a string that we have
      * to further parse, of the format
@@ -718,7 +706,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
      *
      * eg  cbc-essiv:sha256, cbc-plain64
      */
-    ivgen_name = strchr(luks->header.cipher_mode, '-');
+    ivgen_name = strchr(cipher_mode, '-');
     if (!ivgen_name) {
         ret = -EINVAL;
         error_setg(errp, "Unexpected cipher mode string format %s",
@@ -730,13 +718,13 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 
     ivhash_name = strchr(ivgen_name, ':');
     if (!ivhash_name) {
-        ivhash = 0;
+        luks->ivgen_hash_alg = 0;
     } else {
         *ivhash_name = '\0';
         ivhash_name++;
 
-        ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name,
-                                                     &local_err);
+        luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
+                                                                   &local_err);
         if (local_err) {
             ret = -ENOTSUP;
             error_propagate(errp, local_err);
@@ -744,25 +732,27 @@ qcrypto_block_luks_open(QCryptoBlock *block,
         }
     }
 
-    ciphermode = qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode,
-                                                       &local_err);
+    luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
+                                                              &local_err);
     if (local_err) {
         ret = -ENOTSUP;
         error_propagate(errp, local_err);
         goto fail;
     }
 
-    cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
-                                                      ciphermode,
-                                                      luks->header.key_bytes,
-                                                      &local_err);
+    luks->cipher_alg =
+            qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
+                                                  luks->cipher_mode,
+                                                  luks->header.key_bytes,
+                                                  &local_err);
     if (local_err) {
         ret = -ENOTSUP;
         error_propagate(errp, local_err);
         goto fail;
     }
 
-    hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
+    luks->hash_alg =
+            qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
                                                &local_err);
     if (local_err) {
         ret = -ENOTSUP;
@@ -770,23 +760,24 @@ qcrypto_block_luks_open(QCryptoBlock *block,
         goto fail;
     }
 
-    ivalg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
-                                                 &local_err);
+    luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
+                                                           &local_err);
     if (local_err) {
         ret = -ENOTSUP;
         error_propagate(errp, local_err);
         goto fail;
     }
 
-    if (ivalg == QCRYPTO_IVGEN_ALG_ESSIV) {
+    if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
         if (!ivhash_name) {
             ret = -EINVAL;
             error_setg(errp, "Missing IV generator hash specification");
             goto fail;
         }
-        ivcipheralg = qcrypto_block_luks_essiv_cipher(cipheralg,
-                                                      ivhash,
-                                                      &local_err);
+        luks->ivgen_cipher_alg =
+                qcrypto_block_luks_essiv_cipher(luks->cipher_alg,
+                                                luks->ivgen_hash_alg,
+                                                &local_err);
         if (local_err) {
             ret = -ENOTSUP;
             error_propagate(errp, local_err);
@@ -800,21 +791,25 @@ qcrypto_block_luks_open(QCryptoBlock *block,
          * ignore hash names with these ivgens rather than report
          * an error about the invalid usage
          */
-        ivcipheralg = cipheralg;
+        luks->ivgen_cipher_alg = luks->cipher_alg;
     }
 
+
+    g_free(cipher_mode);
+    cipher_mode = NULL;
+    ivgen_name = NULL;
+    ivhash_name = NULL;
+
     if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
         /* Try to find which key slot our password is valid for
          * and unlock the master key from that slot.
          */
+
+        masterkey = g_new0(uint8_t, masterkeylen(luks));
+
         if (qcrypto_block_luks_find_key(block,
                                         password,
-                                        cipheralg, ciphermode,
-                                        hash,
-                                        ivalg,
-                                        ivcipheralg,
-                                        ivhash,
-                                        &masterkey, &masterkeylen,
+                                        masterkey,
                                         readfunc, opaque,
                                         errp) < 0) {
             ret = -EACCES;
@@ -824,21 +819,24 @@ qcrypto_block_luks_open(QCryptoBlock *block,
         /* We have a valid master key now, so can setup the
          * block device payload decryption objects
          */
-        block->kdfhash = hash;
-        block->niv = qcrypto_cipher_get_iv_len(cipheralg,
-                                               ciphermode);
-        block->ivgen = qcrypto_ivgen_new(ivalg,
-                                         ivcipheralg,
-                                         ivhash,
-                                         masterkey, masterkeylen,
+        block->kdfhash = luks->hash_alg;
+        block->niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
+                                               luks->cipher_mode);
+
+        block->ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+                                         luks->ivgen_cipher_alg,
+                                         luks->ivgen_hash_alg,
+                                         masterkey, masterkeylen(luks),
                                          errp);
         if (!block->ivgen) {
             ret = -ENOTSUP;
             goto fail;
         }
 
-        ret = qcrypto_block_init_cipher(block, cipheralg, ciphermode,
-                                        masterkey, masterkeylen, n_threads,
+        ret = qcrypto_block_init_cipher(block, luks->cipher_alg,
+                                        luks->cipher_mode,
+                                        masterkey, masterkeylen(luks),
+                                        n_threads,
                                         errp);
         if (ret < 0) {
             ret = -ENOTSUP;
@@ -850,12 +848,6 @@ qcrypto_block_luks_open(QCryptoBlock *block,
     block->payload_offset = luks->header.payload_offset *
         block->sector_size;
 
-    luks->cipher_alg = cipheralg;
-    luks->cipher_mode = ciphermode;
-    luks->ivgen_alg = ivalg;
-    luks->ivgen_hash_alg = ivhash;
-    luks->hash_alg = hash;
-
     g_free(masterkey);
     g_free(password);
 
@@ -910,7 +902,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 
     memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
     if (!luks_opts.has_iter_time) {
-        luks_opts.iter_time = 2000;
+        luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME;
     }
     if (!luks_opts.has_cipher_alg) {
         luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
@@ -930,6 +922,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
             luks_opts.has_ivgen_hash_alg = true;
         }
     }
+
+    luks = g_new0(QCryptoBlockLUKS, 1);
+    block->opaque = luks;
+
+    luks->cipher_alg = luks_opts.cipher_alg;
+    luks->cipher_mode = luks_opts.cipher_mode;
+    luks->ivgen_alg = luks_opts.ivgen_alg;
+    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
+    luks->hash_alg = luks_opts.hash_alg;
+
+
     /* Note we're allowing ivgen_hash_alg to be set even for
      * non-essiv iv generators that don't need a hash. It will
      * be silently ignored, for compatibility with dm-crypt */
@@ -944,8 +947,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
         return -1;
     }
 
-    luks = g_new0(QCryptoBlockLUKS, 1);
-    block->opaque = luks;
 
     memcpy(luks->header.magic, qcrypto_block_luks_magic,
            QCRYPTO_BLOCK_LUKS_MAGIC_LEN);
@@ -1003,6 +1004,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
         ivcipheralg = luks_opts.cipher_alg;
     }
 
+    luks->ivgen_cipher_alg = ivcipheralg;
+
     strcpy(luks->header.cipher_name, cipher_alg);
     strcpy(luks->header.cipher_mode, cipher_mode_spec);
     strcpy(luks->header.hash_spec, hash_alg);
@@ -1304,12 +1307,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
         goto error;
     }
 
-    luks->cipher_alg = luks_opts.cipher_alg;
-    luks->cipher_mode = luks_opts.cipher_mode;
-    luks->ivgen_alg = luks_opts.ivgen_alg;
-    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
-    luks->hash_alg = luks_opts.hash_alg;
-
     memset(masterkey, 0, luks->header.key_bytes);
     g_free(masterkey);
     memset(slotkey, 0, luks->header.key_bytes);
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring Maxim Levitsky
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 02/13] qcrypto-luks: " Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-20 18:01   ` Max Reitz
  2019-08-22 10:38   ` Daniel P. Berrangé
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 04/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations Maxim Levitsky
                   ` (12 subsequent siblings)
  15 siblings, 2 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

With upcoming key management, the header will
need to be stored after the image is created.

Extracting load header isn't strictly needed, but
do this anyway for the symmetry.

Also I extracted a function that does basic sanity
checks on the just read header, and a function
which parses all the crypto format to make the
code a bit more readable, plus now the code
doesn't destruct the in-header cipher-mode string,
so that the header now can be stored many times,
which is needed for the key management.

Also this allows to contain the endianess conversions
in these functions alone

The header is no longer endian swapped in place,
to prevent (mostly theoretical races I think)
races where someone could see the header in the
process of beeing byteswapped.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 crypto/block-luks.c | 756 ++++++++++++++++++++++++++------------------
 1 file changed, 440 insertions(+), 316 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 48213abde7..6bb369f3b4 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -417,6 +417,427 @@ static int masterkeylen(QCryptoBlockLUKS *luks)
 }
 
 
+/*
+ * Stores the main LUKS header, taking care of endianess
+ */
+static int
+qcrypto_block_luks_store_header(QCryptoBlock *block,
+                                QCryptoBlockWriteFunc writefunc,
+                                void *opaque,
+                                Error **errp)
+{
+    QCryptoBlockLUKS *luks = block->opaque;
+    Error *local_err = NULL;
+    size_t i;
+    QCryptoBlockLUKSHeader *hdr_copy;
+
+    /* Create a copy of the header */
+    hdr_copy = g_new0(QCryptoBlockLUKSHeader, 1);
+    memcpy(hdr_copy, &luks->header, sizeof(QCryptoBlockLUKSHeader));
+
+    /*
+     * Everything on disk uses Big Endian (tm), so flip header fields
+     * before writing them
+     */
+    cpu_to_be16s(&hdr_copy->version);
+    cpu_to_be32s(&hdr_copy->payload_offset);
+    cpu_to_be32s(&hdr_copy->key_bytes);
+    cpu_to_be32s(&hdr_copy->master_key_iterations);
+
+    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+        cpu_to_be32s(&hdr_copy->key_slots[i].active);
+        cpu_to_be32s(&hdr_copy->key_slots[i].iterations);
+        cpu_to_be32s(&hdr_copy->key_slots[i].key_offset);
+        cpu_to_be32s(&hdr_copy->key_slots[i].stripes);
+    }
+
+    /* Write out the partition header and key slot headers */
+    writefunc(block, 0, (const uint8_t *)hdr_copy, sizeof(*hdr_copy),
+              opaque, &local_err);
+
+    g_free(hdr_copy);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Loads the main LUKS header,and byteswaps it to native endianess
+ * And run basic sanity checks on it
+ */
+static int
+qcrypto_block_luks_load_header(QCryptoBlock *block,
+                                QCryptoBlockReadFunc readfunc,
+                                void *opaque,
+                                Error **errp)
+{
+    ssize_t rv;
+    size_t i;
+    int ret = 0;
+    QCryptoBlockLUKS *luks = block->opaque;
+
+    /*
+     * Read the entire LUKS header, minus the key material from
+     * the underlying device
+     */
+
+    rv = readfunc(block, 0,
+                  (uint8_t *)&luks->header,
+                  sizeof(luks->header),
+                  opaque,
+                  errp);
+    if (rv < 0) {
+        ret = rv;
+        goto fail;
+    }
+
+    /*
+     * The header is always stored in big-endian format, so
+     * convert everything to native
+     */
+    be16_to_cpus(&luks->header.version);
+    be32_to_cpus(&luks->header.payload_offset);
+    be32_to_cpus(&luks->header.key_bytes);
+    be32_to_cpus(&luks->header.master_key_iterations);
+
+    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+        be32_to_cpus(&luks->header.key_slots[i].active);
+        be32_to_cpus(&luks->header.key_slots[i].iterations);
+        be32_to_cpus(&luks->header.key_slots[i].key_offset);
+        be32_to_cpus(&luks->header.key_slots[i].stripes);
+    }
+
+
+    return 0;
+fail:
+    return ret;
+}
+
+
+/*
+ * Does basic sanity checks on the LUKS header
+ */
+static int
+qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
+{
+    int ret;
+
+    if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
+               QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
+        error_setg(errp, "Volume is not in LUKS format");
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
+        error_setg(errp, "LUKS version %" PRIu32 " is not supported",
+                   luks->header.version);
+        ret = -ENOTSUP;
+        goto fail;
+    }
+
+    return 0;
+fail:
+    return ret;
+}
+
+
+/*
+ * Parses the crypto parameters that are stored in the LUKS header
+ * to string
+ */
+
+static int
+qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, Error **errp)
+{
+    char *cipher_mode = g_strdup(luks->header.cipher_mode);
+    char *ivgen_name, *ivhash_name;
+    int ret = -1;
+    Error *local_err = NULL;
+
+    /*
+     * The cipher_mode header contains a string that we have
+     * to further parse, of the format
+     *
+     *    <cipher-mode>-<iv-generator>[:<iv-hash>]
+     *
+     * eg  cbc-essiv:sha256, cbc-plain64
+     */
+    ivgen_name = strchr(cipher_mode, '-');
+    if (!ivgen_name) {
+        ret = -EINVAL;
+        error_setg(errp, "Unexpected cipher mode string format %s",
+                   luks->header.cipher_mode);
+        goto out;
+    }
+    *ivgen_name = '\0';
+    ivgen_name++;
+
+    ivhash_name = strchr(ivgen_name, ':');
+    if (!ivhash_name) {
+        luks->ivgen_hash_alg = 0;
+    } else {
+        *ivhash_name = '\0';
+        ivhash_name++;
+
+        luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
+                                                                   &local_err);
+        if (local_err) {
+            ret = -ENOTSUP;
+            error_propagate(errp, local_err);
+            goto out;
+        }
+    }
+
+    luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
+                                                              &local_err);
+    if (local_err) {
+        ret = -ENOTSUP;
+        error_propagate(errp, local_err);
+        goto out;
+    }
+
+    luks->cipher_alg =
+            qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
+                                                  luks->cipher_mode,
+                                                  luks->header.key_bytes,
+                                                  &local_err);
+    if (local_err) {
+        ret = -ENOTSUP;
+        error_propagate(errp, local_err);
+        goto out;
+    }
+
+    luks->hash_alg =
+            qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
+                                               &local_err);
+    if (local_err) {
+        ret = -ENOTSUP;
+        error_propagate(errp, local_err);
+        goto out;
+    }
+
+    luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
+                                                           &local_err);
+    if (local_err) {
+        ret = -ENOTSUP;
+        error_propagate(errp, local_err);
+        goto out;
+    }
+
+    if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
+        if (!ivhash_name) {
+            ret = -EINVAL;
+            error_setg(errp, "Missing IV generator hash specification");
+            goto out;
+        }
+        luks->ivgen_cipher_alg =
+                qcrypto_block_luks_essiv_cipher(luks->cipher_alg,
+                                                luks->ivgen_hash_alg,
+                                                &local_err);
+        if (local_err) {
+            ret = -ENOTSUP;
+            error_propagate(errp, local_err);
+            goto out;
+        }
+    } else {
+
+        /*
+         * Note we parsed the ivhash_name earlier in the cipher_mode
+         * spec string even with plain/plain64 ivgens, but we
+         * will ignore it, since it is irrelevant for these ivgens.
+         * This is for compat with dm-crypt which will silently
+         * ignore hash names with these ivgens rather than report
+         * an error about the invalid usage
+         */
+        luks->ivgen_cipher_alg = luks->cipher_alg;
+    }
+    ret = 0;
+out:
+    g_free(cipher_mode);
+    return ret;
+
+}
+
+/*
+ * Given a key slot,  user password, and the master key,
+ * will store the encrypted master key there, and update the
+ * in-memory header. User must then write the in-memory header
+ *
+ * Returns:
+ *    0 if the keyslot was written successfully
+ *      with the provided password
+ *   -1 if a fatal error occurred while storing the key
+ */
+static int
+qcrypto_block_luks_store_key(QCryptoBlock *block,
+                             uint slot_idx,
+                             const char *password,
+                             uint8_t *masterkey,
+                             uint64_t iter_time,
+                             QCryptoBlockWriteFunc writefunc,
+                             void *opaque,
+                             Error **errp)
+{
+    QCryptoBlockLUKS *luks = block->opaque;
+    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
+    uint8_t *splitkey = NULL;
+    size_t splitkeylen;
+    uint8_t *slotkey = NULL;
+    QCryptoCipher *cipher = NULL;
+    QCryptoIVGen *ivgen = NULL;
+    Error *local_err = NULL;
+    uint64_t iters;
+    int ret = -1;
+
+    if (qcrypto_random_bytes(slot->salt,
+                             QCRYPTO_BLOCK_LUKS_SALT_LEN,
+                             errp) < 0) {
+        goto cleanup;
+    }
+
+    splitkeylen = masterkeylen(luks) * slot->stripes;
+
+    /*
+     * Determine how many iterations are required to
+     * hash the user password while consuming 1 second of compute
+     * time
+     */
+    iters = qcrypto_pbkdf2_count_iters(luks->hash_alg,
+                                       (uint8_t *)password, strlen(password),
+                                       slot->salt,
+                                       QCRYPTO_BLOCK_LUKS_SALT_LEN,
+                                       masterkeylen(luks),
+                                       &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto cleanup;
+    }
+
+    if (iters > (ULLONG_MAX / iter_time)) {
+        error_setg_errno(errp, ERANGE,
+                         "PBKDF iterations %llu too large to scale",
+                         (unsigned long long)iters);
+        goto cleanup;
+    }
+
+    /* iter_time was in millis, but count_iters reported for secs */
+    iters = iters * iter_time / 1000;
+
+    if (iters > UINT32_MAX) {
+        error_setg_errno(errp, ERANGE,
+                         "PBKDF iterations %llu larger than %u",
+                         (unsigned long long)iters, UINT32_MAX);
+        goto cleanup;
+    }
+
+    slot->iterations =
+        MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
+
+
+    /*
+     * Generate a key that we'll use to encrypt the master
+     * key, from the user's password
+     */
+    slotkey = g_new0(uint8_t, masterkeylen(luks));
+    if (qcrypto_pbkdf2(luks->hash_alg,
+                       (uint8_t *)password, strlen(password),
+                       slot->salt,
+                       QCRYPTO_BLOCK_LUKS_SALT_LEN,
+                       slot->iterations,
+                       slotkey, masterkeylen(luks),
+                       errp) < 0) {
+        goto cleanup;
+    }
+
+
+    /*
+     * Setup the encryption objects needed to encrypt the
+     * master key material
+     */
+    cipher = qcrypto_cipher_new(luks->cipher_alg,
+                                luks->cipher_mode,
+                                slotkey, masterkeylen(luks),
+                                errp);
+    if (!cipher) {
+        goto cleanup;
+    }
+
+    ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+                              luks->ivgen_cipher_alg,
+                              luks->ivgen_hash_alg,
+                              slotkey, masterkeylen(luks),
+                              errp);
+    if (!ivgen) {
+        goto cleanup;
+    }
+
+    /*
+     * Before storing the master key, we need to vastly
+     * increase its size, as protection against forensic
+     * disk data recovery
+     */
+    splitkey = g_new0(uint8_t, splitkeylen);
+
+    if (qcrypto_afsplit_encode(luks->hash_alg,
+                               masterkeylen(luks),
+                               slot->stripes,
+                               masterkey,
+                               splitkey,
+                               errp) < 0) {
+        goto cleanup;
+    }
+
+    /*
+     * Now we encrypt the split master key with the key generated
+     * from the user's password, before storing it
+     */
+    if (qcrypto_block_cipher_encrypt_helper(cipher, block->niv, ivgen,
+                                            QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+                                            0,
+                                            splitkey,
+                                            splitkeylen,
+                                            errp) < 0) {
+        goto cleanup;
+    }
+
+    /* Write out the slot's master key material. */
+    if (writefunc(block,
+                  slot->key_offset *
+                  QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+                  splitkey, splitkeylen,
+                  opaque,
+                  errp) != splitkeylen) {
+        goto cleanup;
+    }
+
+    slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
+
+    if (qcrypto_block_luks_store_header(block,  writefunc, opaque, errp)) {
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    if (slotkey) {
+        memset(slotkey, 0, masterkeylen(luks));
+        g_free(slotkey);
+    }
+
+    if (splitkey) {
+        memset(splitkey, 0, splitkeylen);
+        g_free(splitkey);
+    }
+
+    qcrypto_ivgen_free(ivgen);
+    qcrypto_cipher_free(cipher);
+    return ret;
+}
+
 /*
  * Given a key slot, and user password, this will attempt to unlock
  * the master encryption key from the key slot.
@@ -632,14 +1053,9 @@ qcrypto_block_luks_open(QCryptoBlock *block,
                         Error **errp)
 {
     QCryptoBlockLUKS *luks = NULL;
-    Error *local_err = NULL;
     int ret = 0;
-    size_t i;
-    ssize_t rv;
     uint8_t *masterkey = NULL;
-    char *ivgen_name, *ivhash_name;
     char *password = NULL;
-    char *cipher_mode = NULL;
 
     if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
         if (!options->u.luks.key_secret) {
@@ -657,154 +1073,25 @@ qcrypto_block_luks_open(QCryptoBlock *block,
     luks = g_new0(QCryptoBlockLUKS, 1);
     block->opaque = luks;
 
-    /* Read the entire LUKS header, minus the key material from
-     * the underlying device */
-    rv = readfunc(block, 0,
-                  (uint8_t *)&luks->header,
-                  sizeof(luks->header),
-                  opaque,
-                  errp);
-    if (rv < 0) {
-        ret = rv;
-        goto fail;
-    }
-
-    /* The header is always stored in big-endian format, so
-     * convert everything to native */
-    be16_to_cpus(&luks->header.version);
-    be32_to_cpus(&luks->header.payload_offset);
-    be32_to_cpus(&luks->header.key_bytes);
-    be32_to_cpus(&luks->header.master_key_iterations);
-
-    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-        be32_to_cpus(&luks->header.key_slots[i].active);
-        be32_to_cpus(&luks->header.key_slots[i].iterations);
-        be32_to_cpus(&luks->header.key_slots[i].key_offset);
-        be32_to_cpus(&luks->header.key_slots[i].stripes);
-    }
-
-    if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
-               QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
-        error_setg(errp, "Volume is not in LUKS format");
-        ret = -EINVAL;
-        goto fail;
-    }
-    if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
-        error_setg(errp, "LUKS version %" PRIu32 " is not supported",
-                   luks->header.version);
-        ret = -ENOTSUP;
-        goto fail;
-    }
-
-    cipher_mode = g_strdup(luks->header.cipher_mode);
-
-    /*
-     * The cipher_mode header contains a string that we have
-     * to further parse, of the format
-     *
-     *    <cipher-mode>-<iv-generator>[:<iv-hash>]
-     *
-     * eg  cbc-essiv:sha256, cbc-plain64
-     */
-    ivgen_name = strchr(cipher_mode, '-');
-    if (!ivgen_name) {
-        ret = -EINVAL;
-        error_setg(errp, "Unexpected cipher mode string format %s",
-                   luks->header.cipher_mode);
-        goto fail;
-    }
-    *ivgen_name = '\0';
-    ivgen_name++;
-
-    ivhash_name = strchr(ivgen_name, ':');
-    if (!ivhash_name) {
-        luks->ivgen_hash_alg = 0;
-    } else {
-        *ivhash_name = '\0';
-        ivhash_name++;
-
-        luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
-                                                                   &local_err);
-        if (local_err) {
-            ret = -ENOTSUP;
-            error_propagate(errp, local_err);
-            goto fail;
-        }
-    }
-
-    luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
-                                                              &local_err);
-    if (local_err) {
-        ret = -ENOTSUP;
-        error_propagate(errp, local_err);
-        goto fail;
-    }
-
-    luks->cipher_alg =
-            qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
-                                                  luks->cipher_mode,
-                                                  luks->header.key_bytes,
-                                                  &local_err);
-    if (local_err) {
-        ret = -ENOTSUP;
-        error_propagate(errp, local_err);
+    ret = qcrypto_block_luks_load_header(block, readfunc, opaque, errp);
+    if (ret) {
         goto fail;
     }
 
-    luks->hash_alg =
-            qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
-                                               &local_err);
-    if (local_err) {
-        ret = -ENOTSUP;
-        error_propagate(errp, local_err);
+    ret = qcrypto_block_luks_check_header(luks, errp);
+    if (ret) {
         goto fail;
     }
 
-    luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
-                                                           &local_err);
-    if (local_err) {
-        ret = -ENOTSUP;
-        error_propagate(errp, local_err);
+    ret = qcrypto_block_luks_parse_header(luks, errp);
+    if (ret) {
         goto fail;
     }
 
-    if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
-        if (!ivhash_name) {
-            ret = -EINVAL;
-            error_setg(errp, "Missing IV generator hash specification");
-            goto fail;
-        }
-        luks->ivgen_cipher_alg =
-                qcrypto_block_luks_essiv_cipher(luks->cipher_alg,
-                                                luks->ivgen_hash_alg,
-                                                &local_err);
-        if (local_err) {
-            ret = -ENOTSUP;
-            error_propagate(errp, local_err);
-            goto fail;
-        }
-    } else {
-        /* Note we parsed the ivhash_name earlier in the cipher_mode
-         * spec string even with plain/plain64 ivgens, but we
-         * will ignore it, since it is irrelevant for these ivgens.
-         * This is for compat with dm-crypt which will silently
-         * ignore hash names with these ivgens rather than report
-         * an error about the invalid usage
-         */
-        luks->ivgen_cipher_alg = luks->cipher_alg;
-    }
-
-
-    g_free(cipher_mode);
-    cipher_mode = NULL;
-    ivgen_name = NULL;
-    ivhash_name = NULL;
-
     if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
         /* Try to find which key slot our password is valid for
          * and unlock the master key from that slot.
          */
-
         masterkey = g_new0(uint8_t, masterkeylen(luks));
 
         if (qcrypto_block_luks_find_key(block,
@@ -845,12 +1132,10 @@ qcrypto_block_luks_open(QCryptoBlock *block,
     }
 
     block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
-    block->payload_offset = luks->header.payload_offset *
-        block->sector_size;
+    block->payload_offset = luks->header.payload_offset * block->sector_size;
 
     g_free(masterkey);
     g_free(password);
-
     return 0;
 
  fail:
@@ -884,12 +1169,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
     QCryptoBlockCreateOptionsLUKS luks_opts;
     Error *local_err = NULL;
     uint8_t *masterkey = NULL;
-    uint8_t *slotkey = NULL;
-    uint8_t *splitkey = NULL;
     size_t splitkeylen = 0;
     size_t i;
-    QCryptoCipher *cipher = NULL;
-    QCryptoIVGen *ivgen = NULL;
     char *password;
     const char *cipher_alg;
     const char *cipher_mode;
@@ -1112,9 +1393,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
      * to use the first key slot */
     splitkeylen = luks->header.key_bytes * QCRYPTO_BLOCK_LUKS_STRIPES;
     for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-        luks->header.key_slots[i].active = i == 0 ?
-            QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED :
-            QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
+        luks->header.key_slots[i].active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
         luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
 
         /* This calculation doesn't match that shown in the spec,
@@ -1128,107 +1407,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
                        QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
     }
 
-    if (qcrypto_random_bytes(luks->header.key_slots[0].salt,
-                             QCRYPTO_BLOCK_LUKS_SALT_LEN,
-                             errp) < 0) {
-        goto error;
-    }
-
-    /* Again we determine how many iterations are required to
-     * hash the user password while consuming 1 second of compute
-     * time */
-    iters = qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
-                                       (uint8_t *)password, strlen(password),
-                                       luks->header.key_slots[0].salt,
-                                       QCRYPTO_BLOCK_LUKS_SALT_LEN,
-                                       luks->header.key_bytes,
-                                       &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        goto error;
-    }
-
-    if (iters > (ULLONG_MAX / luks_opts.iter_time)) {
-        error_setg_errno(errp, ERANGE,
-                         "PBKDF iterations %llu too large to scale",
-                         (unsigned long long)iters);
-        goto error;
-    }
-
-    /* iter_time was in millis, but count_iters reported for secs */
-    iters = iters * luks_opts.iter_time / 1000;
-
-    if (iters > UINT32_MAX) {
-        error_setg_errno(errp, ERANGE,
-                         "PBKDF iterations %llu larger than %u",
-                         (unsigned long long)iters, UINT32_MAX);
-        goto error;
-    }
-
-    luks->header.key_slots[0].iterations =
-        MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
-
-
-    /* Generate a key that we'll use to encrypt the master
-     * key, from the user's password
-     */
-    slotkey = g_new0(uint8_t, luks->header.key_bytes);
-    if (qcrypto_pbkdf2(luks_opts.hash_alg,
-                       (uint8_t *)password, strlen(password),
-                       luks->header.key_slots[0].salt,
-                       QCRYPTO_BLOCK_LUKS_SALT_LEN,
-                       luks->header.key_slots[0].iterations,
-                       slotkey, luks->header.key_bytes,
-                       errp) < 0) {
-        goto error;
-    }
-
-
-    /* Setup the encryption objects needed to encrypt the
-     * master key material
-     */
-    cipher = qcrypto_cipher_new(luks_opts.cipher_alg,
-                                luks_opts.cipher_mode,
-                                slotkey, luks->header.key_bytes,
-                                errp);
-    if (!cipher) {
-        goto error;
-    }
-
-    ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg,
-                              ivcipheralg,
-                              luks_opts.ivgen_hash_alg,
-                              slotkey, luks->header.key_bytes,
-                              errp);
-    if (!ivgen) {
-        goto error;
-    }
-
-    /* Before storing the master key, we need to vastly
-     * increase its size, as protection against forensic
-     * disk data recovery */
-    splitkey = g_new0(uint8_t, splitkeylen);
-
-    if (qcrypto_afsplit_encode(luks_opts.hash_alg,
-                               luks->header.key_bytes,
-                               luks->header.key_slots[0].stripes,
-                               masterkey,
-                               splitkey,
-                               errp) < 0) {
-        goto error;
-    }
-
-    /* Now we encrypt the split master key with the key generated
-     * from the user's password, before storing it */
-    if (qcrypto_block_cipher_encrypt_helper(cipher, block->niv, ivgen,
-                                            QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
-                                            0,
-                                            splitkey,
-                                            splitkeylen,
-                                            errp) < 0) {
-        goto error;
-    }
-
 
     /* The total size of the LUKS headers is the partition header + key
      * slot headers, rounded up to the nearest sector, combined with
@@ -1253,71 +1431,25 @@ qcrypto_block_luks_create(QCryptoBlock *block,
         goto error;
     }
 
-    /* Everything on disk uses Big Endian, so flip header fields
-     * before writing them */
-    cpu_to_be16s(&luks->header.version);
-    cpu_to_be32s(&luks->header.payload_offset);
-    cpu_to_be32s(&luks->header.key_bytes);
-    cpu_to_be32s(&luks->header.master_key_iterations);
-
-    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-        cpu_to_be32s(&luks->header.key_slots[i].active);
-        cpu_to_be32s(&luks->header.key_slots[i].iterations);
-        cpu_to_be32s(&luks->header.key_slots[i].key_offset);
-        cpu_to_be32s(&luks->header.key_slots[i].stripes);
-    }
-
-
-    /* Write out the partition header and key slot headers */
-    writefunc(block, 0,
-              (const uint8_t *)&luks->header,
-              sizeof(luks->header),
-              opaque,
-              &local_err);
-
-    /* Delay checking local_err until we've byte-swapped */
-
-    /* Byte swap the header back to native, in case we need
-     * to read it again later */
-    be16_to_cpus(&luks->header.version);
-    be32_to_cpus(&luks->header.payload_offset);
-    be32_to_cpus(&luks->header.key_bytes);
-    be32_to_cpus(&luks->header.master_key_iterations);
-
-    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-        be32_to_cpus(&luks->header.key_slots[i].active);
-        be32_to_cpus(&luks->header.key_slots[i].iterations);
-        be32_to_cpus(&luks->header.key_slots[i].key_offset);
-        be32_to_cpus(&luks->header.key_slots[i].stripes);
-    }
-
-    if (local_err) {
-        error_propagate(errp, local_err);
+    /* populate the slot 0 with the password encrypted master key*/
+    /* This will also store the header */
+    if (qcrypto_block_luks_store_key(block,
+                                     0,
+                                     password,
+                                     masterkey,
+                                     luks_opts.iter_time,
+                                     writefunc,
+                                     opaque,
+                                     errp)) {
         goto error;
-    }
+     }
 
-    /* Write out the master key material, starting at the
-     * sector immediately following the partition header. */
-    if (writefunc(block,
-                  luks->header.key_slots[0].key_offset *
-                  QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
-                  splitkey, splitkeylen,
-                  opaque,
-                  errp) != splitkeylen) {
-        goto error;
-    }
 
     memset(masterkey, 0, luks->header.key_bytes);
     g_free(masterkey);
-    memset(slotkey, 0, luks->header.key_bytes);
-    g_free(slotkey);
-    g_free(splitkey);
     g_free(password);
     g_free(cipher_mode_spec);
 
-    qcrypto_ivgen_free(ivgen);
-    qcrypto_cipher_free(cipher);
-
     return 0;
 
  error:
@@ -1325,17 +1457,9 @@ qcrypto_block_luks_create(QCryptoBlock *block,
         memset(masterkey, 0, luks->header.key_bytes);
     }
     g_free(masterkey);
-    if (slotkey) {
-        memset(slotkey, 0, luks->header.key_bytes);
-    }
-    g_free(slotkey);
-    g_free(splitkey);
     g_free(password);
     g_free(cipher_mode_spec);
 
-    qcrypto_ivgen_free(ivgen);
-    qcrypto_cipher_free(cipher);
-
     qcrypto_block_free_cipher(block);
     qcrypto_ivgen_free(block->ivgen);
 
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 04/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (2 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-22 10:47   ` Daniel P. Berrangé
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always Maxim Levitsky
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 crypto/block-luks.c | 64 +++++++++++++++++++++++++++------------------
 1 file changed, 38 insertions(+), 26 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 6bb369f3b4..e1a4df94b7 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -417,6 +417,33 @@ static int masterkeylen(QCryptoBlockLUKS *luks)
 }
 
 
+/*
+ * Returns number of sectors needed to store the key material
+ * given number of anti forensic stripes
+ */
+static int splitkeylen_sectors(QCryptoBlockLUKS *luks, int stripes)
+
+{
+    /*
+     * This calculation doesn't match that shown in the spec,
+     * but instead follows the cryptsetup implementation.
+     */
+
+    size_t header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+                     QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+
+    size_t splitkeylen = masterkeylen(luks) * stripes;
+
+    /* First align the key material size to block size*/
+    size_t splitkeylen_sectors =
+            DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE);
+
+    /* Then also align the key material size to the size of the header */
+    return ROUND_UP(splitkeylen_sectors, header_sectors);
+}
+
+
+
 /*
  * Stores the main LUKS header, taking care of endianess
  */
@@ -1169,7 +1196,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
     QCryptoBlockCreateOptionsLUKS luks_opts;
     Error *local_err = NULL;
     uint8_t *masterkey = NULL;
-    size_t splitkeylen = 0;
+    size_t next_sector;
     size_t i;
     char *password;
     const char *cipher_alg;
@@ -1388,23 +1415,16 @@ qcrypto_block_luks_create(QCryptoBlock *block,
         goto error;
     }
 
+    /* start with the sector that follows the header*/
+    next_sector = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+                  QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
 
-    /* Although LUKS has multiple key slots, we're just going
-     * to use the first key slot */
-    splitkeylen = luks->header.key_bytes * QCRYPTO_BLOCK_LUKS_STRIPES;
     for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-        luks->header.key_slots[i].active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
-        luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
-
-        /* This calculation doesn't match that shown in the spec,
-         * but instead follows the cryptsetup implementation.
-         */
-        luks->header.key_slots[i].key_offset =
-            (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
-             QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
-            (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
-                      (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
-                       QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
+        QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[i];
+        slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
+        slot->key_offset = next_sector;
+        slot->stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
+        next_sector += splitkeylen_sectors(luks, QCRYPTO_BLOCK_LUKS_STRIPES);
     }
 
 
@@ -1412,17 +1432,9 @@ qcrypto_block_luks_create(QCryptoBlock *block,
      * slot headers, rounded up to the nearest sector, combined with
      * the size of each master key material region, also rounded up
      * to the nearest sector */
-    luks->header.payload_offset =
-        (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
-         QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
-        (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
-                  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
-                   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) *
-         QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
-
+    luks->header.payload_offset = next_sector;
     block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
-    block->payload_offset = luks->header.payload_offset *
-        block->sector_size;
+    block->payload_offset = luks->header.payload_offset * block->sector_size;
 
     /* Reserve header space to match payload offset */
     initfunc(block, block->payload_offset, opaque, &local_err);
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (3 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 04/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-20 18:12   ` Max Reitz
  2019-08-21 23:01   ` [Qemu-devel] [Qemu-block] " Nir Soffer
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking Maxim Levitsky
                   ` (10 subsequent siblings)
  15 siblings, 2 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

While there are other places where these are still stored in memory,
this is still one less key material area that can be sniffed with
various side channel attacks



Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 44 insertions(+), 8 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index e1a4df94b7..336e633df4 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -1023,8 +1023,18 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  cleanup:
     qcrypto_ivgen_free(ivgen);
     qcrypto_cipher_free(cipher);
-    g_free(splitkey);
-    g_free(possiblekey);
+
+    if (splitkey) {
+        memset(splitkey, 0, splitkeylen);
+        g_free(splitkey);
+    }
+
+    if (possiblekey) {
+        memset(possiblekey, 0, masterkeylen(luks));
+        g_free(possiblekey);
+
+    }
+
     return ret;
 }
 
@@ -1161,16 +1171,34 @@ qcrypto_block_luks_open(QCryptoBlock *block,
     block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
     block->payload_offset = luks->header.payload_offset * block->sector_size;
 
-    g_free(masterkey);
-    g_free(password);
+    if (masterkey) {
+        memset(masterkey, 0, masterkeylen(luks));
+        g_free(masterkey);
+    }
+
+    if (password) {
+        memset(password, 0, strlen(password));
+        g_free(password);
+    }
+
     return 0;
 
  fail:
-    g_free(masterkey);
+
+    if (masterkey) {
+        memset(masterkey, 0, masterkeylen(luks));
+        g_free(masterkey);
+    }
+
+    if (password) {
+        memset(password, 0, strlen(password));
+        g_free(password);
+    }
+
     qcrypto_block_free_cipher(block);
     qcrypto_ivgen_free(block->ivgen);
+
     g_free(luks);
-    g_free(password);
     return ret;
 }
 
@@ -1459,7 +1487,10 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 
     memset(masterkey, 0, luks->header.key_bytes);
     g_free(masterkey);
+
+    memset(password, 0, strlen(password));
     g_free(password);
+
     g_free(cipher_mode_spec);
 
     return 0;
@@ -1467,9 +1498,14 @@ qcrypto_block_luks_create(QCryptoBlock *block,
  error:
     if (masterkey) {
         memset(masterkey, 0, luks->header.key_bytes);
+        g_free(masterkey);
     }
-    g_free(masterkey);
-    g_free(password);
+
+    if (password) {
+        memset(password, 0, strlen(password));
+        g_free(password);
+    }
+
     g_free(cipher_mode_spec);
 
     qcrypto_block_free_cipher(block);
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (4 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-22 11:04   ` Daniel P. Berrangé
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev) Maxim Levitsky
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

Check that keyslots don't overlap with the data,
and check that keyslots don't overlap with each other.
(this is done using naive O(n^2) nested loops,
but since there are just 8 keyslots, this doens't really matter.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 crypto/block-luks.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 336e633df4..1997e92fe1 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -551,6 +551,8 @@ static int
 qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
 {
     int ret;
+    int i, j;
+
 
     if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
                QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
@@ -566,6 +568,46 @@ qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
         goto fail;
     }
 
+    /* Check all keyslots for corruption  */
+    for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
+
+        QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
+        uint start1 = slot1->key_offset;
+        uint len1 = splitkeylen_sectors(luks, slot1->stripes);
+
+        if (slot1->stripes == 0 ||
+                (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
+                slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED)) {
+
+            error_setg(errp, "Keyslot %i is corrupted", i);
+            ret = -EINVAL;
+            goto fail;
+        }
+
+        if (start1 + len1 > luks->header.payload_offset) {
+            error_setg(errp,
+                       "Keyslot %i is overlapping with the encrypted payload",
+                       i);
+            ret = -EINVAL;
+            goto fail;
+        }
+
+        for (j = i + 1 ; j < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; j++) {
+
+            QCryptoBlockLUKSKeySlot *slot2 = &luks->header.key_slots[j];
+            uint start2 = slot2->key_offset;
+            uint len2 = splitkeylen_sectors(luks, slot2->stripes);
+
+            if (start1 + len1 > start2 && start2 + len2 > start1) {
+                error_setg(errp,
+                           "Keyslots %i and %i are overlapping in the header",
+                           i, j);
+                ret = -EINVAL;
+                goto fail;
+            }
+        }
+
+    }
     return 0;
 fail:
     return ret;
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev)
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (5 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-20 18:27   ` Max Reitz
  2019-08-21 11:47   ` Markus Armbruster
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 08/13] qcrypto: add the plumbing for encryption management Maxim Levitsky
                   ` (8 subsequent siblings)
  15 siblings, 2 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

This adds:

* x-blockdev-update-encryption and x-blockdev-erase-encryption qmp commands
  Both commands take the QCryptoKeyManageOptions
  the x-blockdev-update-encryption is meant for non destructive addition
  of key slots / whatever the encryption driver supports in the future

  x-blockdev-erase-encryption is meant for destructive encryption key erase,
  in some cases even without way to recover the data.


* bdrv_setup_encryption callback in the block driver
  This callback does both the above functions with 'action' parameter

* QCryptoKeyManageOptions with set of options that drivers can use for encryption managment
  Currently it has all the options that LUKS needs, and later it can be extended
  (via union) to support more encryption drivers if needed

* blk_setup_encryption / bdrv_setup_encryption - the usual block layer wrappers.
  Note that bdrv_setup_encryption takes BlockDriverState and not BdrvChild,
  for the ease of use from the qmp code. It is not expected that this function
  will be used by anything but qmp and qemu-img code


Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 block/block-backend.c          |  9 ++++++++
 block/io.c                     | 24 ++++++++++++++++++++
 blockdev.c                     | 40 ++++++++++++++++++++++++++++++++++
 include/block/block.h          | 12 ++++++++++
 include/block/block_int.h      | 11 ++++++++++
 include/sysemu/block-backend.h |  7 ++++++
 qapi/block-core.json           | 36 ++++++++++++++++++++++++++++++
 qapi/crypto.json               | 26 ++++++++++++++++++++++
 8 files changed, 165 insertions(+)

diff --git a/block/block-backend.c b/block/block-backend.c
index 0056b526b8..1b75f28d0c 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -2284,3 +2284,12 @@ const BdrvChild *blk_root(BlockBackend *blk)
 {
     return blk->root;
 }
+
+int blk_setup_encryption(BlockBackend *blk,
+                         enum BlkSetupEncryptionAction action,
+                         QCryptoEncryptionSetupOptions *options,
+                         bool force,
+                         Error **errp)
+{
+    return bdrv_setup_encryption(blk->root->bs, action, options, force, errp);
+}
diff --git a/block/io.c b/block/io.c
index 06305c6ea6..50090afe68 100644
--- a/block/io.c
+++ b/block/io.c
@@ -3256,3 +3256,27 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
 
     return tco.ret;
 }
+
+
+int bdrv_setup_encryption(BlockDriverState *bs,
+                          enum BlkSetupEncryptionAction action,
+                          QCryptoEncryptionSetupOptions *options,
+                          bool force,
+                          Error **errp)
+{
+    Error *local_err = NULL;
+    int ret;
+
+    if (!(bs->open_flags & BDRV_O_RDWR)) {
+        error_setg(errp, "Can't do key management on read only block device");
+        return -ENOTSUP;
+    }
+
+    ret = bs->drv->bdrv_setup_encryption(bs, action, options, force,
+                                         &local_err);
+    if (ret) {
+        error_propagate(errp, local_err);
+        return ret;
+    }
+    return 0;
+}
diff --git a/blockdev.c b/blockdev.c
index 4d141e9a1f..27be251656 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -4563,6 +4563,46 @@ void qmp_block_latency_histogram_set(
     }
 }
 
+void qmp_x_blockdev_update_encryption(const char *node_name,
+                                      bool has_force, bool force,
+                                      QCryptoEncryptionSetupOptions *options,
+                                      Error **errp)
+{
+    BlockDriverState *bs = bdrv_find_node(node_name);
+    Error *local_error = NULL;
+
+    if (!bs) {
+        error_setg(errp, "Cannot find node %s", node_name);
+        return;
+    }
+
+    if (bdrv_setup_encryption(bs, BLK_UPDATE_ENCRYPTION, options,
+                              has_force ? force : false, &local_error)) {
+        error_propagate(errp, local_error);
+    }
+}
+
+
+void qmp_x_blockdev_erase_encryption(const char *node_name,
+                                     bool has_force, bool force,
+                                     QCryptoEncryptionSetupOptions *options,
+                                     Error **errp)
+{
+    BlockDriverState *bs = bdrv_find_node(node_name);
+    Error *local_error = NULL;
+
+    if (!bs) {
+        error_setg(errp, "Cannot find node %s", node_name);
+        return;
+    }
+
+    if (bdrv_setup_encryption(bs, BLK_ERASE_ENCRYPTION, options,
+                              has_force ? force : false, &local_error)) {
+            error_propagate(errp, local_error);
+        }
+}
+
+
 QemuOptsList qemu_common_drive_opts = {
     .name = "drive",
     .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
diff --git a/include/block/block.h b/include/block/block.h
index 50a07c1c33..b55ef4c416 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -276,6 +276,12 @@ enum {
     DEFAULT_PERM_UNCHANGED      = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH,
 };
 
+enum BlkSetupEncryptionAction {
+    BLK_UPDATE_ENCRYPTION,
+    BLK_ERASE_ENCRYPTION,
+
+};
+
 char *bdrv_perm_names(uint64_t perm);
 
 /* disk I/O throttling */
@@ -348,6 +354,12 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset,
 int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
                   Error **errp);
 
+int bdrv_setup_encryption(BlockDriverState *bs,
+                          enum BlkSetupEncryptionAction action,
+                          QCryptoEncryptionSetupOptions *options,
+                          bool force,
+                          Error **errp);
+
 int64_t bdrv_nb_sectors(BlockDriverState *bs);
 int64_t bdrv_getlength(BlockDriverState *bs);
 int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 3aa1e832a8..64c71fe269 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -556,6 +556,16 @@ struct BlockDriver {
     void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host);
     QLIST_ENTRY(BlockDriver) list;
 
+
+    /* Manage encryption keys on the block device */
+    int (*bdrv_setup_encryption)(BlockDriverState *bs,
+                                  enum BlkSetupEncryptionAction action,
+                                  QCryptoEncryptionSetupOptions *options,
+                                  bool force,
+                                  Error **errp);
+
+
+
     /* Pointer to a NULL-terminated array of names of strong options
      * that can be specified for bdrv_open(). A strong option is one
      * that changes the data of a BDS.
@@ -1271,4 +1281,5 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset,
 
 int refresh_total_sectors(BlockDriverState *bs, int64_t hint);
 
+
 #endif /* BLOCK_INT_H */
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 733c4957eb..18e98499fd 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -262,4 +262,11 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
 
 const BdrvChild *blk_root(BlockBackend *blk);
 
+
+int blk_setup_encryption(BlockBackend *blk,
+                         enum BlkSetupEncryptionAction action,
+                         QCryptoEncryptionSetupOptions *options,
+                         bool force,
+                         Error **errp);
+
 #endif
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 0d43d4f37c..53ed411eed 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -5327,3 +5327,39 @@
   'data' : { 'node-name': 'str',
              'iothread': 'StrOrNull',
              '*force': 'bool' } }
+
+
+##
+# @x-blockdev-update-encryption:
+#
+# Update the encryption keys for an encrypted block device
+#
+# @node-name: 	  Name of the blockdev to operate on
+# @force:         Disable safety checks (use with care)
+# @options:       Driver specific options
+#
+
+# Since: 4.2
+##
+{ 'command': 'x-blockdev-update-encryption',
+  'data': { 'node-name' : 'str',
+            '*force' : 'bool',
+            'options': 'QCryptoEncryptionSetupOptions' } }
+
+##
+# @x-blockdev-erase-encryption:
+#
+# Erase the encryption keys for an encrypted block device
+#
+# @node-name: 	  Name of the blockdev to operate on
+# @force:         Disable safety checks (use with care)
+# @options:       Driver specific options
+#
+# Returns: @QCryptoKeyManageResult
+#
+# Since: 4.2
+##
+{ 'command': 'x-blockdev-erase-encryption',
+  'data': { 'node-name' : 'str',
+            '*force' : 'bool',
+            'options': 'QCryptoEncryptionSetupOptions' } }
diff --git a/qapi/crypto.json b/qapi/crypto.json
index b2a4cff683..69e8b086db 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -309,3 +309,29 @@
   'base': 'QCryptoBlockInfoBase',
   'discriminator': 'format',
   'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
+
+
+##
+# @QCryptoEncryptionSetupOptions:
+#
+# Driver specific options for encryption key management.
+#
+# @key-secret: the ID of a QCryptoSecret object providing the password
+#              to add or to erase (optional for erase)
+#
+# @old-key-secret: the ID of a QCryptoSecret object providing the password
+#                  that can currently unlock the image
+#
+# @slot: Key slot to update/erase
+#        (optional, for update will select a free slot,
+#        for erase will erase all slots that match the password)
+#
+# @iter-time: number of milliseconds to spend in
+#             PBKDF passphrase processing. Currently defaults to 2000
+# Since: 4.2
+##
+{ 'struct': 'QCryptoEncryptionSetupOptions',
+  'data': { '*key-secret': 'str',
+            '*old-key-secret': 'str',
+            '*slot': 'int',
+            '*iter-time': 'int' } }
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 08/13] qcrypto: add the plumbing for encryption management
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (6 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev) Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-22 11:16   ` Daniel P. Berrangé
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 09/13] qcrypto-luks: implement the encryption key management Maxim Levitsky
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

This adds qcrypto_block_manage_encryption, which
 is thin wrapper around manage_encryption of the crypto driver
 which is also added

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 crypto/block.c         | 29 +++++++++++++++++++++++++++++
 crypto/blockpriv.h     |  9 +++++++++
 include/crypto/block.h | 27 +++++++++++++++++++++++++++
 3 files changed, 65 insertions(+)

diff --git a/crypto/block.c b/crypto/block.c
index ee96759f7d..5916e49aba 100644
--- a/crypto/block.c
+++ b/crypto/block.c
@@ -20,6 +20,7 @@
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+
 #include "blockpriv.h"
 #include "block-qcow.h"
 #include "block-luks.h"
@@ -282,6 +283,34 @@ void qcrypto_block_free(QCryptoBlock *block)
 }
 
 
+int qcrypto_block_setup_encryption(QCryptoBlock *block,
+                                   QCryptoBlockReadFunc readfunc,
+                                   QCryptoBlockWriteFunc writefunc,
+                                   void *opaque,
+                                   enum BlkSetupEncryptionAction action,
+                                   QCryptoEncryptionSetupOptions *options,
+                                   bool force,
+                                   Error **errp)
+{
+    if (!block->driver->setup_encryption) {
+        error_setg(errp,
+                "Crypto format %s doesn't support management of encryption keys",
+                QCryptoBlockFormat_str(block->format));
+        return -1;
+    }
+
+    return block->driver->setup_encryption(block,
+                                           readfunc,
+                                           writefunc,
+                                           opaque,
+                                           action,
+                                           options,
+                                           force,
+                                           errp);
+}
+
+
+
 typedef int (*QCryptoCipherEncDecFunc)(QCryptoCipher *cipher,
                                         const void *in,
                                         void *out,
diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h
index 71c59cb542..804965dca3 100644
--- a/crypto/blockpriv.h
+++ b/crypto/blockpriv.h
@@ -81,6 +81,15 @@ struct QCryptoBlockDriver {
 
     bool (*has_format)(const uint8_t *buf,
                        size_t buflen);
+
+    int (*setup_encryption)(QCryptoBlock *block,
+                            QCryptoBlockReadFunc readfunc,
+                            QCryptoBlockWriteFunc writefunc,
+                            void *opaque,
+                            enum BlkSetupEncryptionAction action,
+                            QCryptoEncryptionSetupOptions *options,
+                            bool force,
+                            Error **errp);
 };
 
 
diff --git a/include/crypto/block.h b/include/crypto/block.h
index fe12899831..60d46e3efc 100644
--- a/include/crypto/block.h
+++ b/include/crypto/block.h
@@ -23,6 +23,7 @@
 
 #include "crypto/cipher.h"
 #include "crypto/ivgen.h"
+#include "block/block.h"
 
 typedef struct QCryptoBlock QCryptoBlock;
 
@@ -268,4 +269,30 @@ uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block);
  */
 void qcrypto_block_free(QCryptoBlock *block);
 
+
+/**
+ * qcrypto_block_setup_encryption:
+ * @block: the block encryption object
+ *
+ * @readfunc: callback for reading data from the volume header
+ * @writefunc: callback for writing data to the volume header
+ * @opaque: data to pass to @readfunc and @writefunc
+ * @action: tell the driver the setup action (add/erase currently)
+ * @options: driver specific options, that specify
+ *           what encryption settings to manage
+ * @force: hint for the driver to allow unsafe operation
+ * @errp: error pointer
+ *
+ * Adds/Erases a new encryption key using @options
+ *
+ */
+int qcrypto_block_setup_encryption(QCryptoBlock *block,
+                                   QCryptoBlockReadFunc readfunc,
+                                   QCryptoBlockWriteFunc writefunc,
+                                   void *opaque,
+                                   enum BlkSetupEncryptionAction action,
+                                   QCryptoEncryptionSetupOptions *options,
+                                   bool force,
+                                   Error **errp);
+
 #endif /* QCRYPTO_BLOCK_H */
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 09/13] qcrypto-luks: implement the encryption key management
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (7 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 08/13] qcrypto: add the plumbing for encryption management Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-22 11:27   ` Daniel P. Berrangé
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 10/13] block/crypto: " Maxim Levitsky
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 crypto/block-luks.c | 374 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 373 insertions(+), 1 deletion(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 1997e92fe1..2c33643b52 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -72,6 +72,8 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
 
 #define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME 2000
 
+#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40
+
 static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
     'L', 'U', 'K', 'S', 0xBA, 0xBE
 };
@@ -221,6 +223,9 @@ struct QCryptoBlockLUKS {
 
     /* Hash algorithm used in pbkdf2 function */
     QCryptoHashAlgorithm hash_alg;
+
+    /* Name of the secret that was used to open the image */
+    char *secret;
 };
 
 
@@ -1121,6 +1126,194 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 }
 
 
+
+/*
+ * Returns true if a slot i is marked as containing as active
+ * (contains encrypted copy of the master key)
+ */
+
+static bool
+qcrypto_block_luks_slot_active(QCryptoBlockLUKS *luks, int slot_idx)
+{
+    uint32_t val = luks->header.key_slots[slot_idx].active;
+    return val ==  QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
+}
+
+/*
+ * Returns the number of slots that are marked as active
+ * (contains encrypted copy of the master key)
+ */
+
+static int
+qcrypto_block_luks_count_active_slots(QCryptoBlockLUKS *luks)
+{
+    int i, ret = 0;
+
+    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+        if (qcrypto_block_luks_slot_active(luks, i)) {
+            ret++;
+        }
+    }
+    return ret;
+}
+
+
+/*
+ * Finds first key slot which is not active
+ * Returns the key slot index, or -1 if doesn't exist
+ */
+
+static int
+qcrypto_block_luks_find_free_keyslot(QCryptoBlockLUKS *luks)
+{
+    uint i;
+
+    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+        if (!qcrypto_block_luks_slot_active(luks, i)) {
+            return i;
+        }
+    }
+    return -1;
+
+}
+
+/*
+ * Erases an keyslot given its index
+ *
+ * Returns:
+ *    0 if the keyslot was erased successfully
+ *   -1 if a error occurred while erasing the keyslot
+ *
+ */
+
+static int
+qcrypto_block_luks_erase_key(QCryptoBlock *block,
+                             uint slot_idx,
+                             QCryptoBlockWriteFunc writefunc,
+                             void *opaque,
+                             Error **errp)
+{
+    QCryptoBlockLUKS *luks = block->opaque;
+    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
+    uint8_t *garbagekey = NULL;
+    size_t splitkeylen = masterkeylen(luks) * slot->stripes;
+    int i;
+    int ret = -1;
+
+    assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+    assert(splitkeylen > 0);
+
+    garbagekey = g_malloc0(splitkeylen);
+
+    /* Reset the key slot header */
+    memset(slot->salt, 0, QCRYPTO_BLOCK_LUKS_SALT_LEN);
+    slot->iterations = 0;
+    slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
+
+    qcrypto_block_luks_store_header(block,  writefunc, opaque, errp);
+
+    /*
+     * Now try to erase the key material, even if the header
+     * update failed
+     */
+
+    for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS ; i++) {
+        if (qcrypto_random_bytes(garbagekey, splitkeylen, errp) < 0) {
+
+                /*
+                 * If we failed to get the random data, still write
+                 * *something* to the key slot at least once
+                 */
+
+                if (i > 0) {
+                    goto cleanup;
+                }
+        }
+
+        if (writefunc(block, slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+                  garbagekey,
+                  splitkeylen,
+                  opaque,
+                  errp) != splitkeylen) {
+            goto cleanup;
+        }
+    }
+
+    ret = 0;
+cleanup:
+    g_free(garbagekey);
+    return ret;
+}
+
+
+/*
+ * Erase all the keys that match the given password
+ * Will stop when only one keyslot is remaining
+ * Returns 0 is some keys were erased or -1 on failure
+ */
+
+static int
+qcrypto_block_luks_erase_matching_keys(QCryptoBlock *block,
+                             const char *password,
+                             QCryptoBlockReadFunc readfunc,
+                             QCryptoBlockWriteFunc writefunc,
+                             void *opaque,
+                             bool force,
+                             Error **errp)
+{
+    QCryptoBlockLUKS *luks = block->opaque;
+    uint i;
+    int rv, ret = -1;
+    uint8_t *masterkey;
+    uint erased_count = 0;
+    uint active_slot_count = qcrypto_block_luks_count_active_slots(luks);
+
+    masterkey = g_new0(uint8_t, masterkeylen(luks));
+
+    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+
+        /* refuse to erase last key if not forced */
+        if (!force && active_slot_count == 1) {
+            break;
+        }
+
+        rv = qcrypto_block_luks_load_key(block,
+                                         i,
+                                         password,
+                                         masterkey,
+                                         readfunc,
+                                         opaque,
+                                         errp);
+        if (rv < 0) {
+            goto cleanup;
+        }
+        if (rv == 0) {
+            continue;
+        }
+
+        rv = qcrypto_block_luks_erase_key(block, i, writefunc, opaque, errp);
+        if (rv < 0) {
+            goto cleanup;
+        }
+
+        erased_count++;
+        active_slot_count--;
+    }
+
+    if (!erased_count) {
+        error_setg(errp, "Didn't erase a keyslot, because no keyslots "
+                         "match the given password");
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    memset(masterkey, 0, masterkeylen(luks));
+    g_free(masterkey);
+    return ret;
+}
+
+
 static int
 qcrypto_block_luks_open(QCryptoBlock *block,
                         QCryptoBlockOpenOptions *options,
@@ -1151,6 +1344,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 
     luks = g_new0(QCryptoBlockLUKS, 1);
     block->opaque = luks;
+    luks->secret = g_strdup(options->u.luks.key_secret);
 
     ret = qcrypto_block_luks_load_header(block, readfunc, opaque, errp);
     if (ret) {
@@ -1320,6 +1514,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
                    optprefix ? optprefix : "");
         return -1;
     }
+    luks->secret = options->u.luks.key_secret;
+
     password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
     if (!password) {
         return -1;
@@ -1605,7 +1801,9 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
 
 static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
 {
-    g_free(block->opaque);
+    QCryptoBlockLUKS *luks = block->opaque;
+    g_free(luks->secret);
+    g_free(luks);
 }
 
 
@@ -1638,6 +1836,179 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block,
                                         offset, buf, len, errp);
 }
 
+static int
+qcrypto_block_luks_setup_encryption(QCryptoBlock *block,
+                                    QCryptoBlockReadFunc readfunc,
+                                    QCryptoBlockWriteFunc writefunc,
+                                    void *opaque,
+                                    enum BlkSetupEncryptionAction action,
+                                    QCryptoEncryptionSetupOptions *options,
+                                    bool force,
+                                    Error **errp)
+{
+    QCryptoBlockLUKS *luks = block->opaque;
+    char *old_password = NULL;
+    char *password = NULL;
+    const char *old_secret = luks->secret;
+    uint8_t *masterkey = NULL;
+    int slot = -1;
+    int ret = -1;
+    int64_t iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME;
+
+    /* Read given slot and check it */
+    if (options->has_slot) {
+        slot = options->slot;
+        if (slot < 0 || slot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
+            error_setg(errp,
+                       "Given key slot %i is not supported by LUKS", slot);
+             goto cleanup;
+        }
+    }
+
+    if (options->has_iter_time) {
+        iter_time = options->iter_time;
+    }
+
+    switch (action) {
+
+    case BLK_UPDATE_ENCRYPTION:
+
+        /* Check that we are not overwriting an active slot */
+        if (!force && slot != -1 &&
+                    qcrypto_block_luks_slot_active(luks, slot)) {
+
+            error_setg(errp, "Can't update an active key slot %i",
+                       slot);
+            goto cleanup;
+        }
+
+        /* check that we have the passwords*/
+        if (!options->has_key_secret) {
+            error_setg(errp, "Can't add a key slot without a  password");
+            goto cleanup;
+        }
+
+        if (options->has_old_key_secret) {
+            old_secret = options->old_key_secret;
+        }
+
+        /* Read the old password */
+        old_password = qcrypto_secret_lookup_as_utf8(old_secret, errp);
+        if (!old_password) {
+            goto cleanup;
+        }
+
+        masterkey = g_new0(uint8_t, masterkeylen(luks));
+
+        /* Retrieve the master key*/
+        if (qcrypto_block_luks_find_key(block, old_password, masterkey,
+                                    readfunc, opaque, errp) < 0) {
+            error_append_hint(errp,
+                       "Old password that was given doesn't unlock the image");
+            goto cleanup;
+        }
+
+        /* Read the new password*/
+        password = qcrypto_secret_lookup_as_utf8(options->key_secret, errp);
+        if (!password) {
+            goto cleanup;
+        }
+
+        /* Find the new slot to write to */
+        if (slot == -1) {
+            slot = qcrypto_block_luks_find_free_keyslot(luks);
+
+            if (slot == -1) {
+                error_setg(errp,
+                        "Can't add a keyslot - all key slots are in use");
+                goto cleanup;
+
+            }
+        }
+
+        /* Store the master key to the new slot */
+        if (qcrypto_block_luks_store_key(block, slot, password,
+                                     masterkey, iter_time,
+                                     writefunc, opaque, errp)) {
+
+            error_append_hint(errp, "Failed to store the keyslot %i", slot);
+            goto cleanup;
+        }
+        break;
+
+    case BLK_ERASE_ENCRYPTION:
+
+        /* Check that we are not erasing last key slot */
+        if (qcrypto_block_luks_count_active_slots(luks) <= 1) {
+
+            if (!force) {
+                error_setg(errp, "Only one slot active - can't erase");
+                goto cleanup;
+            }
+        }
+
+        if (slot != -1) {
+            /* Check that we are not erasing an inactive slot */
+            if (!qcrypto_block_luks_slot_active(luks, options->slot)) {
+                if (!force) {
+                    error_setg(errp, "Can't erase an inactive key slot %i",
+                               slot);
+                    goto cleanup;
+                }
+            }
+
+            /* Erase the given slot */
+            if (qcrypto_block_luks_erase_key(block, slot,
+                                             writefunc, opaque, errp)) {
+                goto cleanup;
+            }
+
+        } else {
+            if (!options->has_key_secret) {
+                error_setg(errp,
+                           "To erase a keyslot you have to specify either the"
+                           "slot index or a password "
+                           "(to erase all slots that match it)");
+                goto cleanup;
+            }
+
+            password = qcrypto_secret_lookup_as_utf8(options->key_secret, errp);
+            if (!password) {
+                goto cleanup;
+            }
+
+            if (qcrypto_block_luks_erase_matching_keys(block, password,
+                                                       readfunc, writefunc,
+                                                       opaque, force, errp)) {
+                goto cleanup;
+            }
+        }
+        break;
+
+    default:
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+
+    if (old_password) {
+        memset(old_password, 0, strlen(old_password));
+        g_free(old_password);
+    }
+
+    if (password) {
+        memset(password, 0, strlen(password));
+        g_free(password);
+    }
+
+    if (masterkey) {
+        memset(masterkey, 0,  masterkeylen(luks));
+        g_free(masterkey);
+    }
+    return ret;
+}
+
 
 const QCryptoBlockDriver qcrypto_block_driver_luks = {
     .open = qcrypto_block_luks_open,
@@ -1647,4 +2018,5 @@ const QCryptoBlockDriver qcrypto_block_driver_luks = {
     .decrypt = qcrypto_block_luks_decrypt,
     .encrypt = qcrypto_block_luks_encrypt,
     .has_format = qcrypto_block_luks_has_format,
+    .setup_encryption = qcrypto_block_luks_setup_encryption,
 };
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 10/13] block/crypto: implement the encryption key management
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (8 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 09/13] qcrypto-luks: implement the encryption key management Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-22 11:29   ` Daniel P. Berrangé
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 11/13] block/qcow2: implement the encryption key managment Maxim Levitsky
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

This implements the encryption key management
using the generic code in qcrypto layer

This code adds another 'write_func' because the initialization
write_func works directly on the underlying file,
because during the creation, there is no open instance
of the luks driver, but during regular use, we have it,
and should use it instead.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 block/crypto.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 93 insertions(+), 3 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index 42a3f0898b..415b6db041 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -36,6 +36,7 @@ typedef struct BlockCrypto BlockCrypto;
 
 struct BlockCrypto {
     QCryptoBlock *block;
+    bool updating_keys;
 };
 
 
@@ -69,6 +70,24 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
     return ret;
 }
 
+static ssize_t block_crypto_write_func(QCryptoBlock *block,
+                                      size_t offset,
+                                      const uint8_t *buf,
+                                      size_t buflen,
+                                      void *opaque,
+                                      Error **errp)
+{
+    BlockDriverState *bs = opaque;
+    ssize_t ret;
+
+    ret = bdrv_pwrite(bs->file, offset, buf, buflen);
+    if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not write encryption header");
+        return ret;
+    }
+    return ret;
+}
+
 
 struct BlockCryptoCreateData {
     BlockBackend *blk;
@@ -622,6 +641,78 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
     return spec_info;
 }
 
+
+static int
+block_crypto_setup_encryption(BlockDriverState *bs,
+                              enum BlkSetupEncryptionAction action,
+                              QCryptoEncryptionSetupOptions *options,
+                              bool force,
+                              Error **errp)
+{
+    BlockCrypto *crypto = bs->opaque;
+    int ret;
+
+    assert(crypto);
+    assert(crypto->block);
+
+    crypto->updating_keys = true;
+
+    ret = bdrv_child_refresh_perms(bs, bs->file, errp);
+
+    if (ret) {
+        crypto->updating_keys = false;
+        return ret;
+    }
+
+    ret = qcrypto_block_setup_encryption(crypto->block,
+                                          block_crypto_read_func,
+                                          block_crypto_write_func,
+                                          bs,
+                                          action,
+                                          options,
+                                          force,
+                                          errp);
+
+    crypto->updating_keys = false;
+    bdrv_child_refresh_perms(bs, bs->file, errp);
+
+
+    return ret;
+
+}
+
+
+static void
+block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
+                         const BdrvChildRole *role,
+                         BlockReopenQueue *reopen_queue,
+                         uint64_t perm, uint64_t shared,
+                         uint64_t *nperm, uint64_t *nshared)
+{
+
+    BlockCrypto *crypto = bs->opaque;
+
+    /*
+     * This driver doesn't modify LUKS metadata except
+     * when updating the encryption slots.
+     * Allow share-rw=on as a special case.
+     *
+     * Encryption update will set the crypto->updating_keys
+     * during that period and refresh permissions
+     *
+     * */
+
+    if (crypto->updating_keys) {
+        /*need exclusive write access for header update  */
+        perm |= BLK_PERM_WRITE;
+        shared &= ~BLK_PERM_WRITE;
+    }
+
+    bdrv_filter_default_perms(bs, c, role, reopen_queue,
+            perm, shared, nperm, nshared);
+}
+
+
 static const char *const block_crypto_strong_runtime_opts[] = {
     BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
 
@@ -634,9 +725,7 @@ static BlockDriver bdrv_crypto_luks = {
     .bdrv_probe         = block_crypto_probe_luks,
     .bdrv_open          = block_crypto_open_luks,
     .bdrv_close         = block_crypto_close,
-    /* This driver doesn't modify LUKS metadata except when creating image.
-     * Allow share-rw=on as a special case. */
-    .bdrv_child_perm    = bdrv_filter_default_perms,
+    .bdrv_child_perm    = block_crypto_child_perms,
     .bdrv_co_create     = block_crypto_co_create_luks,
     .bdrv_co_create_opts = block_crypto_co_create_opts_luks,
     .bdrv_co_truncate   = block_crypto_co_truncate,
@@ -649,6 +738,7 @@ static BlockDriver bdrv_crypto_luks = {
     .bdrv_getlength     = block_crypto_getlength,
     .bdrv_get_info      = block_crypto_get_info_luks,
     .bdrv_get_specific_info = block_crypto_get_specific_info_luks,
+    .bdrv_setup_encryption = block_crypto_setup_encryption,
 
     .strong_runtime_opts = block_crypto_strong_runtime_opts,
 };
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 11/13] block/qcow2: implement the encryption key managment
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (9 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 10/13] block/crypto: " Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 12/13] qemu-img: implement key management Maxim Levitsky
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

This is the main purpose of the patchset, to enaable
us to manage luks like header, embedded in the qcow2
image, which standard cryptosetup tools don't support.

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

diff --git a/block/qcow2.c b/block/qcow2.c
index 039bdc2f7e..a87e58f36a 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -5086,6 +5086,31 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
     s->signaled_corruption = true;
 }
 
+
+static int qcow2_setup_encryption(BlockDriverState *bs,
+                                  enum BlkSetupEncryptionAction action,
+                                  QCryptoEncryptionSetupOptions *options,
+                                  bool force,
+                                  Error **errp)
+{
+    BDRVQcow2State *s = bs->opaque;
+
+    if (!s->crypto) {
+        error_setg(errp, "Can't manage encryption - image is not encrypted");
+        return -EINVAL;
+    }
+
+    return qcrypto_block_setup_encryption(s->crypto,
+                                          qcow2_crypto_hdr_read_func,
+                                          qcow2_crypto_hdr_write_func,
+                                          bs,
+                                          action,
+                                          options,
+                                          force,
+                                          errp);
+}
+
+
 static QemuOptsList qcow2_create_opts = {
     .name = "qcow2-create-opts",
     .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
@@ -5232,6 +5257,8 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_reopen_bitmaps_rw = qcow2_reopen_bitmaps_rw,
     .bdrv_can_store_new_dirty_bitmap = qcow2_can_store_new_dirty_bitmap,
     .bdrv_remove_persistent_dirty_bitmap = qcow2_remove_persistent_dirty_bitmap,
+
+    .bdrv_setup_encryption = qcow2_setup_encryption,
 };
 
 static void bdrv_qcow2_init(void)
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 12/13] qemu-img: implement key management
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (10 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 11/13] block/qcow2: implement the encryption key managment Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-20 18:29   ` Max Reitz
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 13/13] iotests : add tests for encryption " Maxim Levitsky
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 block/crypto.c   |  16 ++++++
 block/crypto.h   |   3 +
 qemu-img-cmds.hx |  13 +++++
 qemu-img.c       | 140 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 172 insertions(+)

diff --git a/block/crypto.c b/block/crypto.c
index 415b6db041..2fcdf9dd39 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -203,6 +203,22 @@ block_crypto_create_opts_init(QDict *opts, Error **errp)
     return ret;
 }
 
+QCryptoEncryptionSetupOptions *
+block_crypto_setup_opts_init(QDict *opts, Error **errp)
+{
+    Visitor *v;
+    QCryptoEncryptionSetupOptions *ret;
+
+    v = qobject_input_visitor_new_flat_confused(opts, errp);
+    if (!v) {
+        return NULL;
+    }
+
+    visit_type_QCryptoEncryptionSetupOptions(v, NULL, &ret, errp);
+
+    visit_free(v);
+    return ret;
+}
 
 static int block_crypto_open_generic(QCryptoBlockFormat format,
                                      QemuOptsList *opts_spec,
diff --git a/block/crypto.h b/block/crypto.h
index b935695e79..ece4d64aef 100644
--- a/block/crypto.h
+++ b/block/crypto.h
@@ -94,4 +94,7 @@ block_crypto_create_opts_init(QDict *opts, Error **errp);
 QCryptoBlockOpenOptions *
 block_crypto_open_opts_init(QDict *opts, Error **errp);
 
+QCryptoEncryptionSetupOptions *
+block_crypto_setup_opts_init(QDict *opts, Error **errp);
+
 #endif /* BLOCK_CRYPTO_H */
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 1c93e6d185..7816a0adfb 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -19,6 +19,18 @@ STEXI
 @item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
 ETEXI
 
+DEF("add_encryption_key", img_add_encryption_key,
+    "add_encryption_key [--object objectdef] [--image-opts] [--force] -U --keydef key_definition filename")
+STEXI
+@item add_encryption_key [--object @var{objectdef}] [--image-opts] [--force] -U --keydef @var{key_definition} @var{filename}
+ETEXI
+
+DEF("erase_encryption_key", img_erase_encryption_key,
+    "erase_encryption_key [--object objectdef] [--image-opts] [--force] -U --keydef key_definition filename")
+STEXI
+@item erase_encryption_key [--object @var{objectdef}] [--image-opts] [--force] -U --keydef @var{key_definition} @var{filename}
+ETEXI
+
 DEF("bench", img_bench,
     "bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] [-U] filename")
 STEXI
@@ -97,6 +109,7 @@ STEXI
 @item resize [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--preallocation=@var{prealloc}] [-q] [--shrink] @var{filename} [+ | -]@var{size}
 ETEXI
 
+
 STEXI
 @end table
 ETEXI
diff --git a/qemu-img.c b/qemu-img.c
index 79983772de..bc6cd60df1 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -47,6 +47,7 @@
 #include "block/blockjob.h"
 #include "block/qapi.h"
 #include "crypto/init.h"
+#include "block/crypto.h"
 #include "trace/control.h"
 
 #define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \
@@ -70,6 +71,8 @@ enum {
     OPTION_PREALLOCATION = 265,
     OPTION_SHRINK = 266,
     OPTION_SALVAGE = 267,
+    OPTION_FORCE = 268,
+    OPTION_KEYDEF = 269,
 };
 
 typedef enum OutputFormat {
@@ -223,6 +226,14 @@ static QemuOptsList qemu_source_opts = {
     },
 };
 
+static QemuOptsList keydef_opts = {
+    .name = "encryption_opts",
+    .head = QTAILQ_HEAD_INITIALIZER(keydef_opts.head),
+    .desc = {
+        { }
+    },
+};
+
 static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...)
 {
     int ret = 0;
@@ -4997,6 +5008,135 @@ out:
     return ret;
 }
 
+
+static QemuOptsList keydef_options_list = {
+    .name = "encryption",
+    .head = QTAILQ_HEAD_INITIALIZER(keydef_options_list.head),
+    .desc = {
+        { }
+    },
+};
+
+static int setup_encryption(int argc, char **argv,
+        enum BlkSetupEncryptionAction action)
+{
+    static const struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+        {"object", required_argument, 0, OPTION_OBJECT},
+        {"force", no_argument, 0, OPTION_FORCE},
+        {"force-share", no_argument, 0, 'U'},
+        {"keydef", required_argument, 0, OPTION_KEYDEF},
+        {0, 0, 0, 0}
+    };
+
+    BlockBackend *blk = NULL;
+    const char *filename = NULL;
+    bool force_share = false;
+    QemuOpts *keydef_opts = NULL;
+    bool image_opts = false;
+    Error *local_err = NULL;
+    QDict *keydef_dict = NULL;
+    QCryptoEncryptionSetupOptions *qcrypto_options = NULL;
+    bool force = false;
+
+    int ret = 1;
+    int c;
+
+    while ((c = getopt_long(argc, argv, "hU:", long_options, NULL)) != -1) {
+        switch (c) {
+        case '?':
+        case 'h':
+            help();
+            break;
+        case 'U':
+            force_share = true;
+            break;
+
+        case OPTION_KEYDEF:
+            if (keydef_opts) {
+                error_report("Only single --keydef argument is allowed.");
+                goto out;
+            }
+
+            keydef_opts = qemu_opts_parse_noisily(&keydef_options_list,
+                                                  optarg, false);
+            if (!keydef_opts) {
+                goto out;
+            }
+            break;
+
+        case OPTION_OBJECT: {
+            QemuOpts *object_opts = qemu_opts_parse_noisily(&qemu_object_opts,
+                                                  optarg, true);
+            if (!object_opts) {
+                goto out;
+            }
+            break;
+        }
+        case OPTION_IMAGE_OPTS:
+            image_opts = true;
+            break;
+        case OPTION_FORCE:
+            force = true;
+            break;
+        break;
+        }
+    }
+
+    if (qemu_opts_foreach(&qemu_object_opts,
+                          user_creatable_add_opts_foreach,
+                          NULL, &error_fatal)) {
+        goto out;
+    }
+
+    if (argc - optind > 1) {
+        error_report("At most one filename argument is allowed.");
+        goto out;
+    } else if (argc - optind == 1) {
+        filename = argv[optind];
+    } else {
+        error_report("filename is required");
+        goto out;
+
+    }
+
+    blk = img_open(image_opts, filename, NULL, BDRV_O_RDWR,
+                   false, false, force_share);
+    if (!blk) {
+        goto out;
+    }
+
+    keydef_dict = qemu_opts_to_qdict(keydef_opts, NULL);
+    qcrypto_options = block_crypto_setup_opts_init(keydef_dict, &local_err);
+    if (!qcrypto_options) {
+        error_report_err(local_err);
+        goto out;
+    }
+
+    if (blk_setup_encryption(blk, action, qcrypto_options, force, &local_err)) {
+        error_report_err(local_err);
+        goto out;
+    }
+    ret = 0;
+out:
+    qemu_opts_del(keydef_opts);
+    qobject_unref(keydef_dict);
+    qapi_free_QCryptoEncryptionSetupOptions(qcrypto_options);
+    blk_unref(blk);
+    return ret;
+}
+
+static int img_add_encryption_key(int argc, char **argv)
+{
+    return setup_encryption(argc, argv, BLK_UPDATE_ENCRYPTION);
+}
+
+static int img_erase_encryption_key(int argc, char **argv)
+{
+    return setup_encryption(argc, argv, BLK_ERASE_ENCRYPTION);
+}
+
 static const img_cmd_t img_cmds[] = {
 #define DEF(option, callback, arg_string)        \
     { option, callback },
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [Qemu-devel] [PATCH 13/13] iotests : add tests for encryption key management
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (11 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 12/13] qemu-img: implement key management Maxim Levitsky
@ 2019-08-14 20:22 ` Maxim Levitsky
  2019-08-14 21:08 ` [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 " Eric Blake
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-14 20:22 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 tests/qemu-iotests/257           | 197 ++++++++++++++++++++++++++++++
 tests/qemu-iotests/257.out       |  96 +++++++++++++++
 tests/qemu-iotests/258           |  95 +++++++++++++++
 tests/qemu-iotests/258.out       |  30 +++++
 tests/qemu-iotests/259           | 199 +++++++++++++++++++++++++++++++
 tests/qemu-iotests/259.out       |   5 +
 tests/qemu-iotests/common.filter |   5 +-
 tests/qemu-iotests/group         |   3 +
 8 files changed, 628 insertions(+), 2 deletions(-)
 create mode 100755 tests/qemu-iotests/257
 create mode 100644 tests/qemu-iotests/257.out
 create mode 100755 tests/qemu-iotests/258
 create mode 100644 tests/qemu-iotests/258.out
 create mode 100644 tests/qemu-iotests/259
 create mode 100644 tests/qemu-iotests/259.out

diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
new file mode 100755
index 0000000000..5991e4a8c7
--- /dev/null
+++ b/tests/qemu-iotests/257
@@ -0,0 +1,197 @@
+#!/usr/bin/env bash
+#
+# Test encryption key management with luks
+# Based on 134
+#
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# This program 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 program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mlevitsk@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2 luks
+_supported_proto file #TODO
+
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
+
+# you are supposed to see the password as *******, see :-)
+SECRET0="--object secret,id=sec0,data=hunter0"
+SECRET1="--object secret,id=sec1,data=hunter1"
+SECRET2="--object secret,id=sec2,data=hunter2"
+SECRET3="--object secret,id=sec3,data=hunter3"
+SECRETS="$SECRET0 $SECRET1 $SECRET2 $SECRET3"
+
+
+if [ "$IMGFMT" = "qcow2" ] ; then
+	OPTPREFIX="encrypt."
+	EXTRA_IMG_ARGS="-o encrypt.format=luks"
+fi
+
+IMGSPEC0="driver=$IMGFMT,file.filename=$TEST_IMG,${OPTPREFIX}key-secret=sec0"
+IMGSPEC1="driver=$IMGFMT,file.filename=$TEST_IMG,${OPTPREFIX}key-secret=sec1"
+IMGSPEC2="driver=$IMGFMT,file.filename=$TEST_IMG,${OPTPREFIX}key-secret=sec2"
+IMGSPEC3="driver=$IMGFMT,file.filename=$TEST_IMG,${OPTPREFIX}key-secret=sec3"
+
+echo "== creating a test image =="
+_make_test_img $SECRET0 $EXTRA_IMG_ARGS -o "${OPTPREFIX}key-secret=sec0,${OPTPREFIX}iter-time=10"   32M
+
+echo
+echo "== test that key 0 opens the image =="
+$QEMU_IO $SECRET0 -c "read 0 4096" --image-opts $IMGSPEC0 | _filter_qemu_io | _filter_testdir
+
+
+echo
+echo "== adding a password to slot 1 =="
+$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC0 --keydef key-secret=sec1,iter-time=10
+echo "== adding a password to slot 3 =="
+$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef key-secret=sec3,iter-time=100,slot=3
+echo "== adding a password to slot 2 =="
+$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC3 --keydef key-secret=sec2,iter-time=10
+
+echo
+echo "== all secrets should work =="
+for IMGSPEC in $IMGSPEC0 $IMGSPEC1 $IMGSPEC2 $IMGSPEC3; do
+	$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
+done
+
+
+echo
+echo "== erase slot 0 and try it =="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef key-secret=sec0| _filter_img_create
+$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC0 | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== erase slot 2 and try it =="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef slot=2| _filter_img_create
+$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC2 | _filter_qemu_io | _filter_testdir
+
+
+# at this point slots 1 and 3 should be active
+
+echo
+echo "== filling  4 slots with secret 2 =="
+for i in $(seq 0 3) ; do
+	$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC3 --keydef key-secret=sec2,iter-time=10
+done
+
+echo
+echo "== adding secret 0 =="
+	$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC3 --keydef key-secret=sec0,iter-time=10
+
+echo
+echo "== adding secret 3 (last slot) =="
+	$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC3 --keydef key-secret=sec3,iter-time=10
+
+echo
+echo "== trying to add another slot (should fail) =="
+$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC2 --keydef key-secret=sec3,iter-time=10
+
+echo
+echo "== all secrets should work again =="
+for IMGSPEC in $IMGSPEC0 $IMGSPEC1 $IMGSPEC2 $IMGSPEC3; do
+	$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
+done
+
+echo
+echo "== erase all keys of secret 2=="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef key-secret=sec2
+echo "== erase all keys of secret 1=="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef key-secret=sec1
+echo "== erase all keys of secret 0=="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC0 --keydef key-secret=sec0
+echo "== erase all keys of secret 3, except a remaining key =="
+
+
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC3 --keydef key-secret=sec3
+
+
+echo
+echo "== only secret3 should work now  =="
+for IMGSPEC in $IMGSPEC0 $IMGSPEC1 $IMGSPEC2 $IMGSPEC3; do
+	$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
+done
+
+echo
+echo "== add secret0  =="
+$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC3 --keydef key-secret=sec0,iter-time=10
+
+echo "== erase secret3 =="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC0 --keydef key-secret=sec3
+
+echo
+echo "== only secret0 should work now  == "
+for IMGSPEC in $IMGSPEC0 $IMGSPEC1 $IMGSPEC2 $IMGSPEC3; do
+	$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
+done
+
+echo
+echo "== replace secret0 with secret1 (should fail)  =="
+$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC0 --keydef key-secret=sec1,iter-time=10,slot=0
+
+echo
+echo "== replace secret0 with secret1 with force (should work)  =="
+$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC0 --keydef key-secret=sec1,iter-time=10,slot=0 --force
+
+
+echo
+echo "== only secret1 should work now  =="
+for IMGSPEC in $IMGSPEC0 $IMGSPEC1 $IMGSPEC2 $IMGSPEC3; do
+	$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
+done
+
+
+
+echo
+echo "== erase last secret (should fail)  =="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef slot=0
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef key-secret=sec1
+
+
+echo "== erase non existing secrets (should fail)  =="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef key-secret=sec5 --force
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef key-secret=sec0 --force
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef slot=1 --force
+
+echo
+echo "== erase last secret with force by slot (should work)  =="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef slot=0 --force
+
+echo
+echo "== we have no secrets now, data is lost forever =="
+for IMGSPEC in $IMGSPEC0 $IMGSPEC1 $IMGSPEC2 $IMGSPEC3; do
+	$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
+done
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
+
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
new file mode 100644
index 0000000000..77f9c9136e
--- /dev/null
+++ b/tests/qemu-iotests/257.out
@@ -0,0 +1,96 @@
+QA output created by 257
+== creating a test image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432
+
+== test that key 0 opens the image ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== adding a password to slot 1 ==
+== adding a password to slot 3 ==
+== adding a password to slot 2 ==
+
+== all secrets should work ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== erase slot 0 and try it ==
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+
+== erase slot 2 and try it ==
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+
+== filling  4 slots with secret 2 ==
+
+== adding secret 0 ==
+
+== adding secret 3 (last slot) ==
+
+== trying to add another slot (should fail) ==
+qemu-img: Can't add a keyslot - all key slots are in use
+
+== all secrets should work again ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== erase all keys of secret 2==
+== erase all keys of secret 1==
+== erase all keys of secret 0==
+== erase all keys of secret 3, except a remaining key ==
+
+== only secret3 should work now  ==
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== add secret0  ==
+== erase secret3 ==
+
+== only secret0 should work now  ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+
+== replace secret0 with secret1 (should fail)  ==
+qemu-img: Can't update an active key slot 0
+
+== replace secret0 with secret1 with force (should work)  ==
+
+== only secret1 should work now  ==
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+
+== erase last secret (should fail)  ==
+qemu-img: Only one slot active - can't erase
+qemu-img: Only one slot active - can't erase
+== erase non existing secrets (should fail)  ==
+qemu-img: No secret with id 'sec5'
+qemu-img: Didn't erase a keyslot, because no keyslots match the given password
+
+== erase last secret with force by slot (should work)  ==
+
+== we have no secrets now, data is lost forever ==
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+*** done
diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258
new file mode 100755
index 0000000000..84c8ab1907
--- /dev/null
+++ b/tests/qemu-iotests/258
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+#
+# Test that encryption key managment erases the key material
+# in addition to the header
+# Based on 134
+#
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# This program 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 program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mlevitsk@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt luks
+_supported_proto file #TODO
+
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
+
+# you are supposed to see the password as *******, see :-)
+SECRET0="--object secret,id=sec0,data=hunter0"
+SECRET1="--object secret,id=sec1,data=hunter1"
+SECRETS="$SECRET0 $SECRET1"
+
+
+IMGSPEC0="driver=$IMGFMT,file.filename=$TEST_IMG,key-secret=sec0"
+IMGSPEC1="driver=$IMGFMT,file.filename=$TEST_IMG,key-secret=sec1"
+
+echo "== creating a test image =="
+_make_test_img $SECRET0 -o "key-secret=sec0,iter-time=10"   32M
+
+echo
+echo "== test that key 0 opens the image =="
+$QEMU_IO $SECRET0 -c "read 0 4096" --image-opts $IMGSPEC0 | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== adding a password to slot 1 =="
+$QEMU_IMG add_encryption_key $SECRETS --image-opts $IMGSPEC0 --keydef key-secret=sec1,slot=1,iter-time=10
+
+echo
+echo "== 'backup' the image header =="
+dd if=$TEST_IMG_FILE of=${TEST_IMG_FILE}.bk bs=4K skip=0 count=1
+
+echo
+echo "== erase slot 0 =="
+$QEMU_IMG erase_encryption_key $SECRETS --image-opts $IMGSPEC1 --keydef slot=0 | _filter_img_create
+
+echo
+echo "== test that key 0 doesn't open the image =="
+$QEMU_IO $SECRET0 -c "read 0 4096" --image-opts $IMGSPEC0 | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== 'restore' the image header =="
+dd if=${TEST_IMG_FILE}.bk of=${TEST_IMG_FILE} bs=4K skip=0 count=1 conv=notrunc
+
+echo
+echo "== test that key 0 still doesn't open the image (key material is erased) =="
+$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC0 | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== test that key 1 still works =="
+$QEMU_IO $SECRETS -c "read 0 4096" --image-opts $IMGSPEC1 | _filter_qemu_io | _filter_testdir
+
+echo "*** done"
+rm -f $seq.full
+status=0
+
+
+exit 0
diff --git a/tests/qemu-iotests/258.out b/tests/qemu-iotests/258.out
new file mode 100644
index 0000000000..ea448a0ac1
--- /dev/null
+++ b/tests/qemu-iotests/258.out
@@ -0,0 +1,30 @@
+QA output created by 258
+== creating a test image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432
+
+== test that key 0 opens the image ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== adding a password to slot 1 ==
+
+== 'backup' the image header ==
+1+0 records in
+1+0 records out
+
+== erase slot 0 ==
+
+== test that key 0 doesn't open the image ==
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+
+== 'restore' the image header ==
+1+0 records in
+1+0 records out
+
+== test that key 0 still doesn't open the image (key material is erased) ==
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
+
+== test that key 1 still works ==
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/259 b/tests/qemu-iotests/259
new file mode 100644
index 0000000000..360e723a48
--- /dev/null
+++ b/tests/qemu-iotests/259
@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+#
+# Test case QMP's encrypted key management
+#
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# This program 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 program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import iotests
+import os
+import time
+
+test_img = os.path.join(iotests.test_dir, 'test.img')
+
+class Secret:
+    def __init__(self, index):
+        self._id = "keysec" + str(index)
+        # you are not supposed to see the password...
+        self._secret = "hunter" + str(index)
+
+    def id(self):
+        return self._id
+
+    def secret(self):
+        return self._secret
+
+    def to_cmdline_object(self):
+        return  [ "secret,id=" + self._id + ",data=" + self._secret]
+
+    def to_qmp_object(self):
+        return { "qom_type" : "secret", "id": self.id(),
+                 "props": { "data": self.secret() } }
+
+class EncryptionSetupTestCase(iotests.QMPTestCase):
+
+    # test case startup
+    def setUp(self):
+        # start the VM
+        self.vm = iotests.VM()
+        self.vm.launch()
+
+        # create the secrets and load 'em into the VM
+        self.secrets = [ Secret(i) for i in range(0, 4) ]
+        for secret in self.secrets:
+            result = self.vm.qmp("object-add", **secret.to_qmp_object())
+            self.assert_qmp(result, 'return', {})
+
+    # test case shutdown
+    def tearDown(self):
+        # stop the VM
+        self.vm.shutdown()
+
+    # create the encrypted block device
+    def createImg(self, file, secret):
+
+        if iotests.imgfmt == "qcow2":
+            prefix="encrypt."
+            extra_options = [ '-o', "encrypt.format=luks" ]
+        else:
+            prefix=""
+            extra_options = []
+
+        return iotests.qemu_img('create',
+                                '--object', *secret.to_cmdline_object(),
+                                '-f', iotests.imgfmt,
+                                '-o', prefix+'key-secret=' + secret.id(),
+                                '-o', prefix+'iter-time=10',
+                                *extra_options, file, '1M')
+
+    # open an encrypted block device
+    def openLUKS(self, id, file, secret):
+        if iotests.imgfmt == "qcow2":
+            encrypt_options = {
+                'encrypt': {
+                    'format':'luks',
+                    'key-secret' : secret.id()}
+                }
+        else:
+            encrypt_options = {
+                'key-secret' : secret.id()
+            }
+
+        return self.vm.qmp('blockdev-add', **
+            {
+                'driver': iotests.imgfmt,
+                'node-name': id,
+
+                **encrypt_options,
+
+                'file': {
+                    'driver': 'file',
+                    'filename': test_img,
+                }
+            }
+        )
+
+    # close the encrypted block device
+    def closeLUKS(self, id):
+        return self.vm.qmp('blockdev-del', **{ 'node-name': id })
+
+    # add a key to an encrypted block device
+    def addKey(self, id, secret, old_secret = None, slot = None, force = False):
+        args = {
+            'node-name': id,
+            'options' : { 'key-secret' : secret.id(), 'iter-time' : 10}
+        }
+
+        if slot != None:
+            args['options']['slot'] = slot
+        if old_secret != None:
+            args['options']['old-key-secret'] = old_secret.id()
+        if force == True:
+            args['force'] = True
+
+        return self.vm.qmp('x-blockdev-update-encryption', **args)
+
+    # erase a key from an encrypted block device
+    def eraseKey(self, id, secret = None, slot = None, force = False):
+        args = {
+            'node-name': id,
+            'options' : { }
+        }
+
+        if secret != None:
+            args['options']['key-secret'] = secret.id()
+        if slot != None:
+            args['options']['slot'] = slot
+        if force == True:
+            args['force'] = True
+
+        return self.vm.qmp('x-blockdev-erase-encryption', **args)
+
+    def check(self, result):
+        self.assert_qmp(result, 'return', {})
+
+    # create image, and change its key
+    def testChangeKey(self):
+
+        # create the image with secret0 and open it
+        result = self.createImg(test_img, self.secrets[0]);
+        self.check(self.openLUKS("testdev", test_img, self.secrets[0]))
+
+        # add key to slot 1
+        self.check(self.addKey("testdev", secret=self.secrets[1]))
+
+
+        # erase key from slot 0
+        self.check(self.eraseKey("testdev", secret=self.secrets[0]))
+
+        #reopen the image with secret1
+        self.check(self.closeLUKS("testdev"))
+        self.check(self.openLUKS("testdev", test_img, self.secrets[1]))
+
+        # close and erase the image for good
+        self.check(self.closeLUKS("testdev"))
+        os.remove(test_img)
+
+    # test that if we erase the old password,
+    # we can still change the encryption keys using 'old-secret'
+    def testOldPassword(self):
+
+        # create the image with secret0 and open it
+        result = self.createImg(test_img, self.secrets[0]);
+        self.check(self.openLUKS("testdev", test_img, self.secrets[0]))
+
+        # add key to slot 1
+        self.check(self.addKey("testdev", secret=self.secrets[1]))
+
+        # erase key from slot 0
+        self.check(self.eraseKey("testdev", secret=self.secrets[0]))
+
+        # this will fail as the old password is no longer valid
+        result = self.addKey("testdev", secret=self.secrets[2])
+        self.assert_qmp(result, 'error/desc', "Invalid password, cannot unlock any keyslot")
+
+        # this will work
+        self.check(self.addKey("testdev", secret=self.secrets[2], old_secret=self.secrets[1]))
+
+
+        # close and erase the image for good
+        self.check(self.closeLUKS("testdev"))
+        os.remove(test_img)
+
+
+if __name__ == '__main__':
+    # Encrypted formats support
+    iotests.main(iotests.main(supported_fmts=['qcow2','luks']))
diff --git a/tests/qemu-iotests/259.out b/tests/qemu-iotests/259.out
new file mode 100644
index 0000000000..fbc63e62f8
--- /dev/null
+++ b/tests/qemu-iotests/259.out
@@ -0,0 +1,5 @@
+..
+----------------------------------------------------------------------
+Ran 2 tests
+
+OK
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index 35fddc746f..c0d68e8a0c 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -138,8 +138,9 @@ _filter_img_create()
         -e "s# block_state_zero=\\(on\\|off\\)##g" \
         -e "s# log_size=[0-9]\\+##g" \
         -e "s# refcount_bits=[0-9]\\+##g" \
-        -e "s# key-secret=[a-zA-Z0-9]\\+##g" \
-        -e "s# iter-time=[0-9]\\+##g" \
+        -e "s# \\(encrypt\\.\\)\\?key-secret=[a-zA-Z0-9]\\+##g" \
+        -e "s# encrypt\\.format=[a-zA-Z0-9]\\+##g" \
+        -e "s# \\(encrypt\\.\\)\\?iter-time=[0-9]\\+##g" \
         -e "s# force_size=\\(on\\|off\\)##g"
 }
 
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index f13e5f2e23..3db7f0a863 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -271,3 +271,6 @@
 254 rw backing quick
 255 rw quick
 256 rw quick
+257 rw auto
+258 rw auto quick
+259 rw auto
-- 
2.17.2



^ permalink raw reply related	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (12 preceding siblings ...)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 13/13] iotests : add tests for encryption " Maxim Levitsky
@ 2019-08-14 21:08 ` Eric Blake
  2019-08-15  8:49   ` Maxim Levitsky
  2019-08-15  9:10   ` Kevin Wolf
  2019-08-20 17:59 ` Max Reitz
  2019-08-22 11:35 ` Daniel P. Berrangé
  15 siblings, 2 replies; 87+ messages in thread
From: Eric Blake @ 2019-08-14 21:08 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 1840 bytes --]

On 8/14/19 3:22 PM, Maxim Levitsky wrote:

> This is an issue that was raised today on IRC with Kevin Wolf. Really thanks
> for the idea!
> 
> We agreed that this new qmp interface should take the same options as
> blockdev-create does, however since we want to be able to edit the encryption
> slots separately, this implies that we sort of need to allow this on creation
> time as well.
> 
> Also the BlockdevCreateOptions is a union, which is specialized by the driver name
> which is great for creation, but for update, the driver name is already known,
> and thus the user should not be forced to pass it again.
> However qmp doesn't seem to support union type guessing based on actual fields
> given (this might not be desired either), which complicates this somewhat.

Does the idea of a union type with a default value for the discriminator
help?  Maybe we have a discriminator which defaults to 'auto', and add a
union branch 'auto':'any'.  During creation, if the "driver":"auto"
branch is selected (usually implicitly by omitting "driver", but also
possible explicitly), the creation attempt is rejected as invalid
regardless of the contents of the remaining 'any'.  But during amend
usage, if the 'auto' branch is selected, we then add in the proper
"driver":"xyz" and reparse the QAPI object to determine if the remaining
fields in 'any' still meet the specification for the required driver branch.

This idea may still require some tweaks to the QAPI generator, but it's
the best I can come up with for a way to parse an arbitrary JSON object
with unknown validation, then reparse it again after adding more
information that would constrain the parse differently.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-14 21:08 ` [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 " Eric Blake
@ 2019-08-15  8:49   ` Maxim Levitsky
  2019-08-15  9:10   ` Kevin Wolf
  1 sibling, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-15  8:49 UTC (permalink / raw)
  To: Eric Blake, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi

On Wed, 2019-08-14 at 16:08 -0500, Eric Blake wrote:
> On 8/14/19 3:22 PM, Maxim Levitsky wrote:
> 
> > This is an issue that was raised today on IRC with Kevin Wolf. Really thanks
> > for the idea!
> > 
> > We agreed that this new qmp interface should take the same options as
> > blockdev-create does, however since we want to be able to edit the encryption
> > slots separately, this implies that we sort of need to allow this on creation
> > time as well.
> > 
> > Also the BlockdevCreateOptions is a union, which is specialized by the driver name
> > which is great for creation, but for update, the driver name is already known,
> > and thus the user should not be forced to pass it again.
> > However qmp doesn't seem to support union type guessing based on actual fields
> > given (this might not be desired either), which complicates this somewhat.
> 
> Does the idea of a union type with a default value for the discriminator
> help?  Maybe we have a discriminator which defaults to 'auto', and add a
> union branch 'auto':'any'.  During creation, if the "driver":"auto"
> branch is selected (usually implicitly by omitting "driver", but also
> possible explicitly), the creation attempt is rejected as invalid
> regardless of the contents of the remaining 'any'.  But during amend
> usage, if the 'auto' branch is selected, we then add in the proper
> "driver":"xyz" and reparse the QAPI object to determine if the remaining
> fields in 'any' still meet the specification for the required driver branch.
> 
> This idea may still require some tweaks to the QAPI generator, but it's
> the best I can come up with for a way to parse an arbitrary JSON object
> with unknown validation, then reparse it again after adding more
> information that would constrain the parse differently.
> 

This could work, but the idea of doing the parsing twice might not be easy to implement.
We currently have the qmp parser completely separated from the rest of the qemu,
so only once the qmp command parsing is done, the corresponding callback is called.


I am thinking. Since any 'update' commmand would need to sepecify the node to work on,
one could add some kind of expression for the qmp frontend to query the driver of that
node itself, which would solve that problem


Something like that:

{ 'union': 'BlockdevAmendOptions',

  'base': {
      'node-name':         'str' },

  'discriminator': { 'get_block_driver(node-name)' } ,

  'data': {
      'file':           'BlockdevCreateOptionsFile',
      'gluster':        'BlockdevCreateOptionsGluster',
      'luks':           'BlockdevCreateOptionsLUKS',
      'nfs':            'BlockdevCreateOptionsNfs',
      'parallels':      'BlockdevCreateOptionsParallels',
      'qcow':           'BlockdevCreateOptionsQcow',
      'qcow2':          'BlockdevCreateOptionsQcow2',
      'qed':            'BlockdevCreateOptionsQed',
      'rbd':            'BlockdevCreateOptionsRbd',
      'sheepdog':       'BlockdevCreateOptionsSheepdog',
      'ssh':            'BlockdevCreateOptionsSsh',
      'vdi':            'BlockdevCreateOptionsVdi',
      'vhdx':           'BlockdevCreateOptionsVhdx',
      'vmdk':           'BlockdevCreateOptionsVmdk',
      'vpc':            'BlockdevCreateOptionsVpc'
  } }


The 'get_block_driver' expression will make the QMP frontend, take the value of the node-name union field,
and look up the block driver associated with it and use that as a discriminator.

Syntax wise we can (at some expense of readability) use json to express the same like

'discriminator': { 'field' : 'node-name', 'transform': 'getdrivername' },

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-14 21:08 ` [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 " Eric Blake
  2019-08-15  8:49   ` Maxim Levitsky
@ 2019-08-15  9:10   ` Kevin Wolf
  2019-08-15 14:18     ` Markus Armbruster
  1 sibling, 1 reply; 87+ messages in thread
From: Kevin Wolf @ 2019-08-15  9:10 UTC (permalink / raw)
  To: Eric Blake
  Cc: Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, qemu-devel, Max Reitz,
	Stefan Hajnoczi, Maxim Levitsky

[-- Attachment #1: Type: text/plain, Size: 2104 bytes --]

Am 14.08.2019 um 23:08 hat Eric Blake geschrieben:
> On 8/14/19 3:22 PM, Maxim Levitsky wrote:
> 
> > This is an issue that was raised today on IRC with Kevin Wolf. Really thanks
> > for the idea!
> > 
> > We agreed that this new qmp interface should take the same options as
> > blockdev-create does, however since we want to be able to edit the encryption
> > slots separately, this implies that we sort of need to allow this on creation
> > time as well.
> > 
> > Also the BlockdevCreateOptions is a union, which is specialized by the driver name
> > which is great for creation, but for update, the driver name is already known,
> > and thus the user should not be forced to pass it again.
> > However qmp doesn't seem to support union type guessing based on actual fields
> > given (this might not be desired either), which complicates this somewhat.
> 
> Does the idea of a union type with a default value for the discriminator
> help?  Maybe we have a discriminator which defaults to 'auto', and add a
> union branch 'auto':'any'.  During creation, if the "driver":"auto"
> branch is selected (usually implicitly by omitting "driver", but also
> possible explicitly), the creation attempt is rejected as invalid
> regardless of the contents of the remaining 'any'.  But during amend
> usage, if the 'auto' branch is selected, we then add in the proper
> "driver":"xyz" and reparse the QAPI object to determine if the remaining
> fields in 'any' still meet the specification for the required driver branch.
> 
> This idea may still require some tweaks to the QAPI generator, but it's
> the best I can come up with for a way to parse an arbitrary JSON object
> with unknown validation, then reparse it again after adding more
> information that would constrain the parse differently.

Feels like this would be a lot of code just to allow the client to omit
passing a value that it knows anyway. If this were a human interface, I
could understand the desire to make commands less verbose, but for QMP I
honestly don't see the point when it's not trivial.

Kevin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-15  9:10   ` Kevin Wolf
@ 2019-08-15 14:18     ` Markus Armbruster
  2019-08-15 14:44       ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Markus Armbruster @ 2019-08-15 14:18 UTC (permalink / raw)
  To: Kevin Wolf
  Cc: Fam Zheng, Daniel P. Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi,
	Maxim Levitsky

Kevin Wolf <kwolf@redhat.com> writes:

> Am 14.08.2019 um 23:08 hat Eric Blake geschrieben:
>> On 8/14/19 3:22 PM, Maxim Levitsky wrote:
>> 
>> > This is an issue that was raised today on IRC with Kevin Wolf. Really thanks
>> > for the idea!
>> > 
>> > We agreed that this new qmp interface should take the same options as
>> > blockdev-create does, however since we want to be able to edit the encryption
>> > slots separately, this implies that we sort of need to allow this on creation
>> > time as well.
>> > 
>> > Also the BlockdevCreateOptions is a union, which is specialized by the driver name
>> > which is great for creation, but for update, the driver name is already known,
>> > and thus the user should not be forced to pass it again.
>> > However qmp doesn't seem to support union type guessing based on actual fields
>> > given (this might not be desired either), which complicates this somewhat.
>> 
>> Does the idea of a union type with a default value for the discriminator
>> help?  Maybe we have a discriminator which defaults to 'auto', and add a
>> union branch 'auto':'any'.  During creation, if the "driver":"auto"
>> branch is selected (usually implicitly by omitting "driver", but also
>> possible explicitly), the creation attempt is rejected as invalid
>> regardless of the contents of the remaining 'any'.  But during amend
>> usage, if the 'auto' branch is selected, we then add in the proper
>> "driver":"xyz" and reparse the QAPI object to determine if the remaining
>> fields in 'any' still meet the specification for the required driver branch.
>> 
>> This idea may still require some tweaks to the QAPI generator, but it's
>> the best I can come up with for a way to parse an arbitrary JSON object
>> with unknown validation, then reparse it again after adding more
>> information that would constrain the parse differently.
>
> Feels like this would be a lot of code just to allow the client to omit
> passing a value that it knows anyway. If this were a human interface, I
> could understand the desire to make commands less verbose, but for QMP I
> honestly don't see the point when it's not trivial.

Seconded.


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-15 14:18     ` Markus Armbruster
@ 2019-08-15 14:44       ` Maxim Levitsky
  2019-08-15 15:00         ` Eric Blake
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-15 14:44 UTC (permalink / raw)
  To: Markus Armbruster, Kevin Wolf
  Cc: Fam Zheng, Daniel P.Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-15 at 16:18 +0200, Markus Armbruster wrote:
> Kevin Wolf <kwolf@redhat.com> writes:
> 
> > Am 14.08.2019 um 23:08 hat Eric Blake geschrieben:
> > > On 8/14/19 3:22 PM, Maxim Levitsky wrote:
> > > 
> > > > This is an issue that was raised today on IRC with Kevin Wolf. Really thanks
> > > > for the idea!
> > > > 
> > > > We agreed that this new qmp interface should take the same options as
> > > > blockdev-create does, however since we want to be able to edit the encryption
> > > > slots separately, this implies that we sort of need to allow this on creation
> > > > time as well.
> > > > 
> > > > Also the BlockdevCreateOptions is a union, which is specialized by the driver name
> > > > which is great for creation, but for update, the driver name is already known,
> > > > and thus the user should not be forced to pass it again.
> > > > However qmp doesn't seem to support union type guessing based on actual fields
> > > > given (this might not be desired either), which complicates this somewhat.
> > > 
> > > Does the idea of a union type with a default value for the discriminator
> > > help?  Maybe we have a discriminator which defaults to 'auto', and add a
> > > union branch 'auto':'any'.  During creation, if the "driver":"auto"
> > > branch is selected (usually implicitly by omitting "driver", but also
> > > possible explicitly), the creation attempt is rejected as invalid
> > > regardless of the contents of the remaining 'any'.  But during amend
> > > usage, if the 'auto' branch is selected, we then add in the proper
> > > "driver":"xyz" and reparse the QAPI object to determine if the remaining
> > > fields in 'any' still meet the specification for the required driver branch.
> > > 
> > > This idea may still require some tweaks to the QAPI generator, but it's
> > > the best I can come up with for a way to parse an arbitrary JSON object
> > > with unknown validation, then reparse it again after adding more
> > > information that would constrain the parse differently.
> > 
> > Feels like this would be a lot of code just to allow the client to omit
> > passing a value that it knows anyway. If this were a human interface, I
> > could understand the desire to make commands less verbose, but for QMP I
> > honestly don't see the point when it's not trivial.
> 
> Seconded.


But what about my suggestion of adding something like:

{ 'union': 'BlockdevAmendOptions',

  'base': {
      'node-name':         'str' },

  'discriminator': { 'get_block_driver(node-name)' } ,

  'data': {
      'file':           'BlockdevCreateOptionsFile',
      'gluster':        'BlockdevCreateOptionsGluster',
      'luks':           'BlockdevCreateOptionsLUKS',
      'nfs':            'BlockdevCreateOptionsNfs',
      'parallels':      'BlockdevCreateOptionsParallels',
      'qcow':           'BlockdevCreateOptionsQcow',
      'qcow2':          'BlockdevCreateOptionsQcow2',
      'qed':            'BlockdevCreateOptionsQed',
      'rbd':            'BlockdevCreateOptionsRbd',
      'sheepdog':       'BlockdevCreateOptionsSheepdog',
      'ssh':            'BlockdevCreateOptionsSsh',
      'vdi':            'BlockdevCreateOptionsVdi',
      'vhdx':           'BlockdevCreateOptionsVhdx',
      'vmdk':           'BlockdevCreateOptionsVmdk',
      'vpc':            'BlockdevCreateOptionsVpc'
  } }


This shouldn't be hard to do IMHO.

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-15 14:44       ` Maxim Levitsky
@ 2019-08-15 15:00         ` Eric Blake
  2019-08-19 12:35           ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Eric Blake @ 2019-08-15 15:00 UTC (permalink / raw)
  To: Maxim Levitsky, Markus Armbruster, Kevin Wolf
  Cc: Fam Zheng, Daniel P.Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 2122 bytes --]

On 8/15/19 9:44 AM, Maxim Levitsky wrote:

>>>> Does the idea of a union type with a default value for the discriminator
>>>> help?  Maybe we have a discriminator which defaults to 'auto', and add a
>>>> union branch 'auto':'any'.  During creation, if the "driver":"auto"
>>>> branch is selected (usually implicitly by omitting "driver", but also
>>>> possible explicitly), the creation attempt is rejected as invalid
>>>> regardless of the contents of the remaining 'any'.  But during amend
>>>> usage, if the 'auto' branch is selected, we then add in the proper
>>>> "driver":"xyz" and reparse the QAPI object to determine if the remaining
>>>> fields in 'any' still meet the specification for the required driver branch.
>>>>
>>>> This idea may still require some tweaks to the QAPI generator, but it's
>>>> the best I can come up with for a way to parse an arbitrary JSON object
>>>> with unknown validation, then reparse it again after adding more
>>>> information that would constrain the parse differently.
>>>
>>> Feels like this would be a lot of code just to allow the client to omit
>>> passing a value that it knows anyway. If this were a human interface, I
>>> could understand the desire to make commands less verbose, but for QMP I
>>> honestly don't see the point when it's not trivial.
>>
>> Seconded.
> 
> 
> But what about my suggestion of adding something like:
> 
> { 'union': 'BlockdevAmendOptions',
> 
>   'base': {
>       'node-name':         'str' },
> 
>   'discriminator': { 'get_block_driver(node-name)' } ,

Not worth it. It makes the QAPI generator more complex (to invoke
arbitrary code instead of a fixed name) just to avoid a little bit of
complexity in the caller (which is assumed to be a computer, and thus
shouldn't have a hard time providing a sane 'driver' unconditionally).
An HMP wrapper around the QMP command can do whatever magic it needs to
omit driver, but making driver mandatory for QMP is just fine.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 02/13] qcrypto-luks: " Maxim Levitsky
@ 2019-08-15 21:40   ` John Snow
  2019-08-19 14:21     ` Maxim Levitsky
  2019-08-22 10:29     ` Daniel P. Berrangé
  2019-08-20 17:36   ` [Qemu-devel] " Max Reitz
  1 sibling, 2 replies; 87+ messages in thread
From: John Snow @ 2019-08-15 21:40 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi



On 8/14/19 4:22 PM, Maxim Levitsky wrote:
> This is also a preparation for key read/write/erase functions
> 

This is a matter of taste and I am not usually reviewing LUKS patches
(So don't take me too seriously), but I would prefer not to have "misc"
patches and instead split things out by individual changes along with a
nice commit message for each change.

> * use master key len from the header

This touches enough lines that you could make it its own patch, I think.

> * prefer to use crypto params in the QCryptoBlockLUKS
>   over passing them as function arguments

I think the same is true here, and highlighting which variables you are
sticking into state instead of leaving as functional parameters would be
nice to see without all the other changes.

> * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME

This can likely be squashed with whichever patch of yours first needs to
use it, because it's so short.

> * Add comments to various crypto parameters in the QCryptoBlockLUKS
> 

Can probably be squashed with item #2.


> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 213 ++++++++++++++++++++++----------------------
>  1 file changed, 105 insertions(+), 108 deletions(-)
> 
> diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> index 409ab50f20..48213abde7 100644
> --- a/crypto/block-luks.c
> +++ b/crypto/block-luks.c
> @@ -70,6 +70,8 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
>  
>  #define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
>  
> +#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME 2000
> +
>  static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
>      'L', 'U', 'K', 'S', 0xBA, 0xBE
>  };
> @@ -199,13 +201,25 @@ QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592);
>  struct QCryptoBlockLUKS {
>      QCryptoBlockLUKSHeader header;
>  
> -    /* Cache parsed versions of what's in header fields,
> -     * as we can't rely on QCryptoBlock.cipher being
> -     * non-NULL */
> +    /* Main encryption algorithm used for encryption*/
>      QCryptoCipherAlgorithm cipher_alg;
> +
> +    /* Mode of encryption for the selected encryption algorithm */
>      QCryptoCipherMode cipher_mode;
> +
> +    /* Initialization vector generation algorithm */
>      QCryptoIVGenAlgorithm ivgen_alg;
> +
> +    /* Hash algorithm used for IV generation*/
>      QCryptoHashAlgorithm ivgen_hash_alg;
> +
> +    /*
> +     * Encryption algorithm used for IV generation.
> +     * Usually the same as main encryption algorithm
> +     */
> +    QCryptoCipherAlgorithm ivgen_cipher_alg;
> +
> +    /* Hash algorithm used in pbkdf2 function */
>      QCryptoHashAlgorithm hash_alg;
>  };
>  
> @@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
>      }
>  }
>  
> +static int masterkeylen(QCryptoBlockLUKS *luks)
> +{
> +    return luks->header.key_bytes;
> +}
> +
> +

generally QEMU uses snake_case_names; please spell as "master_key_len".

>  /*
>   * Given a key slot, and user password, this will attempt to unlock
>   * the master encryption key from the key slot.
> @@ -410,21 +430,15 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
>   */
>  static int
>  qcrypto_block_luks_load_key(QCryptoBlock *block,
> -                            QCryptoBlockLUKSKeySlot *slot,
> +                            uint slot_idx,
>                              const char *password,
> -                            QCryptoCipherAlgorithm cipheralg,
> -                            QCryptoCipherMode ciphermode,
> -                            QCryptoHashAlgorithm hash,
> -                            QCryptoIVGenAlgorithm ivalg,
> -                            QCryptoCipherAlgorithm ivcipheralg,
> -                            QCryptoHashAlgorithm ivhash,
>                              uint8_t *masterkey,
> -                            size_t masterkeylen,
>                              QCryptoBlockReadFunc readfunc,
>                              void *opaque,
>                              Error **errp)
>  {
>      QCryptoBlockLUKS *luks = block->opaque;
> +    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
>      uint8_t *splitkey;
>      size_t splitkeylen;
>      uint8_t *possiblekey;
> @@ -439,9 +453,9 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
>          return 0;
>      }
>  
> -    splitkeylen = masterkeylen * slot->stripes;
> +    splitkeylen = masterkeylen(luks) * slot->stripes;
>      splitkey = g_new0(uint8_t, splitkeylen);
> -    possiblekey = g_new0(uint8_t, masterkeylen);
> +    possiblekey = g_new0(uint8_t, masterkeylen(luks));
>  
>      /*
>       * The user password is used to generate a (possible)
> @@ -450,11 +464,11 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
>       * the key is correct and validate the results of
>       * decryption later.
>       */
> -    if (qcrypto_pbkdf2(hash,
> +    if (qcrypto_pbkdf2(luks->hash_alg,
>                         (const uint8_t *)password, strlen(password),
>                         slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
>                         slot->iterations,
> -                       possiblekey, masterkeylen,
> +                       possiblekey, masterkeylen(luks),
>                         errp) < 0) {
>          goto cleanup;
>      }
> @@ -478,19 +492,19 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
>  
>      /* Setup the cipher/ivgen that we'll use to try to decrypt
>       * the split master key material */
> -    cipher = qcrypto_cipher_new(cipheralg, ciphermode,
> -                                possiblekey, masterkeylen,
> +    cipher = qcrypto_cipher_new(luks->cipher_alg, luks->cipher_mode,
> +                                possiblekey, masterkeylen(luks),
>                                  errp);
>      if (!cipher) {
>          goto cleanup;
>      }
>  
> -    niv = qcrypto_cipher_get_iv_len(cipheralg,
> -                                    ciphermode);
> -    ivgen = qcrypto_ivgen_new(ivalg,
> -                              ivcipheralg,
> -                              ivhash,
> -                              possiblekey, masterkeylen,
> +    niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
> +                                    luks->cipher_mode);
> +    ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
> +                              luks->ivgen_cipher_alg,
> +                              luks->ivgen_hash_alg,
> +                              possiblekey, masterkeylen(luks),
>                                errp);
>      if (!ivgen) {
>          goto cleanup;
> @@ -519,8 +533,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
>       * Now we've decrypted the split master key, join
>       * it back together to get the actual master key.
>       */
> -    if (qcrypto_afsplit_decode(hash,
> -                               masterkeylen,
> +    if (qcrypto_afsplit_decode(luks->hash_alg,
> +                               masterkeylen(luks),
>                                 slot->stripes,
>                                 splitkey,
>                                 masterkey,
> @@ -537,8 +551,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
>       * then comparing that to the hash stored in the key slot
>       * header
>       */
> -    if (qcrypto_pbkdf2(hash,
> -                       masterkey, masterkeylen,
> +    if (qcrypto_pbkdf2(luks->hash_alg,
> +                       masterkey, masterkeylen(luks),
>                         luks->header.master_key_salt,
>                         QCRYPTO_BLOCK_LUKS_SALT_LEN,
>                         luks->header.master_key_iterations,
> @@ -577,37 +591,19 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
>  static int
>  qcrypto_block_luks_find_key(QCryptoBlock *block,
>                              const char *password,
> -                            QCryptoCipherAlgorithm cipheralg,
> -                            QCryptoCipherMode ciphermode,
> -                            QCryptoHashAlgorithm hash,
> -                            QCryptoIVGenAlgorithm ivalg,
> -                            QCryptoCipherAlgorithm ivcipheralg,
> -                            QCryptoHashAlgorithm ivhash,
> -                            uint8_t **masterkey,
> -                            size_t *masterkeylen,
> +                            uint8_t *masterkey,
>                              QCryptoBlockReadFunc readfunc,
>                              void *opaque,
>                              Error **errp)
>  {
> -    QCryptoBlockLUKS *luks = block->opaque;
>      size_t i;
>      int rv;
>  
> -    *masterkey = g_new0(uint8_t, luks->header.key_bytes);
> -    *masterkeylen = luks->header.key_bytes;
> -
>      for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
>          rv = qcrypto_block_luks_load_key(block,
> -                                         &luks->header.key_slots[i],
> +                                         i,
>                                           password,
> -                                         cipheralg,
> -                                         ciphermode,
> -                                         hash,
> -                                         ivalg,
> -                                         ivcipheralg,
> -                                         ivhash,
> -                                         *masterkey,
> -                                         *masterkeylen,
> +                                         masterkey,
>                                           readfunc,
>                                           opaque,
>                                           errp);
> @@ -620,11 +616,7 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
>      }
>  
>      error_setg(errp, "Invalid password, cannot unlock any keyslot");
> -
>   error:
> -    g_free(*masterkey);
> -    *masterkey = NULL;
> -    *masterkeylen = 0;
>      return -1;
>  }
>  
> @@ -639,21 +631,15 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>                          size_t n_threads,
>                          Error **errp)
>  {
> -    QCryptoBlockLUKS *luks;
> +    QCryptoBlockLUKS *luks = NULL;
>      Error *local_err = NULL;
>      int ret = 0;
>      size_t i;
>      ssize_t rv;
>      uint8_t *masterkey = NULL;
> -    size_t masterkeylen;
>      char *ivgen_name, *ivhash_name;
> -    QCryptoCipherMode ciphermode;
> -    QCryptoCipherAlgorithm cipheralg;
> -    QCryptoIVGenAlgorithm ivalg;
> -    QCryptoCipherAlgorithm ivcipheralg;
> -    QCryptoHashAlgorithm hash;
> -    QCryptoHashAlgorithm ivhash;
>      char *password = NULL;
> +    char *cipher_mode = NULL;
>  
>      if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
>          if (!options->u.luks.key_secret) {
> @@ -710,6 +696,8 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>          goto fail;
>      }
>  
> +    cipher_mode = g_strdup(luks->header.cipher_mode);
> +
>      /*
>       * The cipher_mode header contains a string that we have
>       * to further parse, of the format
> @@ -718,7 +706,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>       *
>       * eg  cbc-essiv:sha256, cbc-plain64
>       */
> -    ivgen_name = strchr(luks->header.cipher_mode, '-');
> +    ivgen_name = strchr(cipher_mode, '-');
>      if (!ivgen_name) {
>          ret = -EINVAL;
>          error_setg(errp, "Unexpected cipher mode string format %s",
> @@ -730,13 +718,13 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>  
>      ivhash_name = strchr(ivgen_name, ':');
>      if (!ivhash_name) {
> -        ivhash = 0;
> +        luks->ivgen_hash_alg = 0;
>      } else {
>          *ivhash_name = '\0';
>          ivhash_name++;
>  
> -        ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name,
> -                                                     &local_err);
> +        luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
> +                                                                   &local_err);
>          if (local_err) {
>              ret = -ENOTSUP;
>              error_propagate(errp, local_err);
> @@ -744,25 +732,27 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>          }
>      }
>  
> -    ciphermode = qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode,
> -                                                       &local_err);
> +    luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
> +                                                              &local_err);
>      if (local_err) {
>          ret = -ENOTSUP;
>          error_propagate(errp, local_err);
>          goto fail;
>      }
>  
> -    cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
> -                                                      ciphermode,
> -                                                      luks->header.key_bytes,
> -                                                      &local_err);
> +    luks->cipher_alg =
> +            qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
> +                                                  luks->cipher_mode,
> +                                                  luks->header.key_bytes,
> +                                                  &local_err);
>      if (local_err) {
>          ret = -ENOTSUP;
>          error_propagate(errp, local_err);
>          goto fail;
>      }
>  
> -    hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
> +    luks->hash_alg =
> +            qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
>                                                 &local_err);
>      if (local_err) {
>          ret = -ENOTSUP;
> @@ -770,23 +760,24 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>          goto fail;
>      }
>  
> -    ivalg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
> -                                                 &local_err);
> +    luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
> +                                                           &local_err);
>      if (local_err) {
>          ret = -ENOTSUP;
>          error_propagate(errp, local_err);
>          goto fail;
>      }
>  
> -    if (ivalg == QCRYPTO_IVGEN_ALG_ESSIV) {
> +    if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
>          if (!ivhash_name) {
>              ret = -EINVAL;
>              error_setg(errp, "Missing IV generator hash specification");
>              goto fail;
>          }
> -        ivcipheralg = qcrypto_block_luks_essiv_cipher(cipheralg,
> -                                                      ivhash,
> -                                                      &local_err);
> +        luks->ivgen_cipher_alg =
> +                qcrypto_block_luks_essiv_cipher(luks->cipher_alg,
> +                                                luks->ivgen_hash_alg,
> +                                                &local_err);
>          if (local_err) {
>              ret = -ENOTSUP;
>              error_propagate(errp, local_err);
> @@ -800,21 +791,25 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>           * ignore hash names with these ivgens rather than report
>           * an error about the invalid usage
>           */
> -        ivcipheralg = cipheralg;
> +        luks->ivgen_cipher_alg = luks->cipher_alg;
>      }
>  
> +
> +    g_free(cipher_mode);
> +    cipher_mode = NULL;
> +    ivgen_name = NULL;
> +    ivhash_name = NULL;
> +
>      if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
>          /* Try to find which key slot our password is valid for
>           * and unlock the master key from that slot.
>           */
> +
> +        masterkey = g_new0(uint8_t, masterkeylen(luks));
> +
>          if (qcrypto_block_luks_find_key(block,
>                                          password,
> -                                        cipheralg, ciphermode,
> -                                        hash,
> -                                        ivalg,
> -                                        ivcipheralg,
> -                                        ivhash,
> -                                        &masterkey, &masterkeylen,
> +                                        masterkey,
>                                          readfunc, opaque,
>                                          errp) < 0) {
>              ret = -EACCES;
> @@ -824,21 +819,24 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>          /* We have a valid master key now, so can setup the
>           * block device payload decryption objects
>           */
> -        block->kdfhash = hash;
> -        block->niv = qcrypto_cipher_get_iv_len(cipheralg,
> -                                               ciphermode);
> -        block->ivgen = qcrypto_ivgen_new(ivalg,
> -                                         ivcipheralg,
> -                                         ivhash,
> -                                         masterkey, masterkeylen,
> +        block->kdfhash = luks->hash_alg;
> +        block->niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
> +                                               luks->cipher_mode);
> +
> +        block->ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
> +                                         luks->ivgen_cipher_alg,
> +                                         luks->ivgen_hash_alg,
> +                                         masterkey, masterkeylen(luks),
>                                           errp);
>          if (!block->ivgen) {
>              ret = -ENOTSUP;
>              goto fail;
>          }
>  
> -        ret = qcrypto_block_init_cipher(block, cipheralg, ciphermode,
> -                                        masterkey, masterkeylen, n_threads,
> +        ret = qcrypto_block_init_cipher(block, luks->cipher_alg,
> +                                        luks->cipher_mode,
> +                                        masterkey, masterkeylen(luks),
> +                                        n_threads,
>                                          errp);
>          if (ret < 0) {
>              ret = -ENOTSUP;
> @@ -850,12 +848,6 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>      block->payload_offset = luks->header.payload_offset *
>          block->sector_size;
>  
> -    luks->cipher_alg = cipheralg;
> -    luks->cipher_mode = ciphermode;
> -    luks->ivgen_alg = ivalg;
> -    luks->ivgen_hash_alg = ivhash;
> -    luks->hash_alg = hash;
> -
>      g_free(masterkey);
>      g_free(password);
>  
> @@ -910,7 +902,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>  
>      memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
>      if (!luks_opts.has_iter_time) {
> -        luks_opts.iter_time = 2000;
> +        luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME;
>      }
>      if (!luks_opts.has_cipher_alg) {
>          luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
> @@ -930,6 +922,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>              luks_opts.has_ivgen_hash_alg = true;
>          }
>      }
> +
> +    luks = g_new0(QCryptoBlockLUKS, 1);
> +    block->opaque = luks;
> +
> +    luks->cipher_alg = luks_opts.cipher_alg;
> +    luks->cipher_mode = luks_opts.cipher_mode;
> +    luks->ivgen_alg = luks_opts.ivgen_alg;
> +    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
> +    luks->hash_alg = luks_opts.hash_alg;
> +
> +
>      /* Note we're allowing ivgen_hash_alg to be set even for
>       * non-essiv iv generators that don't need a hash. It will
>       * be silently ignored, for compatibility with dm-crypt */
> @@ -944,8 +947,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>          return -1;
>      }
>  
> -    luks = g_new0(QCryptoBlockLUKS, 1);
> -    block->opaque = luks;
>  
>      memcpy(luks->header.magic, qcrypto_block_luks_magic,
>             QCRYPTO_BLOCK_LUKS_MAGIC_LEN);
> @@ -1003,6 +1004,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>          ivcipheralg = luks_opts.cipher_alg;
>      }
>  
> +    luks->ivgen_cipher_alg = ivcipheralg;
> +
>      strcpy(luks->header.cipher_name, cipher_alg);
>      strcpy(luks->header.cipher_mode, cipher_mode_spec);
>      strcpy(luks->header.hash_spec, hash_alg);
> @@ -1304,12 +1307,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>          goto error;
>      }
>  
> -    luks->cipher_alg = luks_opts.cipher_alg;
> -    luks->cipher_mode = luks_opts.cipher_mode;
> -    luks->ivgen_alg = luks_opts.ivgen_alg;
> -    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
> -    luks->hash_alg = luks_opts.hash_alg;
> -
>      memset(masterkey, 0, luks->header.key_bytes);
>      g_free(masterkey);
>      memset(slotkey, 0, luks->header.key_bytes);
> 


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-15 15:00         ` Eric Blake
@ 2019-08-19 12:35           ` Maxim Levitsky
  2019-08-21 11:31             ` Markus Armbruster
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-19 12:35 UTC (permalink / raw)
  To: Eric Blake, Markus Armbruster, Kevin Wolf
  Cc: Fam Zheng, Daniel P.Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-15 at 10:00 -0500, Eric Blake wrote:
> On 8/15/19 9:44 AM, Maxim Levitsky wrote:
> 
> > > > > Does the idea of a union type with a default value for the discriminator
> > > > > help?  Maybe we have a discriminator which defaults to 'auto', and add a
> > > > > union branch 'auto':'any'.  During creation, if the "driver":"auto"
> > > > > branch is selected (usually implicitly by omitting "driver", but also
> > > > > possible explicitly), the creation attempt is rejected as invalid
> > > > > regardless of the contents of the remaining 'any'.  But during amend
> > > > > usage, if the 'auto' branch is selected, we then add in the proper
> > > > > "driver":"xyz" and reparse the QAPI object to determine if the remaining
> > > > > fields in 'any' still meet the specification for the required driver branch.
> > > > > 
> > > > > This idea may still require some tweaks to the QAPI generator, but it's
> > > > > the best I can come up with for a way to parse an arbitrary JSON object
> > > > > with unknown validation, then reparse it again after adding more
> > > > > information that would constrain the parse differently.
> > > > 
> > > > Feels like this would be a lot of code just to allow the client to omit
> > > > passing a value that it knows anyway. If this were a human interface, I
> > > > could understand the desire to make commands less verbose, but for QMP I
> > > > honestly don't see the point when it's not trivial.
> > > 
> > > Seconded.
> > 
> > 
> > But what about my suggestion of adding something like:
> > 
> > { 'union': 'BlockdevAmendOptions',
> > 
> >   'base': {
> >       'node-name':         'str' },
> > 
> >   'discriminator': { 'get_block_driver(node-name)' } ,
> 
> Not worth it. It makes the QAPI generator more complex (to invoke
> arbitrary code instead of a fixed name) just to avoid a little bit of
> complexity in the caller (which is assumed to be a computer, and thus
> shouldn't have a hard time providing a sane 'driver' unconditionally).
> An HMP wrapper around the QMP command can do whatever magic it needs to
> omit driver, but making driver mandatory for QMP is just fine.

All right! I kind of not agree with that, since I think even though QMP is a machine language,
it still should be consistent since humans still use it, even if this is humans that code some
tool that use it.

I won't argue with you though, let it be like that.

Best regards,
	Maxim Levitsky

> 




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-15 21:40   ` [Qemu-devel] [Qemu-block] " John Snow
@ 2019-08-19 14:21     ` Maxim Levitsky
  2019-08-22 10:29     ` Daniel P. Berrangé
  1 sibling, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-19 14:21 UTC (permalink / raw)
  To: John Snow, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-15 at 17:40 -0400, John Snow wrote:
> 
> On 8/14/19 4:22 PM, Maxim Levitsky wrote:
> > This is also a preparation for key read/write/erase functions
> > 
> 
> This is a matter of taste and I am not usually reviewing LUKS patches
> (So don't take me too seriously), but I would prefer not to have "misc"
> patches and instead split things out by individual changes along with a
> nice commit message for each change.
> 
> > * use master key len from the header
> 
> This touches enough lines that you could make it its own patch, I think.
> 
> > * prefer to use crypto params in the QCryptoBlockLUKS
> >   over passing them as function arguments
> 
> I think the same is true here, and highlighting which variables you are
> sticking into state instead of leaving as functional parameters would be
> nice to see without all the other changes.
> 
> > * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
> 
> This can likely be squashed with whichever patch of yours first needs to
> use it, because it's so short.
> 
> > * Add comments to various crypto parameters in the QCryptoBlockLUKS
> > 
> 
> Can probably be squashed with item #2.

I mostly agree with you! I usually write everything as one big patch,
and then split it. 
It takes time but this has the benefit of
much less overhead during the development, and it forces kind of self
review on me while doing the split.

This patch I probably didn't split enough, and I'll do it later when I send the next
revision. 

I only had split things to the extent that all the patches are readable and reviewable to avoid wasting time,
on stuff that I will have to probably rewrite anyway.

Thanks a lot for the feedback,

Best regards,
	Maxim Levitsky




> 
> 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 213 ++++++++++++++++++++++----------------------
> >  1 file changed, 105 insertions(+), 108 deletions(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index 409ab50f20..48213abde7 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -70,6 +70,8 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
> >  
> >  #define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
> >  
> > +#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME 2000
> > +
> >  static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
> >      'L', 'U', 'K', 'S', 0xBA, 0xBE
> >  };
> > @@ -199,13 +201,25 @@ QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592);
> >  struct QCryptoBlockLUKS {
> >      QCryptoBlockLUKSHeader header;
> >  
> > -    /* Cache parsed versions of what's in header fields,
> > -     * as we can't rely on QCryptoBlock.cipher being
> > -     * non-NULL */
> > +    /* Main encryption algorithm used for encryption*/
> >      QCryptoCipherAlgorithm cipher_alg;
> > +
> > +    /* Mode of encryption for the selected encryption algorithm */
> >      QCryptoCipherMode cipher_mode;
> > +
> > +    /* Initialization vector generation algorithm */
> >      QCryptoIVGenAlgorithm ivgen_alg;
> > +
> > +    /* Hash algorithm used for IV generation*/
> >      QCryptoHashAlgorithm ivgen_hash_alg;
> > +
> > +    /*
> > +     * Encryption algorithm used for IV generation.
> > +     * Usually the same as main encryption algorithm
> > +     */
> > +    QCryptoCipherAlgorithm ivgen_cipher_alg;
> > +
> > +    /* Hash algorithm used in pbkdf2 function */
> >      QCryptoHashAlgorithm hash_alg;
> >  };
> >  
> > @@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> >      }
> >  }
> >  
> > +static int masterkeylen(QCryptoBlockLUKS *luks)
> > +{
> > +    return luks->header.key_bytes;
> > +}
> > +
> > +
> 
> generally QEMU uses snake_case_names; please spell as "master_key_len".
> 
> >  /*
> >   * Given a key slot, and user password, this will attempt to unlock
> >   * the master encryption key from the key slot.
> > @@ -410,21 +430,15 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> >   */
> >  static int
> >  qcrypto_block_luks_load_key(QCryptoBlock *block,
> > -                            QCryptoBlockLUKSKeySlot *slot,
> > +                            uint slot_idx,
> >                              const char *password,
> > -                            QCryptoCipherAlgorithm cipheralg,
> > -                            QCryptoCipherMode ciphermode,
> > -                            QCryptoHashAlgorithm hash,
> > -                            QCryptoIVGenAlgorithm ivalg,
> > -                            QCryptoCipherAlgorithm ivcipheralg,
> > -                            QCryptoHashAlgorithm ivhash,
> >                              uint8_t *masterkey,
> > -                            size_t masterkeylen,
> >                              QCryptoBlockReadFunc readfunc,
> >                              void *opaque,
> >                              Error **errp)
> >  {
> >      QCryptoBlockLUKS *luks = block->opaque;
> > +    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
> >      uint8_t *splitkey;
> >      size_t splitkeylen;
> >      uint8_t *possiblekey;
> > @@ -439,9 +453,9 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
> >          return 0;
> >      }
> >  
> > -    splitkeylen = masterkeylen * slot->stripes;
> > +    splitkeylen = masterkeylen(luks) * slot->stripes;
> >      splitkey = g_new0(uint8_t, splitkeylen);
> > -    possiblekey = g_new0(uint8_t, masterkeylen);
> > +    possiblekey = g_new0(uint8_t, masterkeylen(luks));
> >  
> >      /*
> >       * The user password is used to generate a (possible)
> > @@ -450,11 +464,11 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
> >       * the key is correct and validate the results of
> >       * decryption later.
> >       */
> > -    if (qcrypto_pbkdf2(hash,
> > +    if (qcrypto_pbkdf2(luks->hash_alg,
> >                         (const uint8_t *)password, strlen(password),
> >                         slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
> >                         slot->iterations,
> > -                       possiblekey, masterkeylen,
> > +                       possiblekey, masterkeylen(luks),
> >                         errp) < 0) {
> >          goto cleanup;
> >      }
> > @@ -478,19 +492,19 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
> >  
> >      /* Setup the cipher/ivgen that we'll use to try to decrypt
> >       * the split master key material */
> > -    cipher = qcrypto_cipher_new(cipheralg, ciphermode,
> > -                                possiblekey, masterkeylen,
> > +    cipher = qcrypto_cipher_new(luks->cipher_alg, luks->cipher_mode,
> > +                                possiblekey, masterkeylen(luks),
> >                                  errp);
> >      if (!cipher) {
> >          goto cleanup;
> >      }
> >  
> > -    niv = qcrypto_cipher_get_iv_len(cipheralg,
> > -                                    ciphermode);
> > -    ivgen = qcrypto_ivgen_new(ivalg,
> > -                              ivcipheralg,
> > -                              ivhash,
> > -                              possiblekey, masterkeylen,
> > +    niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
> > +                                    luks->cipher_mode);
> > +    ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
> > +                              luks->ivgen_cipher_alg,
> > +                              luks->ivgen_hash_alg,
> > +                              possiblekey, masterkeylen(luks),
> >                                errp);
> >      if (!ivgen) {
> >          goto cleanup;
> > @@ -519,8 +533,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
> >       * Now we've decrypted the split master key, join
> >       * it back together to get the actual master key.
> >       */
> > -    if (qcrypto_afsplit_decode(hash,
> > -                               masterkeylen,
> > +    if (qcrypto_afsplit_decode(luks->hash_alg,
> > +                               masterkeylen(luks),
> >                                 slot->stripes,
> >                                 splitkey,
> >                                 masterkey,
> > @@ -537,8 +551,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
> >       * then comparing that to the hash stored in the key slot
> >       * header
> >       */
> > -    if (qcrypto_pbkdf2(hash,
> > -                       masterkey, masterkeylen,
> > +    if (qcrypto_pbkdf2(luks->hash_alg,
> > +                       masterkey, masterkeylen(luks),
> >                         luks->header.master_key_salt,
> >                         QCRYPTO_BLOCK_LUKS_SALT_LEN,
> >                         luks->header.master_key_iterations,
> > @@ -577,37 +591,19 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
> >  static int
> >  qcrypto_block_luks_find_key(QCryptoBlock *block,
> >                              const char *password,
> > -                            QCryptoCipherAlgorithm cipheralg,
> > -                            QCryptoCipherMode ciphermode,
> > -                            QCryptoHashAlgorithm hash,
> > -                            QCryptoIVGenAlgorithm ivalg,
> > -                            QCryptoCipherAlgorithm ivcipheralg,
> > -                            QCryptoHashAlgorithm ivhash,
> > -                            uint8_t **masterkey,
> > -                            size_t *masterkeylen,
> > +                            uint8_t *masterkey,
> >                              QCryptoBlockReadFunc readfunc,
> >                              void *opaque,
> >                              Error **errp)
> >  {
> > -    QCryptoBlockLUKS *luks = block->opaque;
> >      size_t i;
> >      int rv;
> >  
> > -    *masterkey = g_new0(uint8_t, luks->header.key_bytes);
> > -    *masterkeylen = luks->header.key_bytes;
> > -
> >      for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> >          rv = qcrypto_block_luks_load_key(block,
> > -                                         &luks->header.key_slots[i],
> > +                                         i,
> >                                           password,
> > -                                         cipheralg,
> > -                                         ciphermode,
> > -                                         hash,
> > -                                         ivalg,
> > -                                         ivcipheralg,
> > -                                         ivhash,
> > -                                         *masterkey,
> > -                                         *masterkeylen,
> > +                                         masterkey,
> >                                           readfunc,
> >                                           opaque,
> >                                           errp);
> > @@ -620,11 +616,7 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
> >      }
> >  
> >      error_setg(errp, "Invalid password, cannot unlock any keyslot");
> > -
> >   error:
> > -    g_free(*masterkey);
> > -    *masterkey = NULL;
> > -    *masterkeylen = 0;
> >      return -1;
> >  }
> >  
> > @@ -639,21 +631,15 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >                          size_t n_threads,
> >                          Error **errp)
> >  {
> > -    QCryptoBlockLUKS *luks;
> > +    QCryptoBlockLUKS *luks = NULL;
> >      Error *local_err = NULL;
> >      int ret = 0;
> >      size_t i;
> >      ssize_t rv;
> >      uint8_t *masterkey = NULL;
> > -    size_t masterkeylen;
> >      char *ivgen_name, *ivhash_name;
> > -    QCryptoCipherMode ciphermode;
> > -    QCryptoCipherAlgorithm cipheralg;
> > -    QCryptoIVGenAlgorithm ivalg;
> > -    QCryptoCipherAlgorithm ivcipheralg;
> > -    QCryptoHashAlgorithm hash;
> > -    QCryptoHashAlgorithm ivhash;
> >      char *password = NULL;
> > +    char *cipher_mode = NULL;
> >  
> >      if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
> >          if (!options->u.luks.key_secret) {
> > @@ -710,6 +696,8 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >          goto fail;
> >      }
> >  
> > +    cipher_mode = g_strdup(luks->header.cipher_mode);
> > +
> >      /*
> >       * The cipher_mode header contains a string that we have
> >       * to further parse, of the format
> > @@ -718,7 +706,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >       *
> >       * eg  cbc-essiv:sha256, cbc-plain64
> >       */
> > -    ivgen_name = strchr(luks->header.cipher_mode, '-');
> > +    ivgen_name = strchr(cipher_mode, '-');
> >      if (!ivgen_name) {
> >          ret = -EINVAL;
> >          error_setg(errp, "Unexpected cipher mode string format %s",
> > @@ -730,13 +718,13 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >  
> >      ivhash_name = strchr(ivgen_name, ':');
> >      if (!ivhash_name) {
> > -        ivhash = 0;
> > +        luks->ivgen_hash_alg = 0;
> >      } else {
> >          *ivhash_name = '\0';
> >          ivhash_name++;
> >  
> > -        ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name,
> > -                                                     &local_err);
> > +        luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
> > +                                                                   &local_err);
> >          if (local_err) {
> >              ret = -ENOTSUP;
> >              error_propagate(errp, local_err);
> > @@ -744,25 +732,27 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >          }
> >      }
> >  
> > -    ciphermode = qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode,
> > -                                                       &local_err);
> > +    luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
> > +                                                              &local_err);
> >      if (local_err) {
> >          ret = -ENOTSUP;
> >          error_propagate(errp, local_err);
> >          goto fail;
> >      }
> >  
> > -    cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
> > -                                                      ciphermode,
> > -                                                      luks->header.key_bytes,
> > -                                                      &local_err);
> > +    luks->cipher_alg =
> > +            qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
> > +                                                  luks->cipher_mode,
> > +                                                  luks->header.key_bytes,
> > +                                                  &local_err);
> >      if (local_err) {
> >          ret = -ENOTSUP;
> >          error_propagate(errp, local_err);
> >          goto fail;
> >      }
> >  
> > -    hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
> > +    luks->hash_alg =
> > +            qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
> >                                                 &local_err);
> >      if (local_err) {
> >          ret = -ENOTSUP;
> > @@ -770,23 +760,24 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >          goto fail;
> >      }
> >  
> > -    ivalg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
> > -                                                 &local_err);
> > +    luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
> > +                                                           &local_err);
> >      if (local_err) {
> >          ret = -ENOTSUP;
> >          error_propagate(errp, local_err);
> >          goto fail;
> >      }
> >  
> > -    if (ivalg == QCRYPTO_IVGEN_ALG_ESSIV) {
> > +    if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
> >          if (!ivhash_name) {
> >              ret = -EINVAL;
> >              error_setg(errp, "Missing IV generator hash specification");
> >              goto fail;
> >          }
> > -        ivcipheralg = qcrypto_block_luks_essiv_cipher(cipheralg,
> > -                                                      ivhash,
> > -                                                      &local_err);
> > +        luks->ivgen_cipher_alg =
> > +                qcrypto_block_luks_essiv_cipher(luks->cipher_alg,
> > +                                                luks->ivgen_hash_alg,
> > +                                                &local_err);
> >          if (local_err) {
> >              ret = -ENOTSUP;
> >              error_propagate(errp, local_err);
> > @@ -800,21 +791,25 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >           * ignore hash names with these ivgens rather than report
> >           * an error about the invalid usage
> >           */
> > -        ivcipheralg = cipheralg;
> > +        luks->ivgen_cipher_alg = luks->cipher_alg;
> >      }
> >  
> > +
> > +    g_free(cipher_mode);
> > +    cipher_mode = NULL;
> > +    ivgen_name = NULL;
> > +    ivhash_name = NULL;
> > +
> >      if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
> >          /* Try to find which key slot our password is valid for
> >           * and unlock the master key from that slot.
> >           */
> > +
> > +        masterkey = g_new0(uint8_t, masterkeylen(luks));
> > +
> >          if (qcrypto_block_luks_find_key(block,
> >                                          password,
> > -                                        cipheralg, ciphermode,
> > -                                        hash,
> > -                                        ivalg,
> > -                                        ivcipheralg,
> > -                                        ivhash,
> > -                                        &masterkey, &masterkeylen,
> > +                                        masterkey,
> >                                          readfunc, opaque,
> >                                          errp) < 0) {
> >              ret = -EACCES;
> > @@ -824,21 +819,24 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >          /* We have a valid master key now, so can setup the
> >           * block device payload decryption objects
> >           */
> > -        block->kdfhash = hash;
> > -        block->niv = qcrypto_cipher_get_iv_len(cipheralg,
> > -                                               ciphermode);
> > -        block->ivgen = qcrypto_ivgen_new(ivalg,
> > -                                         ivcipheralg,
> > -                                         ivhash,
> > -                                         masterkey, masterkeylen,
> > +        block->kdfhash = luks->hash_alg;
> > +        block->niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
> > +                                               luks->cipher_mode);
> > +
> > +        block->ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
> > +                                         luks->ivgen_cipher_alg,
> > +                                         luks->ivgen_hash_alg,
> > +                                         masterkey, masterkeylen(luks),
> >                                           errp);
> >          if (!block->ivgen) {
> >              ret = -ENOTSUP;
> >              goto fail;
> >          }
> >  
> > -        ret = qcrypto_block_init_cipher(block, cipheralg, ciphermode,
> > -                                        masterkey, masterkeylen, n_threads,
> > +        ret = qcrypto_block_init_cipher(block, luks->cipher_alg,
> > +                                        luks->cipher_mode,
> > +                                        masterkey, masterkeylen(luks),
> > +                                        n_threads,
> >                                          errp);
> >          if (ret < 0) {
> >              ret = -ENOTSUP;
> > @@ -850,12 +848,6 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >      block->payload_offset = luks->header.payload_offset *
> >          block->sector_size;
> >  
> > -    luks->cipher_alg = cipheralg;
> > -    luks->cipher_mode = ciphermode;
> > -    luks->ivgen_alg = ivalg;
> > -    luks->ivgen_hash_alg = ivhash;
> > -    luks->hash_alg = hash;
> > -
> >      g_free(masterkey);
> >      g_free(password);
> >  
> > @@ -910,7 +902,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >  
> >      memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
> >      if (!luks_opts.has_iter_time) {
> > -        luks_opts.iter_time = 2000;
> > +        luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME;
> >      }
> >      if (!luks_opts.has_cipher_alg) {
> >          luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
> > @@ -930,6 +922,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >              luks_opts.has_ivgen_hash_alg = true;
> >          }
> >      }
> > +
> > +    luks = g_new0(QCryptoBlockLUKS, 1);
> > +    block->opaque = luks;
> > +
> > +    luks->cipher_alg = luks_opts.cipher_alg;
> > +    luks->cipher_mode = luks_opts.cipher_mode;
> > +    luks->ivgen_alg = luks_opts.ivgen_alg;
> > +    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
> > +    luks->hash_alg = luks_opts.hash_alg;
> > +
> > +
> >      /* Note we're allowing ivgen_hash_alg to be set even for
> >       * non-essiv iv generators that don't need a hash. It will
> >       * be silently ignored, for compatibility with dm-crypt */
> > @@ -944,8 +947,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >          return -1;
> >      }
> >  
> > -    luks = g_new0(QCryptoBlockLUKS, 1);
> > -    block->opaque = luks;
> >  
> >      memcpy(luks->header.magic, qcrypto_block_luks_magic,
> >             QCRYPTO_BLOCK_LUKS_MAGIC_LEN);
> > @@ -1003,6 +1004,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >          ivcipheralg = luks_opts.cipher_alg;
> >      }
> >  
> > +    luks->ivgen_cipher_alg = ivcipheralg;
> > +
> >      strcpy(luks->header.cipher_name, cipher_alg);
> >      strcpy(luks->header.cipher_mode, cipher_mode_spec);
> >      strcpy(luks->header.hash_spec, hash_alg);
> > @@ -1304,12 +1307,6 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >          goto error;
> >      }
> >  
> > -    luks->cipher_alg = luks_opts.cipher_alg;
> > -    luks->cipher_mode = luks_opts.cipher_mode;
> > -    luks->ivgen_alg = luks_opts.ivgen_alg;
> > -    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
> > -    luks->hash_alg = luks_opts.hash_alg;
> > -
> >      memset(masterkey, 0, luks->header.key_bytes);
> >      g_free(masterkey);
> >      memset(slotkey, 0, luks->header.key_bytes);
> > 




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring Maxim Levitsky
@ 2019-08-20 16:38   ` Max Reitz
  2019-08-22  0:05     ` Maxim Levitsky
  2019-08-21 15:39   ` Daniel P. Berrangé
  1 sibling, 1 reply; 87+ messages in thread
From: Max Reitz @ 2019-08-20 16:38 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 1144 bytes --]

On 14.08.19 22:22, Maxim Levitsky wrote:
> * rename the write_func to create_write_func,
>   and init_func to create_init_func
>   this is  preparation for other write_func that will
>   be used to update the encryption keys.
> 
> No functional changes
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  block/crypto.c | 15 ++++++++-------
>  1 file changed, 8 insertions(+), 7 deletions(-)
> 

I’m not quite sure why you remove or add blank lines seemingly at random...

> diff --git a/block/crypto.c b/block/crypto.c
> index 8237424ae6..42a3f0898b 100644
> --- a/block/crypto.c
> +++ b/block/crypto.c

[...]

> @@ -77,7 +76,7 @@ struct BlockCryptoCreateData {
>  };
>  
>  
> -static ssize_t block_crypto_write_func(QCryptoBlock *block,
> +static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
>                                         size_t offset,
>                                         const uint8_t *buf,
>                                         size_t buflen,

Alignment should be kept at the opening parentheses.

But other than those two things, why not.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 02/13] qcrypto-luks: " Maxim Levitsky
  2019-08-15 21:40   ` [Qemu-devel] [Qemu-block] " John Snow
@ 2019-08-20 17:36   ` Max Reitz
  2019-08-21 23:59     ` Maxim Levitsky
  1 sibling, 1 reply; 87+ messages in thread
From: Max Reitz @ 2019-08-20 17:36 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 6431 bytes --]

On 14.08.19 22:22, Maxim Levitsky wrote:
> This is also a preparation for key read/write/erase functions
> 
> * use master key len from the header
> * prefer to use crypto params in the QCryptoBlockLUKS
>   over passing them as function arguments
> * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
> * Add comments to various crypto parameters in the QCryptoBlockLUKS
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 213 ++++++++++++++++++++++----------------------
>  1 file changed, 105 insertions(+), 108 deletions(-)
> 
> diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> index 409ab50f20..48213abde7 100644
> --- a/crypto/block-luks.c
> +++ b/crypto/block-luks.c

[...]

> @@ -199,13 +201,25 @@ QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592);
>  struct QCryptoBlockLUKS {
>      QCryptoBlockLUKSHeader header;
>  
> -    /* Cache parsed versions of what's in header fields,
> -     * as we can't rely on QCryptoBlock.cipher being
> -     * non-NULL */

Hm, why remove this comment?

> +    /* Main encryption algorithm used for encryption*/
>      QCryptoCipherAlgorithm cipher_alg;
> +
> +    /* Mode of encryption for the selected encryption algorithm */
>      QCryptoCipherMode cipher_mode;
> +
> +    /* Initialization vector generation algorithm */
>      QCryptoIVGenAlgorithm ivgen_alg;
> +
> +    /* Hash algorithm used for IV generation*/
>      QCryptoHashAlgorithm ivgen_hash_alg;
> +
> +    /*
> +     * Encryption algorithm used for IV generation.
> +     * Usually the same as main encryption algorithm
> +     */
> +    QCryptoCipherAlgorithm ivgen_cipher_alg;
> +
> +    /* Hash algorithm used in pbkdf2 function */
>      QCryptoHashAlgorithm hash_alg;
>  };
>  
> @@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
>      }
>  }
>  
> +static int masterkeylen(QCryptoBlockLUKS *luks)

This should be a const pointer.

> +{
> +    return luks->header.key_bytes;
> +}
> +
> +
>  /*
>   * Given a key slot, and user password, this will attempt to unlock
>   * the master encryption key from the key slot.
> @@ -410,21 +430,15 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
>   */
>  static int
>  qcrypto_block_luks_load_key(QCryptoBlock *block,
> -                            QCryptoBlockLUKSKeySlot *slot,
> +                            uint slot_idx,

Did you use uint on purpose or do you mean a plain “unsigned”?

>                              const char *password,
> -                            QCryptoCipherAlgorithm cipheralg,
> -                            QCryptoCipherMode ciphermode,
> -                            QCryptoHashAlgorithm hash,
> -                            QCryptoIVGenAlgorithm ivalg,
> -                            QCryptoCipherAlgorithm ivcipheralg,
> -                            QCryptoHashAlgorithm ivhash,
>                              uint8_t *masterkey,
> -                            size_t masterkeylen,
>                              QCryptoBlockReadFunc readfunc,
>                              void *opaque,
>                              Error **errp)
>  {
>      QCryptoBlockLUKS *luks = block->opaque;
> +    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];

I think this is a great opportunity to make this a const pointer.

>      uint8_t *splitkey;
>      size_t splitkeylen;
>      uint8_t *possiblekey;

[...]

> @@ -710,6 +696,8 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>          goto fail;
>      }
>  
> +    cipher_mode = g_strdup(luks->header.cipher_mode);
> +

This should be freed under the fail label.

(And maybe the fact that this no longer modifies
luks->header.cipher_mode should be mentioned in the commit message, I
don’t know.)

>      /*
>       * The cipher_mode header contains a string that we have
>       * to further parse, of the format

[...]

> @@ -730,13 +718,13 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>  
>      ivhash_name = strchr(ivgen_name, ':');
>      if (!ivhash_name) {
> -        ivhash = 0;
> +        luks->ivgen_hash_alg = 0;

*luks is initialized to 0 anyway, but it doesn’t hurt, of course.

>      } else {
>          *ivhash_name = '\0';
>          ivhash_name++;
>  
> -        ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name,
> -                                                     &local_err);
> +        luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
> +                                                                   &local_err);
>          if (local_err) {
>              ret = -ENOTSUP;
>              error_propagate(errp, local_err);
> @@ -744,25 +732,27 @@ qcrypto_block_luks_open(QCryptoBlock *block,

[...]

>  
> -    hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
> +    luks->hash_alg =
> +            qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
>                                                 &local_err);

Indentation is off now.

>      if (local_err) {
>          ret = -ENOTSUP;

[...]

> @@ -930,6 +922,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>              luks_opts.has_ivgen_hash_alg = true;
>          }
>      }
> +
> +    luks = g_new0(QCryptoBlockLUKS, 1);
> +    block->opaque = luks;
> +
> +    luks->cipher_alg = luks_opts.cipher_alg;
> +    luks->cipher_mode = luks_opts.cipher_mode;
> +    luks->ivgen_alg = luks_opts.ivgen_alg;
> +    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
> +    luks->hash_alg = luks_opts.hash_alg;
> +
> +

Why did you pull this up?  Now @luks is leaked in both of the next error
paths.

>      /* Note we're allowing ivgen_hash_alg to be set even for
>       * non-essiv iv generators that don't need a hash. It will
>       * be silently ignored, for compatibility with dm-crypt */

[...]

> @@ -1003,6 +1004,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>          ivcipheralg = luks_opts.cipher_alg;
>      }
>  
> +    luks->ivgen_cipher_alg = ivcipheralg;
> +

What’s the point in having a dedicated ivcipheralg variable then?

Max

>      strcpy(luks->header.cipher_name, cipher_alg);
>      strcpy(luks->header.cipher_mode, cipher_mode_spec);
>      strcpy(luks->header.hash_spec, hash_alg);


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (13 preceding siblings ...)
  2019-08-14 21:08 ` [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 " Eric Blake
@ 2019-08-20 17:59 ` Max Reitz
  2019-08-21 22:00   ` Maxim Levitsky
  2019-08-22 11:35 ` Daniel P. Berrangé
  15 siblings, 1 reply; 87+ messages in thread
From: Max Reitz @ 2019-08-20 17:59 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 1383 bytes --]

On 14.08.19 22:22, Maxim Levitsky wrote:

[...]

> Testing. This was lightly tested with manual testing and with few iotests that I prepared.
> I haven't yet tested fully the write sharing behavior, nor did I run the whole iotests
> suite to see if this code causes some regressions. Since I will need probably
> to rewrite some chunks of it to change to 'amend' interface, I decided to post it now,
> to see if you have other ideas/comments to add.

I can see that, because half of the qcow2 tests that contain the string
“secret” break:

Failures: 087 134 158 178 188 198 206
Failed 7 of 13 tests

Also, 210 when run with -luks.

Some are just due to different test outputs (because you change
_filter_img_create to filter some encrypt.* parameters), but some of
them are due to aborts.  All of them look like different kinds of heap
corruptions.


I can fully understand not running all iotests (because only the
maintainers do that before pull requests), but just running the iotests
that immediately concern a series seems prudent to me (unless the series
is trivial).

(Just “(cd tests/qemu-iotests && grep -l secret ???)” tells you which
tests to run that may concern themselves with qcow2 encryption, for
example.)


So I suppose I’ll stop reviewing the series in detail and just give a
more cursory glance from now on.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions Maxim Levitsky
@ 2019-08-20 18:01   ` Max Reitz
  2019-08-21 22:43     ` Maxim Levitsky
  2019-08-22 10:38   ` Daniel P. Berrangé
  1 sibling, 1 reply; 87+ messages in thread
From: Max Reitz @ 2019-08-20 18:01 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 1223 bytes --]

On 14.08.19 22:22, Maxim Levitsky wrote:
> With upcoming key management, the header will
> need to be stored after the image is created.
> 
> Extracting load header isn't strictly needed, but
> do this anyway for the symmetry.
> 
> Also I extracted a function that does basic sanity
> checks on the just read header, and a function
> which parses all the crypto format to make the
> code a bit more readable, plus now the code
> doesn't destruct the in-header cipher-mode string,
> so that the header now can be stored many times,
> which is needed for the key management.
> 
> Also this allows to contain the endianess conversions
> in these functions alone
> 
> The header is no longer endian swapped in place,
> to prevent (mostly theoretical races I think)
> races where someone could see the header in the
> process of beeing byteswapped.

The formatting looks weird, it doesn’t look quite 72 characters wide...
 (what commit messages normally use)

> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 756 ++++++++++++++++++++++++++------------------
>  1 file changed, 440 insertions(+), 316 deletions(-)

Also, this commit is just too big.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always Maxim Levitsky
@ 2019-08-20 18:12   ` Max Reitz
  2019-08-21 22:40     ` Maxim Levitsky
  2019-08-22 10:49     ` Daniel P. Berrangé
  2019-08-21 23:01   ` [Qemu-devel] [Qemu-block] " Nir Soffer
  1 sibling, 2 replies; 87+ messages in thread
From: Max Reitz @ 2019-08-20 18:12 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 527 bytes --]

On 14.08.19 22:22, Maxim Levitsky wrote:
> While there are other places where these are still stored in memory,
> this is still one less key material area that can be sniffed with
> various side channel attacks
> 
> 
> 

(Many empty lines here)

> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 44 insertions(+), 8 deletions(-)

Wouldn’t it make sense to introduce a dedicated function for this?

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev) Maxim Levitsky
@ 2019-08-20 18:27   ` Max Reitz
  2019-08-21 22:32     ` Maxim Levitsky
  2019-08-22 11:14     ` Daniel P. Berrangé
  2019-08-21 11:47   ` Markus Armbruster
  1 sibling, 2 replies; 87+ messages in thread
From: Max Reitz @ 2019-08-20 18:27 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 5489 bytes --]

On 14.08.19 22:22, Maxim Levitsky wrote:
> This adds:
> 
> * x-blockdev-update-encryption and x-blockdev-erase-encryption qmp commands
>   Both commands take the QCryptoKeyManageOptions
>   the x-blockdev-update-encryption is meant for non destructive addition
>   of key slots / whatever the encryption driver supports in the future
> 
>   x-blockdev-erase-encryption is meant for destructive encryption key erase,
>   in some cases even without way to recover the data.
> 
> 
> * bdrv_setup_encryption callback in the block driver
>   This callback does both the above functions with 'action' parameter
> 
> * QCryptoKeyManageOptions with set of options that drivers can use for encryption managment
>   Currently it has all the options that LUKS needs, and later it can be extended
>   (via union) to support more encryption drivers if needed
> 
> * blk_setup_encryption / bdrv_setup_encryption - the usual block layer wrappers.
>   Note that bdrv_setup_encryption takes BlockDriverState and not BdrvChild,
>   for the ease of use from the qmp code. It is not expected that this function
>   will be used by anything but qmp and qemu-img code
> 
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  block/block-backend.c          |  9 ++++++++
>  block/io.c                     | 24 ++++++++++++++++++++
>  blockdev.c                     | 40 ++++++++++++++++++++++++++++++++++
>  include/block/block.h          | 12 ++++++++++
>  include/block/block_int.h      | 11 ++++++++++
>  include/sysemu/block-backend.h |  7 ++++++
>  qapi/block-core.json           | 36 ++++++++++++++++++++++++++++++
>  qapi/crypto.json               | 26 ++++++++++++++++++++++
>  8 files changed, 165 insertions(+)

Now I don’t know whether you want to keep this interface at all, because
the cover letter seemed to imply you’d prefer a QMP amend.  But let it
be said that a QMP amend is no trivial task.  I think the most difficult
bit is that the qcow2 implementation currently is inherently an offline
operation.  It isn’t a good idea to use it on a live image.  (Maybe it
works, but it’s definitely not what I had in mind when I wrote it.)

So I’ll still take a quick glance at the interface here.

[...]

> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 0d43d4f37c..53ed411eed 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -5327,3 +5327,39 @@
>    'data' : { 'node-name': 'str',
>               'iothread': 'StrOrNull',
>               '*force': 'bool' } }
> +
> +
> +##
> +# @x-blockdev-update-encryption:
> +#
> +# Update the encryption keys for an encrypted block device
> +#
> +# @node-name: 	  Name of the blockdev to operate on
> +# @force:         Disable safety checks (use with care)
> +# @options:       Driver specific options
> +#
> +
> +# Since: 4.2
> +##
> +{ 'command': 'x-blockdev-update-encryption',
> +  'data': { 'node-name' : 'str',
> +            '*force' : 'bool',
> +            'options': 'QCryptoEncryptionSetupOptions' } }
> +
> +##
> +# @x-blockdev-erase-encryption:
> +#
> +# Erase the encryption keys for an encrypted block device
> +#
> +# @node-name: 	  Name of the blockdev to operate on

Why the tab?

> +# @force:         Disable safety checks (use with care)

I think being a bit more verbose wouldn’t hurt.

(Same above.)

> +# @options:       Driver specific options
> +#
> +# Returns: @QCryptoKeyManageResult
> +#
> +# Since: 4.2
> +##
> +{ 'command': 'x-blockdev-erase-encryption',
> +  'data': { 'node-name' : 'str',
> +            '*force' : 'bool',
> +            'options': 'QCryptoEncryptionSetupOptions' } }
> diff --git a/qapi/crypto.json b/qapi/crypto.json
> index b2a4cff683..69e8b086db 100644
> --- a/qapi/crypto.json
> +++ b/qapi/crypto.json
> @@ -309,3 +309,29 @@
>    'base': 'QCryptoBlockInfoBase',
>    'discriminator': 'format',
>    'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
> +
> +
> +##
> +# @QCryptoEncryptionSetupOptions:
> +#
> +# Driver specific options for encryption key management.

The options do seem LUKS-specific, but the name of this structure does not.

> +# @key-secret: the ID of a QCryptoSecret object providing the password
> +#              to add or to erase (optional for erase)
> +#
> +# @old-key-secret: the ID of a QCryptoSecret object providing the password
> +#                  that can currently unlock the image
> +#
> +# @slot: Key slot to update/erase
> +#        (optional, for update will select a free slot,
> +#        for erase will erase all slots that match the password)
> +#
> +# @iter-time: number of milliseconds to spend in
> +#             PBKDF passphrase processing. Currently defaults to 2000
> +# Since: 4.2
> +##

Does it really make sense to use the same structure for erasing and
updating?  I think there are ways to represent @key-secret vs. @slot
being alternatives to each other for erase; @iter-time doesn’t seem to
make sense for erase; and @slot doesn’t seem to make sense for update.
Also, I don’t know whether to use @key-secret or @old-key-secret for erase.

All in all, it seems more sensible to me to have separate structs for
updating and erasing.

Max

> +{ 'struct': 'QCryptoEncryptionSetupOptions',
> +  'data': { '*key-secret': 'str',
> +            '*old-key-secret': 'str',
> +            '*slot': 'int',
> +            '*iter-time': 'int' } }
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 12/13] qemu-img: implement key management
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 12/13] qemu-img: implement key management Maxim Levitsky
@ 2019-08-20 18:29   ` Max Reitz
  2019-08-21 22:33     ` Maxim Levitsky
  2019-08-22 11:32     ` Daniel P. Berrangé
  0 siblings, 2 replies; 87+ messages in thread
From: Max Reitz @ 2019-08-20 18:29 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 499 bytes --]

On 14.08.19 22:22, Maxim Levitsky wrote:
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  block/crypto.c   |  16 ++++++
>  block/crypto.h   |   3 +
>  qemu-img-cmds.hx |  13 +++++
>  qemu-img.c       | 140 +++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 172 insertions(+)

Yes, this seems a bit weird.  Putting it under amend seems like the
natural thing if that works; if not, I think it should be a single
qemu-img subcommand instead of two.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-19 12:35           ` Maxim Levitsky
@ 2019-08-21 11:31             ` Markus Armbruster
  2019-08-21 13:22               ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Markus Armbruster @ 2019-08-21 11:31 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, Daniel P.Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi

Maxim Levitsky <mlevitsk@redhat.com> writes:

> On Thu, 2019-08-15 at 10:00 -0500, Eric Blake wrote:
>> On 8/15/19 9:44 AM, Maxim Levitsky wrote:
>> 
>> > > > > Does the idea of a union type with a default value for the discriminator
>> > > > > help?  Maybe we have a discriminator which defaults to 'auto', and add a
>> > > > > union branch 'auto':'any'.  During creation, if the "driver":"auto"
>> > > > > branch is selected (usually implicitly by omitting "driver", but also
>> > > > > possible explicitly), the creation attempt is rejected as invalid
>> > > > > regardless of the contents of the remaining 'any'.  But during amend
>> > > > > usage, if the 'auto' branch is selected, we then add in the proper
>> > > > > "driver":"xyz" and reparse the QAPI object to determine if the remaining
>> > > > > fields in 'any' still meet the specification for the required driver branch.
>> > > > > 
>> > > > > This idea may still require some tweaks to the QAPI generator, but it's
>> > > > > the best I can come up with for a way to parse an arbitrary JSON object
>> > > > > with unknown validation, then reparse it again after adding more
>> > > > > information that would constrain the parse differently.
>> > > > 
>> > > > Feels like this would be a lot of code just to allow the client to omit
>> > > > passing a value that it knows anyway. If this were a human interface, I
>> > > > could understand the desire to make commands less verbose, but for QMP I
>> > > > honestly don't see the point when it's not trivial.
>> > > 
>> > > Seconded.
>> > 
>> > 
>> > But what about my suggestion of adding something like:
>> > 
>> > { 'union': 'BlockdevAmendOptions',
>> > 
>> >   'base': {
>> >       'node-name':         'str' },
>> > 
>> >   'discriminator': { 'get_block_driver(node-name)' } ,
>> 
>> Not worth it. It makes the QAPI generator more complex (to invoke
>> arbitrary code instead of a fixed name) just to avoid a little bit of
>> complexity in the caller (which is assumed to be a computer, and thus
>> shouldn't have a hard time providing a sane 'driver' unconditionally).
>> An HMP wrapper around the QMP command can do whatever magic it needs to
>> omit driver, but making driver mandatory for QMP is just fine.
>
> All right! I kind of not agree with that, since I think even though QMP is a machine language,
> it still should be consistent since humans still use it, even if this is humans that code some
> tool that use it.
>
> I won't argue with you though, let it be like that.

Software's fundamental limit is complexity.  We need to pick what we use
it for.  Sometimes, that means saying no to things that would be nice to
have.


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev)
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev) Maxim Levitsky
  2019-08-20 18:27   ` Max Reitz
@ 2019-08-21 11:47   ` Markus Armbruster
  2019-08-21 22:24     ` Maxim Levitsky
  1 sibling, 1 reply; 87+ messages in thread
From: Markus Armbruster @ 2019-08-21 11:47 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi

Maxim Levitsky <mlevitsk@redhat.com> writes:

> This adds:
>
> * x-blockdev-update-encryption and x-blockdev-erase-encryption qmp commands
>   Both commands take the QCryptoKeyManageOptions
>   the x-blockdev-update-encryption is meant for non destructive addition
>   of key slots / whatever the encryption driver supports in the future
>
>   x-blockdev-erase-encryption is meant for destructive encryption key erase,
>   in some cases even without way to recover the data.
>
>
> * bdrv_setup_encryption callback in the block driver
>   This callback does both the above functions with 'action' parameter
>
> * QCryptoKeyManageOptions with set of options that drivers can use for encryption managment
>   Currently it has all the options that LUKS needs, and later it can be extended
>   (via union) to support more encryption drivers if needed
>
> * blk_setup_encryption / bdrv_setup_encryption - the usual block layer wrappers.
>   Note that bdrv_setup_encryption takes BlockDriverState and not BdrvChild,
>   for the ease of use from the qmp code. It is not expected that this function
>   will be used by anything but qmp and qemu-img code
>
>
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
[...]
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 0d43d4f37c..53ed411eed 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -5327,3 +5327,39 @@
>    'data' : { 'node-name': 'str',
>               'iothread': 'StrOrNull',
>               '*force': 'bool' } }
> +
> +
> +##
> +# @x-blockdev-update-encryption:
> +#
> +# Update the encryption keys for an encrypted block device
> +#
> +# @node-name: 	  Name of the blockdev to operate on
> +# @force:         Disable safety checks (use with care)

What checks excactly are disabled?

> +# @options:       Driver specific options
> +#
> +
> +# Since: 4.2
> +##
> +{ 'command': 'x-blockdev-update-encryption',
> +  'data': { 'node-name' : 'str',
> +            '*force' : 'bool',
> +            'options': 'QCryptoEncryptionSetupOptions' } }
> +
> +##
> +# @x-blockdev-erase-encryption:
> +#
> +# Erase the encryption keys for an encrypted block device
> +#
> +# @node-name: 	  Name of the blockdev to operate on
> +# @force:         Disable safety checks (use with care)

Likewise.

> +# @options:       Driver specific options
> +#
> +# Returns: @QCryptoKeyManageResult

Doc comment claims the command returns something, even though it
doesn't.  Please fix.  Sadly, the doc generator fails to flag that.

> +#
> +# Since: 4.2
> +##
> +{ 'command': 'x-blockdev-erase-encryption',
> +  'data': { 'node-name' : 'str',
> +            '*force' : 'bool',
> +            'options': 'QCryptoEncryptionSetupOptions' } }
> diff --git a/qapi/crypto.json b/qapi/crypto.json
> index b2a4cff683..69e8b086db 100644
> --- a/qapi/crypto.json
> +++ b/qapi/crypto.json
> @@ -309,3 +309,29 @@
>    'base': 'QCryptoBlockInfoBase',
>    'discriminator': 'format',
>    'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
> +
> +
> +##
> +# @QCryptoEncryptionSetupOptions:
> +#
> +# Driver specific options for encryption key management.

Specific to which driver?

> +#
> +# @key-secret: the ID of a QCryptoSecret object providing the password
> +#              to add or to erase (optional for erase)
> +#
> +# @old-key-secret: the ID of a QCryptoSecret object providing the password
> +#                  that can currently unlock the image
> +#
> +# @slot: Key slot to update/erase
> +#        (optional, for update will select a free slot,
> +#        for erase will erase all slots that match the password)
> +#
> +# @iter-time: number of milliseconds to spend in
> +#             PBKDF passphrase processing. Currently defaults to 2000
> +# Since: 4.2
> +##
> +{ 'struct': 'QCryptoEncryptionSetupOptions',
> +  'data': { '*key-secret': 'str',
> +            '*old-key-secret': 'str',
> +            '*slot': 'int',
> +            '*iter-time': 'int' } }

The two new commands have identical arguments.  Some of them you factor
out into their own struct.  Can you explain what makes them special?

The extra nesting on the wire is kind of ugly.  We can talk about how to
avoid it once I understand why we want the extra struct.


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-21 11:31             ` Markus Armbruster
@ 2019-08-21 13:22               ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 13:22 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Fam Zheng, Daniel P.Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi

On Wed, 2019-08-21 at 13:31 +0200, Markus Armbruster wrote:
> Maxim Levitsky <mlevitsk@redhat.com> writes:
> 
> > On Thu, 2019-08-15 at 10:00 -0500, Eric Blake wrote:
> > > On 8/15/19 9:44 AM, Maxim Levitsky wrote:
> > > 
> > > > > > > Does the idea of a union type with a default value for the discriminator
> > > > > > > help?  Maybe we have a discriminator which defaults to 'auto', and add a
> > > > > > > union branch 'auto':'any'.  During creation, if the "driver":"auto"
> > > > > > > branch is selected (usually implicitly by omitting "driver", but also
> > > > > > > possible explicitly), the creation attempt is rejected as invalid
> > > > > > > regardless of the contents of the remaining 'any'.  But during amend
> > > > > > > usage, if the 'auto' branch is selected, we then add in the proper
> > > > > > > "driver":"xyz" and reparse the QAPI object to determine if the remaining
> > > > > > > fields in 'any' still meet the specification for the required driver branch.
> > > > > > > 
> > > > > > > This idea may still require some tweaks to the QAPI generator, but it's
> > > > > > > the best I can come up with for a way to parse an arbitrary JSON object
> > > > > > > with unknown validation, then reparse it again after adding more
> > > > > > > information that would constrain the parse differently.
> > > > > > 
> > > > > > Feels like this would be a lot of code just to allow the client to omit
> > > > > > passing a value that it knows anyway. If this were a human interface, I
> > > > > > could understand the desire to make commands less verbose, but for QMP I
> > > > > > honestly don't see the point when it's not trivial.
> > > > > 
> > > > > Seconded.
> > > > 
> > > > 
> > > > But what about my suggestion of adding something like:
> > > > 
> > > > { 'union': 'BlockdevAmendOptions',
> > > > 
> > > >   'base': {
> > > >       'node-name':         'str' },
> > > > 
> > > >   'discriminator': { 'get_block_driver(node-name)' } ,
> > > 
> > > Not worth it. It makes the QAPI generator more complex (to invoke
> > > arbitrary code instead of a fixed name) just to avoid a little bit of
> > > complexity in the caller (which is assumed to be a computer, and thus
> > > shouldn't have a hard time providing a sane 'driver' unconditionally).
> > > An HMP wrapper around the QMP command can do whatever magic it needs to
> > > omit driver, but making driver mandatory for QMP is just fine.
> > 
> > All right! I kind of not agree with that, since I think even though QMP is a machine language,
> > it still should be consistent since humans still use it, even if this is humans that code some
> > tool that use it.
> > 
> > I won't argue with you though, let it be like that.
> 
> Software's fundamental limit is complexity.  We need to pick what we use
> it for.  Sometimes, that means saying no to things that would be nice to
> have.

I fully agree with that and that is usually the exact reason I argue about such things:
Sometimes avoiding complexity in one place, and/or breaking consistency can introduce complexity in other place (like libvirt).

In this particular case, I won't argue about this, but still it kind of feels like
it is a precedent of requiring the user to supply redundant data.

Of all issues all of you pointed out (thanks!!) this is probably the least important one that I should be arguing about, 
so let it be like you say.

Best regards,
	Maxim Levitsky






^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring Maxim Levitsky
  2019-08-20 16:38   ` Max Reitz
@ 2019-08-21 15:39   ` Daniel P. Berrangé
  2019-08-22  0:08     ` Maxim Levitsky
  1 sibling, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-21 15:39 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Wed, Aug 14, 2019 at 11:22:07PM +0300, Maxim Levitsky wrote:
> * rename the write_func to create_write_func,
>   and init_func to create_init_func
>   this is  preparation for other write_func that will
>   be used to update the encryption keys.
> 
> No functional changes
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  block/crypto.c | 15 ++++++++-------
>  1 file changed, 8 insertions(+), 7 deletions(-)
> 
> diff --git a/block/crypto.c b/block/crypto.c
> index 8237424ae6..42a3f0898b 100644
> --- a/block/crypto.c
> +++ b/block/crypto.c
> @@ -51,7 +51,6 @@ static int block_crypto_probe_generic(QCryptoBlockFormat format,
>      }
>  }
>  
> -

Unrelated whitespace change

>  static ssize_t block_crypto_read_func(QCryptoBlock *block,
>                                        size_t offset,
>                                        uint8_t *buf,
> @@ -77,7 +76,7 @@ struct BlockCryptoCreateData {
>  };
>  
>  
> -static ssize_t block_crypto_write_func(QCryptoBlock *block,
> +static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
>                                         size_t offset,
>                                         const uint8_t *buf,
>                                         size_t buflen,

Re-indent.

> @@ -95,8 +94,7 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
>      return ret;
>  }
>  
> -

Unrelated whitespace

> -static ssize_t block_crypto_init_func(QCryptoBlock *block,
> +static ssize_t block_crypto_create_init_func(QCryptoBlock *block,
>                                        size_t headerlen,
>                                        void *opaque,
>                                        Error **errp)

Re-indent.

> @@ -108,7 +106,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
>          return -EFBIG;
>      }
>  
> -    /* User provided size should reflect amount of space made
> +    /*
> +     * User provided size should reflect amount of space made

Unrelated whitespace

>       * available to the guest, so we must take account of that
>       * which will be used by the crypto header
>       */
> @@ -117,6 +116,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
>  }
>  
>  
> +
> +

Unrelated whitespace

>  static QemuOptsList block_crypto_runtime_opts_luks = {
>      .name = "crypto",
>      .head = QTAILQ_HEAD_INITIALIZER(block_crypto_runtime_opts_luks.head),
> @@ -272,8 +273,8 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
>      };
>  
>      crypto = qcrypto_block_create(opts, NULL,
> -                                  block_crypto_init_func,
> -                                  block_crypto_write_func,
> +                                  block_crypto_create_init_func,
> +                                  block_crypto_create_write_func,
>                                    &data,
>                                    errp);

With the whitespace changes removed & indent fixed

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-20 17:59 ` Max Reitz
@ 2019-08-21 22:00   ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 22:00 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Tue, 2019-08-20 at 19:59 +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> 
> [...]
> 
> > Testing. This was lightly tested with manual testing and with few iotests that I prepared.
> > I haven't yet tested fully the write sharing behavior, nor did I run the whole iotests
> > suite to see if this code causes some regressions. Since I will need probably
> > to rewrite some chunks of it to change to 'amend' interface, I decided to post it now,
> > to see if you have other ideas/comments to add.
> 
> I can see that, because half of the qcow2 tests that contain the string
> “secret” break:
> 
> Failures: 087 134 158 178 188 198 206
> Failed 7 of 13 tests
> 
> Also, 210 when run with -luks.
> 
> Some are just due to different test outputs (because you change
> _filter_img_create to filter some encrypt.* parameters), but some of
> them are due to aborts.  All of them look like different kinds of heap
> corruptions.
> 
> 
> I can fully understand not running all iotests (because only the
> maintainers do that before pull requests), but just running the iotests
> that immediately concern a series seems prudent to me (unless the series
> is trivial).
> 
> (Just “(cd tests/qemu-iotests && grep -l secret ???)” tells you which
> tests to run that may concern themselves with qcow2 encryption, for
> example.)
> 
> 
> So I suppose I’ll stop reviewing the series in detail and just give a
> more cursory glance from now on.

Sorry about that! I posted this as RFC, and the reason it is mostly done as opposed to typical RFC which might not
even contain any code was that for most of the time I was sure that API of this is straightforward and won't need
any significant discussion, and in the last minute after I discussed with Kevin on IRC one 
obscure case of block backend permissions that was failing, he told me about the amend interface.
Next time I guess, when new a API is involved, I will post an API RFC first always and then start the implementation.

I fixed both issues that iotests uncovered locally, now all luks and most qcow2 tests pass 
(118 and 194 sometimes fail with qcow2, and this happens regardless of my patches, and same for 162 which seems to fail
always now, also regardless of my patches.
I have a git head after the merge window opened so probably some bugs were added, and maybe already fixed)


The first issue was in 'qcrypto-luks: implement the encryption key management'
where I accidentally stored the name of the secret without strdup'ing in the create flow, so I got double free,
which indeed caused the heap corruptions you have seen.

Basically this line:
    luks->secret = options->u.luks.key_secret;

The second issue as you mention is indeed the change in filters I did. Do you agree with that change btw?
If you ask me, I would even change the filter further and filter all the image options from the qemu command line since these are test inputs anyway.
This allowed me to have the same test for both luks and qcow2 luks encrypted test.

Also I didn't even expect you to run the iotests for me, but
rather just wanted a general RFC level feedback on the whole thing, that is why I even mentioned that I didn't run them.
So sorry for the trouble I caused!

I btw don't agree with you that only maintainers need to run all the iotests fully. 
I think that the patch submitter  should run all the tests that he can to catch as many problems as he can,
_unless_ of course this is an RFC.


Best regards,
Thanks for the review,
Sorry again for the trouble,

Maxim Levitsky





^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev)
  2019-08-21 11:47   ` Markus Armbruster
@ 2019-08-21 22:24     ` Maxim Levitsky
  2019-08-22 14:07       ` Markus Armbruster
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 22:24 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi

On Wed, 2019-08-21 at 13:47 +0200, Markus Armbruster wrote:
> Maxim Levitsky <mlevitsk@redhat.com> writes:
> 
> > This adds:
> > 
> > * x-blockdev-update-encryption and x-blockdev-erase-encryption qmp commands
> >   Both commands take the QCryptoKeyManageOptions
> >   the x-blockdev-update-encryption is meant for non destructive addition
> >   of key slots / whatever the encryption driver supports in the future
> > 
> >   x-blockdev-erase-encryption is meant for destructive encryption key erase,
> >   in some cases even without way to recover the data.
> > 
> > 
> > * bdrv_setup_encryption callback in the block driver
> >   This callback does both the above functions with 'action' parameter
> > 
> > * QCryptoKeyManageOptions with set of options that drivers can use for encryption managment
> >   Currently it has all the options that LUKS needs, and later it can be extended
> >   (via union) to support more encryption drivers if needed
> > 
> > * blk_setup_encryption / bdrv_setup_encryption - the usual block layer wrappers.
> >   Note that bdrv_setup_encryption takes BlockDriverState and not BdrvChild,
> >   for the ease of use from the qmp code. It is not expected that this function
> >   will be used by anything but qmp and qemu-img code
> > 
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> 
> [...]
> > diff --git a/qapi/block-core.json b/qapi/block-core.json
> > index 0d43d4f37c..53ed411eed 100644
> > --- a/qapi/block-core.json
> > +++ b/qapi/block-core.json
> > @@ -5327,3 +5327,39 @@
> >    'data' : { 'node-name': 'str',
> >               'iothread': 'StrOrNull',
> >               '*force': 'bool' } }
> > +
> > +
> > +##
> > +# @x-blockdev-update-encryption:
> > +#
> > +# Update the encryption keys for an encrypted block device
> > +#
> > +# @node-name: 	  Name of the blockdev to operate on
> > +# @force:         Disable safety checks (use with care)
> 
> What checks excactly are disabled?
Ability to overwrite an used slot with a different password. 
If overwrite fails, the image won't be recoverable.

The safe way is to add a new slot, then erase the old
one, but this changes the slot where the password
is stored, unless this procedure is used twice

> 
> > +# @options:       Driver specific options
> > +#
> > +
> > +# Since: 4.2
> > +##
> > +{ 'command': 'x-blockdev-update-encryption',
> > +  'data': { 'node-name' : 'str',
> > +            '*force' : 'bool',
> > +            'options': 'QCryptoEncryptionSetupOptions' } }
> > +
> > +##
> > +# @x-blockdev-erase-encryption:
> > +#
> > +# Erase the encryption keys for an encrypted block device
> > +#
> > +# @node-name: 	  Name of the blockdev to operate on
> > +# @force:         Disable safety checks (use with care)
> 
> Likewise.
1. Erase a slot which is already marked as
erased. Mostly harmless but pointless as well.

2. Erase last keyslot. This irreversibly destroys
any ability to read the data from the device,
unless a backup of the header and the key material is
done prior. Still can be useful when it is desired to
erase the data fast.


> 
> > +# @options:       Driver specific options
> > +#
> > +# Returns: @QCryptoKeyManageResult
> 
> Doc comment claims the command returns something, even though it
> doesn't.  Please fix.  Sadly, the doc generator fails to flag that.
This is leftover, fixed now although most likely this interface will die.
I was initially planning to return
information on which slot was allocated when user left that
decision to the driver.

> 
> > +#
> > +# Since: 4.2
> > +##
> > +{ 'command': 'x-blockdev-erase-encryption',
> > +  'data': { 'node-name' : 'str',
> > +            '*force' : 'bool',
> > +            'options': 'QCryptoEncryptionSetupOptions' } }
> > diff --git a/qapi/crypto.json b/qapi/crypto.json
> > index b2a4cff683..69e8b086db 100644
> > --- a/qapi/crypto.json
> > +++ b/qapi/crypto.json
> > @@ -309,3 +309,29 @@
> >    'base': 'QCryptoBlockInfoBase',
> >    'discriminator': 'format',
> >    'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
> > +
> > +
> > +##
> > +# @QCryptoEncryptionSetupOptions:
> > +#
> > +# Driver specific options for encryption key management.
> 
> Specific to which driver?

This is the same issue, of not beeing able to detect an union.

I was planning to have an union here where we could add
add the driver specific options if we need to have another crypto driver,
however since I discovered that union needs user to pass the driver name,
I just placed it in a struct.

So this struct is supposed to represent driver specific options, but
currently contains only luks options.

> 
> > +#
> > +# @key-secret: the ID of a QCryptoSecret object providing the password
> > +#              to add or to erase (optional for erase)
> > +#
> > +# @old-key-secret: the ID of a QCryptoSecret object providing the password
> > +#                  that can currently unlock the image
> > +#
> > +# @slot: Key slot to update/erase
> > +#        (optional, for update will select a free slot,
> > +#        for erase will erase all slots that match the password)
> > +#
> > +# @iter-time: number of milliseconds to spend in
> > +#             PBKDF passphrase processing. Currently defaults to 2000
> > +# Since: 4.2
> > +##
> > +{ 'struct': 'QCryptoEncryptionSetupOptions',
> > +  'data': { '*key-secret': 'str',
> > +            '*old-key-secret': 'str',
> > +            '*slot': 'int',
> > +            '*iter-time': 'int' } }
> 
> The two new commands have identical arguments.  Some of them you factor
> out into their own struct.  Can you explain what makes them special?


Uniting these means that I need to add some kind of 'action' to the
options, which is kind of adding a subcommand to a qmp command, which is also feels
kind of wrong.

That is why internally this is implemented as one block driver callback,
with action = {erase,update}, but qmp exposes two commands.

I would personally prefer to have that erase field,and I would have to have
it, if I switch to the amend interface.


> 
> The extra nesting on the wire is kind of ugly.  We can talk about how to
> avoid it once I understand why we want the extra struct.
> 
I kind of agree with that but The reason for that is that I designed that interface like that is  to be not specific to luks.

I pass the options structure down the stack till it reaches the luks driver where it can deal with
it. If a new crypto driver is added, all you would have to do is to define new options in the json,
and use them in the new crypto driver. The rest of the code doesn't know what is in that struct.
Kind of the same as done with blockdev-create I guess.

Thanks for the review,
Best regards,
	Maxim Levitsky






^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev)
  2019-08-20 18:27   ` Max Reitz
@ 2019-08-21 22:32     ` Maxim Levitsky
  2019-08-22 11:14     ` Daniel P. Berrangé
  1 sibling, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 22:32 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Tue, 2019-08-20 at 20:27 +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > This adds:
> > 
> > * x-blockdev-update-encryption and x-blockdev-erase-encryption qmp commands
> >   Both commands take the QCryptoKeyManageOptions
> >   the x-blockdev-update-encryption is meant for non destructive addition
> >   of key slots / whatever the encryption driver supports in the future
> > 
> >   x-blockdev-erase-encryption is meant for destructive encryption key erase,
> >   in some cases even without way to recover the data.
> > 
> > 
> > * bdrv_setup_encryption callback in the block driver
> >   This callback does both the above functions with 'action' parameter
> > 
> > * QCryptoKeyManageOptions with set of options that drivers can use for encryption managment
> >   Currently it has all the options that LUKS needs, and later it can be extended
> >   (via union) to support more encryption drivers if needed
> > 
> > * blk_setup_encryption / bdrv_setup_encryption - the usual block layer wrappers.
> >   Note that bdrv_setup_encryption takes BlockDriverState and not BdrvChild,
> >   for the ease of use from the qmp code. It is not expected that this function
> >   will be used by anything but qmp and qemu-img code
> > 
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  block/block-backend.c          |  9 ++++++++
> >  block/io.c                     | 24 ++++++++++++++++++++
> >  blockdev.c                     | 40 ++++++++++++++++++++++++++++++++++
> >  include/block/block.h          | 12 ++++++++++
> >  include/block/block_int.h      | 11 ++++++++++
> >  include/sysemu/block-backend.h |  7 ++++++
> >  qapi/block-core.json           | 36 ++++++++++++++++++++++++++++++
> >  qapi/crypto.json               | 26 ++++++++++++++++++++++
> >  8 files changed, 165 insertions(+)
> 
> Now I don’t know whether you want to keep this interface at all, because
> the cover letter seemed to imply you’d prefer a QMP amend.  But let it
> be said that a QMP amend is no trivial task.  I think the most difficult
> bit is that the qcow2 implementation currently is inherently an offline
> operation.  It isn’t a good idea to use it on a live image.  (Maybe it
> works, but it’s definitely not what I had in mind when I wrote it.)
> 
> So I’ll still take a quick glance at the interface here.
> 
> [...]
> 
> > diff --git a/qapi/block-core.json b/qapi/block-core.json
> > index 0d43d4f37c..53ed411eed 100644
> > --- a/qapi/block-core.json
> > +++ b/qapi/block-core.json
> > @@ -5327,3 +5327,39 @@
> >    'data' : { 'node-name': 'str',
> >               'iothread': 'StrOrNull',
> >               '*force': 'bool' } }
> > +
> > +
> > +##
> > +# @x-blockdev-update-encryption:
> > +#
> > +# Update the encryption keys for an encrypted block device
> > +#
> > +# @node-name: 	  Name of the blockdev to operate on
> > +# @force:         Disable safety checks (use with care)
> > +# @options:       Driver specific options
> > +#
> > +
> > +# Since: 4.2
> > +##
> > +{ 'command': 'x-blockdev-update-encryption',
> > +  'data': { 'node-name' : 'str',
> > +            '*force' : 'bool',
> > +            'options': 'QCryptoEncryptionSetupOptions' } }
> > +
> > +##
> > +# @x-blockdev-erase-encryption:
> > +#
> > +# Erase the encryption keys for an encrypted block device
> > +#
> > +# @node-name: 	  Name of the blockdev to operate on
> 
> Why the tab?
Because checkpatch.pl doesn't warn about this :-)

> 
> > +# @force:         Disable safety checks (use with care)
> 
> I think being a bit more verbose wouldn’t hurt.
> 
> (Same above.)
True about this - this is another reason this is RFC,

I honestly didn't finish the documentation,
since the sudden change to drop all of this interface.


> 
> > +# @options:       Driver specific options
> > +#
> > +# Returns: @QCryptoKeyManageResult
> > +#
> > +# Since: 4.2
> > +##
> > +{ 'command': 'x-blockdev-erase-encryption',
> > +  'data': { 'node-name' : 'str',
> > +            '*force' : 'bool',
> > +            'options': 'QCryptoEncryptionSetupOptions' } }
> > diff --git a/qapi/crypto.json b/qapi/crypto.json
> > index b2a4cff683..69e8b086db 100644
> > --- a/qapi/crypto.json
> > +++ b/qapi/crypto.json
> > @@ -309,3 +309,29 @@
> >    'base': 'QCryptoBlockInfoBase',
> >    'discriminator': 'format',
> >    'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
> > +
> > +
> > +##
> > +# @QCryptoEncryptionSetupOptions:
> > +#
> > +# Driver specific options for encryption key management.
> 
> The options do seem LUKS-specific, but the name of this structure does not.
This is because to be not luks specific we must use some kind of an union
which means that the user has to specify the driver which I didn't want to do.
Now all of you convinced me ( :-) ) to do this so this will be done when I switch
to the amend interface.

> 
> > +# @key-secret: the ID of a QCryptoSecret object providing the password
> > +#              to add or to erase (optional for erase)
> > +#
> > +# @old-key-secret: the ID of a QCryptoSecret object providing the password
> > +#                  that can currently unlock the image
> > +#
> > +# @slot: Key slot to update/erase
> > +#        (optional, for update will select a free slot,
> > +#        for erase will erase all slots that match the password)
> > +#
> > +# @iter-time: number of milliseconds to spend in
> > +#             PBKDF passphrase processing. Currently defaults to 2000
> > +# Since: 4.2
> > +##
> 
> Does it really make sense to use the same structure for erasing and
> updating?  I think there are ways to represent @key-secret vs. @slot
> being alternatives to each other for erase; @iter-time doesn’t seem to
> make sense for erase; and @slot doesn’t seem to make sense for update.
> Also, I don’t know whether to use @key-secret or @old-key-secret for erase.
> 
> All in all, it seems more sensible to me to have separate structs for
> updating and erasing.

The reason for that was to save on code duplication internally.
Internally (as in block device callback, and generic crypto code callback),
both options are folded in one, with 'action' field to distinguish between them
and that structure.
If I use amend interface, I also would have to have some amend option that
will tell to erase the key. It one of the things I wanted to ask you
with that RFC, how would you solve this in single amend interface.

> 
> Max
> 
> > +{ 'struct': 'QCryptoEncryptionSetupOptions',
> > +  'data': { '*key-secret': 'str',
> > +            '*old-key-secret': 'str',
> > +            '*slot': 'int',
> > +            '*iter-time': 'int' } }
> > 
> 
> 

Best regards,
Thanks for the review,
	Maxim Levitsky




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 12/13] qemu-img: implement key management
  2019-08-20 18:29   ` Max Reitz
@ 2019-08-21 22:33     ` Maxim Levitsky
  2019-08-22 11:32     ` Daniel P. Berrangé
  1 sibling, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 22:33 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Tue, 2019-08-20 at 20:29 +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  block/crypto.c   |  16 ++++++
> >  block/crypto.h   |   3 +
> >  qemu-img-cmds.hx |  13 +++++
> >  qemu-img.c       | 140 +++++++++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 172 insertions(+)
> 
> Yes, this seems a bit weird.  Putting it under amend seems like the
> natural thing if that works; if not, I think it should be a single
> qemu-img subcommand instead of two.
> 
> Max
> 
Agree, thats why RFC.

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-20 18:12   ` Max Reitz
@ 2019-08-21 22:40     ` Maxim Levitsky
  2019-08-22 10:49     ` Daniel P. Berrangé
  1 sibling, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 22:40 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Tue, 2019-08-20 at 20:12 +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > While there are other places where these are still stored in memory,
> > this is still one less key material area that can be sniffed with
> > various side channel attacks
> > 
> > 
> > 
> 
> (Many empty lines here)
> 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
> >  1 file changed, 44 insertions(+), 8 deletions(-)
> 
> Wouldn’t it make sense to introduce a dedicated function for this?

Absolutely. I was mostly focused on fixing all the cases first.
I usually refactor such ugly code at the end, but this time I forgot
to do so.

Plus I need to pick a place where to put such function (it can be useful in any place in qemu), 
and first check if maybe glib already has such free+scrub function implemented somewhere.

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-20 18:01   ` Max Reitz
@ 2019-08-21 22:43     ` Maxim Levitsky
  2019-08-22 10:32       ` Daniel P. Berrangé
  2019-08-22 10:34       ` Daniel P. Berrangé
  0 siblings, 2 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 22:43 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Tue, 2019-08-20 at 20:01 +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > With upcoming key management, the header will
> > need to be stored after the image is created.
> > 
> > Extracting load header isn't strictly needed, but
> > do this anyway for the symmetry.
> > 
> > Also I extracted a function that does basic sanity
> > checks on the just read header, and a function
> > which parses all the crypto format to make the
> > code a bit more readable, plus now the code
> > doesn't destruct the in-header cipher-mode string,
> > so that the header now can be stored many times,
> > which is needed for the key management.
> > 
> > Also this allows to contain the endianess conversions
> > in these functions alone
> > 
> > The header is no longer endian swapped in place,
> > to prevent (mostly theoretical races I think)
> > races where someone could see the header in the
> > process of beeing byteswapped.
> 
> The formatting looks weird, it doesn’t look quite 72 characters wide...
>  (what commit messages normally use)
Could you elaborate on that? I thought that code should not
exceed 80 character limit.

> 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 756 ++++++++++++++++++++++++++------------------
> >  1 file changed, 440 insertions(+), 316 deletions(-)
> 
> Also, this commit is just too big.

Yea, but it has no functional changes.
I can split it further, but that won't help much IMHO.

Best regards,
	Maxim Levitsky




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always Maxim Levitsky
  2019-08-20 18:12   ` Max Reitz
@ 2019-08-21 23:01   ` Nir Soffer
  2019-08-21 23:11     ` Maxim Levitsky
  1 sibling, 1 reply; 87+ messages in thread
From: Nir Soffer @ 2019-08-21 23:01 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, QEMU Developers, Markus Armbruster, Stefan Hajnoczi,
	Max Reitz

On Wed, Aug 14, 2019, 23:23 Maxim Levitsky <mlevitsk@redhat.com> wrote:

> While there are other places where these are still stored in memory,
> this is still one less key material area that can be sniffed with
> various side channel attacks
>
>
>
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 44 insertions(+), 8 deletions(-)
>
> diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> index e1a4df94b7..336e633df4 100644
> --- a/crypto/block-luks.c
> +++ b/crypto/block-luks.c
> @@ -1023,8 +1023,18 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
>   cleanup:
>      qcrypto_ivgen_free(ivgen);
>      qcrypto_cipher_free(cipher);
> -    g_free(splitkey);
> -    g_free(possiblekey);
> +
> +    if (splitkey) {
> +        memset(splitkey, 0, splitkeylen);
>

I think we need memset_s() or similar replacement, see

https://www.cryptologie.net/article/419/zeroing-memory-compiler-optimizations-and-memset_s/

+        g_free(splitkey);
> +    }
> +
> +    if (possiblekey) {
> +        memset(possiblekey, 0, masterkeylen(luks));
> +        g_free(possiblekey);
> +
> +    }
> +
>      return ret;
>  }
>
> @@ -1161,16 +1171,34 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>      block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
>      block->payload_offset = luks->header.payload_offset *
> block->sector_size;
>
> -    g_free(masterkey);
> -    g_free(password);
> +    if (masterkey) {
> +        memset(masterkey, 0, masterkeylen(luks));
> +        g_free(masterkey);
> +    }
> +
> +    if (password) {
> +        memset(password, 0, strlen(password));
> +        g_free(password);
> +    }
> +
>      return 0;
>
>   fail:
> -    g_free(masterkey);
> +
> +    if (masterkey) {
> +        memset(masterkey, 0, masterkeylen(luks));
> +        g_free(masterkey);
> +    }
> +
> +    if (password) {
> +        memset(password, 0, strlen(password));
> +        g_free(password);
> +    }
> +
>      qcrypto_block_free_cipher(block);
>      qcrypto_ivgen_free(block->ivgen);
> +
>      g_free(luks);
> -    g_free(password);
>      return ret;
>  }
>
> @@ -1459,7 +1487,10 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>
>      memset(masterkey, 0, luks->header.key_bytes);
>      g_free(masterkey);
> +
> +    memset(password, 0, strlen(password));
>      g_free(password);
> +
>      g_free(cipher_mode_spec);
>
>      return 0;
> @@ -1467,9 +1498,14 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>   error:
>      if (masterkey) {
>          memset(masterkey, 0, luks->header.key_bytes);
> +        g_free(masterkey);
>      }
> -    g_free(masterkey);
> -    g_free(password);
> +
> +    if (password) {
> +        memset(password, 0, strlen(password));
> +        g_free(password);
> +    }
> +
>      g_free(cipher_mode_spec);
>
>      qcrypto_block_free_cipher(block);
> --
> 2.17.2
>
>
>

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-21 23:01   ` [Qemu-devel] [Qemu-block] " Nir Soffer
@ 2019-08-21 23:11     ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 23:11 UTC (permalink / raw)
  To: Nir Soffer
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, QEMU Developers, Markus Armbruster, Stefan Hajnoczi,
	Max Reitz

On Thu, 2019-08-22 at 02:01 +0300, Nir Soffer wrote:
> On Wed, Aug 14, 2019, 23:23 Maxim Levitsky <mlevitsk@redhat.com> wrote:
> 
> > While there are other places where these are still stored in memory,
> > this is still one less key material area that can be sniffed with
> > various side channel attacks
> > 
> > 
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
> >  1 file changed, 44 insertions(+), 8 deletions(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index e1a4df94b7..336e633df4 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -1023,8 +1023,18 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
> >   cleanup:
> >      qcrypto_ivgen_free(ivgen);
> >      qcrypto_cipher_free(cipher);
> > -    g_free(splitkey);
> > -    g_free(possiblekey);
> > +
> > +    if (splitkey) {
> > +        memset(splitkey, 0, splitkeylen);
> > 
> 
> I think we need memset_s() or similar replacement, see
> 
> https://www.cryptologie.net/article/419/zeroing-memory-compiler-optimizations-and-memset_s/

You raise a very very good point here! Thanks!!

Best regards,
	Maxim Levitsky





^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-20 17:36   ` [Qemu-devel] " Max Reitz
@ 2019-08-21 23:59     ` Maxim Levitsky
  2019-08-22 14:32       ` Max Reitz
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-21 23:59 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Tue, 2019-08-20 at 19:36 +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > This is also a preparation for key read/write/erase functions
> > 
> > * use master key len from the header
> > * prefer to use crypto params in the QCryptoBlockLUKS
> >   over passing them as function arguments
> > * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
> > * Add comments to various crypto parameters in the QCryptoBlockLUKS
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 213 ++++++++++++++++++++++----------------------
> >  1 file changed, 105 insertions(+), 108 deletions(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index 409ab50f20..48213abde7 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> 
> [...]
> 
> > @@ -199,13 +201,25 @@ QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592);
> >  struct QCryptoBlockLUKS {
> >      QCryptoBlockLUKSHeader header;
> >  
> > -    /* Cache parsed versions of what's in header fields,
> > -     * as we can't rely on QCryptoBlock.cipher being
> > -     * non-NULL */
> 
> Hm, why remove this comment?

Because the intended uses of these fields changed.
As Daniel explained to me initially none of the crypto parameters
were stored in the QCryptoBlockLUKS, and thus there were all passed
as function arguments.
Later when qemu-img info was added/implemented, there was need to 'cache' them
in the header so that info command could use them after image was opened.

Now after my changes this is no longer true. now these crypto parameters are set early
on create/load and used everywhere to avoid passing them over and over to each
function.

> 
> > +    /* Main encryption algorithm used for encryption*/
> >      QCryptoCipherAlgorithm cipher_alg;
> > +
> > +    /* Mode of encryption for the selected encryption algorithm */
> >      QCryptoCipherMode cipher_mode;
> > +
> > +    /* Initialization vector generation algorithm */
> >      QCryptoIVGenAlgorithm ivgen_alg;
> > +
> > +    /* Hash algorithm used for IV generation*/
> >      QCryptoHashAlgorithm ivgen_hash_alg;
> > +
> > +    /*
> > +     * Encryption algorithm used for IV generation.
> > +     * Usually the same as main encryption algorithm
> > +     */
> > +    QCryptoCipherAlgorithm ivgen_cipher_alg;
> > +
> > +    /* Hash algorithm used in pbkdf2 function */
> >      QCryptoHashAlgorithm hash_alg;
> >  };
> >  
> > @@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> >      }
> >  }
> >  
> > +static int masterkeylen(QCryptoBlockLUKS *luks)
> 
> This should be a const pointer.
I haven't had const in mind while writing this but you are right.
Fixed.


> 
> > +{
> > +    return luks->header.key_bytes;
> > +}
> > +
> > +
> >  /*
> >   * Given a key slot, and user password, this will attempt to unlock
> >   * the master encryption key from the key slot.
> > @@ -410,21 +430,15 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> >   */
> >  static int
> >  qcrypto_block_luks_load_key(QCryptoBlock *block,
> > -                            QCryptoBlockLUKSKeySlot *slot,
> > +                            uint slot_idx,
> 
> Did you use uint on purpose or do you mean a plain “unsigned”?
Well there are just 8 slots, but yea I don't mind to make this an unsigned int.


> 
> >                              const char *password,
> > -                            QCryptoCipherAlgorithm cipheralg,
> > -                            QCryptoCipherMode ciphermode,
> > -                            QCryptoHashAlgorithm hash,
> > -                            QCryptoIVGenAlgorithm ivalg,
> > -                            QCryptoCipherAlgorithm ivcipheralg,
> > -                            QCryptoHashAlgorithm ivhash,
> >                              uint8_t *masterkey,
> > -                            size_t masterkeylen,
> >                              QCryptoBlockReadFunc readfunc,
> >                              void *opaque,
> >                              Error **errp)
> >  {
> >      QCryptoBlockLUKS *luks = block->opaque;
> > +    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
> 
> I think this is a great opportunity to make this a const pointer.
Agree. Done.
> 
> >      uint8_t *splitkey;
> >      size_t splitkeylen;
> >      uint8_t *possiblekey;
> 
> [...]
> 
> > @@ -710,6 +696,8 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >          goto fail;
> >      }
> >  
> > +    cipher_mode = g_strdup(luks->header.cipher_mode);
> > +
> 
> This should be freed under the fail label.
> 
> (And maybe the fact that this no longer modifies
> luks->header.cipher_mode should be mentioned in the commit message, I
> don’t know.)

I swear I documented that in the commit message... yea in the next commit (:-()
Fixed that now.

> 
> >      /*
> >       * The cipher_mode header contains a string that we have
> >       * to further parse, of the format
> 
> [...]
> 
> > @@ -730,13 +718,13 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >  
> >      ivhash_name = strchr(ivgen_name, ':');
> >      if (!ivhash_name) {
> > -        ivhash = 0;
> > +        luks->ivgen_hash_alg = 0;
> 
> *luks is initialized to 0 anyway, but it doesn’t hurt, of course.
> 
> >      } else {
> >          *ivhash_name = '\0';
> >          ivhash_name++;
> >  
> > -        ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name,
> > -                                                     &local_err);
> > +        luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
> > +                                                                   &local_err);
> >          if (local_err) {
> >              ret = -ENOTSUP;
> >              error_propagate(errp, local_err);
> > @@ -744,25 +732,27 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> 
> [...]
> 
> >  
> > -    hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
> > +    luks->hash_alg =
> > +            qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
> >                                                 &local_err);
> 
> Indentation is off now.
True! But at least as you probably noticed I now am aware of that rule, and
so I have way less such errors now :-)

> 
> >      if (local_err) {
> >          ret = -ENOTSUP;
> 
> [...]
> 
> > @@ -930,6 +922,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >              luks_opts.has_ivgen_hash_alg = true;
> >          }
> >      }
> > +
> > +    luks = g_new0(QCryptoBlockLUKS, 1);
> > +    block->opaque = luks;
> > +
> > +    luks->cipher_alg = luks_opts.cipher_alg;
> > +    luks->cipher_mode = luks_opts.cipher_mode;
> > +    luks->ivgen_alg = luks_opts.ivgen_alg;
> > +    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
> > +    luks->hash_alg = luks_opts.hash_alg;
> > +
> > +
> 
> Why did you pull this up?  Now @luks is leaked in both of the next error
> paths.

This is because the purpose of these fields changed. As Daniel explained to me
they were initially added after the fact to serve as a cache of into to present in qemu-img info callback.
But now I use these everywhere in the code so I won't them to be correct as soon as possible to minimize
the risk of calling some function that uses these and would read garbage.

Leak is fixed now, thanks!


> 
> >      /* Note we're allowing ivgen_hash_alg to be set even for
> >       * non-essiv iv generators that don't need a hash. It will
> >       * be silently ignored, for compatibility with dm-crypt */
> 
> [...]
> 
> > @@ -1003,6 +1004,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >          ivcipheralg = luks_opts.cipher_alg;
> >      }
> >  
> > +    luks->ivgen_cipher_alg = ivcipheralg;
> > +
> 
> What’s the point in having a dedicated ivcipheralg variable then?


Leftover, since I added the luks->ivgen_cipher_alg.

The luks->ivgen_cipher_alg is special in the sense that it is not
part of the parsed on-disk header, but rather calculated,
as a workaround for the dm-crypto 'bug'

The long explanation of this is in comment of qcrypto_block_luks_essiv_cipher

I'll remove that local variable, because why not.

> 
> Max
> 
> >      strcpy(luks->header.cipher_name, cipher_alg);
> >      strcpy(luks->header.cipher_mode, cipher_mode_spec);
> >      strcpy(luks->header.hash_spec, hash_alg);
> 
> 


Best regards,
Thanks for the review,
	Maxim Levitsky




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring
  2019-08-20 16:38   ` Max Reitz
@ 2019-08-22  0:05     ` Maxim Levitsky
  2019-08-22 14:34       ` Max Reitz
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22  0:05 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Tue, 2019-08-20 at 18:38 +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > * rename the write_func to create_write_func,
> >   and init_func to create_init_func
> >   this is  preparation for other write_func that will
> >   be used to update the encryption keys.
> > 
> > No functional changes
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  block/crypto.c | 15 ++++++++-------
> >  1 file changed, 8 insertions(+), 7 deletions(-)
> > 
> 
> I’m not quite sure why you remove or add blank lines seemingly at random...

Basically to have consistent two space separation between all functions.
A bit of OCD I confess :-)

> 
> > diff --git a/block/crypto.c b/block/crypto.c
> > index 8237424ae6..42a3f0898b 100644
> > --- a/block/crypto.c
> > +++ b/block/crypto.c
> 
> [...]
> 
> > @@ -77,7 +76,7 @@ struct BlockCryptoCreateData {
> >  };
> >  
> >  
> > -static ssize_t block_crypto_write_func(QCryptoBlock *block,
> > +static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
> >                                         size_t offset,
> >                                         const uint8_t *buf,
> >                                         size_t buflen,
> 
> Alignment should be kept at the opening parentheses.
Opps. I am still trying to learn that rule. Fixed.

> 
> But other than those two things, why not.
> 
> Max
> 

Best regards,
	Thanks for the review
		Maxim Levitsky




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring
  2019-08-21 15:39   ` Daniel P. Berrangé
@ 2019-08-22  0:08     ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22  0:08 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Max Reitz, Stefan Hajnoczi

On Wed, 2019-08-21 at 16:39 +0100, Daniel P. Berrangé wrote:
> On Wed, Aug 14, 2019 at 11:22:07PM +0300, Maxim Levitsky wrote:
> > * rename the write_func to create_write_func,
> >   and init_func to create_init_func
> >   this is  preparation for other write_func that will
> >   be used to update the encryption keys.
> > 
> > No functional changes
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  block/crypto.c | 15 ++++++++-------
> >  1 file changed, 8 insertions(+), 7 deletions(-)
> > 
> > diff --git a/block/crypto.c b/block/crypto.c
> > index 8237424ae6..42a3f0898b 100644
> > --- a/block/crypto.c
> > +++ b/block/crypto.c
> > @@ -51,7 +51,6 @@ static int block_crypto_probe_generic(QCryptoBlockFormat format,
> >      }
> >  }
> >  
> > -
> 
> Unrelated whitespace change
> 
> >  static ssize_t block_crypto_read_func(QCryptoBlock *block,
> >                                        size_t offset,
> >                                        uint8_t *buf,
> > @@ -77,7 +76,7 @@ struct BlockCryptoCreateData {
> >  };
> >  
> >  
> > -static ssize_t block_crypto_write_func(QCryptoBlock *block,
> > +static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
> >                                         size_t offset,
> >                                         const uint8_t *buf,
> >                                         size_t buflen,
> 
> Re-indent.
> 
> > @@ -95,8 +94,7 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
> >      return ret;
> >  }
> >  
> > -
> 
> Unrelated whitespace
> 
> > -static ssize_t block_crypto_init_func(QCryptoBlock *block,
> > +static ssize_t block_crypto_create_init_func(QCryptoBlock *block,
> >                                        size_t headerlen,
> >                                        void *opaque,
> >                                        Error **errp)
> 
> Re-indent.
> 
> > @@ -108,7 +106,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
> >          return -EFBIG;
> >      }
> >  
> > -    /* User provided size should reflect amount of space made
> > +    /*
> > +     * User provided size should reflect amount of space made
> 
> Unrelated whitespace
> 
> >       * available to the guest, so we must take account of that
> >       * which will be used by the crypto header
> >       */
> > @@ -117,6 +116,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
> >  }
> >  
> >  
> > +
> > +
> 
> Unrelated whitespace
> 
> >  static QemuOptsList block_crypto_runtime_opts_luks = {
> >      .name = "crypto",
> >      .head = QTAILQ_HEAD_INITIALIZER(block_crypto_runtime_opts_luks.head),
> > @@ -272,8 +273,8 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
> >      };
> >  
> >      crypto = qcrypto_block_create(opts, NULL,
> > -                                  block_crypto_init_func,
> > -                                  block_crypto_write_func,
> > +                                  block_crypto_create_init_func,
> > +                                  block_crypto_create_write_func,
> >                                    &data,
> >                                    errp);
> 
> With the whitespace changes removed & indent fixed
> 
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> 
> 
> Regards,
> Daniel

Thanks you!

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-15 21:40   ` [Qemu-devel] [Qemu-block] " John Snow
  2019-08-19 14:21     ` Maxim Levitsky
@ 2019-08-22 10:29     ` Daniel P. Berrangé
  2019-08-22 11:04       ` Maxim Levitsky
  1 sibling, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 10:29 UTC (permalink / raw)
  To: John Snow
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi, Maxim Levitsky

On Thu, Aug 15, 2019 at 05:40:11PM -0400, John Snow wrote:
> 
> 
> On 8/14/19 4:22 PM, Maxim Levitsky wrote:
> > This is also a preparation for key read/write/erase functions
> > 
> 
> This is a matter of taste and I am not usually reviewing LUKS patches
> (So don't take me too seriously), but I would prefer not to have "misc"
> patches and instead split things out by individual changes along with a
> nice commit message for each change.
> 
> > * use master key len from the header
> 
> This touches enough lines that you could make it its own patch, I think.
> 
> > * prefer to use crypto params in the QCryptoBlockLUKS
> >   over passing them as function arguments
> 
> I think the same is true here, and highlighting which variables you are
> sticking into state instead of leaving as functional parameters would be
> nice to see without all the other changes.
> 
> > * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
> 
> This can likely be squashed with whichever patch of yours first needs to
> use it, because it's so short.
> 
> > * Add comments to various crypto parameters in the QCryptoBlockLUKS
> > 
> 
> Can probably be squashed with item #2.

Agreed, with all these points  - it is too hard to review this
for correctness with everything merged in one commit, so I'll
wait for v2 before reviewing much more.

> > @@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> >      }
> >  }
> >  
> > +static int masterkeylen(QCryptoBlockLUKS *luks)
> > +{
> > +    return luks->header.key_bytes;
> > +}
> > +
> > +
> 
> generally QEMU uses snake_case_names; please spell as "master_key_len".

Also naming convention in this file expects "qcrypto_block_luks_" prefix
for all methods


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-21 22:43     ` Maxim Levitsky
@ 2019-08-22 10:32       ` Daniel P. Berrangé
  2019-08-22 10:57         ` Maxim Levitsky
  2019-08-22 10:34       ` Daniel P. Berrangé
  1 sibling, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 10:32 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Stefan Hajnoczi, Max Reitz

On Thu, Aug 22, 2019 at 01:43:05AM +0300, Maxim Levitsky wrote:
> On Tue, 2019-08-20 at 20:01 +0200, Max Reitz wrote:
> > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > With upcoming key management, the header will
> > > need to be stored after the image is created.
> > > 
> > > Extracting load header isn't strictly needed, but
> > > do this anyway for the symmetry.
> > > 
> > > Also I extracted a function that does basic sanity
> > > checks on the just read header, and a function
> > > which parses all the crypto format to make the
> > > code a bit more readable, plus now the code
> > > doesn't destruct the in-header cipher-mode string,
> > > so that the header now can be stored many times,
> > > which is needed for the key management.
> > > 
> > > Also this allows to contain the endianess conversions
> > > in these functions alone
> > > 
> > > The header is no longer endian swapped in place,
> > > to prevent (mostly theoretical races I think)
> > > races where someone could see the header in the
> > > process of beeing byteswapped.
> > 
> > The formatting looks weird, it doesn’t look quite 72 characters wide...
> >  (what commit messages normally use)
> Could you elaborate on that? I thought that code should not
> exceed 80 character limit.

Code itself should be wrapped at 80 chars, but it is common for
commit messages to use 72 chars wrapping.


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-21 22:43     ` Maxim Levitsky
  2019-08-22 10:32       ` Daniel P. Berrangé
@ 2019-08-22 10:34       ` Daniel P. Berrangé
  2019-08-25 14:11         ` Maxim Levitsky
  1 sibling, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 10:34 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Stefan Hajnoczi, Max Reitz

On Thu, Aug 22, 2019 at 01:43:05AM +0300, Maxim Levitsky wrote:
> On Tue, 2019-08-20 at 20:01 +0200, Max Reitz wrote:
> > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > With upcoming key management, the header will
> > > need to be stored after the image is created.
> > > 
> > > Extracting load header isn't strictly needed, but
> > > do this anyway for the symmetry.
> > > 
> > > Also I extracted a function that does basic sanity
> > > checks on the just read header, and a function
> > > which parses all the crypto format to make the
> > > code a bit more readable, plus now the code
> > > doesn't destruct the in-header cipher-mode string,
> > > so that the header now can be stored many times,
> > > which is needed for the key management.
> > > 
> > > Also this allows to contain the endianess conversions
> > > in these functions alone
> > > 
> > > The header is no longer endian swapped in place,
> > > to prevent (mostly theoretical races I think)
> > > races where someone could see the header in the
> > > process of beeing byteswapped.
> > 
> > The formatting looks weird, it doesn’t look quite 72 characters wide...
> >  (what commit messages normally use)
> Could you elaborate on that? I thought that code should not
> exceed 80 character limit.
> 
> > 
> > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > ---
> > >  crypto/block-luks.c | 756 ++++++++++++++++++++++++++------------------
> > >  1 file changed, 440 insertions(+), 316 deletions(-)
> > 
> > Also, this commit is just too big.
> 
> Yea, but it has no functional changes.
> I can split it further, but that won't help much IMHO.

I'd find it easier to review if each newly introduced method was a
separate patch. It makes it easier to see which bit of removed
code was added to which method.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions Maxim Levitsky
  2019-08-20 18:01   ` Max Reitz
@ 2019-08-22 10:38   ` Daniel P. Berrangé
  2019-08-25 14:09     ` Maxim Levitsky
  1 sibling, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 10:38 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Wed, Aug 14, 2019 at 11:22:09PM +0300, Maxim Levitsky wrote:
> With upcoming key management, the header will
> need to be stored after the image is created.
> 
> Extracting load header isn't strictly needed, but
> do this anyway for the symmetry.
> 
> Also I extracted a function that does basic sanity
> checks on the just read header, and a function
> which parses all the crypto format to make the
> code a bit more readable, plus now the code
> doesn't destruct the in-header cipher-mode string,
> so that the header now can be stored many times,
> which is needed for the key management.
> 
> Also this allows to contain the endianess conversions
> in these functions alone
> 
> The header is no longer endian swapped in place,
> to prevent (mostly theoretical races I think)
> races where someone could see the header in the
> process of beeing byteswapped.
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 756 ++++++++++++++++++++++++++------------------
>  1 file changed, 440 insertions(+), 316 deletions(-)

>      if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
>          /* Try to find which key slot our password is valid for
>           * and unlock the master key from that slot.
>           */
> -
>          masterkey = g_new0(uint8_t, masterkeylen(luks));
>  
>          if (qcrypto_block_luks_find_key(block,
> @@ -845,12 +1132,10 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>      }
>  
>      block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> -    block->payload_offset = luks->header.payload_offset *
> -        block->sector_size;
> +    block->payload_offset = luks->header.payload_offset * block->sector_size;
>  
>      g_free(masterkey);
>      g_free(password);
> -
>      return 0;

Smoe unrelated whitespace changes here.


> +    /* populate the slot 0 with the password encrypted master key*/
> +    /* This will also store the header */
> +    if (qcrypto_block_luks_store_key(block,
> +                                     0,
> +                                     password,
> +                                     masterkey,
> +                                     luks_opts.iter_time,
> +                                     writefunc,
> +                                     opaque,
> +                                     errp)) {
>          goto error;
> -    }
> +     }

Indent is off by 1


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 04/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 04/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations Maxim Levitsky
@ 2019-08-22 10:47   ` Daniel P. Berrangé
  2019-08-25 14:30     ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 10:47 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Wed, Aug 14, 2019 at 11:22:10PM +0300, Maxim Levitsky wrote:
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 64 +++++++++++++++++++++++++++------------------
>  1 file changed, 38 insertions(+), 26 deletions(-)
> 
> diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> index 6bb369f3b4..e1a4df94b7 100644
> --- a/crypto/block-luks.c
> +++ b/crypto/block-luks.c
> @@ -417,6 +417,33 @@ static int masterkeylen(QCryptoBlockLUKS *luks)
>  }
>  
>  
> +/*
> + * Returns number of sectors needed to store the key material
> + * given number of anti forensic stripes
> + */
> +static int splitkeylen_sectors(QCryptoBlockLUKS *luks, int stripes)

Needs a qcrypto_block_luks_ prefix on method name.

I'd also put 'static int' on a separate line from method name
to reduce too long lines.

> +
> +{
> +    /*
> +     * This calculation doesn't match that shown in the spec,
> +     * but instead follows the cryptsetup implementation.
> +     */
> +
> +    size_t header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> +                     QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;

Following line indent should only be 4 spaces

> +
> +    size_t splitkeylen = masterkeylen(luks) * stripes;
> +
> +    /* First align the key material size to block size*/
> +    size_t splitkeylen_sectors =
> +            DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE);

Again 4 space indent.

> +
> +    /* Then also align the key material size to the size of the header */
> +    return ROUND_UP(splitkeylen_sectors, header_sectors);
> +}
> +
> +
> +
>  /*
>   * Stores the main LUKS header, taking care of endianess
>   */
> @@ -1169,7 +1196,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>      QCryptoBlockCreateOptionsLUKS luks_opts;
>      Error *local_err = NULL;
>      uint8_t *masterkey = NULL;
> -    size_t splitkeylen = 0;
> +    size_t next_sector;
>      size_t i;
>      char *password;
>      const char *cipher_alg;
> @@ -1388,23 +1415,16 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>          goto error;
>      }
>  
> +    /* start with the sector that follows the header*/
> +    next_sector = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> +                  QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;

I'd suggest 'post_header_sector'

>  
> -    /* Although LUKS has multiple key slots, we're just going
> -     * to use the first key slot */
> -    splitkeylen = luks->header.key_bytes * QCRYPTO_BLOCK_LUKS_STRIPES;
>      for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> -        luks->header.key_slots[i].active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
> -        luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
> -
> -        /* This calculation doesn't match that shown in the spec,
> -         * but instead follows the cryptsetup implementation.
> -         */
> -        luks->header.key_slots[i].key_offset =
> -            (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> -             QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
> -            (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
> -                      (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> -                       QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
> +        QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[i];
> +        slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
> +        slot->key_offset = next_sector;
> +        slot->stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
> +        next_sector += splitkeylen_sectors(luks, QCRYPTO_BLOCK_LUKS_STRIPES);

I'm not a fan of the next_sector accumulator .

I'd prefer to see the '* i' part done in splitkeylen_sectors, so that
we have

  slot->key_offset = post_header_sector +
        splitkeylen_sectors(luks, QCRYPTO_BLOCK_LUKS_STRIPES, i);

> @@ -1412,17 +1432,9 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>       * slot headers, rounded up to the nearest sector, combined with
>       * the size of each master key material region, also rounded up
>       * to the nearest sector */
> -    luks->header.payload_offset =
> -        (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> -         QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
> -        (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
> -                  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> -                   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) *
> -         QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
> -
> +    luks->header.payload_offset = next_sector;

  luks->header.payload_offset = post_header_sector +
        splitkeylen_sectors(luks, QCRYPTO_BLOCK_LUKS_STRIPES,
	                    QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);


>      block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> -    block->payload_offset = luks->header.payload_offset *
> -        block->sector_size;
> +    block->payload_offset = luks->header.payload_offset * block->sector_size;


This is reverting a whitspace change done in previous method

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-20 18:12   ` Max Reitz
  2019-08-21 22:40     ` Maxim Levitsky
@ 2019-08-22 10:49     ` Daniel P. Berrangé
  2019-08-22 10:56       ` Maxim Levitsky
  1 sibling, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 10:49 UTC (permalink / raw)
  To: Max Reitz
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Stefan Hajnoczi, Maxim Levitsky

On Tue, Aug 20, 2019 at 08:12:51PM +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > While there are other places where these are still stored in memory,
> > this is still one less key material area that can be sniffed with
> > various side channel attacks
> > 
> > 
> > 
> 
> (Many empty lines here)
> 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
> >  1 file changed, 44 insertions(+), 8 deletions(-)
> 
> Wouldn’t it make sense to introduce a dedicated function for this?

Yes, it would.

In fact I have a series pending which bumps min glib and introduces
use of auto-free functions in this code.

It would be desirable to have a autp-free func for memset+free
so we can just declare the variable

   q_autowipefree char *password = NULL;

and have it result in memset+free

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-22 10:49     ` Daniel P. Berrangé
@ 2019-08-22 10:56       ` Maxim Levitsky
  2019-08-25 15:31         ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22 10:56 UTC (permalink / raw)
  To: Daniel P. Berrangé, Max Reitz
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Stefan Hajnoczi

On Thu, 2019-08-22 at 11:49 +0100, Daniel P. Berrangé wrote:
> On Tue, Aug 20, 2019 at 08:12:51PM +0200, Max Reitz wrote:
> > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > While there are other places where these are still stored in memory,
> > > this is still one less key material area that can be sniffed with
> > > various side channel attacks
> > > 
> > > 
> > > 
> > 
> > (Many empty lines here)
> > 
> > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > ---
> > >  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
> > >  1 file changed, 44 insertions(+), 8 deletions(-)
> > 
> > Wouldn’t it make sense to introduce a dedicated function for this?
> 
> Yes, it would.
> 
> In fact I have a series pending which bumps min glib and introduces
> use of auto-free functions in this code.
> 
> It would be desirable to have a autp-free func for memset+free
> so we can just declare the variable
> 
>    q_autowipefree char *password = NULL;
> 
> and have it result in memset+free
> 

That is perfect.
When do you think you could post the series so that I could rebase
on top of it?

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-22 10:32       ` Daniel P. Berrangé
@ 2019-08-22 10:57         ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22 10:57 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Stefan Hajnoczi, Max Reitz

On Thu, 2019-08-22 at 11:32 +0100, Daniel P. Berrangé wrote:
> On Thu, Aug 22, 2019 at 01:43:05AM +0300, Maxim Levitsky wrote:
> > On Tue, 2019-08-20 at 20:01 +0200, Max Reitz wrote:
> > > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > > With upcoming key management, the header will
> > > > need to be stored after the image is created.
> > > > 
> > > > Extracting load header isn't strictly needed, but
> > > > do this anyway for the symmetry.
> > > > 
> > > > Also I extracted a function that does basic sanity
> > > > checks on the just read header, and a function
> > > > which parses all the crypto format to make the
> > > > code a bit more readable, plus now the code
> > > > doesn't destruct the in-header cipher-mode string,
> > > > so that the header now can be stored many times,
> > > > which is needed for the key management.
> > > > 
> > > > Also this allows to contain the endianess conversions
> > > > in these functions alone
> > > > 
> > > > The header is no longer endian swapped in place,
> > > > to prevent (mostly theoretical races I think)
> > > > races where someone could see the header in the
> > > > process of beeing byteswapped.
> > > 
> > > The formatting looks weird, it doesn’t look quite 72 characters wide...
> > >  (what commit messages normally use)
> > 
> > Could you elaborate on that? I thought that code should not
> > exceed 80 character limit.
> 
> Code itself should be wrapped at 80 chars, but it is common for
> commit messages to use 72 chars wrapping.
> 
> 
> Regards,
> Daniel
All right, will do this.
Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-22 10:29     ` Daniel P. Berrangé
@ 2019-08-22 11:04       ` Maxim Levitsky
  2019-08-22 11:10         ` Daniel P. Berrangé
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22 11:04 UTC (permalink / raw)
  To: Daniel P. Berrangé, John Snow
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 11:29 +0100, Daniel P. Berrangé wrote:
> On Thu, Aug 15, 2019 at 05:40:11PM -0400, John Snow wrote:
> > 
> > 
> > On 8/14/19 4:22 PM, Maxim Levitsky wrote:
> > > This is also a preparation for key read/write/erase functions
> > > 
> > 
> > This is a matter of taste and I am not usually reviewing LUKS patches
> > (So don't take me too seriously), but I would prefer not to have "misc"
> > patches and instead split things out by individual changes along with a
> > nice commit message for each change.
> > 
> > > * use master key len from the header
> > 
> > This touches enough lines that you could make it its own patch, I think.
> > 
> > > * prefer to use crypto params in the QCryptoBlockLUKS
> > >   over passing them as function arguments
> > 
> > I think the same is true here, and highlighting which variables you are
> > sticking into state instead of leaving as functional parameters would be
> > nice to see without all the other changes.
> > 
> > > * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
> > 
> > This can likely be squashed with whichever patch of yours first needs to
> > use it, because it's so short.
> > 
> > > * Add comments to various crypto parameters in the QCryptoBlockLUKS
> > > 
> > 
> > Can probably be squashed with item #2.
> 
> Agreed, with all these points  - it is too hard to review this
> for correctness with everything merged in one commit, so I'll
> wait for v2 before reviewing much more.
> 
> > > @@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> > >      }
> > >  }
> > >  
> > > +static int masterkeylen(QCryptoBlockLUKS *luks)
> > > +{
> > > +    return luks->header.key_bytes;
> > > +}
> > > +
> > > +
> > 
> > generally QEMU uses snake_case_names; please spell as "master_key_len".
> 
> Also naming convention in this file expects "qcrypto_block_luks_" prefix
> for all methods
> 
The point of adding the masterkeylen was that IMHO it wasn't clear that
luks->header.key_bytes is the master key length.

However defining something like qcrypto_block_luks_master_key_len(QCryptoBlockLUKS *luks) 
is way longer, and will force me to wrap just too many places in the code to keep 80
character limit.

Now I am thinking of other ways to make this thing better:

1. How about adding luks->masterkeylen and using it. luks state already has
several parsed values from the header, so using another one wouldn't hurt?


2. how about renaming the luks->header.key_bytes to luks->header->master_key_len?

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking Maxim Levitsky
@ 2019-08-22 11:04   ` Daniel P. Berrangé
  2019-08-25 15:40     ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:04 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Wed, Aug 14, 2019 at 11:22:12PM +0300, Maxim Levitsky wrote:
> Check that keyslots don't overlap with the data,
> and check that keyslots don't overlap with each other.
> (this is done using naive O(n^2) nested loops,
> but since there are just 8 keyslots, this doens't really matter.
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
> 
> diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> index 336e633df4..1997e92fe1 100644
> --- a/crypto/block-luks.c
> +++ b/crypto/block-luks.c
> @@ -551,6 +551,8 @@ static int
>  qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
>  {
>      int ret;
> +    int i, j;
> +
>  
>      if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
>                 QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
> @@ -566,6 +568,46 @@ qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
>          goto fail;
>      }
>  
> +    /* Check all keyslots for corruption  */
> +    for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
> +
> +        QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
> +        uint start1 = slot1->key_offset;
> +        uint len1 = splitkeylen_sectors(luks, slot1->stripes);

Using 'uint' is not normal QEMU style.

Either use 'unsigned int'  or if a specific size is needed
then one of the 'guintNN' types from glib.

This applies elsewhere in this patch series too, but
I'll only comment here & let you find the other cases.

> +
> +        if (slot1->stripes == 0 ||
> +                (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
> +                slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED)) {
> +

Redundant blank line

> +            error_setg(errp, "Keyslot %i is corrupted", i);

I'd do a separate check for stripes and active fields, and then give a
specific error message for each. That way if this does ever trigger
in practice will immediately understand which check failed.

Also using '%d' rather than '%i' is more common convention


> +            ret = -EINVAL;
> +            goto fail;
> +        }
> +
> +        if (start1 + len1 > luks->header.payload_offset) {
> +            error_setg(errp,
> +                       "Keyslot %i is overlapping with the encrypted payload",
> +                       i);
> +            ret = -EINVAL;
> +            goto fail;
> +        }
> +
> +        for (j = i + 1 ; j < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; j++) {
> +

Redundant blank

> +            QCryptoBlockLUKSKeySlot *slot2 = &luks->header.key_slots[j];
> +            uint start2 = slot2->key_offset;
> +            uint len2 = splitkeylen_sectors(luks, slot2->stripes);
> +
> +            if (start1 + len1 > start2 && start2 + len2 > start1) {
> +                error_setg(errp,
> +                           "Keyslots %i and %i are overlapping in the header",

%d

> +                           i, j);
> +                ret = -EINVAL;
> +                goto fail;
> +            }
> +        }
> +
> +    }
>      return 0;
>  fail:
>      return ret;
> -- 
> 2.17.2
> 

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-22 11:04       ` Maxim Levitsky
@ 2019-08-22 11:10         ` Daniel P. Berrangé
  2019-08-22 11:13           ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:10 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Stefan Hajnoczi, Max Reitz, John Snow

On Thu, Aug 22, 2019 at 02:04:28PM +0300, Maxim Levitsky wrote:
> On Thu, 2019-08-22 at 11:29 +0100, Daniel P. Berrangé wrote:
> > On Thu, Aug 15, 2019 at 05:40:11PM -0400, John Snow wrote:
> > > 
> > > 
> > > On 8/14/19 4:22 PM, Maxim Levitsky wrote:
> > > > This is also a preparation for key read/write/erase functions
> > > > 
> > > 
> > > This is a matter of taste and I am not usually reviewing LUKS patches
> > > (So don't take me too seriously), but I would prefer not to have "misc"
> > > patches and instead split things out by individual changes along with a
> > > nice commit message for each change.
> > > 
> > > > * use master key len from the header
> > > 
> > > This touches enough lines that you could make it its own patch, I think.
> > > 
> > > > * prefer to use crypto params in the QCryptoBlockLUKS
> > > >   over passing them as function arguments
> > > 
> > > I think the same is true here, and highlighting which variables you are
> > > sticking into state instead of leaving as functional parameters would be
> > > nice to see without all the other changes.
> > > 
> > > > * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
> > > 
> > > This can likely be squashed with whichever patch of yours first needs to
> > > use it, because it's so short.
> > > 
> > > > * Add comments to various crypto parameters in the QCryptoBlockLUKS
> > > > 
> > > 
> > > Can probably be squashed with item #2.
> > 
> > Agreed, with all these points  - it is too hard to review this
> > for correctness with everything merged in one commit, so I'll
> > wait for v2 before reviewing much more.
> > 
> > > > @@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> > > >      }
> > > >  }
> > > >  
> > > > +static int masterkeylen(QCryptoBlockLUKS *luks)
> > > > +{
> > > > +    return luks->header.key_bytes;
> > > > +}
> > > > +
> > > > +
> > > 
> > > generally QEMU uses snake_case_names; please spell as "master_key_len".
> > 
> > Also naming convention in this file expects "qcrypto_block_luks_" prefix
> > for all methods
> > 
> The point of adding the masterkeylen was that IMHO it wasn't clear that
> luks->header.key_bytes is the master key length.
> 
> However defining something like qcrypto_block_luks_master_key_len(QCryptoBlockLUKS *luks) 
> is way longer, and will force me to wrap just too many places in the code to keep 80
> character limit.
> 
> Now I am thinking of other ways to make this thing better:
> 
> 1. How about adding luks->masterkeylen and using it. luks state already has
> several parsed values from the header, so using another one wouldn't hurt?

With those the parsed values are actually a different format from the
header values, so it makes sense to have duplication.  Duplication
just for sake of having a different name will just be confusing
with some code using one field & some code using the other field
when they are identical.

> 2. how about renaming the luks->header.key_bytes to luks->header->master_key_len?

This is fine.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-22 11:10         ` Daniel P. Berrangé
@ 2019-08-22 11:13           ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22 11:13 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Stefan Hajnoczi, Max Reitz, John Snow

On Thu, 2019-08-22 at 12:10 +0100, Daniel P. Berrangé wrote:
> On Thu, Aug 22, 2019 at 02:04:28PM +0300, Maxim Levitsky wrote:
> > On Thu, 2019-08-22 at 11:29 +0100, Daniel P. Berrangé wrote:
> > > On Thu, Aug 15, 2019 at 05:40:11PM -0400, John Snow wrote:
> > > > 
> > > > 
> > > > On 8/14/19 4:22 PM, Maxim Levitsky wrote:
> > > > > This is also a preparation for key read/write/erase functions
> > > > > 
> > > > 
> > > > This is a matter of taste and I am not usually reviewing LUKS patches
> > > > (So don't take me too seriously), but I would prefer not to have "misc"
> > > > patches and instead split things out by individual changes along with a
> > > > nice commit message for each change.
> > > > 
> > > > > * use master key len from the header
> > > > 
> > > > This touches enough lines that you could make it its own patch, I think.
> > > > 
> > > > > * prefer to use crypto params in the QCryptoBlockLUKS
> > > > >   over passing them as function arguments
> > > > 
> > > > I think the same is true here, and highlighting which variables you are
> > > > sticking into state instead of leaving as functional parameters would be
> > > > nice to see without all the other changes.
> > > > 
> > > > > * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
> > > > 
> > > > This can likely be squashed with whichever patch of yours first needs to
> > > > use it, because it's so short.
> > > > 
> > > > > * Add comments to various crypto parameters in the QCryptoBlockLUKS
> > > > > 
> > > > 
> > > > Can probably be squashed with item #2.
> > > 
> > > Agreed, with all these points  - it is too hard to review this
> > > for correctness with everything merged in one commit, so I'll
> > > wait for v2 before reviewing much more.
> > > 
> > > > > @@ -397,6 +411,12 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> > > > >      }
> > > > >  }
> > > > >  
> > > > > +static int masterkeylen(QCryptoBlockLUKS *luks)
> > > > > +{
> > > > > +    return luks->header.key_bytes;
> > > > > +}
> > > > > +
> > > > > +
> > > > 
> > > > generally QEMU uses snake_case_names; please spell as "master_key_len".
> > > 
> > > Also naming convention in this file expects "qcrypto_block_luks_" prefix
> > > for all methods
> > > 
> > 
> > The point of adding the masterkeylen was that IMHO it wasn't clear that
> > luks->header.key_bytes is the master key length.
> > 
> > However defining something like qcrypto_block_luks_master_key_len(QCryptoBlockLUKS *luks) 
> > is way longer, and will force me to wrap just too many places in the code to keep 80
> > character limit.
> > 
> > Now I am thinking of other ways to make this thing better:
> > 
> > 1. How about adding luks->masterkeylen and using it. luks state already has
> > several parsed values from the header, so using another one wouldn't hurt?
> 
> With those the parsed values are actually a different format from the
> header values, so it makes sense to have duplication.  Duplication
> just for sake of having a different name will just be confusing
> with some code using one field & some code using the other field
> when they are identical.
> 
> > 2. how about renaming the luks->header.key_bytes to luks->header->master_key_len?
> 
> This is fine.

Roger that!
Best regards,
	Maxim Levitsky


> 
> Regards,
> Daniel




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev)
  2019-08-20 18:27   ` Max Reitz
  2019-08-21 22:32     ` Maxim Levitsky
@ 2019-08-22 11:14     ` Daniel P. Berrangé
  1 sibling, 0 replies; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:14 UTC (permalink / raw)
  To: Max Reitz
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Maxim Levitsky, Stefan Hajnoczi

On Tue, Aug 20, 2019 at 08:27:48PM +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > This adds:
> > 
> > * x-blockdev-update-encryption and x-blockdev-erase-encryption qmp commands
> >   Both commands take the QCryptoKeyManageOptions
> >   the x-blockdev-update-encryption is meant for non destructive addition
> >   of key slots / whatever the encryption driver supports in the future
> > 
> >   x-blockdev-erase-encryption is meant for destructive encryption key erase,
> >   in some cases even without way to recover the data.
> > 
> > 
> > * bdrv_setup_encryption callback in the block driver
> >   This callback does both the above functions with 'action' parameter
> > 
> > * QCryptoKeyManageOptions with set of options that drivers can use for encryption managment
> >   Currently it has all the options that LUKS needs, and later it can be extended
> >   (via union) to support more encryption drivers if needed
> > 
> > * blk_setup_encryption / bdrv_setup_encryption - the usual block layer wrappers.
> >   Note that bdrv_setup_encryption takes BlockDriverState and not BdrvChild,
> >   for the ease of use from the qmp code. It is not expected that this function
> >   will be used by anything but qmp and qemu-img code
> > 
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  block/block-backend.c          |  9 ++++++++
> >  block/io.c                     | 24 ++++++++++++++++++++
> >  blockdev.c                     | 40 ++++++++++++++++++++++++++++++++++
> >  include/block/block.h          | 12 ++++++++++
> >  include/block/block_int.h      | 11 ++++++++++
> >  include/sysemu/block-backend.h |  7 ++++++
> >  qapi/block-core.json           | 36 ++++++++++++++++++++++++++++++
> >  qapi/crypto.json               | 26 ++++++++++++++++++++++
> >  8 files changed, 165 insertions(+)
> 
> Now I don’t know whether you want to keep this interface at all, because
> the cover letter seemed to imply you’d prefer a QMP amend.  But let it
> be said that a QMP amend is no trivial task.  I think the most difficult
> bit is that the qcow2 implementation currently is inherently an offline
> operation.  It isn’t a good idea to use it on a live image.  (Maybe it
> works, but it’s definitely not what I had in mind when I wrote it.)

For key mgmt we definitely need to have an online capability.

If layering it into a general purpose format "amend' option
is too complex, then we should keep it separate rather than
making our lives uncecessarily difficult IMHO.

> > diff --git a/qapi/block-core.json b/qapi/block-core.json
> > index 0d43d4f37c..53ed411eed 100644
> > --- a/qapi/block-core.json
> > +++ b/qapi/block-core.json
> > @@ -5327,3 +5327,39 @@
> >    'data' : { 'node-name': 'str',
> >               'iothread': 'StrOrNull',
> >               '*force': 'bool' } }
> > +
> > +
> > +##
> > +# @x-blockdev-update-encryption:
> > +#
> > +# Update the encryption keys for an encrypted block device
> > +#
> > +# @node-name: 	  Name of the blockdev to operate on
> > +# @force:         Disable safety checks (use with care)
> > +# @options:       Driver specific options
> > +#
> > +
> > +# Since: 4.2
> > +##
> > +{ 'command': 'x-blockdev-update-encryption',
> > +  'data': { 'node-name' : 'str',
> > +            '*force' : 'bool',
> > +            'options': 'QCryptoEncryptionSetupOptions' } }
> > +
> > +##
> > +# @x-blockdev-erase-encryption:
> > +#
> > +# Erase the encryption keys for an encrypted block device
> > +#
> > +# @node-name: 	  Name of the blockdev to operate on
> 
> Why the tab?
> 
> > +# @force:         Disable safety checks (use with care)
> 
> I think being a bit more verbose wouldn’t hurt.
> 
> (Same above.)
> 
> > +# @options:       Driver specific options
> > +#
> > +# Returns: @QCryptoKeyManageResult
> > +#
> > +# Since: 4.2
> > +##
> > +{ 'command': 'x-blockdev-erase-encryption',
> > +  'data': { 'node-name' : 'str',
> > +            '*force' : 'bool',
> > +            'options': 'QCryptoEncryptionSetupOptions' } }
> > diff --git a/qapi/crypto.json b/qapi/crypto.json
> > index b2a4cff683..69e8b086db 100644
> > --- a/qapi/crypto.json
> > +++ b/qapi/crypto.json
> > @@ -309,3 +309,29 @@
> >    'base': 'QCryptoBlockInfoBase',
> >    'discriminator': 'format',
> >    'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
> > +
> > +
> > +##
> > +# @QCryptoEncryptionSetupOptions:
> > +#
> > +# Driver specific options for encryption key management.
> 
> The options do seem LUKS-specific, but the name of this structure does not.
> 
> > +# @key-secret: the ID of a QCryptoSecret object providing the password
> > +#              to add or to erase (optional for erase)
> > +#
> > +# @old-key-secret: the ID of a QCryptoSecret object providing the password
> > +#                  that can currently unlock the image
> > +#
> > +# @slot: Key slot to update/erase
> > +#        (optional, for update will select a free slot,
> > +#        for erase will erase all slots that match the password)
> > +#
> > +# @iter-time: number of milliseconds to spend in
> > +#             PBKDF passphrase processing. Currently defaults to 2000
> > +# Since: 4.2
> > +##
> 
> Does it really make sense to use the same structure for erasing and
> updating?  I think there are ways to represent @key-secret vs. @slot
> being alternatives to each other for erase; @iter-time doesn’t seem to
> make sense for erase; and @slot doesn’t seem to make sense for update.
> Also, I don’t know whether to use @key-secret or @old-key-secret for erase.
> 
> All in all, it seems more sensible to me to have separate structs for
> updating and erasing.

I agree, we can get a more understandable schema if each
command has its own struct wit the right options.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 08/13] qcrypto: add the plumbing for encryption management
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 08/13] qcrypto: add the plumbing for encryption management Maxim Levitsky
@ 2019-08-22 11:16   ` Daniel P. Berrangé
  2019-08-22 11:47     ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:16 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Wed, Aug 14, 2019 at 11:22:14PM +0300, Maxim Levitsky wrote:
> This adds qcrypto_block_manage_encryption, which
>  is thin wrapper around manage_encryption of the crypto driver
>  which is also added
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block.c         | 29 +++++++++++++++++++++++++++++
>  crypto/blockpriv.h     |  9 +++++++++
>  include/crypto/block.h | 27 +++++++++++++++++++++++++++
>  3 files changed, 65 insertions(+)
> 
> diff --git a/crypto/block.c b/crypto/block.c
> index ee96759f7d..5916e49aba 100644
> --- a/crypto/block.c
> +++ b/crypto/block.c
> @@ -20,6 +20,7 @@
>  
>  #include "qemu/osdep.h"
>  #include "qapi/error.h"
> +
>  #include "blockpriv.h"
>  #include "block-qcow.h"
>  #include "block-luks.h"
> @@ -282,6 +283,34 @@ void qcrypto_block_free(QCryptoBlock *block)
>  }
>  
>  
> +int qcrypto_block_setup_encryption(QCryptoBlock *block,
> +                                   QCryptoBlockReadFunc readfunc,
> +                                   QCryptoBlockWriteFunc writefunc,
> +                                   void *opaque,
> +                                   enum BlkSetupEncryptionAction action,
> +                                   QCryptoEncryptionSetupOptions *options,
> +                                   bool force,
> +                                   Error **errp)
> +{
> +    if (!block->driver->setup_encryption) {
> +        error_setg(errp,
> +                "Crypto format %s doesn't support management of encryption keys",
> +                QCryptoBlockFormat_str(block->format));
> +        return -1;
> +    }
> +
> +    return block->driver->setup_encryption(block,
> +                                           readfunc,
> +                                           writefunc,
> +                                           opaque,
> +                                           action,
> +                                           options,
> +                                           force,
> +                                           errp);
> +}
> +
> +
> +
>  typedef int (*QCryptoCipherEncDecFunc)(QCryptoCipher *cipher,
>                                          const void *in,
>                                          void *out,
> diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h
> index 71c59cb542..804965dca3 100644
> --- a/crypto/blockpriv.h
> +++ b/crypto/blockpriv.h
> @@ -81,6 +81,15 @@ struct QCryptoBlockDriver {
>  
>      bool (*has_format)(const uint8_t *buf,
>                         size_t buflen);
> +
> +    int (*setup_encryption)(QCryptoBlock *block,
> +                            QCryptoBlockReadFunc readfunc,
> +                            QCryptoBlockWriteFunc writefunc,
> +                            void *opaque,
> +                            enum BlkSetupEncryptionAction action,
> +                            QCryptoEncryptionSetupOptions *options,
> +                            bool force,
> +                            Error **errp);
>  };
>  
>  
> diff --git a/include/crypto/block.h b/include/crypto/block.h
> index fe12899831..60d46e3efc 100644
> --- a/include/crypto/block.h
> +++ b/include/crypto/block.h
> @@ -23,6 +23,7 @@
>  
>  #include "crypto/cipher.h"
>  #include "crypto/ivgen.h"
> +#include "block/block.h"
>  
>  typedef struct QCryptoBlock QCryptoBlock;
>  
> @@ -268,4 +269,30 @@ uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block);
>   */
>  void qcrypto_block_free(QCryptoBlock *block);
>  
> +
> +/**
> + * qcrypto_block_setup_encryption:
> + * @block: the block encryption object
> + *
> + * @readfunc: callback for reading data from the volume header
> + * @writefunc: callback for writing data to the volume header
> + * @opaque: data to pass to @readfunc and @writefunc
> + * @action: tell the driver the setup action (add/erase currently)
> + * @options: driver specific options, that specify
> + *           what encryption settings to manage
> + * @force: hint for the driver to allow unsafe operation
> + * @errp: error pointer
> + *
> + * Adds/Erases a new encryption key using @options

I'd prefer to see separate APIs for add + erase instead
of overloading. It'll lead to a clearer API from callers
POV to see exactly which parameters are for each action.

> + *
> + */
> +int qcrypto_block_setup_encryption(QCryptoBlock *block,
> +                                   QCryptoBlockReadFunc readfunc,
> +                                   QCryptoBlockWriteFunc writefunc,
> +                                   void *opaque,
> +                                   enum BlkSetupEncryptionAction action,
> +                                   QCryptoEncryptionSetupOptions *options,
> +                                   bool force,
> +                                   Error **errp);
> +

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 09/13] qcrypto-luks: implement the encryption key management
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 09/13] qcrypto-luks: implement the encryption key management Maxim Levitsky
@ 2019-08-22 11:27   ` Daniel P. Berrangé
  2019-08-25 17:01     ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:27 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Wed, Aug 14, 2019 at 11:22:15PM +0300, Maxim Levitsky wrote:
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  crypto/block-luks.c | 374 +++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 373 insertions(+), 1 deletion(-)
> 
> diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> index 1997e92fe1..2c33643b52 100644
> --- a/crypto/block-luks.c
> +++ b/crypto/block-luks.c
> @@ -72,6 +72,8 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
>  
>  #define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME 2000
>  
> +#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40
> +
>  static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
>      'L', 'U', 'K', 'S', 0xBA, 0xBE
>  };
> @@ -221,6 +223,9 @@ struct QCryptoBlockLUKS {
>  
>      /* Hash algorithm used in pbkdf2 function */
>      QCryptoHashAlgorithm hash_alg;
> +
> +    /* Name of the secret that was used to open the image */
> +    char *secret;
>  };
>  
>  
> @@ -1121,6 +1126,194 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
>  }
>  
>  
> +
> +/*
> + * Returns true if a slot i is marked as containing as active

s/as containing//

> + * (contains encrypted copy of the master key)
> + */
> +
> +static bool
> +qcrypto_block_luks_slot_active(QCryptoBlockLUKS *luks, int slot_idx)
> +{
> +    uint32_t val = luks->header.key_slots[slot_idx].active;
> +    return val ==  QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
> +}
> +
> +/*
> + * Returns the number of slots that are marked as active
> + * (contains encrypted copy of the master key)
> + */
> +
> +static int
> +qcrypto_block_luks_count_active_slots(QCryptoBlockLUKS *luks)
> +{
> +    int i, ret = 0;

I prefer to see 'size_t' for loop iterators 

> +
> +    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> +        if (qcrypto_block_luks_slot_active(luks, i)) {
> +            ret++;
> +        }
> +    }
> +    return ret;
> +}
> +
> +
> +/*
> + * Finds first key slot which is not active
> + * Returns the key slot index, or -1 if doesn't exist
> + */
> +
> +static int
> +qcrypto_block_luks_find_free_keyslot(QCryptoBlockLUKS *luks)
> +{
> +    uint i;
> +
> +    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> +        if (!qcrypto_block_luks_slot_active(luks, i)) {
> +            return i;
> +        }
> +    }
> +    return -1;
> +
> +}
> +
> +/*
> + * Erases an keyslot given its index
> + *
> + * Returns:
> + *    0 if the keyslot was erased successfully
> + *   -1 if a error occurred while erasing the keyslot
> + *
> + */
> +

Redundant blank line

> +static int
> +qcrypto_block_luks_erase_key(QCryptoBlock *block,
> +                             uint slot_idx,
> +                             QCryptoBlockWriteFunc writefunc,
> +                             void *opaque,
> +                             Error **errp)
> +{
> +    QCryptoBlockLUKS *luks = block->opaque;
> +    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
> +    uint8_t *garbagekey = NULL;
> +    size_t splitkeylen = masterkeylen(luks) * slot->stripes;
> +    int i;
> +    int ret = -1;
> +
> +    assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
> +    assert(splitkeylen > 0);
> +
> +    garbagekey = g_malloc0(splitkeylen);
> +
> +    /* Reset the key slot header */
> +    memset(slot->salt, 0, QCRYPTO_BLOCK_LUKS_SALT_LEN);
> +    slot->iterations = 0;
> +    slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
> +
> +    qcrypto_block_luks_store_header(block,  writefunc, opaque, errp);
> +
> +    /*
> +     * Now try to erase the key material, even if the header
> +     * update failed
> +     */
> +

Redundant blank line

> +    for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS ; i++) {
> +        if (qcrypto_random_bytes(garbagekey, splitkeylen, errp) < 0) {
> +

Again, many more times beelow.

> +                /*
> +                 * If we failed to get the random data, still write
> +                 * *something* to the key slot at least once
> +                 */

Specificially  we write all-zeros, since garbagekey was allocated
with g_malloc0

> +
> +                if (i > 0) {
> +                    goto cleanup;
> +                }
> +        }
> +
> +        if (writefunc(block, slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
> +                  garbagekey,
> +                  splitkeylen,
> +                  opaque,
> +                  errp) != splitkeylen) {

Indent is off - align with '('

> +            goto cleanup;
> +        }
> +    }
> +
> +    ret = 0;
> +cleanup:
> +    g_free(garbagekey);
> +    return ret;
> +}
> +
> +
> +/*
> + * Erase all the keys that match the given password
> + * Will stop when only one keyslot is remaining
> + * Returns 0 is some keys were erased or -1 on failure
> + */
> +
> +static int
> +qcrypto_block_luks_erase_matching_keys(QCryptoBlock *block,
> +                             const char *password,
> +                             QCryptoBlockReadFunc readfunc,
> +                             QCryptoBlockWriteFunc writefunc,
> +                             void *opaque,
> +                             bool force,
> +                             Error **errp)

Indent is off again

> +{
> +    QCryptoBlockLUKS *luks = block->opaque;
> +    uint i;
> +    int rv, ret = -1;
> +    uint8_t *masterkey;
> +    uint erased_count = 0;
> +    uint active_slot_count = qcrypto_block_luks_count_active_slots(luks);
> +
> +    masterkey = g_new0(uint8_t, masterkeylen(luks));
> +
> +    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> +
> +        /* refuse to erase last key if not forced */
> +        if (!force && active_slot_count == 1) {
> +            break;
> +        }

In the case where there was only 1 keyslot to start with,
his break will cause us to report

        error_setg(errp, "Didn't erase a keyslot, because no keyslots "
                         "match the given password");

which is horribly misleading.

Before entering the loop we should check this case and report
a saner message.

> +
> +        rv = qcrypto_block_luks_load_key(block,
> +                                         i,
> +                                         password,
> +                                         masterkey,
> +                                         readfunc,
> +                                         opaque,
> +                                         errp);
> +        if (rv < 0) {
> +            goto cleanup;
> +        }
> +        if (rv == 0) {
> +            continue;
> +        }
> +
> +        rv = qcrypto_block_luks_erase_key(block, i, writefunc, opaque, errp);
> +        if (rv < 0) {
> +            goto cleanup;
> +        }
> +
> +        erased_count++;
> +        active_slot_count--;
> +    }
> +
> +    if (!erased_count) {
> +        error_setg(errp, "Didn't erase a keyslot, because no keyslots "
> +                         "match the given password");
> +        goto cleanup;
> +    }
> +
> +    ret = 0;
> +cleanup:
> +    memset(masterkey, 0, masterkeylen(luks));
> +    g_free(masterkey);
> +    return ret;
> +}
> +
> +
>  static int
>  qcrypto_block_luks_open(QCryptoBlock *block,
>                          QCryptoBlockOpenOptions *options,
> @@ -1151,6 +1344,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
>  
>      luks = g_new0(QCryptoBlockLUKS, 1);
>      block->opaque = luks;
> +    luks->secret = g_strdup(options->u.luks.key_secret);
>  
>      ret = qcrypto_block_luks_load_header(block, readfunc, opaque, errp);
>      if (ret) {
> @@ -1320,6 +1514,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>                     optprefix ? optprefix : "");
>          return -1;
>      }
> +    luks->secret = options->u.luks.key_secret;
> +
>      password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
>      if (!password) {
>          return -1;
> @@ -1605,7 +1801,9 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
>  
>  static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
>  {
> -    g_free(block->opaque);
> +    QCryptoBlockLUKS *luks = block->opaque;
> +    g_free(luks->secret);
> +    g_free(luks);
>  }
>  
>  
> @@ -1638,6 +1836,179 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block,
>                                          offset, buf, len, errp);
>  }
>  
> +static int
> +qcrypto_block_luks_setup_encryption(QCryptoBlock *block,
> +                                    QCryptoBlockReadFunc readfunc,
> +                                    QCryptoBlockWriteFunc writefunc,
> +                                    void *opaque,
> +                                    enum BlkSetupEncryptionAction action,
> +                                    QCryptoEncryptionSetupOptions *options,
> +                                    bool force,
> +                                    Error **errp)
> +{
> +    QCryptoBlockLUKS *luks = block->opaque;
> +    char *old_password = NULL;
> +    char *password = NULL;
> +    const char *old_secret = luks->secret;
> +    uint8_t *masterkey = NULL;
> +    int slot = -1;
> +    int ret = -1;
> +    int64_t iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME;
> +
> +    /* Read given slot and check it */
> +    if (options->has_slot) {
> +        slot = options->slot;
> +        if (slot < 0 || slot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
> +            error_setg(errp,
> +                       "Given key slot %i is not supported by LUKS", slot);
> +             goto cleanup;
> +        }
> +    }
> +
> +    if (options->has_iter_time) {
> +        iter_time = options->iter_time;
> +    }
> +
> +    switch (action) {
> +
> +    case BLK_UPDATE_ENCRYPTION:


The fact that the vast majority of code in this method is inside
a giant switch just reinforces that we should have separate APIs
for erase vs adding keys.

> +
> +        /* Check that we are not overwriting an active slot */
> +        if (!force && slot != -1 &&
> +                    qcrypto_block_luks_slot_active(luks, slot)) {
> +
> +            error_setg(errp, "Can't update an active key slot %i",
> +                       slot);
> +            goto cleanup;
> +        }
> +
> +        /* check that we have the passwords*/
> +        if (!options->has_key_secret) {
> +            error_setg(errp, "Can't add a key slot without a  password");
> +            goto cleanup;
> +        }
> +
> +        if (options->has_old_key_secret) {
> +            old_secret = options->old_key_secret;
> +        }
> +
> +        /* Read the old password */
> +        old_password = qcrypto_secret_lookup_as_utf8(old_secret, errp);
> +        if (!old_password) {
> +            goto cleanup;
> +        }
> +
> +        masterkey = g_new0(uint8_t, masterkeylen(luks));
> +
> +        /* Retrieve the master key*/
> +        if (qcrypto_block_luks_find_key(block, old_password, masterkey,
> +                                    readfunc, opaque, errp) < 0) {

Indent is off

> +            error_append_hint(errp,
> +                       "Old password that was given doesn't unlock the image");

And again, throughout this patch

> +            goto cleanup;
> +        }
> +
> +        /* Read the new password*/
> +        password = qcrypto_secret_lookup_as_utf8(options->key_secret, errp);
> +        if (!password) {
> +            goto cleanup;
> +        }
> +
> +        /* Find the new slot to write to */
> +        if (slot == -1) {
> +            slot = qcrypto_block_luks_find_free_keyslot(luks);
> +
> +            if (slot == -1) {
> +                error_setg(errp,
> +                        "Can't add a keyslot - all key slots are in use");
> +                goto cleanup;
> +
> +            }
> +        }
> +
> +        /* Store the master key to the new slot */
> +        if (qcrypto_block_luks_store_key(block, slot, password,
> +                                     masterkey, iter_time,
> +                                     writefunc, opaque, errp)) {
> +
> +            error_append_hint(errp, "Failed to store the keyslot %i", slot);
> +            goto cleanup;
> +        }
> +        break;
> +
> +    case BLK_ERASE_ENCRYPTION:
> +
> +        /* Check that we are not erasing last key slot */
> +        if (qcrypto_block_luks_count_active_slots(luks) <= 1) {
> +
> +            if (!force) {
> +                error_setg(errp, "Only one slot active - can't erase");
> +                goto cleanup;
> +            }
> +        }
> +
> +        if (slot != -1) {
> +            /* Check that we are not erasing an inactive slot */
> +            if (!qcrypto_block_luks_slot_active(luks, options->slot)) {
> +                if (!force) {
> +                    error_setg(errp, "Can't erase an inactive key slot %i",
> +                               slot);
> +                    goto cleanup;
> +                }
> +            }
> +
> +            /* Erase the given slot */
> +            if (qcrypto_block_luks_erase_key(block, slot,
> +                                             writefunc, opaque, errp)) {
> +                goto cleanup;
> +            }
> +
> +        } else {
> +            if (!options->has_key_secret) {
> +                error_setg(errp,
> +                           "To erase a keyslot you have to specify either the"
> +                           "slot index or a password "
> +                           "(to erase all slots that match it)");
> +                goto cleanup;
> +            }
> +
> +            password = qcrypto_secret_lookup_as_utf8(options->key_secret, errp);
> +            if (!password) {
> +                goto cleanup;
> +            }
> +
> +            if (qcrypto_block_luks_erase_matching_keys(block, password,
> +                                                       readfunc, writefunc,
> +                                                       opaque, force, errp)) {
> +                goto cleanup;
> +            }
> +        }
> +        break;
> +
> +    default:
> +        goto cleanup;
> +    }
> +
> +    ret = 0;
> +cleanup:
> +
> +    if (old_password) {
> +        memset(old_password, 0, strlen(old_password));
> +        g_free(old_password);
> +    }
> +
> +    if (password) {
> +        memset(password, 0, strlen(password));
> +        g_free(password);
> +    }
> +
> +    if (masterkey) {
> +        memset(masterkey, 0,  masterkeylen(luks));
> +        g_free(masterkey);
> +    }
> +    return ret;
> +}
> +
>  
>  const QCryptoBlockDriver qcrypto_block_driver_luks = {
>      .open = qcrypto_block_luks_open,
> @@ -1647,4 +2018,5 @@ const QCryptoBlockDriver qcrypto_block_driver_luks = {
>      .decrypt = qcrypto_block_luks_decrypt,
>      .encrypt = qcrypto_block_luks_encrypt,
>      .has_format = qcrypto_block_luks_has_format,
> +    .setup_encryption = qcrypto_block_luks_setup_encryption,
>  };
> -- 
> 2.17.2
> 

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 10/13] block/crypto: implement the encryption key management
  2019-08-14 20:22 ` [Qemu-devel] [PATCH 10/13] block/crypto: " Maxim Levitsky
@ 2019-08-22 11:29   ` Daniel P. Berrangé
  2019-08-22 11:36     ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:29 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Wed, Aug 14, 2019 at 11:22:16PM +0300, Maxim Levitsky wrote:
> This implements the encryption key management
> using the generic code in qcrypto layer
> 
> This code adds another 'write_func' because the initialization
> write_func works directly on the underlying file,
> because during the creation, there is no open instance
> of the luks driver, but during regular use, we have it,
> and should use it instead.
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
>  block/crypto.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 93 insertions(+), 3 deletions(-)
> 
> diff --git a/block/crypto.c b/block/crypto.c
> index 42a3f0898b..415b6db041 100644
> --- a/block/crypto.c
> +++ b/block/crypto.c
> @@ -36,6 +36,7 @@ typedef struct BlockCrypto BlockCrypto;
>  
>  struct BlockCrypto {
>      QCryptoBlock *block;
> +    bool updating_keys;
>  };
>  
>  
> @@ -69,6 +70,24 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
>      return ret;
>  }
>  
> +static ssize_t block_crypto_write_func(QCryptoBlock *block,
> +                                      size_t offset,
> +                                      const uint8_t *buf,
> +                                      size_t buflen,
> +                                      void *opaque,
> +                                      Error **errp)
> +{
> +    BlockDriverState *bs = opaque;
> +    ssize_t ret;
> +
> +    ret = bdrv_pwrite(bs->file, offset, buf, buflen);
> +    if (ret < 0) {
> +        error_setg_errno(errp, -ret, "Could not write encryption header");
> +        return ret;
> +    }
> +    return ret;
> +}
> +
>  
>  struct BlockCryptoCreateData {
>      BlockBackend *blk;
> @@ -622,6 +641,78 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
>      return spec_info;
>  }
>  
> +
> +static int
> +block_crypto_setup_encryption(BlockDriverState *bs,
> +                              enum BlkSetupEncryptionAction action,
> +                              QCryptoEncryptionSetupOptions *options,
> +                              bool force,
> +                              Error **errp)
> +{
> +    BlockCrypto *crypto = bs->opaque;
> +    int ret;
> +
> +    assert(crypto);
> +    assert(crypto->block);
> +
> +    crypto->updating_keys = true;
> +
> +    ret = bdrv_child_refresh_perms(bs, bs->file, errp);
> +
> +    if (ret) {
> +        crypto->updating_keys = false;
> +        return ret;
> +    }
> +
> +    ret = qcrypto_block_setup_encryption(crypto->block,
> +                                          block_crypto_read_func,
> +                                          block_crypto_write_func,
> +                                          bs,
> +                                          action,
> +                                          options,
> +                                          force,
> +                                          errp);
> +
> +    crypto->updating_keys = false;
> +    bdrv_child_refresh_perms(bs, bs->file, errp);
> +
> +
> +    return ret;
> +
> +}
> +
> +
> +static void
> +block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
> +                         const BdrvChildRole *role,
> +                         BlockReopenQueue *reopen_queue,
> +                         uint64_t perm, uint64_t shared,
> +                         uint64_t *nperm, uint64_t *nshared)
> +{
> +
> +    BlockCrypto *crypto = bs->opaque;
> +
> +    /*
> +     * This driver doesn't modify LUKS metadata except
> +     * when updating the encryption slots.
> +     * Allow share-rw=on as a special case.
> +     *
> +     * Encryption update will set the crypto->updating_keys
> +     * during that period and refresh permissions
> +     *
> +     * */
> +
> +    if (crypto->updating_keys) {
> +        /*need exclusive write access for header update  */
> +        perm |= BLK_PERM_WRITE;
> +        shared &= ~BLK_PERM_WRITE;
> +    }

So if 2 QEMU's have the same LUKS image open, this means that
if one tries to update the header, it will fail to upgrade
its lock & thus be blocked from updating header ?

> +
> +    bdrv_filter_default_perms(bs, c, role, reopen_queue,
> +            perm, shared, nperm, nshared);
> +}

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 12/13] qemu-img: implement key management
  2019-08-20 18:29   ` Max Reitz
  2019-08-21 22:33     ` Maxim Levitsky
@ 2019-08-22 11:32     ` Daniel P. Berrangé
  2019-08-22 14:42       ` Max Reitz
  1 sibling, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:32 UTC (permalink / raw)
  To: Max Reitz
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Maxim Levitsky, Stefan Hajnoczi

On Tue, Aug 20, 2019 at 08:29:55PM +0200, Max Reitz wrote:
> On 14.08.19 22:22, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  block/crypto.c   |  16 ++++++
> >  block/crypto.h   |   3 +
> >  qemu-img-cmds.hx |  13 +++++
> >  qemu-img.c       | 140 +++++++++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 172 insertions(+)
> 
> Yes, this seems a bit weird.  Putting it under amend seems like the
> natural thing if that works; if not, I think it should be a single
> qemu-img subcommand instead of two.

I'm not convinced by overloading two distinct operations on to one
sub-command - doesn't seem to give an obvious benefit to overload
them & IME experiance overloading results in harder to understand
commands due to having distinct args to each command.


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
                   ` (14 preceding siblings ...)
  2019-08-20 17:59 ` Max Reitz
@ 2019-08-22 11:35 ` Daniel P. Berrangé
  2019-08-25 17:10   ` Maxim Levitsky
  15 siblings, 1 reply; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:35 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Wed, Aug 14, 2019 at 11:22:06PM +0300, Maxim Levitsky wrote:
> Hi!
> 
> This patch series implements key management for luks based encryption
> It supports both raw luks images and qcow2 encrypted images.
> 
> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1731898
> 
> There are still several issues that need to be figured out,
> on which the feedback is very welcome, but other than that the code mostly works.
> 
> The main issues are:
> 
> 1. Instead of the proposed blockdev-update-encryption/blockdev-erase-encryption
> interface, it is probably better to implement 'blockdev-amend-options' in qmp,
> and use this both for offline and online key update (with some translation
> layer to convert the qemu-img 'options' to qmp structures)
> 
> This interface already exists for offline qcow2 format options update/
> 
> This is an issue that was raised today on IRC with Kevin Wolf. Really thanks
> for the idea!
> 
> We agreed that this new qmp interface should take the same options as
> blockdev-create does, however since we want to be able to edit the encryption
> slots separately, this implies that we sort of need to allow this on creation
> time as well.
> 
> Also the BlockdevCreateOptions is a union, which is specialized by the driver name
> which is great for creation, but for update, the driver name is already known,
> and thus the user should not be forced to pass it again.
> However qmp doesn't seem to support union type guessing based on actual fields
> given (this might not be desired either), which complicates this somewhat.

Given this design question around the integration into blockdev, I'd
suggest splitting the series into two parts.

One series should do all the work in crypto/ code to support adding
and erasing key slots.

One series should focus on block/ layer QMP/qemu-img integration.

The block layer QAPI stuff shouldn't leak into the crypto/ code.

So this will let us get on with reviewing & unit testing the
crypto code, while we discuss block layer design options in more
detail.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 10/13] block/crypto: implement the encryption key management
  2019-08-22 11:29   ` Daniel P. Berrangé
@ 2019-08-22 11:36     ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22 11:36 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 12:29 +0100, Daniel P. Berrangé wrote:
> On Wed, Aug 14, 2019 at 11:22:16PM +0300, Maxim Levitsky wrote:
> > This implements the encryption key management
> > using the generic code in qcrypto layer
> > 
> > This code adds another 'write_func' because the initialization
> > write_func works directly on the underlying file,
> > because during the creation, there is no open instance
> > of the luks driver, but during regular use, we have it,
> > and should use it instead.
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  block/crypto.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 93 insertions(+), 3 deletions(-)
> > 
> > diff --git a/block/crypto.c b/block/crypto.c
> > index 42a3f0898b..415b6db041 100644
> > --- a/block/crypto.c
> > +++ b/block/crypto.c
> > @@ -36,6 +36,7 @@ typedef struct BlockCrypto BlockCrypto;
> >  
> >  struct BlockCrypto {
> >      QCryptoBlock *block;
> > +    bool updating_keys;
> >  };
> >  
> >  
> > @@ -69,6 +70,24 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
> >      return ret;
> >  }
> >  
> > +static ssize_t block_crypto_write_func(QCryptoBlock *block,
> > +                                      size_t offset,
> > +                                      const uint8_t *buf,
> > +                                      size_t buflen,
> > +                                      void *opaque,
> > +                                      Error **errp)
> > +{
> > +    BlockDriverState *bs = opaque;
> > +    ssize_t ret;
> > +
> > +    ret = bdrv_pwrite(bs->file, offset, buf, buflen);
> > +    if (ret < 0) {
> > +        error_setg_errno(errp, -ret, "Could not write encryption header");
> > +        return ret;
> > +    }
> > +    return ret;
> > +}
> > +
> >  
> >  struct BlockCryptoCreateData {
> >      BlockBackend *blk;
> > @@ -622,6 +641,78 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
> >      return spec_info;
> >  }
> >  
> > +
> > +static int
> > +block_crypto_setup_encryption(BlockDriverState *bs,
> > +                              enum BlkSetupEncryptionAction action,
> > +                              QCryptoEncryptionSetupOptions *options,
> > +                              bool force,
> > +                              Error **errp)
> > +{
> > +    BlockCrypto *crypto = bs->opaque;
> > +    int ret;
> > +
> > +    assert(crypto);
> > +    assert(crypto->block);
> > +
> > +    crypto->updating_keys = true;
> > +
> > +    ret = bdrv_child_refresh_perms(bs, bs->file, errp);
> > +
> > +    if (ret) {
> > +        crypto->updating_keys = false;
> > +        return ret;
> > +    }
> > +
> > +    ret = qcrypto_block_setup_encryption(crypto->block,
> > +                                          block_crypto_read_func,
> > +                                          block_crypto_write_func,
> > +                                          bs,
> > +                                          action,
> > +                                          options,
> > +                                          force,
> > +                                          errp);
> > +
> > +    crypto->updating_keys = false;
> > +    bdrv_child_refresh_perms(bs, bs->file, errp);
> > +
> > +
> > +    return ret;
> > +
> > +}
> > +
> > +
> > +static void
> > +block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
> > +                         const BdrvChildRole *role,
> > +                         BlockReopenQueue *reopen_queue,
> > +                         uint64_t perm, uint64_t shared,
> > +                         uint64_t *nperm, uint64_t *nshared)
> > +{
> > +
> > +    BlockCrypto *crypto = bs->opaque;
> > +
> > +    /*
> > +     * This driver doesn't modify LUKS metadata except
> > +     * when updating the encryption slots.
> > +     * Allow share-rw=on as a special case.
> > +     *
> > +     * Encryption update will set the crypto->updating_keys
> > +     * during that period and refresh permissions
> > +     *
> > +     * */
> > +
> > +    if (crypto->updating_keys) {
> > +        /*need exclusive write access for header update  */
> > +        perm |= BLK_PERM_WRITE;
> > +        shared &= ~BLK_PERM_WRITE;
> > +    }
> 
> So if 2 QEMU's have the same LUKS image open, this means that
> if one tries to update the header, it will fail to upgrade
> its lock & thus be blocked from updating header ?
> 
I guess so. 

That what I understood from our talk with Kevin and then also from
my own understanding of the permission code.
I absolutely don't like the 'global' variable 'crypto->updating_keys'
but we kind of agreed that this must be done like that.
In the defense of this, this code is only needed for backward compatibility,
so maybe this is the right solution after all.

> > +
> > +    bdrv_filter_default_perms(bs, c, role, reopen_queue,
> > +            perm, shared, nperm, nshared);
> > 

Best regards,
	Maxim Levitsky




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 08/13] qcrypto: add the plumbing for encryption management
  2019-08-22 11:16   ` Daniel P. Berrangé
@ 2019-08-22 11:47     ` Maxim Levitsky
  2019-08-22 11:49       ` Daniel P. Berrangé
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22 11:47 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 12:16 +0100, Daniel P. Berrangé wrote:
> On Wed, Aug 14, 2019 at 11:22:14PM +0300, Maxim Levitsky wrote:
> > This adds qcrypto_block_manage_encryption, which
> >  is thin wrapper around manage_encryption of the crypto driver
> >  which is also added
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block.c         | 29 +++++++++++++++++++++++++++++
> >  crypto/blockpriv.h     |  9 +++++++++
> >  include/crypto/block.h | 27 +++++++++++++++++++++++++++
> >  3 files changed, 65 insertions(+)
> > 
> > diff --git a/crypto/block.c b/crypto/block.c
> > index ee96759f7d..5916e49aba 100644
> > --- a/crypto/block.c
> > +++ b/crypto/block.c
> > @@ -20,6 +20,7 @@
> >  
> >  #include "qemu/osdep.h"
> >  #include "qapi/error.h"
> > +
> >  #include "blockpriv.h"
> >  #include "block-qcow.h"
> >  #include "block-luks.h"
> > @@ -282,6 +283,34 @@ void qcrypto_block_free(QCryptoBlock *block)
> >  }
> >  
> >  
> > +int qcrypto_block_setup_encryption(QCryptoBlock *block,
> > +                                   QCryptoBlockReadFunc readfunc,
> > +                                   QCryptoBlockWriteFunc writefunc,
> > +                                   void *opaque,
> > +                                   enum BlkSetupEncryptionAction action,
> > +                                   QCryptoEncryptionSetupOptions *options,
> > +                                   bool force,
> > +                                   Error **errp)
> > +{
> > +    if (!block->driver->setup_encryption) {
> > +        error_setg(errp,
> > +                "Crypto format %s doesn't support management of encryption keys",
> > +                QCryptoBlockFormat_str(block->format));
> > +        return -1;
> > +    }
> > +
> > +    return block->driver->setup_encryption(block,
> > +                                           readfunc,
> > +                                           writefunc,
> > +                                           opaque,
> > +                                           action,
> > +                                           options,
> > +                                           force,
> > +                                           errp);
> > +}
> > +
> > +
> > +
> >  typedef int (*QCryptoCipherEncDecFunc)(QCryptoCipher *cipher,
> >                                          const void *in,
> >                                          void *out,
> > diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h
> > index 71c59cb542..804965dca3 100644
> > --- a/crypto/blockpriv.h
> > +++ b/crypto/blockpriv.h
> > @@ -81,6 +81,15 @@ struct QCryptoBlockDriver {
> >  
> >      bool (*has_format)(const uint8_t *buf,
> >                         size_t buflen);
> > +
> > +    int (*setup_encryption)(QCryptoBlock *block,
> > +                            QCryptoBlockReadFunc readfunc,
> > +                            QCryptoBlockWriteFunc writefunc,
> > +                            void *opaque,
> > +                            enum BlkSetupEncryptionAction action,
> > +                            QCryptoEncryptionSetupOptions *options,
> > +                            bool force,
> > +                            Error **errp);
> >  };
> >  
> >  
> > diff --git a/include/crypto/block.h b/include/crypto/block.h
> > index fe12899831..60d46e3efc 100644
> > --- a/include/crypto/block.h
> > +++ b/include/crypto/block.h
> > @@ -23,6 +23,7 @@
> >  
> >  #include "crypto/cipher.h"
> >  #include "crypto/ivgen.h"
> > +#include "block/block.h"
> >  
> >  typedef struct QCryptoBlock QCryptoBlock;
> >  
> > @@ -268,4 +269,30 @@ uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block);
> >   */
> >  void qcrypto_block_free(QCryptoBlock *block);
> >  
> > +
> > +/**
> > + * qcrypto_block_setup_encryption:
> > + * @block: the block encryption object
> > + *
> > + * @readfunc: callback for reading data from the volume header
> > + * @writefunc: callback for writing data to the volume header
> > + * @opaque: data to pass to @readfunc and @writefunc
> > + * @action: tell the driver the setup action (add/erase currently)
> > + * @options: driver specific options, that specify
> > + *           what encryption settings to manage
> > + * @force: hint for the driver to allow unsafe operation
> > + * @errp: error pointer
> > + *
> > + * Adds/Erases a new encryption key using @options
> 
> I'd prefer to see separate APIs for add + erase instead
> of overloading. It'll lead to a clearer API from callers
> POV to see exactly which parameters are for each action.


The downside of this is some duplication of code in the middle layers.
I don't mind doing that if you are OK with that.

Also note that if we go with amend options, we will have to use the single
API.


Best regards,
	Maxim Levitsky




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 08/13] qcrypto: add the plumbing for encryption management
  2019-08-22 11:47     ` Maxim Levitsky
@ 2019-08-22 11:49       ` Daniel P. Berrangé
  0 siblings, 0 replies; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-22 11:49 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Thu, Aug 22, 2019 at 02:47:03PM +0300, Maxim Levitsky wrote:
> On Thu, 2019-08-22 at 12:16 +0100, Daniel P. Berrangé wrote:
> > On Wed, Aug 14, 2019 at 11:22:14PM +0300, Maxim Levitsky wrote:
> > > This adds qcrypto_block_manage_encryption, which
> > >  is thin wrapper around manage_encryption of the crypto driver
> > >  which is also added
> > > 
> > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > ---
> > >  crypto/block.c         | 29 +++++++++++++++++++++++++++++
> > >  crypto/blockpriv.h     |  9 +++++++++
> > >  include/crypto/block.h | 27 +++++++++++++++++++++++++++
> > >  3 files changed, 65 insertions(+)
> > > 
> > > diff --git a/crypto/block.c b/crypto/block.c
> > > index ee96759f7d..5916e49aba 100644
> > > --- a/crypto/block.c
> > > +++ b/crypto/block.c
> > > @@ -20,6 +20,7 @@
> > >  
> > >  #include "qemu/osdep.h"
> > >  #include "qapi/error.h"
> > > +
> > >  #include "blockpriv.h"
> > >  #include "block-qcow.h"
> > >  #include "block-luks.h"
> > > @@ -282,6 +283,34 @@ void qcrypto_block_free(QCryptoBlock *block)
> > >  }
> > >  
> > >  
> > > +int qcrypto_block_setup_encryption(QCryptoBlock *block,
> > > +                                   QCryptoBlockReadFunc readfunc,
> > > +                                   QCryptoBlockWriteFunc writefunc,
> > > +                                   void *opaque,
> > > +                                   enum BlkSetupEncryptionAction action,
> > > +                                   QCryptoEncryptionSetupOptions *options,
> > > +                                   bool force,
> > > +                                   Error **errp)
> > > +{
> > > +    if (!block->driver->setup_encryption) {
> > > +        error_setg(errp,
> > > +                "Crypto format %s doesn't support management of encryption keys",
> > > +                QCryptoBlockFormat_str(block->format));
> > > +        return -1;
> > > +    }
> > > +
> > > +    return block->driver->setup_encryption(block,
> > > +                                           readfunc,
> > > +                                           writefunc,
> > > +                                           opaque,
> > > +                                           action,
> > > +                                           options,
> > > +                                           force,
> > > +                                           errp);
> > > +}
> > > +
> > > +
> > > +
> > >  typedef int (*QCryptoCipherEncDecFunc)(QCryptoCipher *cipher,
> > >                                          const void *in,
> > >                                          void *out,
> > > diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h
> > > index 71c59cb542..804965dca3 100644
> > > --- a/crypto/blockpriv.h
> > > +++ b/crypto/blockpriv.h
> > > @@ -81,6 +81,15 @@ struct QCryptoBlockDriver {
> > >  
> > >      bool (*has_format)(const uint8_t *buf,
> > >                         size_t buflen);
> > > +
> > > +    int (*setup_encryption)(QCryptoBlock *block,
> > > +                            QCryptoBlockReadFunc readfunc,
> > > +                            QCryptoBlockWriteFunc writefunc,
> > > +                            void *opaque,
> > > +                            enum BlkSetupEncryptionAction action,
> > > +                            QCryptoEncryptionSetupOptions *options,
> > > +                            bool force,
> > > +                            Error **errp);
> > >  };
> > >  
> > >  
> > > diff --git a/include/crypto/block.h b/include/crypto/block.h
> > > index fe12899831..60d46e3efc 100644
> > > --- a/include/crypto/block.h
> > > +++ b/include/crypto/block.h
> > > @@ -23,6 +23,7 @@
> > >  
> > >  #include "crypto/cipher.h"
> > >  #include "crypto/ivgen.h"
> > > +#include "block/block.h"
> > >  
> > >  typedef struct QCryptoBlock QCryptoBlock;
> > >  
> > > @@ -268,4 +269,30 @@ uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block);
> > >   */
> > >  void qcrypto_block_free(QCryptoBlock *block);
> > >  
> > > +
> > > +/**
> > > + * qcrypto_block_setup_encryption:
> > > + * @block: the block encryption object
> > > + *
> > > + * @readfunc: callback for reading data from the volume header
> > > + * @writefunc: callback for writing data to the volume header
> > > + * @opaque: data to pass to @readfunc and @writefunc
> > > + * @action: tell the driver the setup action (add/erase currently)
> > > + * @options: driver specific options, that specify
> > > + *           what encryption settings to manage
> > > + * @force: hint for the driver to allow unsafe operation
> > > + * @errp: error pointer
> > > + *
> > > + * Adds/Erases a new encryption key using @options
> > 
> > I'd prefer to see separate APIs for add + erase instead
> > of overloading. It'll lead to a clearer API from callers
> > POV to see exactly which parameters are for each action.
> 
> 
> The downside of this is some duplication of code in the middle layers.
> I don't mind doing that if you are OK with that.
> 
> Also note that if we go with amend options, we will have to use the single
> API.

That should not be the case. The interface used at the qemu-img/QMP
layer is something for the block layer to be concerned with. THis
code is in the crypto layer and shouldn't depend on stuff defined
in the block layer QAPI schema. The block layer ammend options impl
should translate to the relevant API calls needed at the crypto layer.


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev)
  2019-08-21 22:24     ` Maxim Levitsky
@ 2019-08-22 14:07       ` Markus Armbruster
  2019-08-25 16:42         ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Markus Armbruster @ 2019-08-22 14:07 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi

Maxim Levitsky <mlevitsk@redhat.com> writes:

> On Wed, 2019-08-21 at 13:47 +0200, Markus Armbruster wrote:
>> Maxim Levitsky <mlevitsk@redhat.com> writes:
>> 
>> > This adds:
>> > 
>> > * x-blockdev-update-encryption and x-blockdev-erase-encryption qmp commands
>> >   Both commands take the QCryptoKeyManageOptions
>> >   the x-blockdev-update-encryption is meant for non destructive addition
>> >   of key slots / whatever the encryption driver supports in the future
>> > 
>> >   x-blockdev-erase-encryption is meant for destructive encryption key erase,
>> >   in some cases even without way to recover the data.
>> > 
>> > 
>> > * bdrv_setup_encryption callback in the block driver
>> >   This callback does both the above functions with 'action' parameter
>> > 
>> > * QCryptoKeyManageOptions with set of options that drivers can use for encryption managment
>> >   Currently it has all the options that LUKS needs, and later it can be extended
>> >   (via union) to support more encryption drivers if needed
>> > 
>> > * blk_setup_encryption / bdrv_setup_encryption - the usual block layer wrappers.
>> >   Note that bdrv_setup_encryption takes BlockDriverState and not BdrvChild,
>> >   for the ease of use from the qmp code. It is not expected that this function
>> >   will be used by anything but qmp and qemu-img code
>> > 
>> > 
>> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
>> 
>> [...]
>> > diff --git a/qapi/block-core.json b/qapi/block-core.json
>> > index 0d43d4f37c..53ed411eed 100644
>> > --- a/qapi/block-core.json
>> > +++ b/qapi/block-core.json
>> > @@ -5327,3 +5327,39 @@
>> >    'data' : { 'node-name': 'str',
>> >               'iothread': 'StrOrNull',
>> >               '*force': 'bool' } }
>> > +
>> > +
>> > +##
>> > +# @x-blockdev-update-encryption:
>> > +#
>> > +# Update the encryption keys for an encrypted block device
>> > +#
>> > +# @node-name: 	  Name of the blockdev to operate on
>> > +# @force:         Disable safety checks (use with care)
>> 
>> What checks excactly are disabled?
> Ability to overwrite an used slot with a different password. 
> If overwrite fails, the image won't be recoverable.
>
> The safe way is to add a new slot, then erase the old
> one, but this changes the slot where the password
> is stored, unless this procedure is used twice

Would this be a useful addition to the doc comment?

>> > +# @options:       Driver specific options
>> > +#
>> > +
>> > +# Since: 4.2
>> > +##
>> > +{ 'command': 'x-blockdev-update-encryption',
>> > +  'data': { 'node-name' : 'str',
>> > +            '*force' : 'bool',
>> > +            'options': 'QCryptoEncryptionSetupOptions' } }
>> > +
>> > +##
>> > +# @x-blockdev-erase-encryption:
>> > +#
>> > +# Erase the encryption keys for an encrypted block device
>> > +#
>> > +# @node-name: 	  Name of the blockdev to operate on
>> > +# @force:         Disable safety checks (use with care)
>> 
>> Likewise.
> 1. Erase a slot which is already marked as
> erased. Mostly harmless but pointless as well.
>
> 2. Erase last keyslot. This irreversibly destroys
> any ability to read the data from the device,
> unless a backup of the header and the key material is
> done prior. Still can be useful when it is desired to
> erase the data fast.

Would this be a useful addition to the doc comment?

>> > +# @options:       Driver specific options
>> > +#
>> > +# Returns: @QCryptoKeyManageResult
>> 
>> Doc comment claims the command returns something, even though it
>> doesn't.  Please fix.  Sadly, the doc generator fails to flag that.
> This is leftover, fixed now although most likely this interface will die.
> I was initially planning to return
> information on which slot was allocated when user left that
> decision to the driver.
>
>> 
>> > +#
>> > +# Since: 4.2
>> > +##
>> > +{ 'command': 'x-blockdev-erase-encryption',
>> > +  'data': { 'node-name' : 'str',
>> > +            '*force' : 'bool',
>> > +            'options': 'QCryptoEncryptionSetupOptions' } }

Hmm, all members of @options are optional.  If I don't want to specify
any of them, I still have to say "options": {}.  Should @options be
optional, too?

Question is not relevant for x-blockdev-update-encryption, because
there, options.key-secret isn't actually optional.  Correct?

>> > diff --git a/qapi/crypto.json b/qapi/crypto.json
>> > index b2a4cff683..69e8b086db 100644
>> > --- a/qapi/crypto.json
>> > +++ b/qapi/crypto.json
>> > @@ -309,3 +309,29 @@
>> >    'base': 'QCryptoBlockInfoBase',
>> >    'discriminator': 'format',
>> >    'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
>> > +
>> > +
>> > +##
>> > +# @QCryptoEncryptionSetupOptions:
>> > +#
>> > +# Driver specific options for encryption key management.
>> 
>> Specific to which driver?
>
> This is the same issue, of not beeing able to detect an union.
>
> I was planning to have an union here where we could add
> add the driver specific options if we need to have another crypto driver,
> however since I discovered that union needs user to pass the driver name,
> I just placed it in a struct.
>
> So this struct is supposed to represent driver specific options, but
> currently contains only luks options.

Imagine we find driver DRV needs options.  How would we extend your QAPI
schema then?

We can add DRV's options to this struct.  QMP clients then must use only
the LUKS members when the driver is actually LUKS, and only the DRV
members when it's actually DRV.  Works as long as all members are
optional.  Confusing, and ugly as sin.

We can add a second struct for DRV's options.  Call it
QCryptoEncryptionSetupOptionsDRV.  Add an optional parameter 'drv':
'QCryptoEncryptionSetupOptionsDRV' to x-blockdev-*-encryption, make
existing parameter @options optional.  Can't rename it to @luks
(compatibility break).  Probably want to rename
QCryptoEncryptionSetupOptions to QCryptoEncryptionSetupOptionsLUKS.  QMP
clients then must use @options when the driver is actually LUKS, and
@drv when it's actually DRV.  Less confusing, still ugly.

I'm not happy with either idea.  Do you have a better one?

>> > +#
>> > +# @key-secret: the ID of a QCryptoSecret object providing the password
>> > +#              to add or to erase (optional for erase)
>> > +#
>> > +# @old-key-secret: the ID of a QCryptoSecret object providing the password
>> > +#                  that can currently unlock the image
>> > +#
>> > +# @slot: Key slot to update/erase
>> > +#        (optional, for update will select a free slot,
>> > +#        for erase will erase all slots that match the password)
>> > +#
>> > +# @iter-time: number of milliseconds to spend in
>> > +#             PBKDF passphrase processing. Currently defaults to 2000

Let's scratch "currently".

>> > +# Since: 4.2
>> > +##
>> > +{ 'struct': 'QCryptoEncryptionSetupOptions',
>> > +  'data': { '*key-secret': 'str',
>> > +            '*old-key-secret': 'str',
>> > +            '*slot': 'int',
>> > +            '*iter-time': 'int' } }
>> 
>> The two new commands have identical arguments.  Some of them you factor
>> out into their own struct.  Can you explain what makes them special?
>
>
> Uniting these means that I need to add some kind of 'action' to the
> options, which is kind of adding a subcommand to a qmp command, which is also feels
> kind of wrong.
>
> That is why internally this is implemented as one block driver callback,
> with action = {erase,update}, but qmp exposes two commands.

Yes, multiplexed commands are ugly more often than not.

> I would personally prefer to have that erase field,and I would have to have
> it, if I switch to the amend interface.
>
>
>> 
>> The extra nesting on the wire is kind of ugly.  We can talk about how to
>> avoid it once I understand why we want the extra struct.
>> 
> I kind of agree with that but The reason for that is that I designed that interface like that is  to be not specific to luks.
>
> I pass the options structure down the stack till it reaches the luks driver where it can deal with
> it. If a new crypto driver is added, all you would have to do is to define new options in the json,
> and use them in the new crypto driver. The rest of the code doesn't know what is in that struct.
> Kind of the same as done with blockdev-create I guess.

Actually, blockdev-create is quite different in one important way: it
uses a *union* for driver-specific arguments, not a struct.

If we make QCryptoEncryptionSetupOptions ab union, like
BlockdevCreateOptions is, then the way to grow it to more drivers is
obvious.

We can talk about nesting later.


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-21 23:59     ` Maxim Levitsky
@ 2019-08-22 14:32       ` Max Reitz
  2019-08-25 10:46         ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Max Reitz @ 2019-08-22 14:32 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 2404 bytes --]

On 22.08.19 01:59, Maxim Levitsky wrote:
> On Tue, 2019-08-20 at 19:36 +0200, Max Reitz wrote:
>> On 14.08.19 22:22, Maxim Levitsky wrote:
>>> This is also a preparation for key read/write/erase functions
>>>
>>> * use master key len from the header
>>> * prefer to use crypto params in the QCryptoBlockLUKS
>>>   over passing them as function arguments
>>> * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
>>> * Add comments to various crypto parameters in the QCryptoBlockLUKS
>>>
>>> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
>>> ---
>>>  crypto/block-luks.c | 213 ++++++++++++++++++++++----------------------
>>>  1 file changed, 105 insertions(+), 108 deletions(-)


[...]

>>> @@ -410,21 +430,15 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
>>>   */
>>>  static int
>>>  qcrypto_block_luks_load_key(QCryptoBlock *block,
>>> -                            QCryptoBlockLUKSKeySlot *slot,
>>> +                            uint slot_idx,
>>
>> Did you use uint on purpose or do you mean a plain “unsigned”?
> Well there are just 8 slots, but yea I don't mind to make this an unsigned int.

My point was that “uint” is not a standard C type.

[...]

>>> @@ -930,6 +922,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>>>              luks_opts.has_ivgen_hash_alg = true;
>>>          }
>>>      }
>>> +
>>> +    luks = g_new0(QCryptoBlockLUKS, 1);
>>> +    block->opaque = luks;
>>> +
>>> +    luks->cipher_alg = luks_opts.cipher_alg;
>>> +    luks->cipher_mode = luks_opts.cipher_mode;
>>> +    luks->ivgen_alg = luks_opts.ivgen_alg;
>>> +    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
>>> +    luks->hash_alg = luks_opts.hash_alg;
>>> +
>>> +
>>
>> Why did you pull this up?  Now @luks is leaked in both of the next error
>> paths.
> 
> This is because the purpose of these fields changed. As Daniel explained to me
> they were initially added after the fact to serve as a cache of into to present in qemu-img info callback.
> But now I use these everywhere in the code so I won't them to be correct as soon as possible to minimize
> the risk of calling some function that uses these and would read garbage.

I get that, but I was wondering why you pulled the allocation of @luks
up above the next two conditional blocks.  Allocating and initializing
there should have worked just fine.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring
  2019-08-22  0:05     ` Maxim Levitsky
@ 2019-08-22 14:34       ` Max Reitz
  2019-08-22 15:04         ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Max Reitz @ 2019-08-22 14:34 UTC (permalink / raw)
  To: Maxim Levitsky, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 871 bytes --]

On 22.08.19 02:05, Maxim Levitsky wrote:
> On Tue, 2019-08-20 at 18:38 +0200, Max Reitz wrote:
>> On 14.08.19 22:22, Maxim Levitsky wrote:
>>> * rename the write_func to create_write_func,
>>>   and init_func to create_init_func
>>>   this is  preparation for other write_func that will
>>>   be used to update the encryption keys.
>>>
>>> No functional changes
>>>
>>> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
>>> ---
>>>  block/crypto.c | 15 ++++++++-------
>>>  1 file changed, 8 insertions(+), 7 deletions(-)
>>>
>>
>> I’m not quite sure why you remove or add blank lines seemingly at random...
> 
> Basically to have consistent two space separation between all functions.
> A bit of OCD I confess :-)

Well, it didn’t work because in one place you added two empty lines
where we already had two, so there are four now.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 12/13] qemu-img: implement key management
  2019-08-22 11:32     ` Daniel P. Berrangé
@ 2019-08-22 14:42       ` Max Reitz
  2019-08-25 17:04         ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Max Reitz @ 2019-08-22 14:42 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Maxim Levitsky, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 1158 bytes --]

On 22.08.19 13:32, Daniel P. Berrangé wrote:
> On Tue, Aug 20, 2019 at 08:29:55PM +0200, Max Reitz wrote:
>> On 14.08.19 22:22, Maxim Levitsky wrote:
>>> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
>>> ---
>>>  block/crypto.c   |  16 ++++++
>>>  block/crypto.h   |   3 +
>>>  qemu-img-cmds.hx |  13 +++++
>>>  qemu-img.c       | 140 +++++++++++++++++++++++++++++++++++++++++++++++
>>>  4 files changed, 172 insertions(+)
>>
>> Yes, this seems a bit weird.  Putting it under amend seems like the
>> natural thing if that works; if not, I think it should be a single
>> qemu-img subcommand instead of two.
> 
> I'm not convinced by overloading two distinct operations on to one
> sub-command - doesn't seem to give an obvious benefit to overload
> them & IME experiance overloading results in harder to understand
> commands due to having distinct args to each command.

Because it suits the qemu-img interface we currently have.  For example,
we have a single subcommand for internal snapshot management (“qemu-img
snapshot”), so I think it makes sense to have a single subcommand for
encrypted image management.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring
  2019-08-22 14:34       ` Max Reitz
@ 2019-08-22 15:04         ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-22 15:04 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Thu, 2019-08-22 at 16:34 +0200, Max Reitz wrote:
> On 22.08.19 02:05, Maxim Levitsky wrote:
> > On Tue, 2019-08-20 at 18:38 +0200, Max Reitz wrote:
> > > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > > * rename the write_func to create_write_func,
> > > >   and init_func to create_init_func
> > > >   this is  preparation for other write_func that will
> > > >   be used to update the encryption keys.
> > > > 
> > > > No functional changes
> > > > 
> > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > ---
> > > >  block/crypto.c | 15 ++++++++-------
> > > >  1 file changed, 8 insertions(+), 7 deletions(-)
> > > > 
> > > 
> > > I’m not quite sure why you remove or add blank lines seemingly at random...
> > 
> > Basically to have consistent two space separation between all functions.
> > A bit of OCD I confess :-)
> 
> Well, it didn’t work because in one place you added two empty lines
> where we already had two, so there are four now.
Exactly :-) 

While the reason I sometimes add/remove black lines between functions
is this, this time this was just a leftover from some stuff I removed and forget
to remove the black lines. Usually prior to sending the patches I 'polish' very
carefully such stuff, but this time since I send up the RFC, I didn't do that 
that well thus various issues like that poped up.


Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 02/13] qcrypto-luks: misc refactoring
  2019-08-22 14:32       ` Max Reitz
@ 2019-08-25 10:46         ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 10:46 UTC (permalink / raw)
  To: Max Reitz, qemu-devel
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, Markus Armbruster, Stefan Hajnoczi

On Thu, 2019-08-22 at 16:32 +0200, Max Reitz wrote:
> On 22.08.19 01:59, Maxim Levitsky wrote:
> > On Tue, 2019-08-20 at 19:36 +0200, Max Reitz wrote:
> > > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > > This is also a preparation for key read/write/erase functions
> > > > 
> > > > * use master key len from the header
> > > > * prefer to use crypto params in the QCryptoBlockLUKS
> > > >   over passing them as function arguments
> > > > * define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME
> > > > * Add comments to various crypto parameters in the QCryptoBlockLUKS
> > > > 
> > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > ---
> > > >  crypto/block-luks.c | 213 ++++++++++++++++++++++----------------------
> > > >  1 file changed, 105 insertions(+), 108 deletions(-)
> 
> 
> [...]
> 
> > > > @@ -410,21 +430,15 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
> > > >   */
> > > >  static int
> > > >  qcrypto_block_luks_load_key(QCryptoBlock *block,
> > > > -                            QCryptoBlockLUKSKeySlot *slot,
> > > > +                            uint slot_idx,
> > > 
> > > Did you use uint on purpose or do you mean a plain “unsigned”?
> > 
> > Well there are just 8 slots, but yea I don't mind to make this an unsigned int.
> 
> My point was that “uint” is not a standard C type.

There are lot of non standard types, like the u8/u8/u32/u64 I used to use in the kernel,
so I kind of missed that. Won't use that type anymore :-)


> 
> [...]
> 
> > > > @@ -930,6 +922,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> > > >              luks_opts.has_ivgen_hash_alg = true;
> > > >          }
> > > >      }
> > > > +
> > > > +    luks = g_new0(QCryptoBlockLUKS, 1);
> > > > +    block->opaque = luks;
> > > > +
> > > > +    luks->cipher_alg = luks_opts.cipher_alg;
> > > > +    luks->cipher_mode = luks_opts.cipher_mode;
> > > > +    luks->ivgen_alg = luks_opts.ivgen_alg;
> > > > +    luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
> > > > +    luks->hash_alg = luks_opts.hash_alg;
> > > > +
> > > > +
> > > 
> > > Why did you pull this up?  Now @luks is leaked in both of the next error
> > > paths.
> > 
> > This is because the purpose of these fields changed. As Daniel explained to me
> > they were initially added after the fact to serve as a cache of into to present in qemu-img info callback.
> > But now I use these everywhere in the code so I won't them to be correct as soon as possible to minimize
> > the risk of calling some function that uses these and would read garbage.
> 
> I get that, but I was wondering why you pulled the allocation of @luks
> up above the next two conditional blocks.  Allocating and initializing
> there should have worked just fine.
Yea, I didn't have to, just thought that putting the initialization as above
as possible is a good thing for future.


> 
> Max
> 

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-22 10:38   ` Daniel P. Berrangé
@ 2019-08-25 14:09     ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 14:09 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 11:38 +0100, Daniel P. Berrangé wrote:
> On Wed, Aug 14, 2019 at 11:22:09PM +0300, Maxim Levitsky wrote:
> > With upcoming key management, the header will
> > need to be stored after the image is created.
> > 
> > Extracting load header isn't strictly needed, but
> > do this anyway for the symmetry.
> > 
> > Also I extracted a function that does basic sanity
> > checks on the just read header, and a function
> > which parses all the crypto format to make the
> > code a bit more readable, plus now the code
> > doesn't destruct the in-header cipher-mode string,
> > so that the header now can be stored many times,
> > which is needed for the key management.
> > 
> > Also this allows to contain the endianess conversions
> > in these functions alone
> > 
> > The header is no longer endian swapped in place,
> > to prevent (mostly theoretical races I think)
> > races where someone could see the header in the
> > process of beeing byteswapped.
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 756 ++++++++++++++++++++++++++------------------
> >  1 file changed, 440 insertions(+), 316 deletions(-)
> >      if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
> >          /* Try to find which key slot our password is valid for
> >           * and unlock the master key from that slot.
> >           */
> > -
> >          masterkey = g_new0(uint8_t, masterkeylen(luks));
> >  
> >          if (qcrypto_block_luks_find_key(block,
> > @@ -845,12 +1132,10 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >      }
> >  
> >      block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> > -    block->payload_offset = luks->header.payload_offset *
> > -        block->sector_size;
> > +    block->payload_offset = luks->header.payload_offset * block->sector_size;
> >  
> >      g_free(masterkey);
> >      g_free(password);
> > -
> >      return 0;
> 
> Smoe unrelated whitespace changes here.
> 
> 
> > +    /* populate the slot 0 with the password encrypted master key*/
> > +    /* This will also store the header */
> > +    if (qcrypto_block_luks_store_key(block,
> > +                                     0,
> > +                                     password,
> > +                                     masterkey,
> > +                                     luks_opts.iter_time,
> > +                                     writefunc,
> > +                                     opaque,
> > +                                     errp)) {
> >          goto error;
> > -    }
> > +     }
> 
> Indent is off by 1
> 
> 
> Regards,
> Daniel


Fixed,


Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions
  2019-08-22 10:34       ` Daniel P. Berrangé
@ 2019-08-25 14:11         ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 14:11 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Stefan Hajnoczi, Max Reitz

On Thu, 2019-08-22 at 11:34 +0100, Daniel P. Berrangé wrote:
> On Thu, Aug 22, 2019 at 01:43:05AM +0300, Maxim Levitsky wrote:
> > On Tue, 2019-08-20 at 20:01 +0200, Max Reitz wrote:
> > > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > > With upcoming key management, the header will
> > > > need to be stored after the image is created.
> > > > 
> > > > Extracting load header isn't strictly needed, but
> > > > do this anyway for the symmetry.
> > > > 
> > > > Also I extracted a function that does basic sanity
> > > > checks on the just read header, and a function
> > > > which parses all the crypto format to make the
> > > > code a bit more readable, plus now the code
> > > > doesn't destruct the in-header cipher-mode string,
> > > > so that the header now can be stored many times,
> > > > which is needed for the key management.
> > > > 
> > > > Also this allows to contain the endianess conversions
> > > > in these functions alone
> > > > 
> > > > The header is no longer endian swapped in place,
> > > > to prevent (mostly theoretical races I think)
> > > > races where someone could see the header in the
> > > > process of beeing byteswapped.
> > > 
> > > The formatting looks weird, it doesn’t look quite 72 characters wide...
> > >  (what commit messages normally use)
> > 
> > Could you elaborate on that? I thought that code should not
> > exceed 80 character limit.
> > 
> > > 
> > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > ---
> > > >  crypto/block-luks.c | 756 ++++++++++++++++++++++++++------------------
> > > >  1 file changed, 440 insertions(+), 316 deletions(-)
> > > 
> > > Also, this commit is just too big.
> > 
> > Yea, but it has no functional changes.
> > I can split it further, but that won't help much IMHO.
> 
> I'd find it easier to review if each newly introduced method was a
> separate patch. It makes it easier to see which bit of removed
> code was added to which method.
> 
> Regards,
> Daniel

Done, patch is now split in several patches.
Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 04/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations
  2019-08-22 10:47   ` Daniel P. Berrangé
@ 2019-08-25 14:30     ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 14:30 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 11:47 +0100, Daniel P. Berrangé wrote:
> On Wed, Aug 14, 2019 at 11:22:10PM +0300, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 64 +++++++++++++++++++++++++++------------------
> >  1 file changed, 38 insertions(+), 26 deletions(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index 6bb369f3b4..e1a4df94b7 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -417,6 +417,33 @@ static int masterkeylen(QCryptoBlockLUKS *luks)
> >  }
> >  
> >  
> > +/*
> > + * Returns number of sectors needed to store the key material
> > + * given number of anti forensic stripes
> > + */
> > +static int splitkeylen_sectors(QCryptoBlockLUKS *luks, int stripes)
> 
> Needs a qcrypto_block_luks_ prefix on method name.
Done.

> 
> I'd also put 'static int' on a separate line from method name
> to reduce too long lines.
Done.
> 
> > +
> > +{
> > +    /*
> > +     * This calculation doesn't match that shown in the spec,
> > +     * but instead follows the cryptsetup implementation.
> > +     */
> > +
> > +    size_t header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > +                     QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> 
> Following line indent should only be 4 spaces
I didn't knew that. Fixed.
> 
> > +
> > +    size_t splitkeylen = masterkeylen(luks) * stripes;
> > +
> > +    /* First align the key material size to block size*/
> > +    size_t splitkeylen_sectors =
> > +            DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE);
> 
> Again 4 space indent.
> 
> > +
> > +    /* Then also align the key material size to the size of the header */
> > +    return ROUND_UP(splitkeylen_sectors, header_sectors);
> > +}
> > +
> > +
> > +
> >  /*
> >   * Stores the main LUKS header, taking care of endianess
> >   */
> > @@ -1169,7 +1196,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >      QCryptoBlockCreateOptionsLUKS luks_opts;
> >      Error *local_err = NULL;
> >      uint8_t *masterkey = NULL;
> > -    size_t splitkeylen = 0;
> > +    size_t next_sector;
> >      size_t i;
> >      char *password;
> >      const char *cipher_alg;
> > @@ -1388,23 +1415,16 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >          goto error;
> >      }
> >  
> > +    /* start with the sector that follows the header*/
> > +    next_sector = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > +                  QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> 
> I'd suggest 'post_header_sector'
I called it now header_sectors, and each split key, split_key_sectors.
I hope that this is good enough.
> 
> >  
> > -    /* Although LUKS has multiple key slots, we're just going
> > -     * to use the first key slot */
> > -    splitkeylen = luks->header.key_bytes * QCRYPTO_BLOCK_LUKS_STRIPES;
> >      for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> > -        luks->header.key_slots[i].active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
> > -        luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
> > -
> > -        /* This calculation doesn't match that shown in the spec,
> > -         * but instead follows the cryptsetup implementation.
> > -         */
> > -        luks->header.key_slots[i].key_offset =
> > -            (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > -             QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
> > -            (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
> > -                      (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > -                       QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
> > +        QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[i];
> > +        slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
> > +        slot->key_offset = next_sector;
> > +        slot->stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
> > +        next_sector += splitkeylen_sectors(luks, QCRYPTO_BLOCK_LUKS_STRIPES);
> 
> I'm not a fan of the next_sector accumulator .
I actually think that accumulator is cleaner here,
but I won't argue about this. Fixed.


> 
> I'd prefer to see the '* i' part done in splitkeylen_sectors, so that
> we have
> 
>   slot->key_offset = post_header_sector +
>         splitkeylen_sectors(luks, QCRYPTO_BLOCK_LUKS_STRIPES, i);
> 
> > @@ -1412,17 +1432,9 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >       * slot headers, rounded up to the nearest sector, combined with
> >       * the size of each master key material region, also rounded up
> >       * to the nearest sector */
> > -    luks->header.payload_offset =
> > -        (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > -         QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
> > -        (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
> > -                  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > -                   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) *
> > -         QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
> > -
> > +    luks->header.payload_offset = next_sector;
> 
>   luks->header.payload_offset = post_header_sector +
>         splitkeylen_sectors(luks, QCRYPTO_BLOCK_LUKS_STRIPES,
> 	                    QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
> 
> 
> >      block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> > -    block->payload_offset = luks->header.payload_offset *
> > -        block->sector_size;
> > +    block->payload_offset = luks->header.payload_offset * block->sector_size;
> 
> 
> This is reverting a whitspace change done in previous method
True. Fixed.

> 
> Regards,
> Daniel


Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-22 10:56       ` Maxim Levitsky
@ 2019-08-25 15:31         ` Maxim Levitsky
  2019-08-25 17:15           ` Maxim Levitsky
  2019-08-27  8:55           ` Daniel P. Berrangé
  0 siblings, 2 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 15:31 UTC (permalink / raw)
  To: Daniel P. Berrangé, Max Reitz
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Nir Soffer, Stefan Hajnoczi

On Thu, 2019-08-22 at 13:56 +0300, Maxim Levitsky wrote:
> On Thu, 2019-08-22 at 11:49 +0100, Daniel P. Berrangé wrote:
> > On Tue, Aug 20, 2019 at 08:12:51PM +0200, Max Reitz wrote:
> > > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > > While there are other places where these are still stored in memory,
> > > > this is still one less key material area that can be sniffed with
> > > > various side channel attacks
> > > > 
> > > > 
> > > > 
> > > 
> > > (Many empty lines here)
> > > 
> > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > ---
> > > >  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
> > > >  1 file changed, 44 insertions(+), 8 deletions(-)
> > > 
> > > Wouldn’t it make sense to introduce a dedicated function for this?
> > 
> > Yes, it would.
> > 
> > In fact I have a series pending which bumps min glib and introduces
> > use of auto-free functions in this code.
> > 
> > It would be desirable to have a autp-free func for memset+free
> > so we can just declare the variable
> > 
> >    q_autowipefree char *password = NULL;
> > 
> > and have it result in memset+free
> > 
> 
> That is perfect.
> When do you think you could post the series so that I could rebase
> on top of it?


I am thinking that I will keep my patch as is, just so that code is
consistent in memsetting the secrets (even though as Nir pointed out,
that these will be probably optimized away anyway).
And then when you send your patch you will just remove all
of these memsets.

Is this all right? 

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking
  2019-08-22 11:04   ` Daniel P. Berrangé
@ 2019-08-25 15:40     ` Maxim Levitsky
  2019-08-25 16:08       ` Maxim Levitsky
  0 siblings, 1 reply; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 15:40 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 12:04 +0100, Daniel P. Berrangé wrote:
> On Wed, Aug 14, 2019 at 11:22:12PM +0300, Maxim Levitsky wrote:
> > Check that keyslots don't overlap with the data,
> > and check that keyslots don't overlap with each other.
> > (this is done using naive O(n^2) nested loops,
> > but since there are just 8 keyslots, this doens't really matter.
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 42 insertions(+)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index 336e633df4..1997e92fe1 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -551,6 +551,8 @@ static int
> >  qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
> >  {
> >      int ret;
> > +    int i, j;
> > +
> >  
> >      if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
> >                 QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
> > @@ -566,6 +568,46 @@ qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
> >          goto fail;
> >      }
> >  
> > +    /* Check all keyslots for corruption  */
> > +    for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
> > +
> > +        QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
> > +        uint start1 = slot1->key_offset;
> > +        uint len1 = splitkeylen_sectors(luks, slot1->stripes);
> 
> Using 'uint' is not normal QEMU style.
> 
> Either use 'unsigned int'  or if a specific size is needed
> then one of the 'guintNN' types from glib.
> 
> This applies elsewhere in this patch series too, but
> I'll only comment here & let you find the other cases.

Fixed. Sorry for the noise.

> 
> > +
> > +        if (slot1->stripes == 0 ||
> > +                (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
> > +                slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED)) {
> > +
> 
> Redundant blank line
Fixed
> 
> > +            error_setg(errp, "Keyslot %i is corrupted", i);
> 
> I'd do a separate check for stripes and active fields, and then give a
> specific error message for each. That way if this does ever trigger
> in practice will immediately understand which check failed.
> 
> Also using '%d' rather than '%i' is more common convention
Done.
> 
> 
> > +            ret = -EINVAL;
> > +            goto fail;
> > +        }
> > +
> > +        if (start1 + len1 > luks->header.payload_offset) {
> > +            error_setg(errp,
> > +                       "Keyslot %i is overlapping with the encrypted payload",
> > +                       i);
> > +            ret = -EINVAL;
> > +            goto fail;
> > +        }
> > +
> > +        for (j = i + 1 ; j < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; j++) {
> > +
> 
> Redundant blank
> 
> > +            QCryptoBlockLUKSKeySlot *slot2 = &luks->header.key_slots[j];
> > +            uint start2 = slot2->key_offset;
> > +            uint len2 = splitkeylen_sectors(luks, slot2->stripes);
> > +
> > +            if (start1 + len1 > start2 && start2 + len2 > start1) {
> > +                error_setg(errp,
> > +                           "Keyslots %i and %i are overlapping in the header",
> 
> %d
Fixed.
> 
> > +                           i, j);
> > +                ret = -EINVAL;
> > +                goto fail;
> > +            }
> > +        }
> > +
> > +    }
> >      return 0;
> >  fail:
> >      return ret;
> > -- 
> > 2.17.2
> > 
> 
> Regards,
> Daniel


Best regards,
	Maxim Levitsky




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking
  2019-08-25 15:40     ` Maxim Levitsky
@ 2019-08-25 16:08       ` Maxim Levitsky
  2019-08-26 13:31         ` Eric Blake
  2019-08-27  8:56         ` Daniel P. Berrangé
  0 siblings, 2 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 16:08 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Max Reitz, Stefan Hajnoczi

On Sun, 2019-08-25 at 18:40 +0300, Maxim Levitsky wrote:
> On Thu, 2019-08-22 at 12:04 +0100, Daniel P. Berrangé wrote:
> > On Wed, Aug 14, 2019 at 11:22:12PM +0300, Maxim Levitsky wrote:
> > > Check that keyslots don't overlap with the data,
> > > and check that keyslots don't overlap with each other.
> > > (this is done using naive O(n^2) nested loops,
> > > but since there are just 8 keyslots, this doens't really matter.
> > > 
> > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > ---
> > >  crypto/block-luks.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 42 insertions(+)
> > > 
> > > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > > index 336e633df4..1997e92fe1 100644
> > > --- a/crypto/block-luks.c
> > > +++ b/crypto/block-luks.c
> > > @@ -551,6 +551,8 @@ static int
> > >  qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
> > >  {
> > >      int ret;
> > > +    int i, j;
> > > +
> > >  
> > >      if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
> > >                 QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
> > > @@ -566,6 +568,46 @@ qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
> > >          goto fail;
> > >      }
> > >  
> > > +    /* Check all keyslots for corruption  */
> > > +    for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
> > > +
> > > +        QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
> > > +        uint start1 = slot1->key_offset;
> > > +        uint len1 = splitkeylen_sectors(luks, slot1->stripes);
> > 
> > Using 'uint' is not normal QEMU style.
> > 
> > Either use 'unsigned int'  or if a specific size is needed
> > then one of the 'guintNN' types from glib.
> > 
> > This applies elsewhere in this patch series too, but
> > I'll only comment here & let you find the other cases.
> 
> Fixed. Sorry for the noise.
> 
> > 
> > > +
> > > +        if (slot1->stripes == 0 ||
> > > +                (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
> > > +                slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED)) {
> > > +
> > 
> > Redundant blank line
> 
> Fixed
> > 
> > > +            error_setg(errp, "Keyslot %i is corrupted", i);
> > 
> > I'd do a separate check for stripes and active fields, and then give a
> > specific error message for each. That way if this does ever trigger
> > in practice will immediately understand which check failed.
> > 
> > Also using '%d' rather than '%i' is more common convention
> 
> Done.

Note that I switched i,j to be size_t since you said that you prefer this,
and to print this I apparently need %lu.


[...]

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev)
  2019-08-22 14:07       ` Markus Armbruster
@ 2019-08-25 16:42         ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 16:42 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Fam Zheng, Daniel P. Berrangé,
	qemu-block, qemu-devel, Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 16:07 +0200, Markus Armbruster wrote:
> Maxim Levitsky <mlevitsk@redhat.com> writes:
> 
> > On Wed, 2019-08-21 at 13:47 +0200, Markus Armbruster wrote:
> > > Maxim Levitsky <mlevitsk@redhat.com> writes:
> > > 
> > > > This adds:
> > > > 
> > > > * x-blockdev-update-encryption and x-blockdev-erase-encryption qmp commands
> > > >   Both commands take the QCryptoKeyManageOptions
> > > >   the x-blockdev-update-encryption is meant for non destructive addition
> > > >   of key slots / whatever the encryption driver supports in the future
> > > > 
> > > >   x-blockdev-erase-encryption is meant for destructive encryption key erase,
> > > >   in some cases even without way to recover the data.
> > > > 
> > > > 
> > > > * bdrv_setup_encryption callback in the block driver
> > > >   This callback does both the above functions with 'action' parameter
> > > > 
> > > > * QCryptoKeyManageOptions with set of options that drivers can use for encryption managment
> > > >   Currently it has all the options that LUKS needs, and later it can be extended
> > > >   (via union) to support more encryption drivers if needed
> > > > 
> > > > * blk_setup_encryption / bdrv_setup_encryption - the usual block layer wrappers.
> > > >   Note that bdrv_setup_encryption takes BlockDriverState and not BdrvChild,
> > > >   for the ease of use from the qmp code. It is not expected that this function
> > > >   will be used by anything but qmp and qemu-img code
> > > > 
> > > > 
> > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > 
> > > [...]
> > > > diff --git a/qapi/block-core.json b/qapi/block-core.json
> > > > index 0d43d4f37c..53ed411eed 100644
> > > > --- a/qapi/block-core.json
> > > > +++ b/qapi/block-core.json
> > > > @@ -5327,3 +5327,39 @@
> > > >    'data' : { 'node-name': 'str',
> > > >               'iothread': 'StrOrNull',
> > > >               '*force': 'bool' } }
> > > > +
> > > > +
> > > > +##
> > > > +# @x-blockdev-update-encryption:
> > > > +#
> > > > +# Update the encryption keys for an encrypted block device
> > > > +#
> > > > +# @node-name: 	  Name of the blockdev to operate on
> > > > +# @force:         Disable safety checks (use with care)
> > > 
> > > What checks excactly are disabled?
> > 
> > Ability to overwrite an used slot with a different password. 
> > If overwrite fails, the image won't be recoverable.
> > 
> > The safe way is to add a new slot, then erase the old
> > one, but this changes the slot where the password
> > is stored, unless this procedure is used twice
> 
> Would this be a useful addition to the doc comment?
> 
> > > > +# @options:       Driver specific options
> > > > +#
> > > > +
> > > > +# Since: 4.2
> > > > +##
> > > > +{ 'command': 'x-blockdev-update-encryption',
> > > > +  'data': { 'node-name' : 'str',
> > > > +            '*force' : 'bool',
> > > > +            'options': 'QCryptoEncryptionSetupOptions' } }
> > > > +
> > > > +##
> > > > +# @x-blockdev-erase-encryption:
> > > > +#
> > > > +# Erase the encryption keys for an encrypted block device
> > > > +#
> > > > +# @node-name: 	  Name of the blockdev to operate on
> > > > +# @force:         Disable safety checks (use with care)
> > > 
> > > Likewise.
> > 
> > 1. Erase a slot which is already marked as
> > erased. Mostly harmless but pointless as well.
> > 
> > 2. Erase last keyslot. This irreversibly destroys
> > any ability to read the data from the device,
> > unless a backup of the header and the key material is
> > done prior. Still can be useful when it is desired to
> > erase the data fast.
> 
> Would this be a useful addition to the doc comment?
Yea, but since I'll will switch to the amend interface,
I'll leave it like that for now.


[...]


Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 09/13] qcrypto-luks: implement the encryption key management
  2019-08-22 11:27   ` Daniel P. Berrangé
@ 2019-08-25 17:01     ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 17:01 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 12:27 +0100, Daniel P. Berrangé wrote:
> On Wed, Aug 14, 2019 at 11:22:15PM +0300, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > ---
> >  crypto/block-luks.c | 374 +++++++++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 373 insertions(+), 1 deletion(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index 1997e92fe1..2c33643b52 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -72,6 +72,8 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
> >  
> >  #define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME 2000
> >  
> > +#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40
> > +
> >  static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
> >      'L', 'U', 'K', 'S', 0xBA, 0xBE
> >  };
> > @@ -221,6 +223,9 @@ struct QCryptoBlockLUKS {
> >  
> >      /* Hash algorithm used in pbkdf2 function */
> >      QCryptoHashAlgorithm hash_alg;
> > +
> > +    /* Name of the secret that was used to open the image */
> > +    char *secret;
> >  };
> >  
> >  
> > @@ -1121,6 +1126,194 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
> >  }
> >  
> >  
> > +
> > +/*
> > + * Returns true if a slot i is marked as containing as active
> 
> s/as containing//
> 
> > + * (contains encrypted copy of the master key)
> > + */
> > +
> > +static bool
> > +qcrypto_block_luks_slot_active(QCryptoBlockLUKS *luks, int slot_idx)
> > +{
> > +    uint32_t val = luks->header.key_slots[slot_idx].active;
> > +    return val ==  QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
> > +}
> > +
> > +/*
> > + * Returns the number of slots that are marked as active
> > + * (contains encrypted copy of the master key)
> > + */
> > +
> > +static int
> > +qcrypto_block_luks_count_active_slots(QCryptoBlockLUKS *luks)
> > +{
> > +    int i, ret = 0;
> 
> I prefer to see 'size_t' for loop iterators 
Done

> 
> > +
> > +    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> > +        if (qcrypto_block_luks_slot_active(luks, i)) {
> > +            ret++;
> > +        }
> > +    }
> > +    return ret;
> > +}
> > +
> > +
> > +/*
> > + * Finds first key slot which is not active
> > + * Returns the key slot index, or -1 if doesn't exist
> > + */
> > +
> > +static int
> > +qcrypto_block_luks_find_free_keyslot(QCryptoBlockLUKS *luks)
> > +{
> > +    uint i;
> > +
> > +    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> > +        if (!qcrypto_block_luks_slot_active(luks, i)) {
> > +            return i;
> > +        }
> > +    }
> > +    return -1;
> > +
> > +}
> > +
> > +/*
> > + * Erases an keyslot given its index
> > + *
> > + * Returns:
> > + *    0 if the keyslot was erased successfully
> > + *   -1 if a error occurred while erasing the keyslot
> > + *
> > + */
> > +
> 
> Redundant blank line
Done
> 
> > +static int
> > +qcrypto_block_luks_erase_key(QCryptoBlock *block,
> > +                             uint slot_idx,
> > +                             QCryptoBlockWriteFunc writefunc,
> > +                             void *opaque,
> > +                             Error **errp)
> > +{
> > +    QCryptoBlockLUKS *luks = block->opaque;
> > +    QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
> > +    uint8_t *garbagekey = NULL;
> > +    size_t splitkeylen = masterkeylen(luks) * slot->stripes;
> > +    int i;
> > +    int ret = -1;
> > +
> > +    assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
> > +    assert(splitkeylen > 0);
> > +
> > +    garbagekey = g_malloc0(splitkeylen);
> > +
> > +    /* Reset the key slot header */
> > +    memset(slot->salt, 0, QCRYPTO_BLOCK_LUKS_SALT_LEN);
> > +    slot->iterations = 0;
> > +    slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
> > +
> > +    qcrypto_block_luks_store_header(block,  writefunc, opaque, errp);
> > +
> > +    /*
> > +     * Now try to erase the key material, even if the header
> > +     * update failed
> > +     */
> > +
> 
> Redundant blank line
Fixed.
> 
> > +    for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS ; i++) {
> > +        if (qcrypto_random_bytes(garbagekey, splitkeylen, errp) < 0) {
> > +
> 
> Again, many more times beelow.
> 
> > +                /*
> > +                 * If we failed to get the random data, still write
> > +                 * *something* to the key slot at least once
> > +                 */
> 
> Specificially  we write all-zeros, since garbagekey was allocated
> with g_malloc0
Clarified.
> 
> > +
> > +                if (i > 0) {
> > +                    goto cleanup;
> > +                }
> > +        }
> > +
> > +        if (writefunc(block, slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
> > +                  garbagekey,
> > +                  splitkeylen,
> > +                  opaque,
> > +                  errp) != splitkeylen) {
> 
> Indent is off - align with '('
Fixed.
> 
> > +            goto cleanup;
> > +        }
> > +    }
> > +
> > +    ret = 0;
> > +cleanup:
> > +    g_free(garbagekey);
> > +    return ret;
> > +}
> > +
> > +
> > +/*
> > + * Erase all the keys that match the given password
> > + * Will stop when only one keyslot is remaining
> > + * Returns 0 is some keys were erased or -1 on failure
> > + */
> > +
> > +static int
> > +qcrypto_block_luks_erase_matching_keys(QCryptoBlock *block,
> > +                             const char *password,
> > +                             QCryptoBlockReadFunc readfunc,
> > +                             QCryptoBlockWriteFunc writefunc,
> > +                             void *opaque,
> > +                             bool force,
> > +                             Error **errp)
> 
> Indent is off again
Fixed. Sorry for all the coding errors.
> 
> > +{
> > +    QCryptoBlockLUKS *luks = block->opaque;
> > +    uint i;
> > +    int rv, ret = -1;
> > +    uint8_t *masterkey;
> > +    uint erased_count = 0;
> > +    uint active_slot_count = qcrypto_block_luks_count_active_slots(luks);
> > +
> > +    masterkey = g_new0(uint8_t, masterkeylen(luks));
> > +
> > +    for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> > +
> > +        /* refuse to erase last key if not forced */
> > +        if (!force && active_slot_count == 1) {
> > +            break;
> > +        }
> 
> In the case where there was only 1 keyslot to start with,
> his break will cause us to report
> 
>         error_setg(errp, "Didn't erase a keyslot, because no keyslots "
>                          "match the given password");
> 
> which is horribly misleading.
> 
> Before entering the loop we should check this case and report
> a saner message.

Actually this won't happen, since qcrypto_block_luks_erase_matching_keys
won't be called in this case - the caller right at the start of the erase code
has this:

        if (qcrypto_block_luks_count_active_slots(luks) <= 1) {

            if (!force) {
                error_setg(errp, "Only one slot active - can't erase");
                goto cleanup;
            }
        }




> 
> > +
> > +        rv = qcrypto_block_luks_load_key(block,
> > +                                         i,
> > +                                         password,
> > +                                         masterkey,
> > +                                         readfunc,
> > +                                         opaque,
> > +                                         errp);
> > +        if (rv < 0) {
> > +            goto cleanup;
> > +        }
> > +        if (rv == 0) {
> > +            continue;
> > +        }
> > +
> > +        rv = qcrypto_block_luks_erase_key(block, i, writefunc, opaque, errp);
> > +        if (rv < 0) {
> > +            goto cleanup;
> > +        }
> > +
> > +        erased_count++;
> > +        active_slot_count--;
> > +    }
> > +
> > +    if (!erased_count) {
> > +        error_setg(errp, "Didn't erase a keyslot, because no keyslots "
> > +                         "match the given password");
> > +        goto cleanup;
> > +    }
> > +
> > +    ret = 0;
> > +cleanup:
> > +    memset(masterkey, 0, masterkeylen(luks));
> > +    g_free(masterkey);
> > +    return ret;
> > +}
> > +
> > +
> >  static int
> >  qcrypto_block_luks_open(QCryptoBlock *block,
> >                          QCryptoBlockOpenOptions *options,
> > @@ -1151,6 +1344,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
> >  
> >      luks = g_new0(QCryptoBlockLUKS, 1);
> >      block->opaque = luks;
> > +    luks->secret = g_strdup(options->u.luks.key_secret);
> >  
> >      ret = qcrypto_block_luks_load_header(block, readfunc, opaque, errp);
> >      if (ret) {
> > @@ -1320,6 +1514,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >                     optprefix ? optprefix : "");
> >          return -1;
> >      }
> > +    luks->secret = options->u.luks.key_secret;
> > +
> >      password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
> >      if (!password) {
> >          return -1;
> > @@ -1605,7 +1801,9 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
> >  
> >  static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
> >  {
> > -    g_free(block->opaque);
> > +    QCryptoBlockLUKS *luks = block->opaque;
> > +    g_free(luks->secret);
> > +    g_free(luks);
> >  }
> >  
> >  
> > @@ -1638,6 +1836,179 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block,
> >                                          offset, buf, len, errp);
> >  }
> >  
> > +static int
> > +qcrypto_block_luks_setup_encryption(QCryptoBlock *block,
> > +                                    QCryptoBlockReadFunc readfunc,
> > +                                    QCryptoBlockWriteFunc writefunc,
> > +                                    void *opaque,
> > +                                    enum BlkSetupEncryptionAction action,
> > +                                    QCryptoEncryptionSetupOptions *options,
> > +                                    bool force,
> > +                                    Error **errp)
> > +{
> > +    QCryptoBlockLUKS *luks = block->opaque;
> > +    char *old_password = NULL;
> > +    char *password = NULL;
> > +    const char *old_secret = luks->secret;
> > +    uint8_t *masterkey = NULL;
> > +    int slot = -1;
> > +    int ret = -1;
> > +    int64_t iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME;
> > +
> > +    /* Read given slot and check it */
> > +    if (options->has_slot) {
> > +        slot = options->slot;
> > +        if (slot < 0 || slot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
> > +            error_setg(errp,
> > +                       "Given key slot %i is not supported by LUKS", slot);
> > +             goto cleanup;
> > +        }
> > +    }
> > +
> > +    if (options->has_iter_time) {
> > +        iter_time = options->iter_time;
> > +    }
> > +
> > +    switch (action) {
> > +
> > +    case BLK_UPDATE_ENCRYPTION:
> 
> 
> The fact that the vast majority of code in this method is inside
> a giant switch just reinforces that we should have separate APIs
> for erase vs adding keys.
Both yes and no.
It is true that for luks based encryption we indeed need
two apis. but for other encryptions we might need more or less
Putting it all in one function/callback, with action
allows to hide these details from the rest of the qemu.
This is very different from say a read/write/encrypt/decrypt/create/etc
callbacks where it is obvious that these are common to all the drivers.
I could imagine a encryption format that for instance has some certificates,
which could be changed/revoked/etc, which would need another action
to be added, or something like that.

Anyway if I use amend interface, it is a single interface which
I will have to adapt to all the luks needs.


> 
> > +
> > +        /* Check that we are not overwriting an active slot */
> > +        if (!force && slot != -1 &&
> > +                    qcrypto_block_luks_slot_active(luks, slot)) {
> > +
> > +            error_setg(errp, "Can't update an active key slot %i",
> > +                       slot);
> > +            goto cleanup;
> > +        }
> > +
> > +        /* check that we have the passwords*/
> > +        if (!options->has_key_secret) {
> > +            error_setg(errp, "Can't add a key slot without a  password");
> > +            goto cleanup;
> > +        }
> > +
> > +        if (options->has_old_key_secret) {
> > +            old_secret = options->old_key_secret;
> > +        }
> > +
> > +        /* Read the old password */
> > +        old_password = qcrypto_secret_lookup_as_utf8(old_secret, errp);
> > +        if (!old_password) {
> > +            goto cleanup;
> > +        }
> > +
> > +        masterkey = g_new0(uint8_t, masterkeylen(luks));
> > +
> > +        /* Retrieve the master key*/
> > +        if (qcrypto_block_luks_find_key(block, old_password, masterkey,
> > +                                    readfunc, opaque, errp) < 0) {
> 
> Indent is off
True and sorry about that

> 
> > +            error_append_hint(errp,
> > +                       "Old password that was given doesn't unlock the image");
> 
> And again, throughout this patch
True :-(
> 
> > +            goto cleanup;
> > +        }
> > +
> > +        /* Read the new password*/
> > +        password = qcrypto_secret_lookup_as_utf8(options->key_secret, errp);
> > +        if (!password) {
> > +            goto cleanup;
> > +        }
> > +
> > +        /* Find the new slot to write to */
> > +        if (slot == -1) {
> > +            slot = qcrypto_block_luks_find_free_keyslot(luks);
> > +
> > +            if (slot == -1) {
> > +                error_setg(errp,
> > +                        "Can't add a keyslot - all key slots are in use");
> > +                goto cleanup;
> > +
> > +            }
> > +        }
> > +
> > +        /* Store the master key to the new slot */
> > +        if (qcrypto_block_luks_store_key(block, slot, password,
> > +                                     masterkey, iter_time,
> > +                                     writefunc, opaque, errp)) {
> > +
> > +            error_append_hint(errp, "Failed to store the keyslot %i", slot);
> > +            goto cleanup;
> > +        }
> > +        break;
> > +
> > +    case BLK_ERASE_ENCRYPTION:
> > +
> > +        /* Check that we are not erasing last key slot */
> > +        if (qcrypto_block_luks_count_active_slots(luks) <= 1) {
> > +
> > +            if (!force) {
> > +                error_setg(errp, "Only one slot active - can't erase");
> > +                goto cleanup;
> > +            }
> > +        }
> > +
> > +        if (slot != -1) {
> > +            /* Check that we are not erasing an inactive slot */
> > +            if (!qcrypto_block_luks_slot_active(luks, options->slot)) {
> > +                if (!force) {
> > +                    error_setg(errp, "Can't erase an inactive key slot %i",
> > +                               slot);
> > +                    goto cleanup;
> > +                }
> > +            }
> > +
> > +            /* Erase the given slot */
> > +            if (qcrypto_block_luks_erase_key(block, slot,
> > +                                             writefunc, opaque, errp)) {
> > +                goto cleanup;
> > +            }
> > +
> > +        } else {
> > +            if (!options->has_key_secret) {
> > +                error_setg(errp,
> > +                           "To erase a keyslot you have to specify either the"
> > +                           "slot index or a password "
> > +                           "(to erase all slots that match it)");
> > +                goto cleanup;
> > +            }
> > +
> > +            password = qcrypto_secret_lookup_as_utf8(options->key_secret, errp);
> > +            if (!password) {
> > +                goto cleanup;
> > +            }
> > +
> > +            if (qcrypto_block_luks_erase_matching_keys(block, password,
> > +                                                       readfunc, writefunc,
> > +                                                       opaque, force, errp)) {
> > +                goto cleanup;
> > +            }
> > +        }
> > +        break;
> > +
> > +    default:
> > +        goto cleanup;
> > +    }
> > +
> > +    ret = 0;
> > +cleanup:
> > +
> > +    if (old_password) {
> > +        memset(old_password, 0, strlen(old_password));
> > +        g_free(old_password);
> > +    }
> > +
> > +    if (password) {
> > +        memset(password, 0, strlen(password));
> > +        g_free(password);
> > +    }
> > +
> > +    if (masterkey) {
> > +        memset(masterkey, 0,  masterkeylen(luks));
> > +        g_free(masterkey);
> > +    }
> > +    return ret;
> > +}
> > +
> >  
> >  const QCryptoBlockDriver qcrypto_block_driver_luks = {
> >      .open = qcrypto_block_luks_open,
> > @@ -1647,4 +2018,5 @@ const QCryptoBlockDriver qcrypto_block_driver_luks = {
> >      .decrypt = qcrypto_block_luks_decrypt,
> >      .encrypt = qcrypto_block_luks_encrypt,
> >      .has_format = qcrypto_block_luks_has_format,
> > +    .setup_encryption = qcrypto_block_luks_setup_encryption,
> >  };
> > -- 
> > 2.17.2
> > 
> 
> Regards,
> Daniel


Thank you very much for the review,
	Best regards,
		Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 12/13] qemu-img: implement key management
  2019-08-22 14:42       ` Max Reitz
@ 2019-08-25 17:04         ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 17:04 UTC (permalink / raw)
  To: Max Reitz, Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Stefan Hajnoczi

On Thu, 2019-08-22 at 16:42 +0200, Max Reitz wrote:
> On 22.08.19 13:32, Daniel P. Berrangé wrote:
> > On Tue, Aug 20, 2019 at 08:29:55PM +0200, Max Reitz wrote:
> > > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > ---
> > > >  block/crypto.c   |  16 ++++++
> > > >  block/crypto.h   |   3 +
> > > >  qemu-img-cmds.hx |  13 +++++
> > > >  qemu-img.c       | 140 +++++++++++++++++++++++++++++++++++++++++++++++
> > > >  4 files changed, 172 insertions(+)
> > > 
> > > Yes, this seems a bit weird.  Putting it under amend seems like the
> > > natural thing if that works; if not, I think it should be a single
> > > qemu-img subcommand instead of two.
> > 
> > I'm not convinced by overloading two distinct operations on to one
> > sub-command - doesn't seem to give an obvious benefit to overload
> > them & IME experiance overloading results in harder to understand
> > commands due to having distinct args to each command.
> 
> Because it suits the qemu-img interface we currently have.  For example,
> we have a single subcommand for internal snapshot management (“qemu-img
> snapshot”), so I think it makes sense to have a single subcommand for
> encrypted image management.

I personally don't care, other that I do thing that the best here is to use
the amend interface.

> 
> Max
> 

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management
  2019-08-22 11:35 ` Daniel P. Berrangé
@ 2019-08-25 17:10   ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 17:10 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Max Reitz, Stefan Hajnoczi

On Thu, 2019-08-22 at 12:35 +0100, Daniel P. Berrangé wrote:
> On Wed, Aug 14, 2019 at 11:22:06PM +0300, Maxim Levitsky wrote:
> > Hi!
> > 
> > This patch series implements key management for luks based encryption
> > It supports both raw luks images and qcow2 encrypted images.
> > 
> > Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1731898
> > 
> > There are still several issues that need to be figured out,
> > on which the feedback is very welcome, but other than that the code mostly works.
> > 
> > The main issues are:
> > 
> > 1. Instead of the proposed blockdev-update-encryption/blockdev-erase-encryption
> > interface, it is probably better to implement 'blockdev-amend-options' in qmp,
> > and use this both for offline and online key update (with some translation
> > layer to convert the qemu-img 'options' to qmp structures)
> > 
> > This interface already exists for offline qcow2 format options update/
> > 
> > This is an issue that was raised today on IRC with Kevin Wolf. Really thanks
> > for the idea!
> > 
> > We agreed that this new qmp interface should take the same options as
> > blockdev-create does, however since we want to be able to edit the encryption
> > slots separately, this implies that we sort of need to allow this on creation
> > time as well.
> > 
> > Also the BlockdevCreateOptions is a union, which is specialized by the driver name
> > which is great for creation, but for update, the driver name is already known,
> > and thus the user should not be forced to pass it again.
> > However qmp doesn't seem to support union type guessing based on actual fields
> > given (this might not be desired either), which complicates this somewhat.
> 
> Given this design question around the integration into blockdev, I'd
> suggest splitting the series into two parts.
> 
> One series should do all the work in crypto/ code to support adding
> and erasing key slots.
> 
> One series should focus on block/ layer QMP/qemu-img integration.
> 
> The block layer QAPI stuff shouldn't leak into the crypto/ code.
> 
> So this will let us get on with reviewing & unit testing the
> crypto code, while we discuss block layer design options in more
> detail.
> 
> Regards,
> Daniel


I think we need 3 series here.


1. All the re-factoring/preparation work I done in luks crypto driver, which can be merged
now, pending minor changes from the review.
I think that it at least doesn't make the code worse.

2. Common code for the block layer to support key management this way or another,
   can be even added with not a single driver implementing it.

1,2 don't depend on each other mostly.


3. Key management in LUKS, which needs both 1,2, but thankfully is mostly implemented,
and won't need to change much from the current implementation.


So I'll send 1 now, and I will star working on 2.

Last week we (I and Daniel) defined a draft of amend interface,
and if time permits we will work on that tomorrow to finalize the
interface.

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-25 15:31         ` Maxim Levitsky
@ 2019-08-25 17:15           ` Maxim Levitsky
  2019-08-27  8:55           ` Daniel P. Berrangé
  1 sibling, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-25 17:15 UTC (permalink / raw)
  To: Daniel P. Berrangé, Max Reitz
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Nir Soffer, Stefan Hajnoczi

On Sun, 2019-08-25 at 18:31 +0300, Maxim Levitsky wrote:
> On Thu, 2019-08-22 at 13:56 +0300, Maxim Levitsky wrote:
> > On Thu, 2019-08-22 at 11:49 +0100, Daniel P. Berrangé wrote:
> > > On Tue, Aug 20, 2019 at 08:12:51PM +0200, Max Reitz wrote:
> > > > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > > > While there are other places where these are still stored in memory,
> > > > > this is still one less key material area that can be sniffed with
> > > > > various side channel attacks
> > > > > 
> > > > > 
> > > > > 
> > > > 
> > > > (Many empty lines here)
> > > > 
> > > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > > ---
> > > > >  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
> > > > >  1 file changed, 44 insertions(+), 8 deletions(-)
> > > > 
> > > > Wouldn’t it make sense to introduce a dedicated function for this?
> > > 
> > > Yes, it would.
> > > 
> > > In fact I have a series pending which bumps min glib and introduces
> > > use of auto-free functions in this code.
> > > 
> > > It would be desirable to have a autp-free func for memset+free
> > > so we can just declare the variable
> > > 
> > >    q_autowipefree char *password = NULL;
> > > 
> > > and have it result in memset+free
> > > 
> > 
> > That is perfect.
> > When do you think you could post the series so that I could rebase
> > on top of it?
> 
> 
> I am thinking that I will keep my patch as is, just so that code is
> consistent in memsetting the secrets (even though as Nir pointed out,
> that these will be probably optimized away anyway).
> And then when you send your patch you will just remove all
> of these memsets.
> 
> Is this all right? 

I see that your series actually already got merged.
Can I now implement the 'q_autowipefree', or do I need another glib version bump
for that?

Best regards,
	Maxim Levitsky


> 
> Best regards,
> 	Maxim Levitsky




^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking
  2019-08-25 16:08       ` Maxim Levitsky
@ 2019-08-26 13:31         ` Eric Blake
  2019-08-26 13:39           ` Maxim Levitsky
  2019-08-27  8:56         ` Daniel P. Berrangé
  1 sibling, 1 reply; 87+ messages in thread
From: Eric Blake @ 2019-08-26 13:31 UTC (permalink / raw)
  To: Maxim Levitsky, Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Max Reitz, Stefan Hajnoczi


[-- Attachment #1.1: Type: text/plain, Size: 695 bytes --]

On 8/25/19 11:08 AM, Maxim Levitsky wrote:

>>> I'd do a separate check for stripes and active fields, and then give a
>>> specific error message for each. That way if this does ever trigger
>>> in practice will immediately understand which check failed.
>>>
>>> Also using '%d' rather than '%i' is more common convention
>>
>> Done.
> 
> Note that I switched i,j to be size_t since you said that you prefer this,
> and to print this I apparently need %lu.

Actually, for size_t, you need %zu. %lu/size_t will cause warnings on
32-bit platforms.


-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking
  2019-08-26 13:31         ` Eric Blake
@ 2019-08-26 13:39           ` Maxim Levitsky
  0 siblings, 0 replies; 87+ messages in thread
From: Maxim Levitsky @ 2019-08-26 13:39 UTC (permalink / raw)
  To: Eric Blake, Daniel P. Berrangé
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Max Reitz, Stefan Hajnoczi

On Mon, 2019-08-26 at 08:31 -0500, Eric Blake wrote:
> On 8/25/19 11:08 AM, Maxim Levitsky wrote:
> 
> > > > I'd do a separate check for stripes and active fields, and then give a
> > > > specific error message for each. That way if this does ever trigger
> > > > in practice will immediately understand which check failed.
> > > > 
> > > > Also using '%d' rather than '%i' is more common convention
> > > 
> > > Done.
> > 
> > Note that I switched i,j to be size_t since you said that you prefer this,
> > and to print this I apparently need %lu.
> 
> Actually, for size_t, you need %zu. %lu/size_t will cause warnings on
> 32-bit platforms.
> 
> 
Thank you!
I have read something like that on the internet, but I wondered,
what actually is the most portable way.

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always
  2019-08-25 15:31         ` Maxim Levitsky
  2019-08-25 17:15           ` Maxim Levitsky
@ 2019-08-27  8:55           ` Daniel P. Berrangé
  1 sibling, 0 replies; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-27  8:55 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Markus Armbruster, qemu-devel,
	Nir Soffer, Stefan Hajnoczi, Max Reitz

On Sun, Aug 25, 2019 at 06:31:02PM +0300, Maxim Levitsky wrote:
> On Thu, 2019-08-22 at 13:56 +0300, Maxim Levitsky wrote:
> > On Thu, 2019-08-22 at 11:49 +0100, Daniel P. Berrangé wrote:
> > > On Tue, Aug 20, 2019 at 08:12:51PM +0200, Max Reitz wrote:
> > > > On 14.08.19 22:22, Maxim Levitsky wrote:
> > > > > While there are other places where these are still stored in memory,
> > > > > this is still one less key material area that can be sniffed with
> > > > > various side channel attacks
> > > > > 
> > > > > 
> > > > > 
> > > > 
> > > > (Many empty lines here)
> > > > 
> > > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > > ---
> > > > >  crypto/block-luks.c | 52 ++++++++++++++++++++++++++++++++++++++-------
> > > > >  1 file changed, 44 insertions(+), 8 deletions(-)
> > > > 
> > > > Wouldn’t it make sense to introduce a dedicated function for this?
> > > 
> > > Yes, it would.
> > > 
> > > In fact I have a series pending which bumps min glib and introduces
> > > use of auto-free functions in this code.
> > > 
> > > It would be desirable to have a autp-free func for memset+free
> > > so we can just declare the variable
> > > 
> > >    q_autowipefree char *password = NULL;
> > > 
> > > and have it result in memset+free
> > > 
> > 
> > That is perfect.
> > When do you think you could post the series so that I could rebase
> > on top of it?
> 
> 
> I am thinking that I will keep my patch as is, just so that code is
> consistent in memsetting the secrets (even though as Nir pointed out,
> that these will be probably optimized away anyway).
> And then when you send your patch you will just remove all
> of these memsets.

I'm fine with you continuing to use memset, since this is a pre-existing
problem in the code that you are not making worse. We'll figure out the
fix separately.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking
  2019-08-25 16:08       ` Maxim Levitsky
  2019-08-26 13:31         ` Eric Blake
@ 2019-08-27  8:56         ` Daniel P. Berrangé
  1 sibling, 0 replies; 87+ messages in thread
From: Daniel P. Berrangé @ 2019-08-27  8:56 UTC (permalink / raw)
  To: Maxim Levitsky
  Cc: Kevin Wolf, Fam Zheng, qemu-block, qemu-devel, Markus Armbruster,
	Max Reitz, Stefan Hajnoczi

On Sun, Aug 25, 2019 at 07:08:00PM +0300, Maxim Levitsky wrote:
> On Sun, 2019-08-25 at 18:40 +0300, Maxim Levitsky wrote:
> > On Thu, 2019-08-22 at 12:04 +0100, Daniel P. Berrangé wrote:
> > > On Wed, Aug 14, 2019 at 11:22:12PM +0300, Maxim Levitsky wrote:
> > > > Check that keyslots don't overlap with the data,
> > > > and check that keyslots don't overlap with each other.
> > > > (this is done using naive O(n^2) nested loops,
> > > > but since there are just 8 keyslots, this doens't really matter.
> > > > 
> > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> > > > ---
> > > >  crypto/block-luks.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> > > >  1 file changed, 42 insertions(+)
> > > > 
> > > > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > > > index 336e633df4..1997e92fe1 100644
> > > > --- a/crypto/block-luks.c
> > > > +++ b/crypto/block-luks.c
> > > > @@ -551,6 +551,8 @@ static int
> > > >  qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
> > > >  {
> > > >      int ret;
> > > > +    int i, j;
> > > > +
> > > >  
> > > >      if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
> > > >                 QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
> > > > @@ -566,6 +568,46 @@ qcrypto_block_luks_check_header(QCryptoBlockLUKS *luks, Error **errp)
> > > >          goto fail;
> > > >      }
> > > >  
> > > > +    /* Check all keyslots for corruption  */
> > > > +    for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
> > > > +
> > > > +        QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
> > > > +        uint start1 = slot1->key_offset;
> > > > +        uint len1 = splitkeylen_sectors(luks, slot1->stripes);
> > > 
> > > Using 'uint' is not normal QEMU style.
> > > 
> > > Either use 'unsigned int'  or if a specific size is needed
> > > then one of the 'guintNN' types from glib.
> > > 
> > > This applies elsewhere in this patch series too, but
> > > I'll only comment here & let you find the other cases.
> > 
> > Fixed. Sorry for the noise.
> > 
> > > 
> > > > +
> > > > +        if (slot1->stripes == 0 ||
> > > > +                (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
> > > > +                slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED)) {
> > > > +
> > > 
> > > Redundant blank line
> > 
> > Fixed
> > > 
> > > > +            error_setg(errp, "Keyslot %i is corrupted", i);
> > > 
> > > I'd do a separate check for stripes and active fields, and then give a
> > > specific error message for each. That way if this does ever trigger
> > > in practice will immediately understand which check failed.
> > > 
> > > Also using '%d' rather than '%i' is more common convention
> > 
> > Done.
> 
> Note that I switched i,j to be size_t since you said that you prefer this,
> and to print this I apparently need %lu.

For size_t, you need %zu to be 32/64-bit agnostic  (ssize_t is %zd)


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 87+ messages in thread

end of thread, other threads:[~2019-08-27  8:58 UTC | newest]

Thread overview: 87+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-14 20:22 [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 key management Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 01/13] block-crypto: misc refactoring Maxim Levitsky
2019-08-20 16:38   ` Max Reitz
2019-08-22  0:05     ` Maxim Levitsky
2019-08-22 14:34       ` Max Reitz
2019-08-22 15:04         ` Maxim Levitsky
2019-08-21 15:39   ` Daniel P. Berrangé
2019-08-22  0:08     ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 02/13] qcrypto-luks: " Maxim Levitsky
2019-08-15 21:40   ` [Qemu-devel] [Qemu-block] " John Snow
2019-08-19 14:21     ` Maxim Levitsky
2019-08-22 10:29     ` Daniel P. Berrangé
2019-08-22 11:04       ` Maxim Levitsky
2019-08-22 11:10         ` Daniel P. Berrangé
2019-08-22 11:13           ` Maxim Levitsky
2019-08-20 17:36   ` [Qemu-devel] " Max Reitz
2019-08-21 23:59     ` Maxim Levitsky
2019-08-22 14:32       ` Max Reitz
2019-08-25 10:46         ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 03/13] qcrypto-luks: refactoring: extract load/store/check/parse header functions Maxim Levitsky
2019-08-20 18:01   ` Max Reitz
2019-08-21 22:43     ` Maxim Levitsky
2019-08-22 10:32       ` Daniel P. Berrangé
2019-08-22 10:57         ` Maxim Levitsky
2019-08-22 10:34       ` Daniel P. Berrangé
2019-08-25 14:11         ` Maxim Levitsky
2019-08-22 10:38   ` Daniel P. Berrangé
2019-08-25 14:09     ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 04/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations Maxim Levitsky
2019-08-22 10:47   ` Daniel P. Berrangé
2019-08-25 14:30     ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 05/13] qcrypto-luks: clear the masterkey and password before freeing them always Maxim Levitsky
2019-08-20 18:12   ` Max Reitz
2019-08-21 22:40     ` Maxim Levitsky
2019-08-22 10:49     ` Daniel P. Berrangé
2019-08-22 10:56       ` Maxim Levitsky
2019-08-25 15:31         ` Maxim Levitsky
2019-08-25 17:15           ` Maxim Levitsky
2019-08-27  8:55           ` Daniel P. Berrangé
2019-08-21 23:01   ` [Qemu-devel] [Qemu-block] " Nir Soffer
2019-08-21 23:11     ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking Maxim Levitsky
2019-08-22 11:04   ` Daniel P. Berrangé
2019-08-25 15:40     ` Maxim Levitsky
2019-08-25 16:08       ` Maxim Levitsky
2019-08-26 13:31         ` Eric Blake
2019-08-26 13:39           ` Maxim Levitsky
2019-08-27  8:56         ` Daniel P. Berrangé
2019-08-14 20:22 ` [Qemu-devel] [PATCH 07/13] block: add manage-encryption command (qmp and blockdev) Maxim Levitsky
2019-08-20 18:27   ` Max Reitz
2019-08-21 22:32     ` Maxim Levitsky
2019-08-22 11:14     ` Daniel P. Berrangé
2019-08-21 11:47   ` Markus Armbruster
2019-08-21 22:24     ` Maxim Levitsky
2019-08-22 14:07       ` Markus Armbruster
2019-08-25 16:42         ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 08/13] qcrypto: add the plumbing for encryption management Maxim Levitsky
2019-08-22 11:16   ` Daniel P. Berrangé
2019-08-22 11:47     ` Maxim Levitsky
2019-08-22 11:49       ` Daniel P. Berrangé
2019-08-14 20:22 ` [Qemu-devel] [PATCH 09/13] qcrypto-luks: implement the encryption key management Maxim Levitsky
2019-08-22 11:27   ` Daniel P. Berrangé
2019-08-25 17:01     ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 10/13] block/crypto: " Maxim Levitsky
2019-08-22 11:29   ` Daniel P. Berrangé
2019-08-22 11:36     ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 11/13] block/qcow2: implement the encryption key managment Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 12/13] qemu-img: implement key management Maxim Levitsky
2019-08-20 18:29   ` Max Reitz
2019-08-21 22:33     ` Maxim Levitsky
2019-08-22 11:32     ` Daniel P. Berrangé
2019-08-22 14:42       ` Max Reitz
2019-08-25 17:04         ` Maxim Levitsky
2019-08-14 20:22 ` [Qemu-devel] [PATCH 13/13] iotests : add tests for encryption " Maxim Levitsky
2019-08-14 21:08 ` [Qemu-devel] [PATCH 00/13] RFC: luks/encrypted qcow2 " Eric Blake
2019-08-15  8:49   ` Maxim Levitsky
2019-08-15  9:10   ` Kevin Wolf
2019-08-15 14:18     ` Markus Armbruster
2019-08-15 14:44       ` Maxim Levitsky
2019-08-15 15:00         ` Eric Blake
2019-08-19 12:35           ` Maxim Levitsky
2019-08-21 11:31             ` Markus Armbruster
2019-08-21 13:22               ` Maxim Levitsky
2019-08-20 17:59 ` Max Reitz
2019-08-21 22:00   ` Maxim Levitsky
2019-08-22 11:35 ` Daniel P. Berrangé
2019-08-25 17:10   ` Maxim Levitsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).