xref: /openbmc/qemu/crypto/block.c (revision 438c78da)
1 /*
2  * QEMU Crypto block device encryption
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 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 #include "qemu/osdep.h"
22 #include "qapi/error.h"
23 #include "blockpriv.h"
24 #include "block-qcow.h"
25 #include "block-luks.h"
26 
27 static const QCryptoBlockDriver *qcrypto_block_drivers[] = {
28     [Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow,
29     [Q_CRYPTO_BLOCK_FORMAT_LUKS] = &qcrypto_block_driver_luks,
30 };
31 
32 
33 bool qcrypto_block_has_format(QCryptoBlockFormat format,
34                               const uint8_t *buf,
35                               size_t len)
36 {
37     const QCryptoBlockDriver *driver;
38 
39     if (format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
40         !qcrypto_block_drivers[format]) {
41         return false;
42     }
43 
44     driver = qcrypto_block_drivers[format];
45 
46     return driver->has_format(buf, len);
47 }
48 
49 
50 QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options,
51                                  const char *optprefix,
52                                  QCryptoBlockReadFunc readfunc,
53                                  void *opaque,
54                                  unsigned int flags,
55                                  Error **errp)
56 {
57     QCryptoBlock *block = g_new0(QCryptoBlock, 1);
58 
59     block->format = options->format;
60 
61     if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
62         !qcrypto_block_drivers[options->format]) {
63         error_setg(errp, "Unsupported block driver %s",
64                    QCryptoBlockFormat_str(options->format));
65         g_free(block);
66         return NULL;
67     }
68 
69     block->driver = qcrypto_block_drivers[options->format];
70 
71     if (block->driver->open(block, options, optprefix,
72                             readfunc, opaque, flags, errp) < 0) {
73         g_free(block);
74         return NULL;
75     }
76 
77     return block;
78 }
79 
80 
81 QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
82                                    const char *optprefix,
83                                    QCryptoBlockInitFunc initfunc,
84                                    QCryptoBlockWriteFunc writefunc,
85                                    void *opaque,
86                                    Error **errp)
87 {
88     QCryptoBlock *block = g_new0(QCryptoBlock, 1);
89 
90     block->format = options->format;
91 
92     if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
93         !qcrypto_block_drivers[options->format]) {
94         error_setg(errp, "Unsupported block driver %s",
95                    QCryptoBlockFormat_str(options->format));
96         g_free(block);
97         return NULL;
98     }
99 
100     block->driver = qcrypto_block_drivers[options->format];
101 
102     if (block->driver->create(block, options, optprefix, initfunc,
103                               writefunc, opaque, errp) < 0) {
104         g_free(block);
105         return NULL;
106     }
107 
108     return block;
109 }
110 
111 
112 QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block,
113                                          Error **errp)
114 {
115     QCryptoBlockInfo *info = g_new0(QCryptoBlockInfo, 1);
116 
117     info->format = block->format;
118 
119     if (block->driver->get_info &&
120         block->driver->get_info(block, info, errp) < 0) {
121         g_free(info);
122         return NULL;
123     }
124 
125     return info;
126 }
127 
128 
129 int qcrypto_block_decrypt(QCryptoBlock *block,
130                           uint64_t offset,
131                           uint8_t *buf,
132                           size_t len,
133                           Error **errp)
134 {
135     return block->driver->decrypt(block, offset, buf, len, errp);
136 }
137 
138 
139 int qcrypto_block_encrypt(QCryptoBlock *block,
140                           uint64_t offset,
141                           uint8_t *buf,
142                           size_t len,
143                           Error **errp)
144 {
145     return block->driver->encrypt(block, offset, buf, len, errp);
146 }
147 
148 
149 QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block)
150 {
151     return block->cipher;
152 }
153 
154 
155 QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block)
156 {
157     return block->ivgen;
158 }
159 
160 
161 QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block)
162 {
163     return block->kdfhash;
164 }
165 
166 
167 uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block)
168 {
169     return block->payload_offset;
170 }
171 
172 
173 uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block)
174 {
175     return block->sector_size;
176 }
177 
178 
179 void qcrypto_block_free(QCryptoBlock *block)
180 {
181     if (!block) {
182         return;
183     }
184 
185     block->driver->cleanup(block);
186 
187     qcrypto_cipher_free(block->cipher);
188     qcrypto_ivgen_free(block->ivgen);
189     g_free(block);
190 }
191 
192 
193 int qcrypto_block_decrypt_helper(QCryptoCipher *cipher,
194                                  size_t niv,
195                                  QCryptoIVGen *ivgen,
196                                  int sectorsize,
197                                  uint64_t offset,
198                                  uint8_t *buf,
199                                  size_t len,
200                                  Error **errp)
201 {
202     uint8_t *iv;
203     int ret = -1;
204     uint64_t startsector = offset / sectorsize;
205 
206     assert(QEMU_IS_ALIGNED(offset, sectorsize));
207     assert(QEMU_IS_ALIGNED(len, sectorsize));
208 
209     iv = niv ? g_new0(uint8_t, niv) : NULL;
210 
211     while (len > 0) {
212         size_t nbytes;
213         if (niv) {
214             if (qcrypto_ivgen_calculate(ivgen,
215                                         startsector,
216                                         iv, niv,
217                                         errp) < 0) {
218                 goto cleanup;
219             }
220 
221             if (qcrypto_cipher_setiv(cipher,
222                                      iv, niv,
223                                      errp) < 0) {
224                 goto cleanup;
225             }
226         }
227 
228         nbytes = len > sectorsize ? sectorsize : len;
229         if (qcrypto_cipher_decrypt(cipher, buf, buf,
230                                    nbytes, errp) < 0) {
231             goto cleanup;
232         }
233 
234         startsector++;
235         buf += nbytes;
236         len -= nbytes;
237     }
238 
239     ret = 0;
240  cleanup:
241     g_free(iv);
242     return ret;
243 }
244 
245 
246 int qcrypto_block_encrypt_helper(QCryptoCipher *cipher,
247                                  size_t niv,
248                                  QCryptoIVGen *ivgen,
249                                  int sectorsize,
250                                  uint64_t offset,
251                                  uint8_t *buf,
252                                  size_t len,
253                                  Error **errp)
254 {
255     uint8_t *iv;
256     int ret = -1;
257     uint64_t startsector = offset / sectorsize;
258 
259     assert(QEMU_IS_ALIGNED(offset, sectorsize));
260     assert(QEMU_IS_ALIGNED(len, sectorsize));
261 
262     iv = niv ? g_new0(uint8_t, niv) : NULL;
263 
264     while (len > 0) {
265         size_t nbytes;
266         if (niv) {
267             if (qcrypto_ivgen_calculate(ivgen,
268                                         startsector,
269                                         iv, niv,
270                                         errp) < 0) {
271                 goto cleanup;
272             }
273 
274             if (qcrypto_cipher_setiv(cipher,
275                                      iv, niv,
276                                      errp) < 0) {
277                 goto cleanup;
278             }
279         }
280 
281         nbytes = len > sectorsize ? sectorsize : len;
282         if (qcrypto_cipher_encrypt(cipher, buf, buf,
283                                    nbytes, errp) < 0) {
284             goto cleanup;
285         }
286 
287         startsector++;
288         buf += nbytes;
289         len -= nbytes;
290     }
291 
292     ret = 0;
293  cleanup:
294     g_free(iv);
295     return ret;
296 }
297