xref: /qemu/crypto/block-qcow.c (revision 07f0d32641e04703e6ed9626d06179060ea772ca)
1  /*
2   * QEMU Crypto block device encryption QCow/QCow2 AES-CBC format
3   *
4   * Copyright (c) 2015-2016 Red Hat, Inc.
5   *
6   * This library is free software; you can redistribute it and/or
7   * modify it under the terms of the GNU Lesser General Public
8   * License as published by the Free Software Foundation; either
9   * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18   *
19   */
20  
21  /*
22   * Note that the block encryption implemented in this file is broken
23   * by design. This exists only to allow data to be liberated from
24   * existing qcow[2] images and should not be used in any new areas.
25   */
26  
27  #include "qemu/osdep.h"
28  #include "qapi/error.h"
29  
30  #include "block-qcow.h"
31  #include "crypto/secret.h"
32  
33  #define QCRYPTO_BLOCK_QCOW_SECTOR_SIZE 512
34  
35  
36  static bool
37  qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED,
38                                size_t buf_size G_GNUC_UNUSED)
39  {
40      return false;
41  }
42  
43  
44  static int
45  qcrypto_block_qcow_init(QCryptoBlock *block,
46                          const char *keysecret,
47                          Error **errp)
48  {
49      char *password;
50      int ret;
51      uint8_t keybuf[16];
52      int len;
53  
54      memset(keybuf, 0, 16);
55  
56      password = qcrypto_secret_lookup_as_utf8(keysecret, errp);
57      if (!password) {
58          return -1;
59      }
60  
61      len = strlen(password);
62      memcpy(keybuf, password, MIN(len, sizeof(keybuf)));
63      g_free(password);
64  
65      block->niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALGO_AES_128,
66                                             QCRYPTO_CIPHER_MODE_CBC);
67      block->ivgen = qcrypto_ivgen_new(QCRYPTO_IV_GEN_ALGO_PLAIN64,
68                                       0, 0, NULL, 0, errp);
69      if (!block->ivgen) {
70          ret = -ENOTSUP;
71          goto fail;
72      }
73  
74      ret = qcrypto_block_init_cipher(block, QCRYPTO_CIPHER_ALGO_AES_128,
75                                      QCRYPTO_CIPHER_MODE_CBC,
76                                      keybuf, G_N_ELEMENTS(keybuf),
77                                      errp);
78      if (ret < 0) {
79          ret = -ENOTSUP;
80          goto fail;
81      }
82  
83      block->sector_size = QCRYPTO_BLOCK_QCOW_SECTOR_SIZE;
84      block->payload_offset = 0;
85  
86      return 0;
87  
88   fail:
89      qcrypto_block_free_cipher(block);
90      qcrypto_ivgen_free(block->ivgen);
91      return ret;
92  }
93  
94  
95  static int
96  qcrypto_block_qcow_open(QCryptoBlock *block,
97                          QCryptoBlockOpenOptions *options,
98                          const char *optprefix,
99                          QCryptoBlockReadFunc readfunc G_GNUC_UNUSED,
100                          void *opaque G_GNUC_UNUSED,
101                          unsigned int flags,
102                          Error **errp)
103  {
104      if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) {
105          block->sector_size = QCRYPTO_BLOCK_QCOW_SECTOR_SIZE;
106          block->payload_offset = 0;
107          return 0;
108      } else {
109          if (!options->u.qcow.key_secret) {
110              error_setg(errp,
111                         "Parameter '%skey-secret' is required for cipher",
112                         optprefix ? optprefix : "");
113              return -1;
114          }
115          return qcrypto_block_qcow_init(block, options->u.qcow.key_secret,
116                                         errp);
117      }
118  }
119  
120  
121  static int
122  qcrypto_block_qcow_create(QCryptoBlock *block,
123                            QCryptoBlockCreateOptions *options,
124                            const char *optprefix,
125                            QCryptoBlockInitFunc initfunc G_GNUC_UNUSED,
126                            QCryptoBlockWriteFunc writefunc G_GNUC_UNUSED,
127                            void *opaque G_GNUC_UNUSED,
128                            Error **errp)
129  {
130      if (!options->u.qcow.key_secret) {
131          error_setg(errp, "Parameter '%skey-secret' is required for cipher",
132                     optprefix ? optprefix : "");
133          return -1;
134      }
135      /* QCow2 has no special header, since everything is hardwired */
136      return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp);
137  }
138  
139  
140  static void
141  qcrypto_block_qcow_cleanup(QCryptoBlock *block)
142  {
143  }
144  
145  
146  static int
147  qcrypto_block_qcow_decrypt(QCryptoBlock *block,
148                             uint64_t offset,
149                             uint8_t *buf,
150                             size_t len,
151                             Error **errp)
152  {
153      assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE));
154      assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE));
155      return qcrypto_block_decrypt_helper(block,
156                                          QCRYPTO_BLOCK_QCOW_SECTOR_SIZE,
157                                          offset, buf, len, errp);
158  }
159  
160  
161  static int
162  qcrypto_block_qcow_encrypt(QCryptoBlock *block,
163                             uint64_t offset,
164                             uint8_t *buf,
165                             size_t len,
166                             Error **errp)
167  {
168      assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE));
169      assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE));
170      return qcrypto_block_encrypt_helper(block,
171                                          QCRYPTO_BLOCK_QCOW_SECTOR_SIZE,
172                                          offset, buf, len, errp);
173  }
174  
175  
176  const QCryptoBlockDriver qcrypto_block_driver_qcow = {
177      .open = qcrypto_block_qcow_open,
178      .create = qcrypto_block_qcow_create,
179      .cleanup = qcrypto_block_qcow_cleanup,
180      .decrypt = qcrypto_block_qcow_decrypt,
181      .encrypt = qcrypto_block_qcow_encrypt,
182      .has_format = qcrypto_block_qcow_has_format,
183  };
184