xref: /openbmc/qemu/tests/unit/test-crypto-block.c (revision 2344f98e)
1 /*
2  * QEMU Crypto block encryption
3  *
4  * Copyright (c) 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 #include "qemu/osdep.h"
22 #include "qapi/error.h"
23 #include "crypto/init.h"
24 #include "crypto/block.h"
25 #include "qemu/buffer.h"
26 #include "qemu/module.h"
27 #include "crypto/secret.h"
28 #ifndef _WIN32
29 #include <sys/resource.h>
30 #endif
31 
32 #if (defined(_WIN32) || defined RUSAGE_THREAD) && \
33     (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT))
34 #define TEST_LUKS
35 #else
36 #undef TEST_LUKS
37 #endif
38 
39 static QCryptoBlockCreateOptions qcow_create_opts = {
40     .format = Q_CRYPTO_BLOCK_FORMAT_QCOW,
41     .u.qcow = {
42         .has_key_secret = true,
43         .key_secret = (char *)"sec0",
44     },
45 };
46 
47 static QCryptoBlockOpenOptions qcow_open_opts = {
48     .format = Q_CRYPTO_BLOCK_FORMAT_QCOW,
49     .u.qcow = {
50         .has_key_secret = true,
51         .key_secret = (char *)"sec0",
52     },
53 };
54 
55 
56 #ifdef TEST_LUKS
57 static QCryptoBlockOpenOptions luks_open_opts = {
58     .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
59     .u.luks = {
60         .has_key_secret = true,
61         .key_secret = (char *)"sec0",
62     },
63 };
64 
65 
66 /* Creation with all default values */
67 static QCryptoBlockCreateOptions luks_create_opts_default = {
68     .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
69     .u.luks = {
70         .has_key_secret = true,
71         .key_secret = (char *)"sec0",
72     },
73 };
74 
75 
76 /* ...and with explicit values */
77 static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = {
78     .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
79     .u.luks = {
80         .has_key_secret = true,
81         .key_secret = (char *)"sec0",
82         .has_cipher_alg = true,
83         .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
84         .has_cipher_mode = true,
85         .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
86         .has_ivgen_alg = true,
87         .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
88     },
89 };
90 
91 
92 static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = {
93     .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
94     .u.luks = {
95         .has_key_secret = true,
96         .key_secret = (char *)"sec0",
97         .has_cipher_alg = true,
98         .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
99         .has_cipher_mode = true,
100         .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
101         .has_ivgen_alg = true,
102         .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV,
103         .has_ivgen_hash_alg = true,
104         .ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256,
105         .has_hash_alg = true,
106         .hash_alg = QCRYPTO_HASH_ALG_SHA1,
107     },
108 };
109 #endif /* TEST_LUKS */
110 
111 
112 static struct QCryptoBlockTestData {
113     const char *path;
114     QCryptoBlockCreateOptions *create_opts;
115     QCryptoBlockOpenOptions *open_opts;
116 
117     bool expect_header;
118 
119     QCryptoCipherAlgorithm cipher_alg;
120     QCryptoCipherMode cipher_mode;
121     QCryptoHashAlgorithm hash_alg;
122 
123     QCryptoIVGenAlgorithm ivgen_alg;
124     QCryptoHashAlgorithm ivgen_hash;
125 
126     bool slow;
127 } test_data[] = {
128     {
129         .path = "/crypto/block/qcow",
130         .create_opts = &qcow_create_opts,
131         .open_opts = &qcow_open_opts,
132 
133         .expect_header = false,
134 
135         .cipher_alg = QCRYPTO_CIPHER_ALG_AES_128,
136         .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
137 
138         .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
139     },
140 #ifdef TEST_LUKS
141     {
142         .path = "/crypto/block/luks/default",
143         .create_opts = &luks_create_opts_default,
144         .open_opts = &luks_open_opts,
145 
146         .expect_header = true,
147 
148         .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
149         .cipher_mode = QCRYPTO_CIPHER_MODE_XTS,
150         .hash_alg = QCRYPTO_HASH_ALG_SHA256,
151 
152         .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
153 
154         .slow = true,
155     },
156     {
157         .path = "/crypto/block/luks/aes-256-cbc-plain64",
158         .create_opts = &luks_create_opts_aes256_cbc_plain64,
159         .open_opts = &luks_open_opts,
160 
161         .expect_header = true,
162 
163         .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
164         .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
165         .hash_alg = QCRYPTO_HASH_ALG_SHA256,
166 
167         .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64,
168 
169         .slow = true,
170     },
171     {
172         .path = "/crypto/block/luks/aes-256-cbc-essiv",
173         .create_opts = &luks_create_opts_aes256_cbc_essiv,
174         .open_opts = &luks_open_opts,
175 
176         .expect_header = true,
177 
178         .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256,
179         .cipher_mode = QCRYPTO_CIPHER_MODE_CBC,
180         .hash_alg = QCRYPTO_HASH_ALG_SHA1,
181 
182         .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV,
183         .ivgen_hash = QCRYPTO_HASH_ALG_SHA256,
184 
185         .slow = true,
186     },
187 #endif
188 };
189 
190 
191 static int test_block_read_func(QCryptoBlock *block,
192                                 size_t offset,
193                                 uint8_t *buf,
194                                 size_t buflen,
195                                 void *opaque,
196                                 Error **errp)
197 {
198     Buffer *header = opaque;
199 
200     g_assert_cmpint(offset + buflen, <=, header->capacity);
201 
202     memcpy(buf, header->buffer + offset, buflen);
203 
204     return 0;
205 }
206 
207 
208 static int test_block_init_func(QCryptoBlock *block,
209                                 size_t headerlen,
210                                 void *opaque,
211                                 Error **errp)
212 {
213     Buffer *header = opaque;
214 
215     g_assert_cmpint(header->capacity, ==, 0);
216 
217     buffer_reserve(header, headerlen);
218 
219     return 0;
220 }
221 
222 
223 static int test_block_write_func(QCryptoBlock *block,
224                                  size_t offset,
225                                  const uint8_t *buf,
226                                  size_t buflen,
227                                  void *opaque,
228                                  Error **errp)
229 {
230     Buffer *header = opaque;
231 
232     g_assert_cmpint(buflen + offset, <=, header->capacity);
233 
234     memcpy(header->buffer + offset, buf, buflen);
235     header->offset = offset + buflen;
236 
237     return 0;
238 }
239 
240 
241 static Object *test_block_secret(void)
242 {
243     return object_new_with_props(
244         TYPE_QCRYPTO_SECRET,
245         object_get_objects_root(),
246         "sec0",
247         &error_abort,
248         "data", "123456",
249         NULL);
250 }
251 
252 static void test_block_assert_setup(const struct QCryptoBlockTestData *data,
253                                     QCryptoBlock *blk)
254 {
255     QCryptoIVGen *ivgen;
256     QCryptoCipher *cipher;
257 
258     ivgen = qcrypto_block_get_ivgen(blk);
259     cipher = qcrypto_block_get_cipher(blk);
260 
261     g_assert(ivgen);
262     g_assert(cipher);
263 
264     g_assert_cmpint(data->cipher_alg, ==, cipher->alg);
265     g_assert_cmpint(data->cipher_mode, ==, cipher->mode);
266     g_assert_cmpint(data->hash_alg, ==,
267                     qcrypto_block_get_kdf_hash(blk));
268 
269     g_assert_cmpint(data->ivgen_alg, ==,
270                     qcrypto_ivgen_get_algorithm(ivgen));
271     g_assert_cmpint(data->ivgen_hash, ==,
272                     qcrypto_ivgen_get_hash(ivgen));
273 }
274 
275 
276 static void test_block(gconstpointer opaque)
277 {
278     const struct QCryptoBlockTestData *data = opaque;
279     QCryptoBlock *blk;
280     Buffer header;
281     Object *sec = test_block_secret();
282 
283     memset(&header, 0, sizeof(header));
284     buffer_init(&header, "header");
285 
286     blk = qcrypto_block_create(data->create_opts, NULL,
287                                test_block_init_func,
288                                test_block_write_func,
289                                &header,
290                                &error_abort);
291     g_assert(blk);
292 
293     if (data->expect_header) {
294         g_assert_cmpint(header.capacity, >, 0);
295     } else {
296         g_assert_cmpint(header.capacity, ==, 0);
297     }
298 
299     test_block_assert_setup(data, blk);
300 
301     qcrypto_block_free(blk);
302     object_unparent(sec);
303 
304     /* Ensure we can't open without the secret */
305     blk = qcrypto_block_open(data->open_opts, NULL,
306                              test_block_read_func,
307                              &header,
308                              0,
309                              1,
310                              NULL);
311     g_assert(blk == NULL);
312 
313     /* Ensure we can't open without the secret, unless NO_IO */
314     blk = qcrypto_block_open(data->open_opts, NULL,
315                              test_block_read_func,
316                              &header,
317                              QCRYPTO_BLOCK_OPEN_NO_IO,
318                              1,
319                              &error_abort);
320 
321     g_assert(qcrypto_block_get_cipher(blk) == NULL);
322     g_assert(qcrypto_block_get_ivgen(blk) == NULL);
323 
324     qcrypto_block_free(blk);
325 
326 
327     /* Now open for real with secret */
328     sec = test_block_secret();
329     blk = qcrypto_block_open(data->open_opts, NULL,
330                              test_block_read_func,
331                              &header,
332                              0,
333                              1,
334                              &error_abort);
335     g_assert(blk);
336 
337     test_block_assert_setup(data, blk);
338 
339     qcrypto_block_free(blk);
340 
341     object_unparent(sec);
342 
343     buffer_free(&header);
344 }
345 
346 
347 int main(int argc, char **argv)
348 {
349     gsize i;
350 
351     module_call_init(MODULE_INIT_QOM);
352     g_test_init(&argc, &argv, NULL);
353 
354     g_assert(qcrypto_init(NULL) == 0);
355 
356     for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
357         if (test_data[i].open_opts->format == Q_CRYPTO_BLOCK_FORMAT_LUKS &&
358             !qcrypto_hash_supports(test_data[i].hash_alg)) {
359             continue;
360         }
361         if (!test_data[i].slow ||
362             g_test_slow()) {
363             g_test_add_data_func(test_data[i].path, &test_data[i], test_block);
364         }
365     }
366 
367     return g_test_run();
368 }
369