]> git.proxmox.com Git - mirror_qemu.git/blame - crypto/cipher-gnutls.c.inc
include/qemu: Add TCGCPUOps typedef to typedefs.h
[mirror_qemu.git] / crypto / cipher-gnutls.c.inc
CommitLineData
3d2b61ff
DB
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#if GNUTLS_VERSION_NUMBER >= 0x030608
27#define QEMU_GNUTLS_XTS
28#endif
29
30bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
31 QCryptoCipherMode mode)
32{
33
34 switch (mode) {
35 case QCRYPTO_CIPHER_MODE_ECB:
36 case QCRYPTO_CIPHER_MODE_CBC:
37 switch (alg) {
38 case QCRYPTO_CIPHER_ALG_AES_128:
39 case QCRYPTO_CIPHER_ALG_AES_192:
40 case QCRYPTO_CIPHER_ALG_AES_256:
41 case QCRYPTO_CIPHER_ALG_DES:
42 case QCRYPTO_CIPHER_ALG_3DES:
43 return true;
44 default:
45 return false;
46 }
47#ifdef QEMU_GNUTLS_XTS
48 case QCRYPTO_CIPHER_MODE_XTS:
49 switch (alg) {
50 case QCRYPTO_CIPHER_ALG_AES_128:
51 case QCRYPTO_CIPHER_ALG_AES_256:
52 return true;
53 default:
54 return false;
55 }
56#endif
57 default:
58 return false;
59 }
60}
61
62typedef struct QCryptoCipherGnutls QCryptoCipherGnutls;
63struct QCryptoCipherGnutls {
64 QCryptoCipher base;
65 gnutls_cipher_hd_t handle; /* XTS & CBC mode */
66 gnutls_cipher_algorithm_t galg; /* ECB mode */
67 guint8 *key; /* ECB mode */
68 size_t nkey; /* ECB mode */
69 size_t blocksize;
70};
71
72
73static void
74qcrypto_gnutls_cipher_free(QCryptoCipher *cipher)
75{
76 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
77
78 g_free(ctx->key);
79 if (ctx->handle) {
80 gnutls_cipher_deinit(ctx->handle);
81 }
82 g_free(ctx);
83}
84
85
86static int
87qcrypto_gnutls_cipher_encrypt(QCryptoCipher *cipher,
88 const void *in,
89 void *out,
90 size_t len,
91 Error **errp)
92{
93 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
94 int err;
95
96 if (len % ctx->blocksize) {
97 error_setg(errp, "Length %zu must be a multiple of block size %zu",
98 len, ctx->blocksize);
99 return -1;
100 }
101
102 if (ctx->handle) { /* CBC / XTS mode */
103 err = gnutls_cipher_encrypt2(ctx->handle,
104 in, len,
105 out, len);
106 if (err != 0) {
107 error_setg(errp, "Cannot encrypt data: %s",
108 gnutls_strerror(err));
109 return -1;
110 }
111 } else { /* ECB mode very inefficiently faked with CBC */
112 g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
113 while (len) {
114 gnutls_cipher_hd_t handle;
115 gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
5f6d4f79 116 err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
3d2b61ff
DB
117 if (err != 0) {
118 error_setg(errp, "Cannot initialize cipher: %s",
119 gnutls_strerror(err));
120 return -1;
121 }
122
123 gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
124
125 err = gnutls_cipher_encrypt2(handle,
126 in, ctx->blocksize,
127 out, ctx->blocksize);
128 if (err != 0) {
129 gnutls_cipher_deinit(handle);
130 error_setg(errp, "Cannot encrypt data: %s",
131 gnutls_strerror(err));
132 return -1;
133 }
134 gnutls_cipher_deinit(handle);
135
136 len -= ctx->blocksize;
137 in += ctx->blocksize;
138 out += ctx->blocksize;
139 }
140 }
141
142 return 0;
143}
144
145
146static int
147qcrypto_gnutls_cipher_decrypt(QCryptoCipher *cipher,
148 const void *in,
149 void *out,
150 size_t len,
151 Error **errp)
152{
153 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
154 int err;
155
156 if (len % ctx->blocksize) {
157 error_setg(errp, "Length %zu must be a multiple of block size %zu",
158 len, ctx->blocksize);
159 return -1;
160 }
161
162 if (ctx->handle) { /* CBC / XTS mode */
163 err = gnutls_cipher_decrypt2(ctx->handle,
164 in, len,
165 out, len);
166
167 if (err != 0) {
168 error_setg(errp, "Cannot decrypt data: %s",
169 gnutls_strerror(err));
170 return -1;
171 }
172 } else { /* ECB mode very inefficiently faked with CBC */
173 g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
174 while (len) {
175 gnutls_cipher_hd_t handle;
176 gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
5f6d4f79 177 err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
3d2b61ff
DB
178 if (err != 0) {
179 error_setg(errp, "Cannot initialize cipher: %s",
180 gnutls_strerror(err));
181 return -1;
182 }
183
184 gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
185
186 err = gnutls_cipher_decrypt2(handle,
187 in, ctx->blocksize,
188 out, ctx->blocksize);
189 if (err != 0) {
190 gnutls_cipher_deinit(handle);
191 error_setg(errp, "Cannot encrypt data: %s",
192 gnutls_strerror(err));
193 return -1;
194 }
195 gnutls_cipher_deinit(handle);
196
197 len -= ctx->blocksize;
198 in += ctx->blocksize;
199 out += ctx->blocksize;
200 }
201 }
202
203 return 0;
204}
205
206static int
207qcrypto_gnutls_cipher_setiv(QCryptoCipher *cipher,
208 const uint8_t *iv, size_t niv,
209 Error **errp)
210{
211 QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
212
213 if (niv != ctx->blocksize) {
214 error_setg(errp, "Expected IV size %zu not %zu",
215 ctx->blocksize, niv);
216 return -1;
217 }
218
219 gnutls_cipher_set_iv(ctx->handle, (unsigned char *)iv, niv);
220
221 return 0;
222}
223
224
225static struct QCryptoCipherDriver gnutls_driver = {
226 .cipher_encrypt = qcrypto_gnutls_cipher_encrypt,
227 .cipher_decrypt = qcrypto_gnutls_cipher_decrypt,
228 .cipher_setiv = qcrypto_gnutls_cipher_setiv,
229 .cipher_free = qcrypto_gnutls_cipher_free,
230};
231
232static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
233 QCryptoCipherMode mode,
234 const uint8_t *key,
235 size_t nkey,
236 Error **errp)
237{
238 QCryptoCipherGnutls *ctx;
239 gnutls_datum_t gkey = { (unsigned char *)key, nkey };
240 gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN;
241 int err;
242
243 switch (mode) {
244#ifdef QEMU_GNUTLS_XTS
245 case QCRYPTO_CIPHER_MODE_XTS:
246 switch (alg) {
247 case QCRYPTO_CIPHER_ALG_AES_128:
248 galg = GNUTLS_CIPHER_AES_128_XTS;
249 break;
250 case QCRYPTO_CIPHER_ALG_AES_256:
251 galg = GNUTLS_CIPHER_AES_256_XTS;
252 break;
253 default:
254 break;
255 }
256 break;
257#endif
258
259 case QCRYPTO_CIPHER_MODE_ECB:
260 case QCRYPTO_CIPHER_MODE_CBC:
261 switch (alg) {
262 case QCRYPTO_CIPHER_ALG_AES_128:
263 galg = GNUTLS_CIPHER_AES_128_CBC;
264 break;
265 case QCRYPTO_CIPHER_ALG_AES_192:
266 galg = GNUTLS_CIPHER_AES_192_CBC;
267 break;
268 case QCRYPTO_CIPHER_ALG_AES_256:
269 galg = GNUTLS_CIPHER_AES_256_CBC;
270 break;
271 case QCRYPTO_CIPHER_ALG_DES:
272 galg = GNUTLS_CIPHER_DES_CBC;
273 break;
274 case QCRYPTO_CIPHER_ALG_3DES:
275 galg = GNUTLS_CIPHER_3DES_CBC;
276 break;
277 default:
278 break;
279 }
280 break;
281 default:
282 break;
283 }
284
285 if (galg == GNUTLS_CIPHER_UNKNOWN) {
286 error_setg(errp, "Unsupported cipher algorithm %s with %s mode",
287 QCryptoCipherAlgorithm_str(alg),
288 QCryptoCipherMode_str(mode));
289 return NULL;
290 }
291
292 if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
293 return NULL;
294 }
295
296 ctx = g_new0(QCryptoCipherGnutls, 1);
297 ctx->base.driver = &gnutls_driver;
298
299 if (mode == QCRYPTO_CIPHER_MODE_ECB) {
300 ctx->key = g_new0(guint8, nkey);
301 memcpy(ctx->key, key, nkey);
302 ctx->nkey = nkey;
303 ctx->galg = galg;
304 } else {
305 err = gnutls_cipher_init(&ctx->handle, galg, &gkey, NULL);
306 if (err != 0) {
307 error_setg(errp, "Cannot initialize cipher: %s",
308 gnutls_strerror(err));
309 goto error;
310 }
311 }
312
313 if (alg == QCRYPTO_CIPHER_ALG_DES ||
314 alg == QCRYPTO_CIPHER_ALG_3DES)
315 ctx->blocksize = 8;
316 else
317 ctx->blocksize = 16;
318
319 /*
320 * Our API contract for requires iv to be optional
321 * but nettle gets unhappy when called by gnutls
322 * in this case, so we just force set a default
323 * all-zeros IV, to match behaviour of other backends.
324 */
325 if (mode != QCRYPTO_CIPHER_MODE_ECB) {
326 g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
327 gnutls_cipher_set_iv(ctx->handle, iv, ctx->blocksize);
328 }
329
330 return &ctx->base;
331
332 error:
333 qcrypto_gnutls_cipher_free(&ctx->base);
334 return NULL;
335}