master
inc 481 lines 14.3 KB
Raw
1 /*
2 * QEMU Crypto cipher gnutls algorithms
3 *
4 * Copyright (c) 2021 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 "cipherpriv.h"
23
24 #include <gnutls/crypto.h>
25
26 bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
27 QCryptoCipherMode mode)
28 {
29
30 switch (mode) {
31 case QCRYPTO_CIPHER_MODE_ECB:
32 case QCRYPTO_CIPHER_MODE_CBC:
33 switch (alg) {
34 case QCRYPTO_CIPHER_ALGO_AES_128:
35 case QCRYPTO_CIPHER_ALGO_AES_192:
36 case QCRYPTO_CIPHER_ALGO_AES_256:
37 case QCRYPTO_CIPHER_ALGO_DES:
38 case QCRYPTO_CIPHER_ALGO_3DES:
39 return true;
40 default:
41 return false;
42 }
43 case QCRYPTO_CIPHER_MODE_XTS:
44 switch (alg) {
45 case QCRYPTO_CIPHER_ALGO_AES_128:
46 case QCRYPTO_CIPHER_ALGO_AES_256:
47 return true;
48 default:
49 return false;
50 }
51 case QCRYPTO_CIPHER_MODE_GCM:
52 switch (alg) {
53 case QCRYPTO_CIPHER_ALGO_AES_128:
54 case QCRYPTO_CIPHER_ALGO_AES_192:
55 case QCRYPTO_CIPHER_ALGO_AES_256:
56 return true;
57 default:
58 return false;
59 }
60 default:
61 return false;
62 }
63 }
64
65 typedef struct QCryptoCipherGnutls QCryptoCipherGnutls;
66 struct QCryptoCipherGnutls {
67 QCryptoCipher base;
68 gnutls_cipher_hd_t handle; /* XTS & CBC mode */
69 gnutls_cipher_algorithm_t galg; /* ECB mode */
70 guint8 *key; /* ECB mode */
71 size_t nkey; /* ECB mode */
72 size_t blocksize;
73 };
74
75
76 static void
77 qcrypto_gnutls_cipher_free(QCryptoCipher *cipher)
78 {
79 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
80
81 g_free(ctx->key);
82 if (ctx->handle) {
83 gnutls_cipher_deinit(ctx->handle);
84 }
85 g_free(ctx);
86 }
87
88
89 static int
90 qcrypto_gnutls_cipher_encrypt(QCryptoCipher *cipher,
91 const void *in,
92 void *out,
93 size_t len,
94 Error **errp)
95 {
96 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
97 int err;
98
99 if (len % ctx->blocksize) {
100 error_setg(errp, "Length %zu must be a multiple of block size %zu",
101 len, ctx->blocksize);
102 return -1;
103 }
104
105 if (ctx->handle) { /* CBC / XTS mode */
106 err = gnutls_cipher_encrypt2(ctx->handle,
107 in, len,
108 out, len);
109 if (err != 0) {
110 error_setg(errp, "Cannot encrypt data: %s",
111 gnutls_strerror(err));
112 return -1;
113 }
114 } else { /* ECB mode very inefficiently faked with CBC */
115 g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
116 while (len) {
117 gnutls_cipher_hd_t handle;
118 gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
119 err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
120 if (err != 0) {
121 error_setg(errp, "Cannot initialize cipher: %s",
122 gnutls_strerror(err));
123 return -1;
124 }
125
126 gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
127
128 err = gnutls_cipher_encrypt2(handle,
129 in, ctx->blocksize,
130 out, ctx->blocksize);
131 if (err != 0) {
132 gnutls_cipher_deinit(handle);
133 error_setg(errp, "Cannot encrypt data: %s",
134 gnutls_strerror(err));
135 return -1;
136 }
137 gnutls_cipher_deinit(handle);
138
139 len -= ctx->blocksize;
140 in += ctx->blocksize;
141 out += ctx->blocksize;
142 }
143 }
144
145 return 0;
146 }
147
148
149 static int
150 qcrypto_gnutls_cipher_decrypt(QCryptoCipher *cipher,
151 const void *in,
152 void *out,
153 size_t len,
154 Error **errp)
155 {
156 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
157 int err;
158
159 if (len % ctx->blocksize) {
160 error_setg(errp, "Length %zu must be a multiple of block size %zu",
161 len, ctx->blocksize);
162 return -1;
163 }
164
165 if (ctx->handle) { /* CBC / XTS mode */
166 err = gnutls_cipher_decrypt2(ctx->handle,
167 in, len,
168 out, len);
169
170 if (err != 0) {
171 error_setg(errp, "Cannot decrypt data: %s",
172 gnutls_strerror(err));
173 return -1;
174 }
175 } else { /* ECB mode very inefficiently faked with CBC */
176 g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
177 while (len) {
178 gnutls_cipher_hd_t handle;
179 gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
180 err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
181 if (err != 0) {
182 error_setg(errp, "Cannot initialize cipher: %s",
183 gnutls_strerror(err));
184 return -1;
185 }
186
187 gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
188
189 err = gnutls_cipher_decrypt2(handle,
190 in, ctx->blocksize,
191 out, ctx->blocksize);
192 if (err != 0) {
193 gnutls_cipher_deinit(handle);
194 error_setg(errp, "Cannot encrypt data: %s",
195 gnutls_strerror(err));
196 return -1;
197 }
198 gnutls_cipher_deinit(handle);
199
200 len -= ctx->blocksize;
201 in += ctx->blocksize;
202 out += ctx->blocksize;
203 }
204 }
205
206 return 0;
207 }
208
209 static int
210 qcrypto_gnutls_cipher_setiv(QCryptoCipher *cipher,
211 const uint8_t *iv, size_t niv,
212 Error **errp)
213 {
214 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
215
216 if (niv != ctx->blocksize) {
217 error_setg(errp, "Expected IV size %zu not %zu",
218 ctx->blocksize, niv);
219 return -1;
220 }
221
222 gnutls_cipher_set_iv(ctx->handle, (unsigned char *)iv, niv);
223
224 return 0;
225 }
226
227
228 static struct QCryptoCipherDriver gnutls_driver = {
229 .cipher_encrypt = qcrypto_gnutls_cipher_encrypt,
230 .cipher_decrypt = qcrypto_gnutls_cipher_decrypt,
231 .cipher_setiv = qcrypto_gnutls_cipher_setiv,
232 .cipher_free = qcrypto_gnutls_cipher_free,
233 };
234
235 /*
236 * GCM is an AEAD stream mode: the nonce need not match the block size, the
237 * message length need not be a multiple of the block size, associated data is
238 * fed with gnutls_cipher_add_auth() and the authentication tag is read back
239 * with gnutls_cipher_tag().
240 */
241 static int
242 qcrypto_gnutls_cipher_encrypt_gcm(QCryptoCipher *cipher,
243 const void *in, void *out,
244 size_t len, Error **errp)
245 {
246 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
247 int err;
248
249 err = gnutls_cipher_encrypt2(ctx->handle, in, len, out, len);
250 if (err != 0) {
251 error_setg(errp, "Cannot encrypt data: %s", gnutls_strerror(err));
252 return -1;
253 }
254
255 return 0;
256 }
257
258 static int
259 qcrypto_gnutls_cipher_decrypt_gcm(QCryptoCipher *cipher,
260 const void *in, void *out,
261 size_t len, Error **errp)
262 {
263 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
264 int err;
265
266 err = gnutls_cipher_decrypt2(ctx->handle, in, len, out, len);
267 if (err != 0) {
268 error_setg(errp, "Cannot decrypt data: %s", gnutls_strerror(err));
269 return -1;
270 }
271
272 return 0;
273 }
274
275 static int
276 qcrypto_gnutls_cipher_setiv_gcm(QCryptoCipher *cipher,
277 const uint8_t *iv, size_t niv,
278 Error **errp)
279 {
280 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
281
282 gnutls_cipher_set_iv(ctx->handle, (void *)iv, niv);
283
284 return 0;
285 }
286
287 static int
288 qcrypto_gnutls_cipher_setaad_gcm(QCryptoCipher *cipher,
289 const uint8_t *aad, size_t len,
290 Error **errp)
291 {
292 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
293 int err;
294
295 err = gnutls_cipher_add_auth(ctx->handle, aad, len);
296 if (err != 0) {
297 error_setg(errp, "Cannot add associated data: %s",
298 gnutls_strerror(err));
299 return -1;
300 }
301
302 return 0;
303 }
304
305 static int
306 qcrypto_gnutls_cipher_gettag_gcm(QCryptoCipher *cipher,
307 uint8_t *tag, size_t len,
308 Error **errp)
309 {
310 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
311 int err;
312
313 err = gnutls_cipher_tag(ctx->handle, tag, len);
314 if (err != 0) {
315 error_setg(errp, "Cannot get authentication tag: %s",
316 gnutls_strerror(err));
317 return -1;
318 }
319
320 return 0;
321 }
322
323 static struct QCryptoCipherDriver gnutls_gcm_driver = {
324 .cipher_encrypt = qcrypto_gnutls_cipher_encrypt_gcm,
325 .cipher_decrypt = qcrypto_gnutls_cipher_decrypt_gcm,
326 .cipher_setiv = qcrypto_gnutls_cipher_setiv_gcm,
327 .cipher_setaad = qcrypto_gnutls_cipher_setaad_gcm,
328 .cipher_gettag = qcrypto_gnutls_cipher_gettag_gcm,
329 .cipher_free = qcrypto_gnutls_cipher_free,
330 };
331
332 static QCryptoCipher *
333 qcrypto_gnutls_aes_gcm_ctx_new(QCryptoCipherAlgo alg, const uint8_t *key,
334 size_t nkey, Error **errp)
335 {
336 gnutls_datum_t gkey = { (unsigned char *)key, nkey };
337 gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN;
338 QCryptoCipherGnutls *ctx;
339 int err;
340
341 switch (alg) {
342 case QCRYPTO_CIPHER_ALGO_AES_128:
343 galg = GNUTLS_CIPHER_AES_128_GCM;
344 break;
345 case QCRYPTO_CIPHER_ALGO_AES_192:
346 galg = GNUTLS_CIPHER_AES_192_GCM;
347 break;
348 case QCRYPTO_CIPHER_ALGO_AES_256:
349 galg = GNUTLS_CIPHER_AES_256_GCM;
350 break;
351 default:
352 error_setg(errp, "Unsupported cipher algorithm %s with GCM mode",
353 QCryptoCipherAlgo_str(alg));
354 return NULL;
355 }
356
357 if (!qcrypto_cipher_validate_key_length(alg, QCRYPTO_CIPHER_MODE_GCM,
358 nkey, errp)) {
359 return NULL;
360 }
361
362 ctx = g_new0(QCryptoCipherGnutls, 1);
363 ctx->base.driver = &gnutls_gcm_driver;
364 ctx->blocksize = 16;
365
366 err = gnutls_cipher_init(&ctx->handle, galg, &gkey, NULL);
367 if (err != 0) {
368 error_setg(errp, "Cannot initialize cipher: %s", gnutls_strerror(err));
369 g_free(ctx);
370 return NULL;
371 }
372
373 return &ctx->base;
374 }
375
376 static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
377 QCryptoCipherMode mode,
378 const uint8_t *key,
379 size_t nkey,
380 Error **errp)
381 {
382 QCryptoCipherGnutls *ctx;
383 gnutls_datum_t gkey = { (unsigned char *)key, nkey };
384 gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN;
385 int err;
386
387 if (mode == QCRYPTO_CIPHER_MODE_GCM) {
388 return qcrypto_gnutls_aes_gcm_ctx_new(alg, key, nkey, errp);
389 }
390
391 switch (mode) {
392 case QCRYPTO_CIPHER_MODE_XTS:
393 switch (alg) {
394 case QCRYPTO_CIPHER_ALGO_AES_128:
395 galg = GNUTLS_CIPHER_AES_128_XTS;
396 break;
397 case QCRYPTO_CIPHER_ALGO_AES_256:
398 galg = GNUTLS_CIPHER_AES_256_XTS;
399 break;
400 default:
401 break;
402 }
403 break;
404
405 case QCRYPTO_CIPHER_MODE_ECB:
406 case QCRYPTO_CIPHER_MODE_CBC:
407 switch (alg) {
408 case QCRYPTO_CIPHER_ALGO_AES_128:
409 galg = GNUTLS_CIPHER_AES_128_CBC;
410 break;
411 case QCRYPTO_CIPHER_ALGO_AES_192:
412 galg = GNUTLS_CIPHER_AES_192_CBC;
413 break;
414 case QCRYPTO_CIPHER_ALGO_AES_256:
415 galg = GNUTLS_CIPHER_AES_256_CBC;
416 break;
417 case QCRYPTO_CIPHER_ALGO_DES:
418 galg = GNUTLS_CIPHER_DES_CBC;
419 break;
420 case QCRYPTO_CIPHER_ALGO_3DES:
421 galg = GNUTLS_CIPHER_3DES_CBC;
422 break;
423 default:
424 break;
425 }
426 break;
427 default:
428 break;
429 }
430
431 if (galg == GNUTLS_CIPHER_UNKNOWN) {
432 error_setg(errp, "Unsupported cipher algorithm %s with %s mode",
433 QCryptoCipherAlgo_str(alg),
434 QCryptoCipherMode_str(mode));
435 return NULL;
436 }
437
438 if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
439 return NULL;
440 }
441
442 ctx = g_new0(QCryptoCipherGnutls, 1);
443 ctx->base.driver = &gnutls_driver;
444
445 if (mode == QCRYPTO_CIPHER_MODE_ECB) {
446 ctx->key = g_new0(guint8, nkey);
447 memcpy(ctx->key, key, nkey);
448 ctx->nkey = nkey;
449 ctx->galg = galg;
450 } else {
451 err = gnutls_cipher_init(&ctx->handle, galg, &gkey, NULL);
452 if (err != 0) {
453 error_setg(errp, "Cannot initialize cipher: %s",
454 gnutls_strerror(err));
455 goto error;
456 }
457 }
458
459 if (alg == QCRYPTO_CIPHER_ALGO_DES ||
460 alg == QCRYPTO_CIPHER_ALGO_3DES)
461 ctx->blocksize = 8;
462 else
463 ctx->blocksize = 16;
464
465 /*
466 * Our API contract for requires iv to be optional
467 * but nettle gets unhappy when called by gnutls
468 * in this case, so we just force set a default
469 * all-zeros IV, to match behaviour of other backends.
470 */
471 if (mode != QCRYPTO_CIPHER_MODE_ECB) {
472 g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
473 gnutls_cipher_set_iv(ctx->handle, iv, ctx->blocksize);
474 }
475
476 return &ctx->base;
477
478 error:
479 qcrypto_gnutls_cipher_free(&ctx->base);
480 return NULL;
481 }