Title: [214822] trunk/Source/WebCore
Revision
214822
Author
zandober...@gmail.com
Date
2017-04-03 11:12:38 -0700 (Mon, 03 Apr 2017)

Log Message

[GCrypt] Implement AES_GCM support
https://bugs.webkit.org/show_bug.cgi?id=170271

Reviewed by Michael Catanzaro.

Source/WebCore:

Implement the CryptoAlgorithmAES_GCM::platform{Encrypt,Decrypt}
functionality for configurations that use libgcrypt. This is done
by leveraging the gcry_cipher_* APIs for the AES algorithm that's
deducted appropriately from the key size and the GCM cipher mode.

No new tests -- current ones cover this sufficiently, but are not yet
enabled due to other missing platform-specific SUBTLE_CRYPTO
implementations.

* crypto/gcrypt/CryptoAlgorithmAES_GCMGCrypt.cpp:
(WebCore::gcryptEncrypt):
(WebCore::gcryptDecrypt):
(WebCore::CryptoAlgorithmAES_GCM::platformEncrypt):
(WebCore::CryptoAlgorithmAES_GCM::platformDecrypt):

Source/WebCore/PAL:

* pal/crypto/gcrypt/Handle.h:
(PAL::GCrypt::HandleDeleter<gcry_cipher_hd_t>::operator()): Specialize
the HandleDeleter<> template for the gcry_cipher_hd_t type.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (214821 => 214822)


--- trunk/Source/WebCore/ChangeLog	2017-04-03 18:10:29 UTC (rev 214821)
+++ trunk/Source/WebCore/ChangeLog	2017-04-03 18:12:38 UTC (rev 214822)
@@ -1,5 +1,27 @@
 2017-04-03  Zan Dobersek  <zdober...@igalia.com>
 
+        [GCrypt] Implement AES_GCM support
+        https://bugs.webkit.org/show_bug.cgi?id=170271
+
+        Reviewed by Michael Catanzaro.
+
+        Implement the CryptoAlgorithmAES_GCM::platform{Encrypt,Decrypt}
+        functionality for configurations that use libgcrypt. This is done
+        by leveraging the gcry_cipher_* APIs for the AES algorithm that's
+        deducted appropriately from the key size and the GCM cipher mode.
+
+        No new tests -- current ones cover this sufficiently, but are not yet
+        enabled due to other missing platform-specific SUBTLE_CRYPTO
+        implementations.
+
+        * crypto/gcrypt/CryptoAlgorithmAES_GCMGCrypt.cpp:
+        (WebCore::gcryptEncrypt):
+        (WebCore::gcryptDecrypt):
+        (WebCore::CryptoAlgorithmAES_GCM::platformEncrypt):
+        (WebCore::CryptoAlgorithmAES_GCM::platformDecrypt):
+
+2017-04-03  Zan Dobersek  <zdober...@igalia.com>
+
         [GCrypt] Implement PBKDF2 support
         https://bugs.webkit.org/show_bug.cgi?id=170270
 

Modified: trunk/Source/WebCore/PAL/ChangeLog (214821 => 214822)


--- trunk/Source/WebCore/PAL/ChangeLog	2017-04-03 18:10:29 UTC (rev 214821)
+++ trunk/Source/WebCore/PAL/ChangeLog	2017-04-03 18:12:38 UTC (rev 214822)
@@ -1,3 +1,14 @@
+2017-04-03  Zan Dobersek  <zdober...@igalia.com>
+
+        [GCrypt] Implement AES_GCM support
+        https://bugs.webkit.org/show_bug.cgi?id=170271
+
+        Reviewed by Michael Catanzaro.
+
+        * pal/crypto/gcrypt/Handle.h:
+        (PAL::GCrypt::HandleDeleter<gcry_cipher_hd_t>::operator()): Specialize
+        the HandleDeleter<> template for the gcry_cipher_hd_t type.
+
 2017-03-30  Zan Dobersek  <zdober...@igalia.com>
 
         [GCrypt] Add the Utilities.h header

Modified: trunk/Source/WebCore/PAL/pal/crypto/gcrypt/Handle.h (214821 => 214822)


--- trunk/Source/WebCore/PAL/pal/crypto/gcrypt/Handle.h	2017-04-03 18:10:29 UTC (rev 214821)
+++ trunk/Source/WebCore/PAL/pal/crypto/gcrypt/Handle.h	2017-04-03 18:12:38 UTC (rev 214822)
@@ -83,6 +83,14 @@
 };
 
 template<>
+struct HandleDeleter<gcry_cipher_hd_t> {
+    void operator()(gcry_cipher_hd_t handle)
+    {
+        gcry_cipher_close(handle);
+    }
+};
+
+template<>
 struct HandleDeleter<gcry_mac_hd_t> {
     void operator()(gcry_mac_hd_t handle)
     {

Modified: trunk/Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_GCMGCrypt.cpp (214821 => 214822)


--- trunk/Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_GCMGCrypt.cpp	2017-04-03 18:10:29 UTC (rev 214821)
+++ trunk/Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_GCMGCrypt.cpp	2017-04-03 18:12:38 UTC (rev 214822)
@@ -1,5 +1,7 @@
 /*
  * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017 Metrological Group B.V.
+ * Copyright (C) 2017 Igalia S.L.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,21 +30,195 @@
 
 #if ENABLE(SUBTLE_CRYPTO)
 
+#include "CryptoAlgorithmAesGcmParams.h"
+#include "CryptoKeyAES.h"
 #include "ExceptionCode.h"
 #include "NotImplemented.h"
+#include "ScriptExecutionContext.h"
+#include <pal/crypto/gcrypt/Handle.h>
+#include <pal/crypto/gcrypt/Utilities.h>
+#include <wtf/CryptographicUtilities.h>
 
 namespace WebCore {
 
-void CryptoAlgorithmAES_GCM::platformEncrypt(std::unique_ptr<CryptoAlgorithmParameters>&&, Ref<CryptoKey>&&, Vector<uint8_t>&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&)
+static std::optional<Vector<uint8_t>> gcryptEncrypt(const Vector<uint8_t>& key, const Vector<uint8_t>& iv, Vector<uint8_t>&& plainText, const Vector<uint8_t>& additionalData, uint8_t tagLength)
 {
-    notImplemented();
+    auto algorithm = PAL::GCrypt::aesAlgorithmForKeySize(key.size() * 8);
+    if (!algorithm)
+        return std::nullopt;
+
+    PAL::GCrypt::Handle<gcry_cipher_hd_t> handle;
+    gcry_error_t error = gcry_cipher_open(&handle, *algorithm, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE);
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    error = gcry_cipher_setkey(handle, key.data(), key.size());
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    error = gcry_cipher_setiv(handle, iv.data(), iv.size());
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    if (!additionalData.isEmpty()) {
+        error = gcry_cipher_authenticate(handle, additionalData.data(), additionalData.size());
+        if (error != GPG_ERR_NO_ERROR) {
+            PAL::GCrypt::logError(error);
+            return std::nullopt;
+        }
+    }
+
+    error = gcry_cipher_final(handle);
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    Vector<uint8_t> output(plainText.size());
+    error = gcry_cipher_encrypt(handle, output.data(), output.size(), plainText.data(), plainText.size());
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    if (tagLength) {
+        Vector<uint8_t> tag(tagLength);
+        error = gcry_cipher_gettag(handle, tag.data(), tag.size());
+        if (error != GPG_ERR_NO_ERROR) {
+            PAL::GCrypt::logError(error);
+            return std::nullopt;
+        }
+
+        output.appendVector(tag);
+    }
+
+    return output;
 }
 
-void CryptoAlgorithmAES_GCM::platformDecrypt(std::unique_ptr<CryptoAlgorithmParameters>&&, Ref<CryptoKey>&&, Vector<uint8_t>&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&)
+static std::optional<Vector<uint8_t>> gcryptDecrypt(const Vector<uint8_t>& key, const Vector<uint8_t>& iv, Vector<uint8_t>&& cipherText, const Vector<uint8_t>& additionalData, uint8_t tagLength)
 {
-    notImplemented();
+    auto algorithm = PAL::GCrypt::aesAlgorithmForKeySize(key.size() * 8);
+    if (!algorithm)
+        return std::nullopt;
+
+    PAL::GCrypt::Handle<gcry_cipher_hd_t> handle;
+    gcry_error_t error = gcry_cipher_open(&handle, *algorithm, GCRY_CIPHER_MODE_GCM, 0);
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    error = gcry_cipher_setkey(handle, key.data(), key.size());
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    error = gcry_cipher_setiv(handle, iv.data(), iv.size());
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    if (!additionalData.isEmpty()) {
+        error = gcry_cipher_authenticate(handle, additionalData.data(), additionalData.size());
+        if (error != GPG_ERR_NO_ERROR) {
+            PAL::GCrypt::logError(error);
+            return std::nullopt;
+        }
+    }
+
+    error = gcry_cipher_final(handle);
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    size_t cipherLength = cipherText.size() - tagLength;
+    Vector<uint8_t> output(cipherLength);
+    error = gcry_cipher_decrypt(handle, output.data(), output.size(), cipherText.data(), cipherLength);
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    if (tagLength) {
+        Vector<uint8_t> tag(tagLength);
+        error = gcry_cipher_gettag(handle, tag.data(), tagLength);
+        if (error != GPG_ERR_NO_ERROR) {
+            PAL::GCrypt::logError(error);
+            return std::nullopt;
+        }
+
+        if (constantTimeMemcmp(tag.data(), cipherText.data() + cipherLength, tagLength))
+            return std::nullopt;
+    }
+
+    return output;
 }
 
+void CryptoAlgorithmAES_GCM::platformEncrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& plainText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
+{
+    context.ref();
+    workQueue.dispatch(
+        [parameters = WTFMove(parameters), key = WTFMove(key), plainText = WTFMove(plainText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
+            auto& aesParameters = downcast<CryptoAlgorithmAesGcmParams>(*parameters);
+            auto& aesKey = downcast<CryptoKeyAES>(key.get());
+
+            auto output = gcryptEncrypt(aesKey.key(), aesParameters.ivVector(), WTFMove(plainText), aesParameters.additionalDataVector(), aesParameters.tagLength.value_or(0) / 8);
+            if (!output) {
+                // We should only dereference callbacks after being back to the Document/Worker threads.
+                context.postTask(
+                    [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
+                        exceptionCallback(OperationError);
+                        context.deref();
+                    });
+                return;
+            }
+
+            // We should only dereference callbacks after being back to the Document/Worker threads.
+            context.postTask(
+                [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) mutable {
+                    callback(WTFMove(output));
+                    context.deref();
+                });
+        });
+}
+
+void CryptoAlgorithmAES_GCM::platformDecrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& cipherText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
+{
+    context.ref();
+    workQueue.dispatch(
+        [parameters = WTFMove(parameters), key = WTFMove(key), cipherText = WTFMove(cipherText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
+            auto& aesParameters = downcast<CryptoAlgorithmAesGcmParams>(*parameters);
+            auto& aesKey = downcast<CryptoKeyAES>(key.get());
+
+            auto output = gcryptDecrypt(aesKey.key(), aesParameters.ivVector(), WTFMove(cipherText), aesParameters.additionalDataVector(), aesParameters.tagLength.value_or(0) / 8);
+            if (!output) {
+                // We should only dereference callbacks after being back to the Document/Worker threads.
+                context.postTask(
+                    [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
+                        exceptionCallback(OperationError);
+                        context.deref();
+                    });
+                return;
+            }
+
+            // We should only dereference callbacks after being back to the Document/Worker threads.
+            context.postTask(
+                [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) mutable {
+                    callback(WTFMove(output));
+                    context.deref();
+                });
+        });
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SUBTLE_CRYPTO)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to