crypto/cipher-gnutls: Implement AES-GCM
Add the AES-GCM AEAD mode to the gnutls backend so it is available when QEMU is built with gnutls (neither gcrypt nor nettle). GCM uses the incremental gnutls_cipher_* API with the GNUTLS_CIPHER_AES_*_GCM algorithms: gnutls_cipher_set_iv() sets the nonce, gnutls_cipher_add_auth() feeds the associated data, gnutls_cipher_encrypt2()/decrypt2() process the message, and gnutls_cipher_tag() reads back the authentication tag. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-12-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater <clg@redhat.com>
Jamin Lin committed
Aug 11, 2026 at 06:01 UTC
66c320fd5d492cc5287e4f5bde52def3efbd149e
1 file changed
+154
crypto/cipher-gnutls.c.inc
+154
@@ -48,6 +48,15 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
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
}
@@ -223,6 +232,147 @@ static struct QCryptoCipherDriver gnutls_driver = {
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,
@@ -234,6 +384,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
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) {