Commit b0f7e8cc97 for qemu.org
commit b0f7e8cc97d16133bfd7ce344b525188d68e30f1
Author: Jamin Lin <jamin_lin@aspeedtech.com>
Date: Tue Aug 11 06:01:30 2026 +0000
crypto/cipher-nettle: Implement AES-GCM
Add the AES-GCM AEAD mode to the nettle backend so it is available when
QEMU is built with nettle instead of gcrypt. GCM is driven through
nettle's generic gcm_* interface, using the AES encrypt function for both
directions: gcm_set_iv() sets the (typically 96-bit) nonce, gcm_update()
feeds the associated data, gcm_encrypt()/gcm_decrypt() need not be block
aligned, and gcm_digest() produces 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-11-jamin_lin@aspeedtech.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc
index 1afdc391b4..d4847f0efe 100644
--- a/crypto/cipher-nettle.c.inc
+++ b/crypto/cipher-nettle.c.inc
@@ -27,6 +27,7 @@
#include <nettle/twofish.h>
#include <nettle/ctr.h>
#include <nettle/xts.h>
+#include <nettle/gcm.h>
#ifdef CONFIG_CRYPTO_SM4
#include <nettle/sm4.h>
#endif
@@ -410,6 +411,125 @@ DEFINE_ECB(qcrypto_nettle_sm4,
sm4_encrypt_native, sm4_decrypt_native)
#endif
+/*
+ * GCM is an AEAD mode built on AES (128-bit block only). Drive it through the
+ * generic gcm_* interface, using the block cipher's encrypt function for both
+ * directions; associated data is fed with gcm_update() and the authentication
+ * tag is produced by gcm_digest().
+ */
+typedef struct QCryptoNettleAESGCM {
+ QCryptoCipher base;
+ struct gcm_key gcm_key;
+ struct gcm_ctx gcm_ctx;
+ union {
+ struct aes128_ctx aes128;
+ struct aes192_ctx aes192;
+ struct aes256_ctx aes256;
+ } cipher;
+ nettle_cipher_func *encrypt;
+} QCryptoNettleAESGCM;
+
+static int qcrypto_nettle_aes_gcm_setiv(QCryptoCipher *cipher,
+ const uint8_t *iv, size_t niv,
+ Error **errp)
+{
+ QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base);
+
+ gcm_set_iv(&ctx->gcm_ctx, &ctx->gcm_key, niv, iv);
+ return 0;
+}
+
+static int qcrypto_nettle_aes_gcm_setaad(QCryptoCipher *cipher,
+ const uint8_t *aad, size_t len,
+ Error **errp)
+{
+ QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base);
+
+ gcm_update(&ctx->gcm_ctx, &ctx->gcm_key, len, aad);
+ return 0;
+}
+
+static int qcrypto_nettle_aes_gcm_encrypt(QCryptoCipher *cipher,
+ const void *in, void *out,
+ size_t len, Error **errp)
+{
+ QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base);
+
+ gcm_encrypt(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt,
+ len, out, in);
+ return 0;
+}
+
+static int qcrypto_nettle_aes_gcm_decrypt(QCryptoCipher *cipher,
+ const void *in, void *out,
+ size_t len, Error **errp)
+{
+ QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base);
+
+ gcm_decrypt(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt,
+ len, out, in);
+ return 0;
+}
+
+static int qcrypto_nettle_aes_gcm_gettag(QCryptoCipher *cipher,
+ uint8_t *tag, size_t len,
+ Error **errp)
+{
+ QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base);
+
+ gcm_digest(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt,
+ len, tag);
+ return 0;
+}
+
+static const struct QCryptoCipherDriver qcrypto_nettle_aes_gcm_driver = {
+ .cipher_encrypt = qcrypto_nettle_aes_gcm_encrypt,
+ .cipher_decrypt = qcrypto_nettle_aes_gcm_decrypt,
+ .cipher_setiv = qcrypto_nettle_aes_gcm_setiv,
+ .cipher_setaad = qcrypto_nettle_aes_gcm_setaad,
+ .cipher_gettag = qcrypto_nettle_aes_gcm_gettag,
+ .cipher_free = qcrypto_cipher_ctx_free,
+};
+
+static QCryptoCipher *qcrypto_nettle_aes_gcm_ctx_new(QCryptoCipherAlgo alg,
+ const uint8_t *key,
+ size_t nkey,
+ Error **errp)
+{
+ QCryptoNettleAESGCM *ctx;
+
+ if (!qcrypto_cipher_validate_key_length(alg, QCRYPTO_CIPHER_MODE_GCM,
+ nkey, errp)) {
+ return NULL;
+ }
+
+ ctx = g_new0(QCryptoNettleAESGCM, 1);
+ ctx->base.driver = &qcrypto_nettle_aes_gcm_driver;
+
+ switch (alg) {
+ case QCRYPTO_CIPHER_ALGO_AES_128:
+ aes128_set_encrypt_key(&ctx->cipher.aes128, key);
+ ctx->encrypt = aes128_encrypt_native;
+ break;
+ case QCRYPTO_CIPHER_ALGO_AES_192:
+ aes192_set_encrypt_key(&ctx->cipher.aes192, key);
+ ctx->encrypt = aes192_encrypt_native;
+ break;
+ case QCRYPTO_CIPHER_ALGO_AES_256:
+ aes256_set_encrypt_key(&ctx->cipher.aes256, key);
+ ctx->encrypt = aes256_encrypt_native;
+ break;
+ default:
+ error_setg(errp, "Unsupported cipher algorithm %s with GCM mode",
+ QCryptoCipherAlgo_str(alg));
+ g_free(ctx);
+ return NULL;
+ }
+
+ gcm_set_key(&ctx->gcm_key, &ctx->cipher, ctx->encrypt);
+ return &ctx->base;
+}
+
bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
QCryptoCipherMode mode)
{
@@ -440,6 +560,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
case QCRYPTO_CIPHER_MODE_XTS:
case QCRYPTO_CIPHER_MODE_CTR:
return true;
+ case QCRYPTO_CIPHER_MODE_GCM:
+ return alg == QCRYPTO_CIPHER_ALGO_AES_128 ||
+ alg == QCRYPTO_CIPHER_ALGO_AES_192 ||
+ alg == QCRYPTO_CIPHER_ALGO_AES_256;
default:
return false;
}
@@ -451,6 +575,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
size_t nkey,
Error **errp)
{
+ if (mode == QCRYPTO_CIPHER_MODE_GCM) {
+ return qcrypto_nettle_aes_gcm_ctx_new(alg, key, nkey, errp);
+ }
+
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
case QCRYPTO_CIPHER_MODE_CBC: