[PATCH v3 09/16] crypto/cipher-gcrypt: Implement AES-GCM

Jamin Lin <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
Map QCRYPTO_CIPHER_MODE_GCM to GCRY_CIPHER_MODE_GCM and advertise it in
qcrypto_cipher_supports() for 128-bit block ciphers. Add a GCM driver
whose setiv accepts the (typically 96-bit) nonce, whose encrypt/decrypt
do not require block-aligned lengths, and which implements setaad via
gcry_cipher_authenticate() and gettag via gcry_cipher_gettag().

Signed-off-by: Jamin Lin <[email protected]>
Reviewed-by: Daniel P. Berrangé <[email protected]>
Acked-by: Daniel P. Berrangé <[email protected]>
---
 crypto/cipher-gcrypt.c.inc | 101 +++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)

diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc
index 12eb9ddb5a..fce09a3c77 100644
--- a/crypto/cipher-gcrypt.c.inc
+++ b/crypto/cipher-gcrypt.c.inc
@@ -65,6 +65,8 @@ static int qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode)
         return GCRY_CIPHER_MODE_CBC;
     case QCRYPTO_CIPHER_MODE_CTR:
         return GCRY_CIPHER_MODE_CTR;
+    case QCRYPTO_CIPHER_MODE_GCM:
+        return GCRY_CIPHER_MODE_GCM;
     default:
         return GCRY_CIPHER_MODE_NONE;
     }
@@ -104,6 +106,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
     case QCRYPTO_CIPHER_MODE_XTS:
     case QCRYPTO_CIPHER_MODE_CTR:
         return true;
+    case QCRYPTO_CIPHER_MODE_GCM:
+        /* GCM requires a 128-bit block cipher. */
+        return gcry_cipher_get_algo_blklen(
+                   qcrypto_cipher_alg_to_gcry_alg(alg)) == 16;
     default:
         return false;
     }
@@ -228,6 +234,99 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = {
     .cipher_free = qcrypto_gcrypt_ctx_free,
 };
 
+/*
+ * GCM is an AEAD stream mode: the IV/nonce need not match the block size,
+ * the message length need not be a multiple of the block size, associated
+ * data is fed with gcry_cipher_authenticate() and the authentication tag is
+ * read back with gcry_cipher_gettag().
+ */
+static int qcrypto_gcrypt_gcm_setiv(QCryptoCipher *cipher,
+                                    const uint8_t *iv, size_t niv,
+                                    Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    gcry_cipher_reset(ctx->handle);
+    err = gcry_cipher_setiv(ctx->handle, iv, niv);
+    if (err != 0) {
+        error_setg(errp, "Cannot set IV: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qcrypto_gcrypt_gcm_setaad(QCryptoCipher *cipher,
+                                     const uint8_t *aad, size_t len,
+                                     Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    err = gcry_cipher_authenticate(ctx->handle, aad, len);
+    if (err != 0) {
+        error_setg(errp, "Cannot set AAD: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qcrypto_gcrypt_gcm_encrypt(QCryptoCipher *cipher, const void *in,
+                                      void *out, size_t len, Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    err = gcry_cipher_encrypt(ctx->handle, out, len, in, len);
+    if (err != 0) {
+        error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qcrypto_gcrypt_gcm_decrypt(QCryptoCipher *cipher, const void *in,
+                                      void *out, size_t len, Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    err = gcry_cipher_decrypt(ctx->handle, out, len, in, len);
+    if (err != 0) {
+        error_setg(errp, "Cannot decrypt data: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qcrypto_gcrypt_gcm_gettag(QCryptoCipher *cipher,
+                                     uint8_t *tag, size_t len, Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    err = gcry_cipher_gettag(ctx->handle, tag, len);
+    if (err != 0) {
+        error_setg(errp, "Cannot get tag: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static const struct QCryptoCipherDriver qcrypto_gcrypt_gcm_driver = {
+    .cipher_encrypt = qcrypto_gcrypt_gcm_encrypt,
+    .cipher_decrypt = qcrypto_gcrypt_gcm_decrypt,
+    .cipher_setiv = qcrypto_gcrypt_gcm_setiv,
+    .cipher_setaad = qcrypto_gcrypt_gcm_setaad,
+    .cipher_gettag = qcrypto_gcrypt_gcm_gettag,
+    .cipher_free = qcrypto_gcrypt_ctx_free,
+};
+
 static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
                                              QCryptoCipherMode mode,
                                              const uint8_t *key,
@@ -259,6 +358,8 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
 
     if (mode == QCRYPTO_CIPHER_MODE_CTR) {
         drv = &qcrypto_gcrypt_ctr_driver;
+    } else if (mode == QCRYPTO_CIPHER_MODE_GCM) {
+        drv = &qcrypto_gcrypt_gcm_driver;
     } else {
         drv = &qcrypto_gcrypt_driver;
     }
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.