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 <[email protected]>
---
 crypto/cipher-nettle.c.inc | 128 +++++++++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+)

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:
-- 
2.43.0

Reply via email to