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

Reply via email to