diff mbox

[PULL,10/12] block: convert qcow/qcow2 to use generic cipher API

Message ID 1436278368-13449-11-git-send-email-pbonzini@redhat.com
State New
Headers show

Commit Message

Paolo Bonzini July 7, 2015, 2:12 p.m. UTC
From: "Daniel P. Berrange" <berrange@redhat.com>

Switch the qcow/qcow2 block driver over to use the generic cipher
API, this allows it to use the pluggable AES implementations,
instead of being hardcoded to use QEMU's built-in impl.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-Id: <1435770638-25715-10-git-send-email-berrange@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block/qcow.c          | 102 +++++++++++++++++++++++++++++++++++++-------------
 block/qcow2-cluster.c |  46 ++++++++++++++++++-----
 block/qcow2.c         |  95 +++++++++++++++++++++++-----------------------
 block/qcow2.h         |  13 +++----
 4 files changed, 165 insertions(+), 91 deletions(-)

Comments

Christian Borntraeger July 9, 2015, 10:17 a.m. UTC | #1
Am 07.07.2015 um 16:12 schrieb Paolo Bonzini:
> From: "Daniel P. Berrange" <berrange@redhat.com>
> 
> Switch the qcow/qcow2 block driver over to use the generic cipher
> API, this allows it to use the pluggable AES implementations,
> instead of being hardcoded to use QEMU's built-in impl.
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> Message-Id: <1435770638-25715-10-git-send-email-berrange@redhat.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

For whatever reason this breaks migration(or virsh restore)
from guests that were created with an older version of QEMU.



Thread 1 (Thread 0x3fffb856bd0 (LWP 32226)):
#0  0x0000000080354a54 in qcrypto_cipher_free (cipher=0x0) at /home/cborntra/REPOS/qemu/crypto/cipher-builtin.c:357
#1  0x00000000802ca912 in qcow2_close (bs=0x80a0cea0) at /home/cborntra/REPOS/qemu/block/qcow2.c:1477
#2  0x00000000802caa32 in qcow2_invalidate_cache (bs=0x80a0cea0, errp=0x81a3fc58) at /home/cborntra/REPOS/qemu/block/qcow2.c:1509
#3  0x000000008029bac0 in bdrv_invalidate_cache (bs=0x80a0cea0, errp=0x81a3fd08) at /home/cborntra/REPOS/qemu/block.c:3135
#4  0x000000008029bbe6 in bdrv_invalidate_cache_all (errp=0x81a3fdd0) at /home/cborntra/REPOS/qemu/block.c:3160
#5  0x000000008021de50 in process_incoming_migration_co (opaque=0x80acaae0) at /home/cborntra/REPOS/qemu/migration/migration.c:160
#6  0x00000000802ab96a in coroutine_trampoline (i0=0, i1=-2137149984) at /home/cborntra/REPOS/qemu/coroutine-ucontext.c:80
#7  0x000003fffc463ca2 in __makecontext_ret () from /lib64/libc.so.6
Backtrace stopped: previous frame identical to this frame (corrupt stack?)


> ---
>  block/qcow.c          | 102 +++++++++++++++++++++++++++++++++++++-------------
>  block/qcow2-cluster.c |  46 ++++++++++++++++++-----
>  block/qcow2.c         |  95 +++++++++++++++++++++++-----------------------
>  block/qcow2.h         |  13 +++----
>  4 files changed, 165 insertions(+), 91 deletions(-)
> 
> diff --git a/block/qcow.c b/block/qcow.c
> index bf5c570..01fba54 100644
> --- a/block/qcow.c
> +++ b/block/qcow.c
> @@ -26,7 +26,7 @@
>  #include "qemu/module.h"
>  #include <zlib.h>
>  #include "qapi/qmp/qerror.h"
> -#include "crypto/aes.h"
> +#include "crypto/cipher.h"
>  #include "migration/migration.h"
> 
>  /**************************************************************/
> @@ -72,10 +72,8 @@ typedef struct BDRVQcowState {
>      uint8_t *cluster_cache;
>      uint8_t *cluster_data;
>      uint64_t cluster_cache_offset;
> -    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
> +    QCryptoCipher *cipher; /* NULL if no key yet */
>      uint32_t crypt_method_header;
> -    AES_KEY aes_encrypt_key;
> -    AES_KEY aes_decrypt_key;
>      CoMutex lock;
>      Error *migration_blocker;
>  } BDRVQcowState;
> @@ -154,6 +152,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
>          ret = -EINVAL;
>          goto fail;
>      }
> +    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
> +        error_setg(errp, "AES cipher not available");
> +        ret = -EINVAL;
> +        goto fail;
> +    }
>      s->crypt_method_header = header.crypt_method;
>      if (s->crypt_method_header) {
>          bs->encrypted = 1;
> @@ -260,6 +263,7 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
>      BDRVQcowState *s = bs->opaque;
>      uint8_t keybuf[16];
>      int len, i;
> +    Error *err;
> 
>      memset(keybuf, 0, 16);
>      len = strlen(key);
> @@ -271,38 +275,67 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
>          keybuf[i] = key[i];
>      }
>      assert(bs->encrypted);
> -    s->crypt_method = s->crypt_method_header;
> 
> -    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
> -        return -1;
> -    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
> +    qcrypto_cipher_free(s->cipher);
> +    s->cipher = qcrypto_cipher_new(
> +        QCRYPTO_CIPHER_ALG_AES_128,
> +        QCRYPTO_CIPHER_MODE_CBC,
> +        keybuf, G_N_ELEMENTS(keybuf),
> +        &err);
> +
> +    if (!s->cipher) {
> +        /* XXX would be nice if errors in this method could
> +         * be properly propagate to the caller. Would need
> +         * the bdrv_set_key() API signature to be fixed. */
> +        error_free(err);
>          return -1;
> +    }
>      return 0;
>  }
> 
>  /* The crypt function is compatible with the linux cryptoloop
>     algorithm for < 4 GB images. NOTE: out_buf == in_buf is
>     supported */
> -static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
> -                            uint8_t *out_buf, const uint8_t *in_buf,
> -                            int nb_sectors, int enc,
> -                            const AES_KEY *key)
> +static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
> +                           uint8_t *out_buf, const uint8_t *in_buf,
> +                           int nb_sectors, bool enc, Error **errp)
>  {
>      union {
>          uint64_t ll[2];
>          uint8_t b[16];
>      } ivec;
>      int i;
> +    int ret;
> 
>      for(i = 0; i < nb_sectors; i++) {
>          ivec.ll[0] = cpu_to_le64(sector_num);
>          ivec.ll[1] = 0;
> -        AES_cbc_encrypt(in_buf, out_buf, 512, key,
> -                        ivec.b, enc);
> +        if (qcrypto_cipher_setiv(s->cipher,
> +                                 ivec.b, G_N_ELEMENTS(ivec.b),
> +                                 errp) < 0) {
> +            return -1;
> +        }
> +        if (enc) {
> +            ret = qcrypto_cipher_encrypt(s->cipher,
> +                                         in_buf,
> +                                         out_buf,
> +                                         512,
> +                                         errp);
> +        } else {
> +            ret = qcrypto_cipher_decrypt(s->cipher,
> +                                         in_buf,
> +                                         out_buf,
> +                                         512,
> +                                         errp);
> +        }
> +        if (ret < 0) {
> +            return -1;
> +        }
>          sector_num++;
>          in_buf += 512;
>          out_buf += 512;
>      }
> +    return 0;
>  }
> 
>  /* 'allocate' is:
> @@ -416,15 +449,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
>                  if (bs->encrypted &&
>                      (n_end - n_start) < s->cluster_sectors) {
>                      uint64_t start_sect;
> -                    assert(s->crypt_method);
> +                    assert(s->cipher);
>                      start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
>                      memset(s->cluster_data + 512, 0x00, 512);
>                      for(i = 0; i < s->cluster_sectors; i++) {
>                          if (i < n_start || i >= n_end) {
> -                            encrypt_sectors(s, start_sect + i,
> -                                            s->cluster_data,
> -                                            s->cluster_data + 512, 1, 1,
> -                                            &s->aes_encrypt_key);
> +                            Error *err = NULL;
> +                            if (encrypt_sectors(s, start_sect + i,
> +                                                s->cluster_data,
> +                                                s->cluster_data + 512, 1,
> +                                                true, &err) < 0) {
> +                                error_free(err);
> +                                errno = EIO;
> +                                return -1;
> +                            }
>                              if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
>                                              s->cluster_data, 512) != 512)
>                                  return -1;
> @@ -464,7 +502,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
>      if (!cluster_offset) {
>          return 0;
>      }
> -    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) {
> +    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) {
>          return BDRV_BLOCK_DATA;
>      }
>      cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
> @@ -531,6 +569,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
>      QEMUIOVector hd_qiov;
>      uint8_t *buf;
>      void *orig_buf;
> +    Error *err = NULL;
> 
>      if (qiov->niov > 1) {
>          buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
> @@ -594,10 +633,11 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
>                  break;
>              }
>              if (bs->encrypted) {
> -                assert(s->crypt_method);
> -                encrypt_sectors(s, sector_num, buf, buf,
> -                                n, 0,
> -                                &s->aes_decrypt_key);
> +                assert(s->cipher);
> +                if (encrypt_sectors(s, sector_num, buf, buf,
> +                                    n, false, &err) < 0) {
> +                    goto fail;
> +                }
>              }
>          }
>          ret = 0;
> @@ -618,6 +658,7 @@ done:
>      return ret;
> 
>  fail:
> +    error_free(err);
>      ret = -EIO;
>      goto done;
>  }
> @@ -666,12 +707,17 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
>              break;
>          }
>          if (bs->encrypted) {
> -            assert(s->crypt_method);
> +            Error *err = NULL;
> +            assert(s->cipher);
>              if (!cluster_data) {
>                  cluster_data = g_malloc0(s->cluster_size);
>              }
> -            encrypt_sectors(s, sector_num, cluster_data, buf,
> -                            n, 1, &s->aes_encrypt_key);
> +            if (encrypt_sectors(s, sector_num, cluster_data, buf,
> +                                n, true, &err) < 0) {
> +                error_free(err);
> +                ret = -EIO;
> +                break;
> +            }
>              src_buf = cluster_data;
>          } else {
>              src_buf = buf;
> @@ -708,6 +754,8 @@ static void qcow_close(BlockDriverState *bs)
>  {
>      BDRVQcowState *s = bs->opaque;
> 
> +    qcrypto_cipher_free(s->cipher);
> +    s->cipher = NULL;
>      g_free(s->l1_table);
>      qemu_vfree(s->l2_cache);
>      g_free(s->cluster_cache);
> diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
> index 1a5c97a..b43f186 100644
> --- a/block/qcow2-cluster.c
> +++ b/block/qcow2-cluster.c
> @@ -339,26 +339,47 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab
>  /* The crypt function is compatible with the linux cryptoloop
>     algorithm for < 4 GB images. NOTE: out_buf == in_buf is
>     supported */
> -void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
> -                           uint8_t *out_buf, const uint8_t *in_buf,
> -                           int nb_sectors, int enc,
> -                           const AES_KEY *key)
> +int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
> +                          uint8_t *out_buf, const uint8_t *in_buf,
> +                          int nb_sectors, bool enc,
> +                          Error **errp)
>  {
>      union {
>          uint64_t ll[2];
>          uint8_t b[16];
>      } ivec;
>      int i;
> +    int ret;
> 
>      for(i = 0; i < nb_sectors; i++) {
>          ivec.ll[0] = cpu_to_le64(sector_num);
>          ivec.ll[1] = 0;
> -        AES_cbc_encrypt(in_buf, out_buf, 512, key,
> -                        ivec.b, enc);
> +        if (qcrypto_cipher_setiv(s->cipher,
> +                                 ivec.b, G_N_ELEMENTS(ivec.b),
> +                                 errp) < 0) {
> +            return -1;
> +        }
> +        if (enc) {
> +            ret = qcrypto_cipher_encrypt(s->cipher,
> +                                         in_buf,
> +                                         out_buf,
> +                                         512,
> +                                         errp);
> +        } else {
> +            ret = qcrypto_cipher_decrypt(s->cipher,
> +                                         in_buf,
> +                                         out_buf,
> +                                         512,
> +                                         errp);
> +        }
> +        if (ret < 0) {
> +            return -1;
> +        }
>          sector_num++;
>          in_buf += 512;
>          out_buf += 512;
>      }
> +    return 0;
>  }
> 
>  static int coroutine_fn copy_sectors(BlockDriverState *bs,
> @@ -401,10 +422,15 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
>      }
> 
>      if (bs->encrypted) {
> -        assert(s->crypt_method);
> -        qcow2_encrypt_sectors(s, start_sect + n_start,
> -                        iov.iov_base, iov.iov_base, n, 1,
> -                        &s->aes_encrypt_key);
> +        Error *err = NULL;
> +        assert(s->cipher);
> +        if (qcow2_encrypt_sectors(s, start_sect + n_start,
> +                                  iov.iov_base, iov.iov_base, n,
> +                                  true, &err) < 0) {
> +            ret = -EIO;
> +            error_free(err);
> +            goto out;
> +        }
>      }
> 
>      ret = qcow2_pre_write_overlap_check(bs, 0,
> diff --git a/block/qcow2.c b/block/qcow2.c
> index 85e0731..76c331b 100644
> --- a/block/qcow2.c
> +++ b/block/qcow2.c
> @@ -698,6 +698,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
>          ret = -EINVAL;
>          goto fail;
>      }
> +    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
> +        error_setg(errp, "AES cipher not available");
> +        ret = -EINVAL;
> +        goto fail;
> +    }
>      s->crypt_method_header = header.crypt_method;
>      if (s->crypt_method_header) {
>          bs->encrypted = 1;
> @@ -1031,6 +1036,7 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
>      BDRVQcowState *s = bs->opaque;
>      uint8_t keybuf[16];
>      int len, i;
> +    Error *err = NULL;
> 
>      memset(keybuf, 0, 16);
>      len = strlen(key);
> @@ -1042,30 +1048,21 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
>          keybuf[i] = key[i];
>      }
>      assert(bs->encrypted);
> -    s->crypt_method = s->crypt_method_header;
> 
> -    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
> -        return -1;
> -    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
> +    qcrypto_cipher_free(s->cipher);
> +    s->cipher = qcrypto_cipher_new(
> +        QCRYPTO_CIPHER_ALG_AES_128,
> +        QCRYPTO_CIPHER_MODE_CBC,
> +        keybuf, G_N_ELEMENTS(keybuf),
> +        &err);
> +
> +    if (!s->cipher) {
> +        /* XXX would be nice if errors in this method could
> +         * be properly propagate to the caller. Would need
> +         * the bdrv_set_key() API signature to be fixed. */
> +        error_free(err);
>          return -1;
> -#if 0
> -    /* test */
> -    {
> -        uint8_t in[16];
> -        uint8_t out[16];
> -        uint8_t tmp[16];
> -        for(i=0;i<16;i++)
> -            in[i] = i;
> -        AES_encrypt(in, tmp, &s->aes_encrypt_key);
> -        AES_decrypt(tmp, out, &s->aes_decrypt_key);
> -        for(i = 0; i < 16; i++)
> -            printf(" %02x", tmp[i]);
> -        printf("\n");
> -        for(i = 0; i < 16; i++)
> -            printf(" %02x", out[i]);
> -        printf("\n");
>      }
> -#endif
>      return 0;
>  }
> 
> @@ -1108,7 +1105,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
>      }
> 
>      if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
> -        !s->crypt_method) {
> +        !s->cipher) {
>          index_in_cluster = sector_num & (s->cluster_sectors - 1);
>          cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
>          status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
> @@ -1158,7 +1155,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
> 
>          /* prepare next request */
>          cur_nr_sectors = remaining_sectors;
> -        if (s->crypt_method) {
> +        if (s->cipher) {
>              cur_nr_sectors = MIN(cur_nr_sectors,
>                  QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
>          }
> @@ -1230,7 +1227,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
>              }
> 
>              if (bs->encrypted) {
> -                assert(s->crypt_method);
> +                assert(s->cipher);
> 
>                  /*
>                   * For encrypted images, read everything into a temporary
> @@ -1263,9 +1260,15 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
>                  goto fail;
>              }
>              if (bs->encrypted) {
> -                assert(s->crypt_method);
> -                qcow2_encrypt_sectors(s, sector_num,  cluster_data,
> -                    cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key);
> +                assert(s->cipher);
> +                Error *err = NULL;
> +                if (qcow2_encrypt_sectors(s, sector_num,  cluster_data,
> +                                          cluster_data, cur_nr_sectors, false,
> +                                          &err) < 0) {
> +                    error_free(err);
> +                    ret = -EIO;
> +                    goto fail;
> +                }
>                  qemu_iovec_from_buf(qiov, bytes_done,
>                      cluster_data, 512 * cur_nr_sectors);
>              }
> @@ -1343,7 +1346,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
>              cur_nr_sectors * 512);
> 
>          if (bs->encrypted) {
> -            assert(s->crypt_method);
> +            Error *err = NULL;
> +            assert(s->cipher);
>              if (!cluster_data) {
>                  cluster_data = qemu_try_blockalign(bs->file,
>                                                     QCOW_MAX_CRYPT_CLUSTERS
> @@ -1358,8 +1362,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
>                     QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
>              qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
> 
> -            qcow2_encrypt_sectors(s, sector_num, cluster_data,
> -                cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key);
> +            if (qcow2_encrypt_sectors(s, sector_num, cluster_data,
> +                                      cluster_data, cur_nr_sectors,
> +                                      true, &err) < 0) {
> +                error_free(err);
> +                ret = -EIO;
> +                goto fail;
> +            }
> 
>              qemu_iovec_reset(&hd_qiov);
>              qemu_iovec_add(&hd_qiov, cluster_data,
> @@ -1465,6 +1474,9 @@ static void qcow2_close(BlockDriverState *bs)
>      qcow2_cache_destroy(bs, s->l2_table_cache);
>      qcow2_cache_destroy(bs, s->refcount_block_cache);
> 
> +    qcrypto_cipher_free(s->cipher);
> +    s->cipher = NULL;
> +
>      g_free(s->unknown_header_fields);
>      cleanup_unknown_header_ext(bs);
> 
> @@ -1481,9 +1493,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>  {
>      BDRVQcowState *s = bs->opaque;
>      int flags = s->flags;
> -    AES_KEY aes_encrypt_key;
> -    AES_KEY aes_decrypt_key;
> -    uint32_t crypt_method = 0;
> +    QCryptoCipher *cipher = NULL;
>      QDict *options;
>      Error *local_err = NULL;
>      int ret;
> @@ -1493,12 +1503,8 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>       * that means we don't have to worry about reopening them here.
>       */
> 
> -    if (bs->encrypted) {
> -        assert(s->crypt_method);
> -        crypt_method = s->crypt_method;
> -        memcpy(&aes_encrypt_key, &s->aes_encrypt_key, sizeof(aes_encrypt_key));
> -        memcpy(&aes_decrypt_key, &s->aes_decrypt_key, sizeof(aes_decrypt_key));
> -    }
> +    cipher = s->cipher;
> +    s->cipher = NULL;
> 
>      qcow2_close(bs);
> 
> @@ -1523,11 +1529,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>          return;
>      }
> 
> -    if (bs->encrypted) {
> -        s->crypt_method = crypt_method;
> -        memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
> -        memcpy(&s->aes_decrypt_key, &aes_decrypt_key, sizeof(aes_decrypt_key));
> -    }
> +    s->cipher = cipher;
>  }
> 
>  static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
> @@ -2728,8 +2730,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>              backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
>          } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
>              encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
> -                                        s->crypt_method);
> -            if (encrypt != !!s->crypt_method) {
> +                                        !!s->cipher);
> +
> +            if (encrypt != !!s->cipher) {
>                  fprintf(stderr, "Changing the encryption flag is not "
>                          "supported.\n");
>                  return -ENOTSUP;
> diff --git a/block/qcow2.h b/block/qcow2.h
> index 462147c..72e1328 100644
> --- a/block/qcow2.h
> +++ b/block/qcow2.h
> @@ -25,7 +25,7 @@
>  #ifndef BLOCK_QCOW2_H
>  #define BLOCK_QCOW2_H
> 
> -#include "crypto/aes.h"
> +#include "crypto/cipher.h"
>  #include "block/coroutine.h"
> 
>  //#define DEBUG_ALLOC
> @@ -253,10 +253,8 @@ typedef struct BDRVQcowState {
> 
>      CoMutex lock;
> 
> -    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
> +    QCryptoCipher *cipher; /* current cipher, NULL if no key yet */
>      uint32_t crypt_method_header;
> -    AES_KEY aes_encrypt_key;
> -    AES_KEY aes_decrypt_key;
>      uint64_t snapshots_offset;
>      int snapshots_size;
>      unsigned int nb_snapshots;
> @@ -536,10 +534,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
>  int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
>  void qcow2_l2_cache_reset(BlockDriverState *bs);
>  int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
> -void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
> -                     uint8_t *out_buf, const uint8_t *in_buf,
> -                     int nb_sectors, int enc,
> -                     const AES_KEY *key);
> +int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
> +                          uint8_t *out_buf, const uint8_t *in_buf,
> +                          int nb_sectors, bool enc, Error **errp);
> 
>  int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
>      int *num, uint64_t *cluster_offset);
>
Christian Borntraeger July 9, 2015, 10:53 a.m. UTC | #2
Forgot some CCs (patch author and migration folks)


Am 09.07.2015 um 12:17 schrieb Christian Borntraeger:
> Am 07.07.2015 um 16:12 schrieb Paolo Bonzini:
>> From: "Daniel P. Berrange" <berrange@redhat.com>
>>
>> Switch the qcow/qcow2 block driver over to use the generic cipher
>> API, this allows it to use the pluggable AES implementations,
>> instead of being hardcoded to use QEMU's built-in impl.
>>
>> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
>> Message-Id: <1435770638-25715-10-git-send-email-berrange@redhat.com>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> 
> For whatever reason this breaks migration(or virsh restore)
> from guests that were created with an older version of QEMU.
> 
> 
> 
> Thread 1 (Thread 0x3fffb856bd0 (LWP 32226)):
> #0  0x0000000080354a54 in qcrypto_cipher_free (cipher=0x0) at /home/cborntra/REPOS/qemu/crypto/cipher-builtin.c:357
> #1  0x00000000802ca912 in qcow2_close (bs=0x80a0cea0) at /home/cborntra/REPOS/qemu/block/qcow2.c:1477
> #2  0x00000000802caa32 in qcow2_invalidate_cache (bs=0x80a0cea0, errp=0x81a3fc58) at /home/cborntra/REPOS/qemu/block/qcow2.c:1509
> #3  0x000000008029bac0 in bdrv_invalidate_cache (bs=0x80a0cea0, errp=0x81a3fd08) at /home/cborntra/REPOS/qemu/block.c:3135
> #4  0x000000008029bbe6 in bdrv_invalidate_cache_all (errp=0x81a3fdd0) at /home/cborntra/REPOS/qemu/block.c:3160
> #5  0x000000008021de50 in process_incoming_migration_co (opaque=0x80acaae0) at /home/cborntra/REPOS/qemu/migration/migration.c:160
> #6  0x00000000802ab96a in coroutine_trampoline (i0=0, i1=-2137149984) at /home/cborntra/REPOS/qemu/coroutine-ucontext.c:80
> #7  0x000003fffc463ca2 in __makecontext_ret () from /lib64/libc.so.6
> Backtrace stopped: previous frame identical to this frame (corrupt stack?)
> 
> 
>> ---
>>  block/qcow.c          | 102 +++++++++++++++++++++++++++++++++++++-------------
>>  block/qcow2-cluster.c |  46 ++++++++++++++++++-----
>>  block/qcow2.c         |  95 +++++++++++++++++++++++-----------------------
>>  block/qcow2.h         |  13 +++----
>>  4 files changed, 165 insertions(+), 91 deletions(-)
>>
>> diff --git a/block/qcow.c b/block/qcow.c
>> index bf5c570..01fba54 100644
>> --- a/block/qcow.c
>> +++ b/block/qcow.c
>> @@ -26,7 +26,7 @@
>>  #include "qemu/module.h"
>>  #include <zlib.h>
>>  #include "qapi/qmp/qerror.h"
>> -#include "crypto/aes.h"
>> +#include "crypto/cipher.h"
>>  #include "migration/migration.h"
>>
>>  /**************************************************************/
>> @@ -72,10 +72,8 @@ typedef struct BDRVQcowState {
>>      uint8_t *cluster_cache;
>>      uint8_t *cluster_data;
>>      uint64_t cluster_cache_offset;
>> -    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
>> +    QCryptoCipher *cipher; /* NULL if no key yet */
>>      uint32_t crypt_method_header;
>> -    AES_KEY aes_encrypt_key;
>> -    AES_KEY aes_decrypt_key;
>>      CoMutex lock;
>>      Error *migration_blocker;
>>  } BDRVQcowState;
>> @@ -154,6 +152,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
>>          ret = -EINVAL;
>>          goto fail;
>>      }
>> +    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
>> +        error_setg(errp, "AES cipher not available");
>> +        ret = -EINVAL;
>> +        goto fail;
>> +    }
>>      s->crypt_method_header = header.crypt_method;
>>      if (s->crypt_method_header) {
>>          bs->encrypted = 1;
>> @@ -260,6 +263,7 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
>>      BDRVQcowState *s = bs->opaque;
>>      uint8_t keybuf[16];
>>      int len, i;
>> +    Error *err;
>>
>>      memset(keybuf, 0, 16);
>>      len = strlen(key);
>> @@ -271,38 +275,67 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
>>          keybuf[i] = key[i];
>>      }
>>      assert(bs->encrypted);
>> -    s->crypt_method = s->crypt_method_header;
>>
>> -    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
>> -        return -1;
>> -    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
>> +    qcrypto_cipher_free(s->cipher);
>> +    s->cipher = qcrypto_cipher_new(
>> +        QCRYPTO_CIPHER_ALG_AES_128,
>> +        QCRYPTO_CIPHER_MODE_CBC,
>> +        keybuf, G_N_ELEMENTS(keybuf),
>> +        &err);
>> +
>> +    if (!s->cipher) {
>> +        /* XXX would be nice if errors in this method could
>> +         * be properly propagate to the caller. Would need
>> +         * the bdrv_set_key() API signature to be fixed. */
>> +        error_free(err);
>>          return -1;
>> +    }
>>      return 0;
>>  }
>>
>>  /* The crypt function is compatible with the linux cryptoloop
>>     algorithm for < 4 GB images. NOTE: out_buf == in_buf is
>>     supported */
>> -static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>> -                            uint8_t *out_buf, const uint8_t *in_buf,
>> -                            int nb_sectors, int enc,
>> -                            const AES_KEY *key)
>> +static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>> +                           uint8_t *out_buf, const uint8_t *in_buf,
>> +                           int nb_sectors, bool enc, Error **errp)
>>  {
>>      union {
>>          uint64_t ll[2];
>>          uint8_t b[16];
>>      } ivec;
>>      int i;
>> +    int ret;
>>
>>      for(i = 0; i < nb_sectors; i++) {
>>          ivec.ll[0] = cpu_to_le64(sector_num);
>>          ivec.ll[1] = 0;
>> -        AES_cbc_encrypt(in_buf, out_buf, 512, key,
>> -                        ivec.b, enc);
>> +        if (qcrypto_cipher_setiv(s->cipher,
>> +                                 ivec.b, G_N_ELEMENTS(ivec.b),
>> +                                 errp) < 0) {
>> +            return -1;
>> +        }
>> +        if (enc) {
>> +            ret = qcrypto_cipher_encrypt(s->cipher,
>> +                                         in_buf,
>> +                                         out_buf,
>> +                                         512,
>> +                                         errp);
>> +        } else {
>> +            ret = qcrypto_cipher_decrypt(s->cipher,
>> +                                         in_buf,
>> +                                         out_buf,
>> +                                         512,
>> +                                         errp);
>> +        }
>> +        if (ret < 0) {
>> +            return -1;
>> +        }
>>          sector_num++;
>>          in_buf += 512;
>>          out_buf += 512;
>>      }
>> +    return 0;
>>  }
>>
>>  /* 'allocate' is:
>> @@ -416,15 +449,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
>>                  if (bs->encrypted &&
>>                      (n_end - n_start) < s->cluster_sectors) {
>>                      uint64_t start_sect;
>> -                    assert(s->crypt_method);
>> +                    assert(s->cipher);
>>                      start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
>>                      memset(s->cluster_data + 512, 0x00, 512);
>>                      for(i = 0; i < s->cluster_sectors; i++) {
>>                          if (i < n_start || i >= n_end) {
>> -                            encrypt_sectors(s, start_sect + i,
>> -                                            s->cluster_data,
>> -                                            s->cluster_data + 512, 1, 1,
>> -                                            &s->aes_encrypt_key);
>> +                            Error *err = NULL;
>> +                            if (encrypt_sectors(s, start_sect + i,
>> +                                                s->cluster_data,
>> +                                                s->cluster_data + 512, 1,
>> +                                                true, &err) < 0) {
>> +                                error_free(err);
>> +                                errno = EIO;
>> +                                return -1;
>> +                            }
>>                              if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
>>                                              s->cluster_data, 512) != 512)
>>                                  return -1;
>> @@ -464,7 +502,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
>>      if (!cluster_offset) {
>>          return 0;
>>      }
>> -    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) {
>> +    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) {
>>          return BDRV_BLOCK_DATA;
>>      }
>>      cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
>> @@ -531,6 +569,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
>>      QEMUIOVector hd_qiov;
>>      uint8_t *buf;
>>      void *orig_buf;
>> +    Error *err = NULL;
>>
>>      if (qiov->niov > 1) {
>>          buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
>> @@ -594,10 +633,11 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
>>                  break;
>>              }
>>              if (bs->encrypted) {
>> -                assert(s->crypt_method);
>> -                encrypt_sectors(s, sector_num, buf, buf,
>> -                                n, 0,
>> -                                &s->aes_decrypt_key);
>> +                assert(s->cipher);
>> +                if (encrypt_sectors(s, sector_num, buf, buf,
>> +                                    n, false, &err) < 0) {
>> +                    goto fail;
>> +                }
>>              }
>>          }
>>          ret = 0;
>> @@ -618,6 +658,7 @@ done:
>>      return ret;
>>
>>  fail:
>> +    error_free(err);
>>      ret = -EIO;
>>      goto done;
>>  }
>> @@ -666,12 +707,17 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
>>              break;
>>          }
>>          if (bs->encrypted) {
>> -            assert(s->crypt_method);
>> +            Error *err = NULL;
>> +            assert(s->cipher);
>>              if (!cluster_data) {
>>                  cluster_data = g_malloc0(s->cluster_size);
>>              }
>> -            encrypt_sectors(s, sector_num, cluster_data, buf,
>> -                            n, 1, &s->aes_encrypt_key);
>> +            if (encrypt_sectors(s, sector_num, cluster_data, buf,
>> +                                n, true, &err) < 0) {
>> +                error_free(err);
>> +                ret = -EIO;
>> +                break;
>> +            }
>>              src_buf = cluster_data;
>>          } else {
>>              src_buf = buf;
>> @@ -708,6 +754,8 @@ static void qcow_close(BlockDriverState *bs)
>>  {
>>      BDRVQcowState *s = bs->opaque;
>>
>> +    qcrypto_cipher_free(s->cipher);
>> +    s->cipher = NULL;
>>      g_free(s->l1_table);
>>      qemu_vfree(s->l2_cache);
>>      g_free(s->cluster_cache);
>> diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
>> index 1a5c97a..b43f186 100644
>> --- a/block/qcow2-cluster.c
>> +++ b/block/qcow2-cluster.c
>> @@ -339,26 +339,47 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab
>>  /* The crypt function is compatible with the linux cryptoloop
>>     algorithm for < 4 GB images. NOTE: out_buf == in_buf is
>>     supported */
>> -void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>> -                           uint8_t *out_buf, const uint8_t *in_buf,
>> -                           int nb_sectors, int enc,
>> -                           const AES_KEY *key)
>> +int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>> +                          uint8_t *out_buf, const uint8_t *in_buf,
>> +                          int nb_sectors, bool enc,
>> +                          Error **errp)
>>  {
>>      union {
>>          uint64_t ll[2];
>>          uint8_t b[16];
>>      } ivec;
>>      int i;
>> +    int ret;
>>
>>      for(i = 0; i < nb_sectors; i++) {
>>          ivec.ll[0] = cpu_to_le64(sector_num);
>>          ivec.ll[1] = 0;
>> -        AES_cbc_encrypt(in_buf, out_buf, 512, key,
>> -                        ivec.b, enc);
>> +        if (qcrypto_cipher_setiv(s->cipher,
>> +                                 ivec.b, G_N_ELEMENTS(ivec.b),
>> +                                 errp) < 0) {
>> +            return -1;
>> +        }
>> +        if (enc) {
>> +            ret = qcrypto_cipher_encrypt(s->cipher,
>> +                                         in_buf,
>> +                                         out_buf,
>> +                                         512,
>> +                                         errp);
>> +        } else {
>> +            ret = qcrypto_cipher_decrypt(s->cipher,
>> +                                         in_buf,
>> +                                         out_buf,
>> +                                         512,
>> +                                         errp);
>> +        }
>> +        if (ret < 0) {
>> +            return -1;
>> +        }
>>          sector_num++;
>>          in_buf += 512;
>>          out_buf += 512;
>>      }
>> +    return 0;
>>  }
>>
>>  static int coroutine_fn copy_sectors(BlockDriverState *bs,
>> @@ -401,10 +422,15 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
>>      }
>>
>>      if (bs->encrypted) {
>> -        assert(s->crypt_method);
>> -        qcow2_encrypt_sectors(s, start_sect + n_start,
>> -                        iov.iov_base, iov.iov_base, n, 1,
>> -                        &s->aes_encrypt_key);
>> +        Error *err = NULL;
>> +        assert(s->cipher);
>> +        if (qcow2_encrypt_sectors(s, start_sect + n_start,
>> +                                  iov.iov_base, iov.iov_base, n,
>> +                                  true, &err) < 0) {
>> +            ret = -EIO;
>> +            error_free(err);
>> +            goto out;
>> +        }
>>      }
>>
>>      ret = qcow2_pre_write_overlap_check(bs, 0,
>> diff --git a/block/qcow2.c b/block/qcow2.c
>> index 85e0731..76c331b 100644
>> --- a/block/qcow2.c
>> +++ b/block/qcow2.c
>> @@ -698,6 +698,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
>>          ret = -EINVAL;
>>          goto fail;
>>      }
>> +    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
>> +        error_setg(errp, "AES cipher not available");
>> +        ret = -EINVAL;
>> +        goto fail;
>> +    }
>>      s->crypt_method_header = header.crypt_method;
>>      if (s->crypt_method_header) {
>>          bs->encrypted = 1;
>> @@ -1031,6 +1036,7 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
>>      BDRVQcowState *s = bs->opaque;
>>      uint8_t keybuf[16];
>>      int len, i;
>> +    Error *err = NULL;
>>
>>      memset(keybuf, 0, 16);
>>      len = strlen(key);
>> @@ -1042,30 +1048,21 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
>>          keybuf[i] = key[i];
>>      }
>>      assert(bs->encrypted);
>> -    s->crypt_method = s->crypt_method_header;
>>
>> -    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
>> -        return -1;
>> -    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
>> +    qcrypto_cipher_free(s->cipher);
>> +    s->cipher = qcrypto_cipher_new(
>> +        QCRYPTO_CIPHER_ALG_AES_128,
>> +        QCRYPTO_CIPHER_MODE_CBC,
>> +        keybuf, G_N_ELEMENTS(keybuf),
>> +        &err);
>> +
>> +    if (!s->cipher) {
>> +        /* XXX would be nice if errors in this method could
>> +         * be properly propagate to the caller. Would need
>> +         * the bdrv_set_key() API signature to be fixed. */
>> +        error_free(err);
>>          return -1;
>> -#if 0
>> -    /* test */
>> -    {
>> -        uint8_t in[16];
>> -        uint8_t out[16];
>> -        uint8_t tmp[16];
>> -        for(i=0;i<16;i++)
>> -            in[i] = i;
>> -        AES_encrypt(in, tmp, &s->aes_encrypt_key);
>> -        AES_decrypt(tmp, out, &s->aes_decrypt_key);
>> -        for(i = 0; i < 16; i++)
>> -            printf(" %02x", tmp[i]);
>> -        printf("\n");
>> -        for(i = 0; i < 16; i++)
>> -            printf(" %02x", out[i]);
>> -        printf("\n");
>>      }
>> -#endif
>>      return 0;
>>  }
>>
>> @@ -1108,7 +1105,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
>>      }
>>
>>      if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
>> -        !s->crypt_method) {
>> +        !s->cipher) {
>>          index_in_cluster = sector_num & (s->cluster_sectors - 1);
>>          cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
>>          status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
>> @@ -1158,7 +1155,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
>>
>>          /* prepare next request */
>>          cur_nr_sectors = remaining_sectors;
>> -        if (s->crypt_method) {
>> +        if (s->cipher) {
>>              cur_nr_sectors = MIN(cur_nr_sectors,
>>                  QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
>>          }
>> @@ -1230,7 +1227,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
>>              }
>>
>>              if (bs->encrypted) {
>> -                assert(s->crypt_method);
>> +                assert(s->cipher);
>>
>>                  /*
>>                   * For encrypted images, read everything into a temporary
>> @@ -1263,9 +1260,15 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
>>                  goto fail;
>>              }
>>              if (bs->encrypted) {
>> -                assert(s->crypt_method);
>> -                qcow2_encrypt_sectors(s, sector_num,  cluster_data,
>> -                    cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key);
>> +                assert(s->cipher);
>> +                Error *err = NULL;
>> +                if (qcow2_encrypt_sectors(s, sector_num,  cluster_data,
>> +                                          cluster_data, cur_nr_sectors, false,
>> +                                          &err) < 0) {
>> +                    error_free(err);
>> +                    ret = -EIO;
>> +                    goto fail;
>> +                }
>>                  qemu_iovec_from_buf(qiov, bytes_done,
>>                      cluster_data, 512 * cur_nr_sectors);
>>              }
>> @@ -1343,7 +1346,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
>>              cur_nr_sectors * 512);
>>
>>          if (bs->encrypted) {
>> -            assert(s->crypt_method);
>> +            Error *err = NULL;
>> +            assert(s->cipher);
>>              if (!cluster_data) {
>>                  cluster_data = qemu_try_blockalign(bs->file,
>>                                                     QCOW_MAX_CRYPT_CLUSTERS
>> @@ -1358,8 +1362,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
>>                     QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
>>              qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
>>
>> -            qcow2_encrypt_sectors(s, sector_num, cluster_data,
>> -                cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key);
>> +            if (qcow2_encrypt_sectors(s, sector_num, cluster_data,
>> +                                      cluster_data, cur_nr_sectors,
>> +                                      true, &err) < 0) {
>> +                error_free(err);
>> +                ret = -EIO;
>> +                goto fail;
>> +            }
>>
>>              qemu_iovec_reset(&hd_qiov);
>>              qemu_iovec_add(&hd_qiov, cluster_data,
>> @@ -1465,6 +1474,9 @@ static void qcow2_close(BlockDriverState *bs)
>>      qcow2_cache_destroy(bs, s->l2_table_cache);
>>      qcow2_cache_destroy(bs, s->refcount_block_cache);
>>
>> +    qcrypto_cipher_free(s->cipher);
>> +    s->cipher = NULL;
>> +
>>      g_free(s->unknown_header_fields);
>>      cleanup_unknown_header_ext(bs);
>>
>> @@ -1481,9 +1493,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>>  {
>>      BDRVQcowState *s = bs->opaque;
>>      int flags = s->flags;
>> -    AES_KEY aes_encrypt_key;
>> -    AES_KEY aes_decrypt_key;
>> -    uint32_t crypt_method = 0;
>> +    QCryptoCipher *cipher = NULL;
>>      QDict *options;
>>      Error *local_err = NULL;
>>      int ret;
>> @@ -1493,12 +1503,8 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>>       * that means we don't have to worry about reopening them here.
>>       */
>>
>> -    if (bs->encrypted) {
>> -        assert(s->crypt_method);
>> -        crypt_method = s->crypt_method;
>> -        memcpy(&aes_encrypt_key, &s->aes_encrypt_key, sizeof(aes_encrypt_key));
>> -        memcpy(&aes_decrypt_key, &s->aes_decrypt_key, sizeof(aes_decrypt_key));
>> -    }
>> +    cipher = s->cipher;
>> +    s->cipher = NULL;
>>
>>      qcow2_close(bs);
>>
>> @@ -1523,11 +1529,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>>          return;
>>      }
>>
>> -    if (bs->encrypted) {
>> -        s->crypt_method = crypt_method;
>> -        memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
>> -        memcpy(&s->aes_decrypt_key, &aes_decrypt_key, sizeof(aes_decrypt_key));
>> -    }
>> +    s->cipher = cipher;
>>  }
>>
>>  static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
>> @@ -2728,8 +2730,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>>              backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
>>          } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
>>              encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
>> -                                        s->crypt_method);
>> -            if (encrypt != !!s->crypt_method) {
>> +                                        !!s->cipher);
>> +
>> +            if (encrypt != !!s->cipher) {
>>                  fprintf(stderr, "Changing the encryption flag is not "
>>                          "supported.\n");
>>                  return -ENOTSUP;
>> diff --git a/block/qcow2.h b/block/qcow2.h
>> index 462147c..72e1328 100644
>> --- a/block/qcow2.h
>> +++ b/block/qcow2.h
>> @@ -25,7 +25,7 @@
>>  #ifndef BLOCK_QCOW2_H
>>  #define BLOCK_QCOW2_H
>>
>> -#include "crypto/aes.h"
>> +#include "crypto/cipher.h"
>>  #include "block/coroutine.h"
>>
>>  //#define DEBUG_ALLOC
>> @@ -253,10 +253,8 @@ typedef struct BDRVQcowState {
>>
>>      CoMutex lock;
>>
>> -    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
>> +    QCryptoCipher *cipher; /* current cipher, NULL if no key yet */
>>      uint32_t crypt_method_header;
>> -    AES_KEY aes_encrypt_key;
>> -    AES_KEY aes_decrypt_key;
>>      uint64_t snapshots_offset;
>>      int snapshots_size;
>>      unsigned int nb_snapshots;
>> @@ -536,10 +534,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
>>  int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
>>  void qcow2_l2_cache_reset(BlockDriverState *bs);
>>  int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
>> -void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>> -                     uint8_t *out_buf, const uint8_t *in_buf,
>> -                     int nb_sectors, int enc,
>> -                     const AES_KEY *key);
>> +int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>> +                          uint8_t *out_buf, const uint8_t *in_buf,
>> +                          int nb_sectors, bool enc, Error **errp);
>>
>>  int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
>>      int *num, uint64_t *cluster_offset);
>>
> 
>
Christian Borntraeger July 9, 2015, 11:20 a.m. UTC | #3
Am 09.07.2015 um 12:53 schrieb Christian Borntraeger:
> Forgot some CCs (patch author and migration folks)
> 
> 
> Am 09.07.2015 um 12:17 schrieb Christian Borntraeger:
>> Am 07.07.2015 um 16:12 schrieb Paolo Bonzini:
>>> From: "Daniel P. Berrange" <berrange@redhat.com>
>>>
>>> Switch the qcow/qcow2 block driver over to use the generic cipher
>>> API, this allows it to use the pluggable AES implementations,
>>> instead of being hardcoded to use QEMU's built-in impl.
>>>
>>> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
>>> Message-Id: <1435770638-25715-10-git-send-email-berrange@redhat.com>
>>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>>
>> For whatever reason this breaks migration(or virsh restore)
>> from guests that were created with an older version of QEMU.

I should not send emails today :-/

I wanted to say, that this is the stack trace of a crash.


>> Thread 1 (Thread 0x3fffb856bd0 (LWP 32226)):
>> #0  0x0000000080354a54 in qcrypto_cipher_free (cipher=0x0) at /home/cborntra/REPOS/qemu/crypto/cipher-builtin.c:357
>> #1  0x00000000802ca912 in qcow2_close (bs=0x80a0cea0) at /home/cborntra/REPOS/qemu/block/qcow2.c:1477
>> #2  0x00000000802caa32 in qcow2_invalidate_cache (bs=0x80a0cea0, errp=0x81a3fc58) at /home/cborntra/REPOS/qemu/block/qcow2.c:1509
>> #3  0x000000008029bac0 in bdrv_invalidate_cache (bs=0x80a0cea0, errp=0x81a3fd08) at /home/cborntra/REPOS/qemu/block.c:3135
>> #4  0x000000008029bbe6 in bdrv_invalidate_cache_all (errp=0x81a3fdd0) at /home/cborntra/REPOS/qemu/block.c:3160
>> #5  0x000000008021de50 in process_incoming_migration_co (opaque=0x80acaae0) at /home/cborntra/REPOS/qemu/migration/migration.c:160
>> #6  0x00000000802ab96a in coroutine_trampoline (i0=0, i1=-2137149984) at /home/cborntra/REPOS/qemu/coroutine-ucontext.c:80
>> #7  0x000003fffc463ca2 in __makecontext_ret () from /lib64/libc.so.6
>> Backtrace stopped: previous frame identical to this frame (corrupt stack?)
>>
>>
>>> ---
>>>  block/qcow.c          | 102 +++++++++++++++++++++++++++++++++++++-------------
>>>  block/qcow2-cluster.c |  46 ++++++++++++++++++-----
>>>  block/qcow2.c         |  95 +++++++++++++++++++++++-----------------------
>>>  block/qcow2.h         |  13 +++----
>>>  4 files changed, 165 insertions(+), 91 deletions(-)
>>>
>>> diff --git a/block/qcow.c b/block/qcow.c
>>> index bf5c570..01fba54 100644
>>> --- a/block/qcow.c
>>> +++ b/block/qcow.c
>>> @@ -26,7 +26,7 @@
>>>  #include "qemu/module.h"
>>>  #include <zlib.h>
>>>  #include "qapi/qmp/qerror.h"
>>> -#include "crypto/aes.h"
>>> +#include "crypto/cipher.h"
>>>  #include "migration/migration.h"
>>>
>>>  /**************************************************************/
>>> @@ -72,10 +72,8 @@ typedef struct BDRVQcowState {
>>>      uint8_t *cluster_cache;
>>>      uint8_t *cluster_data;
>>>      uint64_t cluster_cache_offset;
>>> -    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
>>> +    QCryptoCipher *cipher; /* NULL if no key yet */
>>>      uint32_t crypt_method_header;
>>> -    AES_KEY aes_encrypt_key;
>>> -    AES_KEY aes_decrypt_key;
>>>      CoMutex lock;
>>>      Error *migration_blocker;
>>>  } BDRVQcowState;
>>> @@ -154,6 +152,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
>>>          ret = -EINVAL;
>>>          goto fail;
>>>      }
>>> +    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
>>> +        error_setg(errp, "AES cipher not available");
>>> +        ret = -EINVAL;
>>> +        goto fail;
>>> +    }
>>>      s->crypt_method_header = header.crypt_method;
>>>      if (s->crypt_method_header) {
>>>          bs->encrypted = 1;
>>> @@ -260,6 +263,7 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
>>>      BDRVQcowState *s = bs->opaque;
>>>      uint8_t keybuf[16];
>>>      int len, i;
>>> +    Error *err;
>>>
>>>      memset(keybuf, 0, 16);
>>>      len = strlen(key);
>>> @@ -271,38 +275,67 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
>>>          keybuf[i] = key[i];
>>>      }
>>>      assert(bs->encrypted);
>>> -    s->crypt_method = s->crypt_method_header;
>>>
>>> -    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
>>> -        return -1;
>>> -    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
>>> +    qcrypto_cipher_free(s->cipher);
>>> +    s->cipher = qcrypto_cipher_new(
>>> +        QCRYPTO_CIPHER_ALG_AES_128,
>>> +        QCRYPTO_CIPHER_MODE_CBC,
>>> +        keybuf, G_N_ELEMENTS(keybuf),
>>> +        &err);
>>> +
>>> +    if (!s->cipher) {
>>> +        /* XXX would be nice if errors in this method could
>>> +         * be properly propagate to the caller. Would need
>>> +         * the bdrv_set_key() API signature to be fixed. */
>>> +        error_free(err);
>>>          return -1;
>>> +    }
>>>      return 0;
>>>  }
>>>
>>>  /* The crypt function is compatible with the linux cryptoloop
>>>     algorithm for < 4 GB images. NOTE: out_buf == in_buf is
>>>     supported */
>>> -static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>>> -                            uint8_t *out_buf, const uint8_t *in_buf,
>>> -                            int nb_sectors, int enc,
>>> -                            const AES_KEY *key)
>>> +static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>>> +                           uint8_t *out_buf, const uint8_t *in_buf,
>>> +                           int nb_sectors, bool enc, Error **errp)
>>>  {
>>>      union {
>>>          uint64_t ll[2];
>>>          uint8_t b[16];
>>>      } ivec;
>>>      int i;
>>> +    int ret;
>>>
>>>      for(i = 0; i < nb_sectors; i++) {
>>>          ivec.ll[0] = cpu_to_le64(sector_num);
>>>          ivec.ll[1] = 0;
>>> -        AES_cbc_encrypt(in_buf, out_buf, 512, key,
>>> -                        ivec.b, enc);
>>> +        if (qcrypto_cipher_setiv(s->cipher,
>>> +                                 ivec.b, G_N_ELEMENTS(ivec.b),
>>> +                                 errp) < 0) {
>>> +            return -1;
>>> +        }
>>> +        if (enc) {
>>> +            ret = qcrypto_cipher_encrypt(s->cipher,
>>> +                                         in_buf,
>>> +                                         out_buf,
>>> +                                         512,
>>> +                                         errp);
>>> +        } else {
>>> +            ret = qcrypto_cipher_decrypt(s->cipher,
>>> +                                         in_buf,
>>> +                                         out_buf,
>>> +                                         512,
>>> +                                         errp);
>>> +        }
>>> +        if (ret < 0) {
>>> +            return -1;
>>> +        }
>>>          sector_num++;
>>>          in_buf += 512;
>>>          out_buf += 512;
>>>      }
>>> +    return 0;
>>>  }
>>>
>>>  /* 'allocate' is:
>>> @@ -416,15 +449,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
>>>                  if (bs->encrypted &&
>>>                      (n_end - n_start) < s->cluster_sectors) {
>>>                      uint64_t start_sect;
>>> -                    assert(s->crypt_method);
>>> +                    assert(s->cipher);
>>>                      start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
>>>                      memset(s->cluster_data + 512, 0x00, 512);
>>>                      for(i = 0; i < s->cluster_sectors; i++) {
>>>                          if (i < n_start || i >= n_end) {
>>> -                            encrypt_sectors(s, start_sect + i,
>>> -                                            s->cluster_data,
>>> -                                            s->cluster_data + 512, 1, 1,
>>> -                                            &s->aes_encrypt_key);
>>> +                            Error *err = NULL;
>>> +                            if (encrypt_sectors(s, start_sect + i,
>>> +                                                s->cluster_data,
>>> +                                                s->cluster_data + 512, 1,
>>> +                                                true, &err) < 0) {
>>> +                                error_free(err);
>>> +                                errno = EIO;
>>> +                                return -1;
>>> +                            }
>>>                              if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
>>>                                              s->cluster_data, 512) != 512)
>>>                                  return -1;
>>> @@ -464,7 +502,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
>>>      if (!cluster_offset) {
>>>          return 0;
>>>      }
>>> -    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) {
>>> +    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) {
>>>          return BDRV_BLOCK_DATA;
>>>      }
>>>      cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
>>> @@ -531,6 +569,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
>>>      QEMUIOVector hd_qiov;
>>>      uint8_t *buf;
>>>      void *orig_buf;
>>> +    Error *err = NULL;
>>>
>>>      if (qiov->niov > 1) {
>>>          buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
>>> @@ -594,10 +633,11 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
>>>                  break;
>>>              }
>>>              if (bs->encrypted) {
>>> -                assert(s->crypt_method);
>>> -                encrypt_sectors(s, sector_num, buf, buf,
>>> -                                n, 0,
>>> -                                &s->aes_decrypt_key);
>>> +                assert(s->cipher);
>>> +                if (encrypt_sectors(s, sector_num, buf, buf,
>>> +                                    n, false, &err) < 0) {
>>> +                    goto fail;
>>> +                }
>>>              }
>>>          }
>>>          ret = 0;
>>> @@ -618,6 +658,7 @@ done:
>>>      return ret;
>>>
>>>  fail:
>>> +    error_free(err);
>>>      ret = -EIO;
>>>      goto done;
>>>  }
>>> @@ -666,12 +707,17 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
>>>              break;
>>>          }
>>>          if (bs->encrypted) {
>>> -            assert(s->crypt_method);
>>> +            Error *err = NULL;
>>> +            assert(s->cipher);
>>>              if (!cluster_data) {
>>>                  cluster_data = g_malloc0(s->cluster_size);
>>>              }
>>> -            encrypt_sectors(s, sector_num, cluster_data, buf,
>>> -                            n, 1, &s->aes_encrypt_key);
>>> +            if (encrypt_sectors(s, sector_num, cluster_data, buf,
>>> +                                n, true, &err) < 0) {
>>> +                error_free(err);
>>> +                ret = -EIO;
>>> +                break;
>>> +            }
>>>              src_buf = cluster_data;
>>>          } else {
>>>              src_buf = buf;
>>> @@ -708,6 +754,8 @@ static void qcow_close(BlockDriverState *bs)
>>>  {
>>>      BDRVQcowState *s = bs->opaque;
>>>
>>> +    qcrypto_cipher_free(s->cipher);
>>> +    s->cipher = NULL;
>>>      g_free(s->l1_table);
>>>      qemu_vfree(s->l2_cache);
>>>      g_free(s->cluster_cache);
>>> diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
>>> index 1a5c97a..b43f186 100644
>>> --- a/block/qcow2-cluster.c
>>> +++ b/block/qcow2-cluster.c
>>> @@ -339,26 +339,47 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab
>>>  /* The crypt function is compatible with the linux cryptoloop
>>>     algorithm for < 4 GB images. NOTE: out_buf == in_buf is
>>>     supported */
>>> -void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>>> -                           uint8_t *out_buf, const uint8_t *in_buf,
>>> -                           int nb_sectors, int enc,
>>> -                           const AES_KEY *key)
>>> +int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>>> +                          uint8_t *out_buf, const uint8_t *in_buf,
>>> +                          int nb_sectors, bool enc,
>>> +                          Error **errp)
>>>  {
>>>      union {
>>>          uint64_t ll[2];
>>>          uint8_t b[16];
>>>      } ivec;
>>>      int i;
>>> +    int ret;
>>>
>>>      for(i = 0; i < nb_sectors; i++) {
>>>          ivec.ll[0] = cpu_to_le64(sector_num);
>>>          ivec.ll[1] = 0;
>>> -        AES_cbc_encrypt(in_buf, out_buf, 512, key,
>>> -                        ivec.b, enc);
>>> +        if (qcrypto_cipher_setiv(s->cipher,
>>> +                                 ivec.b, G_N_ELEMENTS(ivec.b),
>>> +                                 errp) < 0) {
>>> +            return -1;
>>> +        }
>>> +        if (enc) {
>>> +            ret = qcrypto_cipher_encrypt(s->cipher,
>>> +                                         in_buf,
>>> +                                         out_buf,
>>> +                                         512,
>>> +                                         errp);
>>> +        } else {
>>> +            ret = qcrypto_cipher_decrypt(s->cipher,
>>> +                                         in_buf,
>>> +                                         out_buf,
>>> +                                         512,
>>> +                                         errp);
>>> +        }
>>> +        if (ret < 0) {
>>> +            return -1;
>>> +        }
>>>          sector_num++;
>>>          in_buf += 512;
>>>          out_buf += 512;
>>>      }
>>> +    return 0;
>>>  }
>>>
>>>  static int coroutine_fn copy_sectors(BlockDriverState *bs,
>>> @@ -401,10 +422,15 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
>>>      }
>>>
>>>      if (bs->encrypted) {
>>> -        assert(s->crypt_method);
>>> -        qcow2_encrypt_sectors(s, start_sect + n_start,
>>> -                        iov.iov_base, iov.iov_base, n, 1,
>>> -                        &s->aes_encrypt_key);
>>> +        Error *err = NULL;
>>> +        assert(s->cipher);
>>> +        if (qcow2_encrypt_sectors(s, start_sect + n_start,
>>> +                                  iov.iov_base, iov.iov_base, n,
>>> +                                  true, &err) < 0) {
>>> +            ret = -EIO;
>>> +            error_free(err);
>>> +            goto out;
>>> +        }
>>>      }
>>>
>>>      ret = qcow2_pre_write_overlap_check(bs, 0,
>>> diff --git a/block/qcow2.c b/block/qcow2.c
>>> index 85e0731..76c331b 100644
>>> --- a/block/qcow2.c
>>> +++ b/block/qcow2.c
>>> @@ -698,6 +698,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
>>>          ret = -EINVAL;
>>>          goto fail;
>>>      }
>>> +    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
>>> +        error_setg(errp, "AES cipher not available");
>>> +        ret = -EINVAL;
>>> +        goto fail;
>>> +    }
>>>      s->crypt_method_header = header.crypt_method;
>>>      if (s->crypt_method_header) {
>>>          bs->encrypted = 1;
>>> @@ -1031,6 +1036,7 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
>>>      BDRVQcowState *s = bs->opaque;
>>>      uint8_t keybuf[16];
>>>      int len, i;
>>> +    Error *err = NULL;
>>>
>>>      memset(keybuf, 0, 16);
>>>      len = strlen(key);
>>> @@ -1042,30 +1048,21 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
>>>          keybuf[i] = key[i];
>>>      }
>>>      assert(bs->encrypted);
>>> -    s->crypt_method = s->crypt_method_header;
>>>
>>> -    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
>>> -        return -1;
>>> -    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
>>> +    qcrypto_cipher_free(s->cipher);
>>> +    s->cipher = qcrypto_cipher_new(
>>> +        QCRYPTO_CIPHER_ALG_AES_128,
>>> +        QCRYPTO_CIPHER_MODE_CBC,
>>> +        keybuf, G_N_ELEMENTS(keybuf),
>>> +        &err);
>>> +
>>> +    if (!s->cipher) {
>>> +        /* XXX would be nice if errors in this method could
>>> +         * be properly propagate to the caller. Would need
>>> +         * the bdrv_set_key() API signature to be fixed. */
>>> +        error_free(err);
>>>          return -1;
>>> -#if 0
>>> -    /* test */
>>> -    {
>>> -        uint8_t in[16];
>>> -        uint8_t out[16];
>>> -        uint8_t tmp[16];
>>> -        for(i=0;i<16;i++)
>>> -            in[i] = i;
>>> -        AES_encrypt(in, tmp, &s->aes_encrypt_key);
>>> -        AES_decrypt(tmp, out, &s->aes_decrypt_key);
>>> -        for(i = 0; i < 16; i++)
>>> -            printf(" %02x", tmp[i]);
>>> -        printf("\n");
>>> -        for(i = 0; i < 16; i++)
>>> -            printf(" %02x", out[i]);
>>> -        printf("\n");
>>>      }
>>> -#endif
>>>      return 0;
>>>  }
>>>
>>> @@ -1108,7 +1105,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
>>>      }
>>>
>>>      if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
>>> -        !s->crypt_method) {
>>> +        !s->cipher) {
>>>          index_in_cluster = sector_num & (s->cluster_sectors - 1);
>>>          cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
>>>          status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
>>> @@ -1158,7 +1155,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
>>>
>>>          /* prepare next request */
>>>          cur_nr_sectors = remaining_sectors;
>>> -        if (s->crypt_method) {
>>> +        if (s->cipher) {
>>>              cur_nr_sectors = MIN(cur_nr_sectors,
>>>                  QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
>>>          }
>>> @@ -1230,7 +1227,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
>>>              }
>>>
>>>              if (bs->encrypted) {
>>> -                assert(s->crypt_method);
>>> +                assert(s->cipher);
>>>
>>>                  /*
>>>                   * For encrypted images, read everything into a temporary
>>> @@ -1263,9 +1260,15 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
>>>                  goto fail;
>>>              }
>>>              if (bs->encrypted) {
>>> -                assert(s->crypt_method);
>>> -                qcow2_encrypt_sectors(s, sector_num,  cluster_data,
>>> -                    cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key);
>>> +                assert(s->cipher);
>>> +                Error *err = NULL;
>>> +                if (qcow2_encrypt_sectors(s, sector_num,  cluster_data,
>>> +                                          cluster_data, cur_nr_sectors, false,
>>> +                                          &err) < 0) {
>>> +                    error_free(err);
>>> +                    ret = -EIO;
>>> +                    goto fail;
>>> +                }
>>>                  qemu_iovec_from_buf(qiov, bytes_done,
>>>                      cluster_data, 512 * cur_nr_sectors);
>>>              }
>>> @@ -1343,7 +1346,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
>>>              cur_nr_sectors * 512);
>>>
>>>          if (bs->encrypted) {
>>> -            assert(s->crypt_method);
>>> +            Error *err = NULL;
>>> +            assert(s->cipher);
>>>              if (!cluster_data) {
>>>                  cluster_data = qemu_try_blockalign(bs->file,
>>>                                                     QCOW_MAX_CRYPT_CLUSTERS
>>> @@ -1358,8 +1362,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
>>>                     QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
>>>              qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
>>>
>>> -            qcow2_encrypt_sectors(s, sector_num, cluster_data,
>>> -                cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key);
>>> +            if (qcow2_encrypt_sectors(s, sector_num, cluster_data,
>>> +                                      cluster_data, cur_nr_sectors,
>>> +                                      true, &err) < 0) {
>>> +                error_free(err);
>>> +                ret = -EIO;
>>> +                goto fail;
>>> +            }
>>>
>>>              qemu_iovec_reset(&hd_qiov);
>>>              qemu_iovec_add(&hd_qiov, cluster_data,
>>> @@ -1465,6 +1474,9 @@ static void qcow2_close(BlockDriverState *bs)
>>>      qcow2_cache_destroy(bs, s->l2_table_cache);
>>>      qcow2_cache_destroy(bs, s->refcount_block_cache);
>>>
>>> +    qcrypto_cipher_free(s->cipher);
>>> +    s->cipher = NULL;
>>> +
>>>      g_free(s->unknown_header_fields);
>>>      cleanup_unknown_header_ext(bs);
>>>
>>> @@ -1481,9 +1493,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>>>  {
>>>      BDRVQcowState *s = bs->opaque;
>>>      int flags = s->flags;
>>> -    AES_KEY aes_encrypt_key;
>>> -    AES_KEY aes_decrypt_key;
>>> -    uint32_t crypt_method = 0;
>>> +    QCryptoCipher *cipher = NULL;
>>>      QDict *options;
>>>      Error *local_err = NULL;
>>>      int ret;
>>> @@ -1493,12 +1503,8 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>>>       * that means we don't have to worry about reopening them here.
>>>       */
>>>
>>> -    if (bs->encrypted) {
>>> -        assert(s->crypt_method);
>>> -        crypt_method = s->crypt_method;
>>> -        memcpy(&aes_encrypt_key, &s->aes_encrypt_key, sizeof(aes_encrypt_key));
>>> -        memcpy(&aes_decrypt_key, &s->aes_decrypt_key, sizeof(aes_decrypt_key));
>>> -    }
>>> +    cipher = s->cipher;
>>> +    s->cipher = NULL;
>>>
>>>      qcow2_close(bs);
>>>
>>> @@ -1523,11 +1529,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
>>>          return;
>>>      }
>>>
>>> -    if (bs->encrypted) {
>>> -        s->crypt_method = crypt_method;
>>> -        memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
>>> -        memcpy(&s->aes_decrypt_key, &aes_decrypt_key, sizeof(aes_decrypt_key));
>>> -    }
>>> +    s->cipher = cipher;
>>>  }
>>>
>>>  static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
>>> @@ -2728,8 +2730,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>>>              backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
>>>          } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
>>>              encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
>>> -                                        s->crypt_method);
>>> -            if (encrypt != !!s->crypt_method) {
>>> +                                        !!s->cipher);
>>> +
>>> +            if (encrypt != !!s->cipher) {
>>>                  fprintf(stderr, "Changing the encryption flag is not "
>>>                          "supported.\n");
>>>                  return -ENOTSUP;
>>> diff --git a/block/qcow2.h b/block/qcow2.h
>>> index 462147c..72e1328 100644
>>> --- a/block/qcow2.h
>>> +++ b/block/qcow2.h
>>> @@ -25,7 +25,7 @@
>>>  #ifndef BLOCK_QCOW2_H
>>>  #define BLOCK_QCOW2_H
>>>
>>> -#include "crypto/aes.h"
>>> +#include "crypto/cipher.h"
>>>  #include "block/coroutine.h"
>>>
>>>  //#define DEBUG_ALLOC
>>> @@ -253,10 +253,8 @@ typedef struct BDRVQcowState {
>>>
>>>      CoMutex lock;
>>>
>>> -    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
>>> +    QCryptoCipher *cipher; /* current cipher, NULL if no key yet */
>>>      uint32_t crypt_method_header;
>>> -    AES_KEY aes_encrypt_key;
>>> -    AES_KEY aes_decrypt_key;
>>>      uint64_t snapshots_offset;
>>>      int snapshots_size;
>>>      unsigned int nb_snapshots;
>>> @@ -536,10 +534,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
>>>  int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
>>>  void qcow2_l2_cache_reset(BlockDriverState *bs);
>>>  int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
>>> -void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>>> -                     uint8_t *out_buf, const uint8_t *in_buf,
>>> -                     int nb_sectors, int enc,
>>> -                     const AES_KEY *key);
>>> +int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>>> +                          uint8_t *out_buf, const uint8_t *in_buf,
>>> +                          int nb_sectors, bool enc, Error **errp);
>>>
>>>  int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
>>>      int *num, uint64_t *cluster_offset);
>>>
>>
>>
> 
>
Aurelien Jarno July 9, 2015, 2:51 p.m. UTC | #4
On 2015-07-09 12:53, Christian Borntraeger wrote:
> Forgot some CCs (patch author and migration folks)
> 
> 
> Am 09.07.2015 um 12:17 schrieb Christian Borntraeger:
> > Am 07.07.2015 um 16:12 schrieb Paolo Bonzini:
> >> From: "Daniel P. Berrange" <berrange@redhat.com>
> >>
> >> Switch the qcow/qcow2 block driver over to use the generic cipher
> >> API, this allows it to use the pluggable AES implementations,
> >> instead of being hardcoded to use QEMU's built-in impl.
> >>
> >> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> >> Message-Id: <1435770638-25715-10-git-send-email-berrange@redhat.com>
> >> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> > 
> > For whatever reason this breaks migration(or virsh restore)
> > from guests that were created with an older version of QEMU.
> > 
> > 
> > 
> > Thread 1 (Thread 0x3fffb856bd0 (LWP 32226)):
> > #0  0x0000000080354a54 in qcrypto_cipher_free (cipher=0x0) at /home/cborntra/REPOS/qemu/crypto/cipher-builtin.c:357
> > #1  0x00000000802ca912 in qcow2_close (bs=0x80a0cea0) at /home/cborntra/REPOS/qemu/block/qcow2.c:1477
> > #2  0x00000000802caa32 in qcow2_invalidate_cache (bs=0x80a0cea0, errp=0x81a3fc58) at /home/cborntra/REPOS/qemu/block/qcow2.c:1509
> > #3  0x000000008029bac0 in bdrv_invalidate_cache (bs=0x80a0cea0, errp=0x81a3fd08) at /home/cborntra/REPOS/qemu/block.c:3135
> > #4  0x000000008029bbe6 in bdrv_invalidate_cache_all (errp=0x81a3fdd0) at /home/cborntra/REPOS/qemu/block.c:3160
> > #5  0x000000008021de50 in process_incoming_migration_co (opaque=0x80acaae0) at /home/cborntra/REPOS/qemu/migration/migration.c:160
> > #6  0x00000000802ab96a in coroutine_trampoline (i0=0, i1=-2137149984) at /home/cborntra/REPOS/qemu/coroutine-ucontext.c:80
> > #7  0x000003fffc463ca2 in __makecontext_ret () from /lib64/libc.so.6
> > Backtrace stopped: previous frame identical to this frame (corrupt stack?)

This is the same kind of backtrace I got on a MIPS host (see my other
mail). The reason is that a NULL pointer is dereferenced before testing
it is non NULL in qcrypto_cipher_free.
diff mbox

Patch

diff --git a/block/qcow.c b/block/qcow.c
index bf5c570..01fba54 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -26,7 +26,7 @@ 
 #include "qemu/module.h"
 #include <zlib.h>
 #include "qapi/qmp/qerror.h"
-#include "crypto/aes.h"
+#include "crypto/cipher.h"
 #include "migration/migration.h"
 
 /**************************************************************/
@@ -72,10 +72,8 @@  typedef struct BDRVQcowState {
     uint8_t *cluster_cache;
     uint8_t *cluster_data;
     uint64_t cluster_cache_offset;
-    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+    QCryptoCipher *cipher; /* NULL if no key yet */
     uint32_t crypt_method_header;
-    AES_KEY aes_encrypt_key;
-    AES_KEY aes_decrypt_key;
     CoMutex lock;
     Error *migration_blocker;
 } BDRVQcowState;
@@ -154,6 +152,11 @@  static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
         ret = -EINVAL;
         goto fail;
     }
+    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
+        error_setg(errp, "AES cipher not available");
+        ret = -EINVAL;
+        goto fail;
+    }
     s->crypt_method_header = header.crypt_method;
     if (s->crypt_method_header) {
         bs->encrypted = 1;
@@ -260,6 +263,7 @@  static int qcow_set_key(BlockDriverState *bs, const char *key)
     BDRVQcowState *s = bs->opaque;
     uint8_t keybuf[16];
     int len, i;
+    Error *err;
 
     memset(keybuf, 0, 16);
     len = strlen(key);
@@ -271,38 +275,67 @@  static int qcow_set_key(BlockDriverState *bs, const char *key)
         keybuf[i] = key[i];
     }
     assert(bs->encrypted);
-    s->crypt_method = s->crypt_method_header;
 
-    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
-        return -1;
-    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
+    qcrypto_cipher_free(s->cipher);
+    s->cipher = qcrypto_cipher_new(
+        QCRYPTO_CIPHER_ALG_AES_128,
+        QCRYPTO_CIPHER_MODE_CBC,
+        keybuf, G_N_ELEMENTS(keybuf),
+        &err);
+
+    if (!s->cipher) {
+        /* XXX would be nice if errors in this method could
+         * be properly propagate to the caller. Would need
+         * the bdrv_set_key() API signature to be fixed. */
+        error_free(err);
         return -1;
+    }
     return 0;
 }
 
 /* The crypt function is compatible with the linux cryptoloop
    algorithm for < 4 GB images. NOTE: out_buf == in_buf is
    supported */
-static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
-                            uint8_t *out_buf, const uint8_t *in_buf,
-                            int nb_sectors, int enc,
-                            const AES_KEY *key)
+static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+                           uint8_t *out_buf, const uint8_t *in_buf,
+                           int nb_sectors, bool enc, Error **errp)
 {
     union {
         uint64_t ll[2];
         uint8_t b[16];
     } ivec;
     int i;
+    int ret;
 
     for(i = 0; i < nb_sectors; i++) {
         ivec.ll[0] = cpu_to_le64(sector_num);
         ivec.ll[1] = 0;
-        AES_cbc_encrypt(in_buf, out_buf, 512, key,
-                        ivec.b, enc);
+        if (qcrypto_cipher_setiv(s->cipher,
+                                 ivec.b, G_N_ELEMENTS(ivec.b),
+                                 errp) < 0) {
+            return -1;
+        }
+        if (enc) {
+            ret = qcrypto_cipher_encrypt(s->cipher,
+                                         in_buf,
+                                         out_buf,
+                                         512,
+                                         errp);
+        } else {
+            ret = qcrypto_cipher_decrypt(s->cipher,
+                                         in_buf,
+                                         out_buf,
+                                         512,
+                                         errp);
+        }
+        if (ret < 0) {
+            return -1;
+        }
         sector_num++;
         in_buf += 512;
         out_buf += 512;
     }
+    return 0;
 }
 
 /* 'allocate' is:
@@ -416,15 +449,20 @@  static uint64_t get_cluster_offset(BlockDriverState *bs,
                 if (bs->encrypted &&
                     (n_end - n_start) < s->cluster_sectors) {
                     uint64_t start_sect;
-                    assert(s->crypt_method);
+                    assert(s->cipher);
                     start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
                     memset(s->cluster_data + 512, 0x00, 512);
                     for(i = 0; i < s->cluster_sectors; i++) {
                         if (i < n_start || i >= n_end) {
-                            encrypt_sectors(s, start_sect + i,
-                                            s->cluster_data,
-                                            s->cluster_data + 512, 1, 1,
-                                            &s->aes_encrypt_key);
+                            Error *err = NULL;
+                            if (encrypt_sectors(s, start_sect + i,
+                                                s->cluster_data,
+                                                s->cluster_data + 512, 1,
+                                                true, &err) < 0) {
+                                error_free(err);
+                                errno = EIO;
+                                return -1;
+                            }
                             if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
                                             s->cluster_data, 512) != 512)
                                 return -1;
@@ -464,7 +502,7 @@  static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
     if (!cluster_offset) {
         return 0;
     }
-    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) {
+    if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) {
         return BDRV_BLOCK_DATA;
     }
     cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
@@ -531,6 +569,7 @@  static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
     QEMUIOVector hd_qiov;
     uint8_t *buf;
     void *orig_buf;
+    Error *err = NULL;
 
     if (qiov->niov > 1) {
         buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
@@ -594,10 +633,11 @@  static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
                 break;
             }
             if (bs->encrypted) {
-                assert(s->crypt_method);
-                encrypt_sectors(s, sector_num, buf, buf,
-                                n, 0,
-                                &s->aes_decrypt_key);
+                assert(s->cipher);
+                if (encrypt_sectors(s, sector_num, buf, buf,
+                                    n, false, &err) < 0) {
+                    goto fail;
+                }
             }
         }
         ret = 0;
@@ -618,6 +658,7 @@  done:
     return ret;
 
 fail:
+    error_free(err);
     ret = -EIO;
     goto done;
 }
@@ -666,12 +707,17 @@  static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
             break;
         }
         if (bs->encrypted) {
-            assert(s->crypt_method);
+            Error *err = NULL;
+            assert(s->cipher);
             if (!cluster_data) {
                 cluster_data = g_malloc0(s->cluster_size);
             }
-            encrypt_sectors(s, sector_num, cluster_data, buf,
-                            n, 1, &s->aes_encrypt_key);
+            if (encrypt_sectors(s, sector_num, cluster_data, buf,
+                                n, true, &err) < 0) {
+                error_free(err);
+                ret = -EIO;
+                break;
+            }
             src_buf = cluster_data;
         } else {
             src_buf = buf;
@@ -708,6 +754,8 @@  static void qcow_close(BlockDriverState *bs)
 {
     BDRVQcowState *s = bs->opaque;
 
+    qcrypto_cipher_free(s->cipher);
+    s->cipher = NULL;
     g_free(s->l1_table);
     qemu_vfree(s->l2_cache);
     g_free(s->cluster_cache);
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 1a5c97a..b43f186 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -339,26 +339,47 @@  static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab
 /* The crypt function is compatible with the linux cryptoloop
    algorithm for < 4 GB images. NOTE: out_buf == in_buf is
    supported */
-void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
-                           uint8_t *out_buf, const uint8_t *in_buf,
-                           int nb_sectors, int enc,
-                           const AES_KEY *key)
+int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+                          uint8_t *out_buf, const uint8_t *in_buf,
+                          int nb_sectors, bool enc,
+                          Error **errp)
 {
     union {
         uint64_t ll[2];
         uint8_t b[16];
     } ivec;
     int i;
+    int ret;
 
     for(i = 0; i < nb_sectors; i++) {
         ivec.ll[0] = cpu_to_le64(sector_num);
         ivec.ll[1] = 0;
-        AES_cbc_encrypt(in_buf, out_buf, 512, key,
-                        ivec.b, enc);
+        if (qcrypto_cipher_setiv(s->cipher,
+                                 ivec.b, G_N_ELEMENTS(ivec.b),
+                                 errp) < 0) {
+            return -1;
+        }
+        if (enc) {
+            ret = qcrypto_cipher_encrypt(s->cipher,
+                                         in_buf,
+                                         out_buf,
+                                         512,
+                                         errp);
+        } else {
+            ret = qcrypto_cipher_decrypt(s->cipher,
+                                         in_buf,
+                                         out_buf,
+                                         512,
+                                         errp);
+        }
+        if (ret < 0) {
+            return -1;
+        }
         sector_num++;
         in_buf += 512;
         out_buf += 512;
     }
+    return 0;
 }
 
 static int coroutine_fn copy_sectors(BlockDriverState *bs,
@@ -401,10 +422,15 @@  static int coroutine_fn copy_sectors(BlockDriverState *bs,
     }
 
     if (bs->encrypted) {
-        assert(s->crypt_method);
-        qcow2_encrypt_sectors(s, start_sect + n_start,
-                        iov.iov_base, iov.iov_base, n, 1,
-                        &s->aes_encrypt_key);
+        Error *err = NULL;
+        assert(s->cipher);
+        if (qcow2_encrypt_sectors(s, start_sect + n_start,
+                                  iov.iov_base, iov.iov_base, n,
+                                  true, &err) < 0) {
+            ret = -EIO;
+            error_free(err);
+            goto out;
+        }
     }
 
     ret = qcow2_pre_write_overlap_check(bs, 0,
diff --git a/block/qcow2.c b/block/qcow2.c
index 85e0731..76c331b 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -698,6 +698,11 @@  static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
         ret = -EINVAL;
         goto fail;
     }
+    if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
+        error_setg(errp, "AES cipher not available");
+        ret = -EINVAL;
+        goto fail;
+    }
     s->crypt_method_header = header.crypt_method;
     if (s->crypt_method_header) {
         bs->encrypted = 1;
@@ -1031,6 +1036,7 @@  static int qcow2_set_key(BlockDriverState *bs, const char *key)
     BDRVQcowState *s = bs->opaque;
     uint8_t keybuf[16];
     int len, i;
+    Error *err = NULL;
 
     memset(keybuf, 0, 16);
     len = strlen(key);
@@ -1042,30 +1048,21 @@  static int qcow2_set_key(BlockDriverState *bs, const char *key)
         keybuf[i] = key[i];
     }
     assert(bs->encrypted);
-    s->crypt_method = s->crypt_method_header;
 
-    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
-        return -1;
-    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
+    qcrypto_cipher_free(s->cipher);
+    s->cipher = qcrypto_cipher_new(
+        QCRYPTO_CIPHER_ALG_AES_128,
+        QCRYPTO_CIPHER_MODE_CBC,
+        keybuf, G_N_ELEMENTS(keybuf),
+        &err);
+
+    if (!s->cipher) {
+        /* XXX would be nice if errors in this method could
+         * be properly propagate to the caller. Would need
+         * the bdrv_set_key() API signature to be fixed. */
+        error_free(err);
         return -1;
-#if 0
-    /* test */
-    {
-        uint8_t in[16];
-        uint8_t out[16];
-        uint8_t tmp[16];
-        for(i=0;i<16;i++)
-            in[i] = i;
-        AES_encrypt(in, tmp, &s->aes_encrypt_key);
-        AES_decrypt(tmp, out, &s->aes_decrypt_key);
-        for(i = 0; i < 16; i++)
-            printf(" %02x", tmp[i]);
-        printf("\n");
-        for(i = 0; i < 16; i++)
-            printf(" %02x", out[i]);
-        printf("\n");
     }
-#endif
     return 0;
 }
 
@@ -1108,7 +1105,7 @@  static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
     }
 
     if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
-        !s->crypt_method) {
+        !s->cipher) {
         index_in_cluster = sector_num & (s->cluster_sectors - 1);
         cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
         status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
@@ -1158,7 +1155,7 @@  static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
 
         /* prepare next request */
         cur_nr_sectors = remaining_sectors;
-        if (s->crypt_method) {
+        if (s->cipher) {
             cur_nr_sectors = MIN(cur_nr_sectors,
                 QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
         }
@@ -1230,7 +1227,7 @@  static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
             }
 
             if (bs->encrypted) {
-                assert(s->crypt_method);
+                assert(s->cipher);
 
                 /*
                  * For encrypted images, read everything into a temporary
@@ -1263,9 +1260,15 @@  static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
                 goto fail;
             }
             if (bs->encrypted) {
-                assert(s->crypt_method);
-                qcow2_encrypt_sectors(s, sector_num,  cluster_data,
-                    cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key);
+                assert(s->cipher);
+                Error *err = NULL;
+                if (qcow2_encrypt_sectors(s, sector_num,  cluster_data,
+                                          cluster_data, cur_nr_sectors, false,
+                                          &err) < 0) {
+                    error_free(err);
+                    ret = -EIO;
+                    goto fail;
+                }
                 qemu_iovec_from_buf(qiov, bytes_done,
                     cluster_data, 512 * cur_nr_sectors);
             }
@@ -1343,7 +1346,8 @@  static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
             cur_nr_sectors * 512);
 
         if (bs->encrypted) {
-            assert(s->crypt_method);
+            Error *err = NULL;
+            assert(s->cipher);
             if (!cluster_data) {
                 cluster_data = qemu_try_blockalign(bs->file,
                                                    QCOW_MAX_CRYPT_CLUSTERS
@@ -1358,8 +1362,13 @@  static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
                    QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
             qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
 
-            qcow2_encrypt_sectors(s, sector_num, cluster_data,
-                cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key);
+            if (qcow2_encrypt_sectors(s, sector_num, cluster_data,
+                                      cluster_data, cur_nr_sectors,
+                                      true, &err) < 0) {
+                error_free(err);
+                ret = -EIO;
+                goto fail;
+            }
 
             qemu_iovec_reset(&hd_qiov);
             qemu_iovec_add(&hd_qiov, cluster_data,
@@ -1465,6 +1474,9 @@  static void qcow2_close(BlockDriverState *bs)
     qcow2_cache_destroy(bs, s->l2_table_cache);
     qcow2_cache_destroy(bs, s->refcount_block_cache);
 
+    qcrypto_cipher_free(s->cipher);
+    s->cipher = NULL;
+
     g_free(s->unknown_header_fields);
     cleanup_unknown_header_ext(bs);
 
@@ -1481,9 +1493,7 @@  static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
 {
     BDRVQcowState *s = bs->opaque;
     int flags = s->flags;
-    AES_KEY aes_encrypt_key;
-    AES_KEY aes_decrypt_key;
-    uint32_t crypt_method = 0;
+    QCryptoCipher *cipher = NULL;
     QDict *options;
     Error *local_err = NULL;
     int ret;
@@ -1493,12 +1503,8 @@  static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
      * that means we don't have to worry about reopening them here.
      */
 
-    if (bs->encrypted) {
-        assert(s->crypt_method);
-        crypt_method = s->crypt_method;
-        memcpy(&aes_encrypt_key, &s->aes_encrypt_key, sizeof(aes_encrypt_key));
-        memcpy(&aes_decrypt_key, &s->aes_decrypt_key, sizeof(aes_decrypt_key));
-    }
+    cipher = s->cipher;
+    s->cipher = NULL;
 
     qcow2_close(bs);
 
@@ -1523,11 +1529,7 @@  static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
         return;
     }
 
-    if (bs->encrypted) {
-        s->crypt_method = crypt_method;
-        memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
-        memcpy(&s->aes_decrypt_key, &aes_decrypt_key, sizeof(aes_decrypt_key));
-    }
+    s->cipher = cipher;
 }
 
 static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
@@ -2728,8 +2730,9 @@  static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
             backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
         } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
             encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
-                                        s->crypt_method);
-            if (encrypt != !!s->crypt_method) {
+                                        !!s->cipher);
+
+            if (encrypt != !!s->cipher) {
                 fprintf(stderr, "Changing the encryption flag is not "
                         "supported.\n");
                 return -ENOTSUP;
diff --git a/block/qcow2.h b/block/qcow2.h
index 462147c..72e1328 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -25,7 +25,7 @@ 
 #ifndef BLOCK_QCOW2_H
 #define BLOCK_QCOW2_H
 
-#include "crypto/aes.h"
+#include "crypto/cipher.h"
 #include "block/coroutine.h"
 
 //#define DEBUG_ALLOC
@@ -253,10 +253,8 @@  typedef struct BDRVQcowState {
 
     CoMutex lock;
 
-    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+    QCryptoCipher *cipher; /* current cipher, NULL if no key yet */
     uint32_t crypt_method_header;
-    AES_KEY aes_encrypt_key;
-    AES_KEY aes_decrypt_key;
     uint64_t snapshots_offset;
     int snapshots_size;
     unsigned int nb_snapshots;
@@ -536,10 +534,9 @@  int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
 int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
 void qcow2_l2_cache_reset(BlockDriverState *bs);
 int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
-void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
-                     uint8_t *out_buf, const uint8_t *in_buf,
-                     int nb_sectors, int enc,
-                     const AES_KEY *key);
+int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+                          uint8_t *out_buf, const uint8_t *in_buf,
+                          int nb_sectors, bool enc, Error **errp);
 
 int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
     int *num, uint64_t *cluster_offset);