@samitouri / QOSamiQemu / commits / 66c320fd5d

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) {