[GCrypt] AES_CTR support
authorzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 19 Jun 2017 11:56:26 +0000 (11:56 +0000)
committerzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 19 Jun 2017 11:56:26 +0000 (11:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=171420

Reviewed by Michael Catanzaro.

Source/WebCore:

Implement AES_CTR support for build configurations that use libgcrypt.

Both encryption and decryption operations are handled in a single gcryptAES_CTR() function,
with the specific operation being passed as the first argument. The appropriate AES
algorithm is picked, and a gcry_cipher_hd_t object is created and has the given key set.
This key will remain the same throughout the gcry_cipher_hd_t lifetime, even after
gcry_cipher_reset() calls.

The encrypt/decrypt operation is wrapped into a helper lambda functor that accepts the
given counter and input data. It resets the cipher object, sets the counter data, and
performs the specified operation, returning the output data.

libgcrypt doesn't support setting counter data on a gcry_cipher_hd_t object with only
part of that data being used as the actual counter, with the rest acting as a nonce, like
the Web Crypto specification allows. We have to implement the support for that on our own.

We compute the number of blocks we'll be processing and the upper exclusive limit for the
given counter length. We immediately bail if the counter limit is less than the computed
block count, since that would mean that the counter values would be repeated.

We short-cut to a direct operation call if the counter length matches size of the counter
data -- we don't have to adjust the counter data in any way if that's the case.

Otherwise we move counter data into the MPI format. The nonce and the actual counter MPIs
can split out of the counter data MPI with the modulus operation and the counter limit MPI.

We take another shortcut straight to the operation call if we're able to determine that the
'counter leeway' value, i.e. the difference between the initial counter MPI and the counter
limit MPI, is larger or equal to the predicted block size -- if that's the case, the counter
won't wrap around and change the nonce data.

In worst-case scenario the counter data will wrap around and we have to address that. The
current implementation takes the slowest possible path for the moment, encrypting/decrypting
each block separately. For each step the counter is combined with the nonce, the resulting
MPI data retrieved and passed to the operation function, and the returned block output
appended to the final output vector. The counter MPI is then incremented and ran through the
modulus operation, limiting the MPI value to the previously-computed counter limit.

No new tests -- relevant tests are passing and are unskipped.

* crypto/gcrypt/CryptoAlgorithmAES_CTRGCrypt.cpp:
(WebCore::callOperation):
(WebCore::gcryptAES_CTR):
(WebCore::CryptoAlgorithmAES_CTR::platformEncrypt):
(WebCore::CryptoAlgorithmAES_CTR::platformDecrypt):

Source/WebCore/PAL:

* pal/crypto/gcrypt/Utilities.h: Add a GCryptCipherOperation type alias that can be used
for the gcry_cipher_decrypt or gcry_cipher_encrypt function pointers.

LayoutTests:

* platform/gtk/TestExpectations: Unskip the relevant AES_CTR tests.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@218484 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/platform/gtk/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/pal/crypto/gcrypt/Utilities.h
Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_CTRGCrypt.cpp

index 405edfe..712aa32 100644 (file)
@@ -1,3 +1,12 @@
+2017-06-19  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] AES_CTR support
+        https://bugs.webkit.org/show_bug.cgi?id=171420
+
+        Reviewed by Michael Catanzaro.
+
+        * platform/gtk/TestExpectations: Unskip the relevant AES_CTR tests.
+
 2017-06-19  Fujii Hironori  <Hironori.Fujii@sony.com>
 
         [GTK] Layout Test imported/w3c/web-platform-tests/webrtc/RTCPeerConnection-createOffer.html crashes
index de70c49..76aef14 100644 (file)
@@ -752,23 +752,6 @@ webkit.org/b/133122 crypto/subtle/aes-cfb-import-key-unwrap-raw-key.html [ Skip
 webkit.org/b/133122 crypto/subtle/aes-cfb-import-key-wrap-jwk-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/aes-cfb-import-key-wrap-raw-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/aes-cfb-import-raw-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-encrypt-malformed-parameters.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-generate-export-key-jwk-length-128.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-generate-export-key-jwk-length-192.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-generate-export-key-jwk-length-256.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-generate-export-raw-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-generate-key-encrypt-decrypt.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-generate-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-jwk-key-length-128.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-jwk-key-length-192.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-jwk-key-length-256.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-key-decrypt.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-key-encrypt.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-key-unwrap-jwk-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-key-unwrap-raw-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-key-wrap-jwk-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-key-wrap-raw-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/aes-ctr-import-raw-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ecdh-derive-bits-malformed-parametrs.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ecdh-generate-export-key-pkcs8-p256.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ecdh-generate-export-key-pkcs8-p384.html [ Skip ]
@@ -827,10 +810,6 @@ webkit.org/b/133122 crypto/workers/subtle/aes-cfb-import-key-decrypt.html [ Skip
 webkit.org/b/133122 crypto/workers/subtle/aes-cfb-import-key-encrypt.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/aes-cfb-import-key-unwrap-key.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/aes-cfb-import-key-wrap-key.html [ Skip ]
-webkit.org/b/133122 crypto/workers/subtle/aes-ctr-import-key-decrypt.html [ Skip ]
-webkit.org/b/133122 crypto/workers/subtle/aes-ctr-import-key-encrypt.html [ Skip ]
-webkit.org/b/133122 crypto/workers/subtle/aes-ctr-import-key-unwrap-key.html [ Skip ]
-webkit.org/b/133122 crypto/workers/subtle/aes-ctr-import-key-wrap-key.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/aes-postMessage-worker.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/ec-generate-export-pkcs8-key.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/ec-generate-export-spki-key.html [ Skip ]
@@ -866,9 +845,7 @@ webkit.org/b/133320 crypto/webkitSubtle/hmac-sign-verify.html [ Pass ]
 # These are the imported web-platform-tests covering crypto sections we haven't implemented yet.
 imported/w3c/web-platform-tests/WebCryptoAPI/ [ Slow ]
 webkit.org/b/133122 imported/w3c/web-platform-tests/WebCryptoAPI/encrypt_decrypt/aes_cbc.worker.html [ Failure ]
-webkit.org/b/133122 imported/w3c/web-platform-tests/WebCryptoAPI/encrypt_decrypt/aes_ctr.worker.html [ Failure ]
 webkit.org/b/133122 imported/w3c/web-platform-tests/WebCryptoAPI/encrypt_decrypt/rsa.worker.html [ Failure ]
-webkit.org/b/172594 imported/w3c/web-platform-tests/WebCryptoAPI/encrypt_decrypt/test_aes_ctr.https.html [ Failure ]
 webkit.org/b/172594 imported/w3c/web-platform-tests/WebCryptoAPI/encrypt_decrypt/test_aes_cbc.https.html [ Failure ]
 webkit.org/b/172594 imported/w3c/web-platform-tests/WebCryptoAPI/encrypt_decrypt/test_rsa_oaep.https.html [ Failure ]
 webkit.org/b/169272 imported/w3c/web-platform-tests/WebCryptoAPI/import_export/ec_importKey.worker.html [ Failure ]
index 02b385e..391ff27 100644 (file)
@@ -1,3 +1,56 @@
+2017-06-19  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] AES_CTR support
+        https://bugs.webkit.org/show_bug.cgi?id=171420
+
+        Reviewed by Michael Catanzaro.
+
+        Implement AES_CTR support for build configurations that use libgcrypt.
+
+        Both encryption and decryption operations are handled in a single gcryptAES_CTR() function,
+        with the specific operation being passed as the first argument. The appropriate AES
+        algorithm is picked, and a gcry_cipher_hd_t object is created and has the given key set.
+        This key will remain the same throughout the gcry_cipher_hd_t lifetime, even after
+        gcry_cipher_reset() calls.
+
+        The encrypt/decrypt operation is wrapped into a helper lambda functor that accepts the
+        given counter and input data. It resets the cipher object, sets the counter data, and
+        performs the specified operation, returning the output data.
+
+        libgcrypt doesn't support setting counter data on a gcry_cipher_hd_t object with only
+        part of that data being used as the actual counter, with the rest acting as a nonce, like
+        the Web Crypto specification allows. We have to implement the support for that on our own.
+
+        We compute the number of blocks we'll be processing and the upper exclusive limit for the
+        given counter length. We immediately bail if the counter limit is less than the computed
+        block count, since that would mean that the counter values would be repeated.
+
+        We short-cut to a direct operation call if the counter length matches size of the counter
+        data -- we don't have to adjust the counter data in any way if that's the case.
+
+        Otherwise we move counter data into the MPI format. The nonce and the actual counter MPIs
+        can split out of the counter data MPI with the modulus operation and the counter limit MPI.
+
+        We take another shortcut straight to the operation call if we're able to determine that the
+        'counter leeway' value, i.e. the difference between the initial counter MPI and the counter
+        limit MPI, is larger or equal to the predicted block size -- if that's the case, the counter
+        won't wrap around and change the nonce data.
+
+        In worst-case scenario the counter data will wrap around and we have to address that. The
+        current implementation takes the slowest possible path for the moment, encrypting/decrypting
+        each block separately. For each step the counter is combined with the nonce, the resulting
+        MPI data retrieved and passed to the operation function, and the returned block output
+        appended to the final output vector. The counter MPI is then incremented and ran through the
+        modulus operation, limiting the MPI value to the previously-computed counter limit.
+
+        No new tests -- relevant tests are passing and are unskipped.
+
+        * crypto/gcrypt/CryptoAlgorithmAES_CTRGCrypt.cpp:
+        (WebCore::callOperation):
+        (WebCore::gcryptAES_CTR):
+        (WebCore::CryptoAlgorithmAES_CTR::platformEncrypt):
+        (WebCore::CryptoAlgorithmAES_CTR::platformDecrypt):
+
 2017-05-14 Frederic Wang  <fwang@igalia.com>
 
         Add heuristic to avoid flattening "fullscreen" iframes
index c2cd6b5..d74f32a 100644 (file)
@@ -1,3 +1,13 @@
+2017-06-19  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] AES_CTR support
+        https://bugs.webkit.org/show_bug.cgi?id=171420
+
+        Reviewed by Michael Catanzaro.
+
+        * pal/crypto/gcrypt/Utilities.h: Add a GCryptCipherOperation type alias that can be used
+        for the gcry_cipher_decrypt or gcry_cipher_encrypt function pointers.
+
 2017-06-16  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [iOS DnD] Upstream iOS drag and drop implementation into OpenSource WebKit
index 3ddf8d0..14608ff 100644 (file)
@@ -39,6 +39,8 @@ namespace GCrypt {
             gcry_cipher_ctl ((a), GCRYCTL_FINALIZE, NULL, 0)
 #endif
 
+using GCryptCipherOperation = gcry_error_t(gcry_cipher_hd_t, void*, size_t, const void*, size_t);
+
 static inline void logError(gcry_error_t error)
 {
     WTFLogAlways("libgcrypt error: source '%s', description '%s'",
index b356d15..89749e1 100644 (file)
@@ -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
 
 #if ENABLE(SUBTLE_CRYPTO)
 
+#include "CryptoAlgorithmAesCtrParams.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>
 
 namespace WebCore {
 
-void CryptoAlgorithmAES_CTR::platformEncrypt(std::unique_ptr<CryptoAlgorithmParameters>&&, Ref<CryptoKey>&&, Vector<uint8_t>&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&)
+// This is a helper function that resets the cipher object, sets the provided counter data,
+// and executes the encrypt or decrypt operation, retrieving and returning the output data.
+static std::optional<Vector<uint8_t>> callOperation(GCryptCipherOperation operation, gcry_cipher_hd_t handle, const Vector<uint8_t>& counter, const uint8_t* data, const size_t size)
 {
-    notImplemented();
+    gcry_error_t error = gcry_cipher_reset(handle);
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    error = gcry_cipher_setctr(handle, counter.data(), counter.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(size);
+    error = operation(handle, output.data(), output.size(), data, size);
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    return output;
+}
+
+static std::optional<Vector<uint8_t>> gcryptAES_CTR(GCryptCipherOperation operation, const Vector<uint8_t>& key, const Vector<uint8_t>& counter, size_t counterLength, const Vector<uint8_t>& inputText)
+{
+    constexpr size_t blockSize = 16;
+    auto algorithm = PAL::GCrypt::aesAlgorithmForKeySize(key.size() * 8);
+    if (!algorithm)
+        return std::nullopt;
+
+    // Construct the libgcrypt cipher object and attach the key to it. Key information on this
+    // cipher object will live through any gcry_cipher_reset() calls.
+    PAL::GCrypt::Handle<gcry_cipher_hd_t> handle;
+    gcry_error_t error = gcry_cipher_open(&handle, *algorithm, GCRY_CIPHER_MODE_CTR, 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;
+    }
+
+    // Calculate the block count: ((inputText.size() + blockSize - 1) / blockSize), remainder discarded.
+    PAL::GCrypt::Handle<gcry_mpi_t> blockCountMPI(gcry_mpi_new(0));
+    {
+        PAL::GCrypt::Handle<gcry_mpi_t> blockSizeMPI(gcry_mpi_set_ui(nullptr, blockSize));
+        PAL::GCrypt::Handle<gcry_mpi_t> roundedUpSize(gcry_mpi_set_ui(nullptr, inputText.size()));
+
+        gcry_mpi_add_ui(roundedUpSize, roundedUpSize, blockSize - 1);
+        gcry_mpi_div(blockCountMPI, nullptr, roundedUpSize, blockSizeMPI, 0);
+    }
+
+    // Calculate the counter limit for the specified counter length: (2 << counterLength).
+    // (counterLimitMPI - 1) is the maximum value the counter can hold -- essentially it's
+    // a bit-mask for valid counter values.
+    PAL::GCrypt::Handle<gcry_mpi_t> counterLimitMPI(gcry_mpi_set_ui(nullptr, 1));
+    gcry_mpi_mul_2exp(counterLimitMPI, counterLimitMPI, counterLength);
+
+    // Counter values must not repeat for a given cipher text. If the counter limit (i.e.
+    // the number of unique counter values we could produce for the specified counter
+    // length) is lower than the deduced block count, we bail.
+    if (gcry_mpi_cmp(counterLimitMPI, blockCountMPI) < 0)
+        return std::nullopt;
+
+    // If the counter length, in bits, matches the size of the counter data, we don't have to
+    // use any part of the counter Vector<> as nonce. This allows us to directly encrypt or
+    // decrypt all the provided data in a single step.
+    if (counterLength == counter.size() * 8)
+        return callOperation(operation, handle, counter, inputText.data(), inputText.size());
+
+    // Scan the counter data into the MPI format. We'll do all the counter computations with
+    // the MPI API.
+    PAL::GCrypt::Handle<gcry_mpi_t> counterDataMPI;
+    error = gcry_mpi_scan(&counterDataMPI, GCRYMPI_FMT_USG, counter.data(), counter.size(), nullptr);
+    if (error != GPG_ERR_NO_ERROR) {
+        PAL::GCrypt::logError(error);
+        return std::nullopt;
+    }
+
+    // Extract the counter MPI from the counterDataMPI: (counterDataMPI % counterLimitMPI).
+    // This MPI represents solely the counter value, as initially provided.
+    PAL::GCrypt::Handle<gcry_mpi_t> counterMPI(gcry_mpi_new(0));
+    gcry_mpi_mod(counterMPI, counterDataMPI, counterLimitMPI);
+
+    {
+        // Calculate the leeway of the initially-provided counter: counterLimitMPI - counterMPI.
+        // This is essentially the number of blocks we can encrypt/decrypt with that counter
+        // (incrementing it after each operation) before the counter wraps around to 0.
+        PAL::GCrypt::Handle<gcry_mpi_t> counterLeewayMPI(gcry_mpi_new(0));
+        gcry_mpi_sub(counterLeewayMPI, counterLimitMPI, counterMPI);
+
+        // If counterLeewayMPI is larger or equal to the deduced block count, we can directly
+        // encrypt or decrypt the provided data in a single step since it's ensured that the
+        // counter won't overflow.
+        if (gcry_mpi_cmp(counterLeewayMPI, blockCountMPI) >= 0)
+            return callOperation(operation, handle, counter, inputText.data(), inputText.size());
+    }
+
+    // From here onwards we're dealing with a counter of which the length doesn't match the
+    // provided data, meaning we'll also have to manage the nonce data. The counter will also
+    // wrap around, so we'll have to address that too.
+
+    // Determine the nonce MPI that we'll use to reconstruct the counter data for each block:
+    // (counterDataMPI - counterMPI). This is equivalent to counterDataMPI with the lowest
+    // counterLength bits cleared.
+    PAL::GCrypt::Handle<gcry_mpi_t> nonceMPI(gcry_mpi_new(0));
+    gcry_mpi_sub(nonceMPI, counterDataMPI, counterMPI);
+
+    // FIXME: This should be optimized further by first encrypting the amount of blocks for
+    // which the counter won't yet wrap around, and then encrypting the rest of the blocks
+    // starting from the counter set to 0.
+
+    Vector<uint8_t> output;
+    Vector<uint8_t> blockCounterData(16);
+    size_t inputTextSize = inputText.size();
+
+    for (size_t i = 0; i < inputTextSize; i += 16) {
+        size_t blockInputSize = std::min<size_t>(16, inputTextSize - i);
+
+        // Construct the block-specific counter: (nonceMPI + counterMPI).
+        PAL::GCrypt::Handle<gcry_mpi_t> blockCounterMPI(gcry_mpi_new(0));
+        gcry_mpi_add(blockCounterMPI, nonceMPI, counterMPI);
+
+        error = gcry_mpi_print(GCRYMPI_FMT_USG, blockCounterData.data(), blockCounterData.size(), nullptr, blockCounterMPI);
+        if (error != GPG_ERR_NO_ERROR) {
+            PAL::GCrypt::logError(error);
+            return std::nullopt;
+        }
+
+        // Encrypt/decrypt this single block with the block-specific counter. Output for this
+        // single block is appended to the general output vector.
+        auto blockOutput = callOperation(operation, handle, blockCounterData, inputText.data() + i, blockInputSize);
+        if (!blockOutput)
+            return std::nullopt;
+
+        output.appendVector(*blockOutput);
+
+        // Increment the counter. The modulus operation takes care of any wrap-around.
+        PAL::GCrypt::Handle<gcry_mpi_t> counterIncrementMPI(gcry_mpi_new(0));
+        gcry_mpi_add_ui(counterIncrementMPI, counterMPI, 1);
+        gcry_mpi_mod(counterMPI, counterIncrementMPI, counterLimitMPI);
+    }
+
+    return output;
 }
 
-void CryptoAlgorithmAES_CTR::platformDecrypt(std::unique_ptr<CryptoAlgorithmParameters>&&, Ref<CryptoKey>&&, Vector<uint8_t>&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&)
+void CryptoAlgorithmAES_CTR::platformEncrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& plainText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
 {
-    notImplemented();
+    context.ref();
+    workQueue.dispatch(
+        [parameters = WTFMove(parameters), key = WTFMove(key), plainText = WTFMove(plainText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
+            auto& aesParameters = downcast<CryptoAlgorithmAesCtrParams>(*parameters);
+            auto& aesKey = downcast<CryptoKeyAES>(key.get());
+
+            auto output = gcryptAES_CTR(gcry_cipher_encrypt, aesKey.key(), aesParameters.counterVector(), aesParameters.length, plainText);
+            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_CTR::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<CryptoAlgorithmAesCtrParams>(*parameters);
+            auto& aesKey = downcast<CryptoKeyAES>(key.get());
+
+            auto output = gcryptAES_CTR(gcry_cipher_decrypt, aesKey.key(), aesParameters.counterVector(), aesParameters.length, cipherText);
+            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