/* * Copyright (C) 2016-2019 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "SubtleCrypto.h" #if ENABLE(WEB_CRYPTO) #include "CryptoAlgorithm.h" #include "CryptoAlgorithmRegistry.h" #include "JSAesCbcCfbParams.h" #include "JSAesCtrParams.h" #include "JSAesGcmParams.h" #include "JSAesKeyParams.h" #include "JSCryptoAlgorithmParameters.h" #include "JSCryptoKey.h" #include "JSCryptoKeyPair.h" #include "JSDOMPromiseDeferred.h" #include "JSDOMWrapper.h" #include "JSEcKeyParams.h" #include "JSEcdhKeyDeriveParams.h" #include "JSEcdsaParams.h" #include "JSHkdfParams.h" #include "JSHmacKeyParams.h" #include "JSJsonWebKey.h" #include "JSPbkdf2Params.h" #include "JSRsaHashedImportParams.h" #include "JSRsaHashedKeyGenParams.h" #include "JSRsaKeyGenParams.h" #include "JSRsaOaepParams.h" #include "JSRsaPssParams.h" #include namespace WebCore { using namespace JSC; SubtleCrypto::SubtleCrypto(ScriptExecutionContext* context) : ContextDestructionObserver(context) , m_workQueue(WorkQueue::create("com.apple.WebKit.CryptoQueue")) { } SubtleCrypto::~SubtleCrypto() = default; enum class Operations { Encrypt, Decrypt, Sign, Verify, Digest, GenerateKey, DeriveBits, ImportKey, WrapKey, UnwrapKey, GetKeyLength }; static ExceptionOr> normalizeCryptoAlgorithmParameters(JSGlobalObject&, WebCore::SubtleCrypto::AlgorithmIdentifier, Operations); static ExceptionOr toHashIdentifier(JSGlobalObject& state, SubtleCrypto::AlgorithmIdentifier algorithmIdentifier) { auto digestParams = normalizeCryptoAlgorithmParameters(state, algorithmIdentifier, Operations::Digest); if (digestParams.hasException()) return digestParams.releaseException(); return digestParams.returnValue()->identifier; } static ExceptionOr> normalizeCryptoAlgorithmParameters(JSGlobalObject& state, SubtleCrypto::AlgorithmIdentifier algorithmIdentifier, Operations operation) { VM& vm = state.vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (WTF::holds_alternative(algorithmIdentifier)) { auto newParams = Strong(vm, constructEmptyObject(&state)); newParams->putDirect(vm, Identifier::fromString(vm, "name"), jsString(vm, WTF::get(algorithmIdentifier))); return normalizeCryptoAlgorithmParameters(state, newParams, operation); } auto& value = WTF::get>(algorithmIdentifier); auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto identifier = CryptoAlgorithmRegistry::singleton().identifier(params.name); if (UNLIKELY(!identifier)) return Exception { NotSupportedError }; std::unique_ptr result; switch (operation) { case Operations::Encrypt: case Operations::Decrypt: switch (*identifier) { case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: result = makeUnique(params); break; case CryptoAlgorithmIdentifier::RSA_OAEP: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::AES_CBC: case CryptoAlgorithmIdentifier::AES_CFB: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::AES_CTR: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::AES_GCM: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } default: return Exception { NotSupportedError }; } break; case Operations::Sign: case Operations::Verify: switch (*identifier) { case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: case CryptoAlgorithmIdentifier::HMAC: result = makeUnique(params); break; case CryptoAlgorithmIdentifier::ECDSA: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto hashIdentifier = toHashIdentifier(state, params.hash); if (hashIdentifier.hasException()) return hashIdentifier.releaseException(); params.hashIdentifier = hashIdentifier.releaseReturnValue(); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::RSA_PSS: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } default: return Exception { NotSupportedError }; } break; case Operations::Digest: switch (*identifier) { case CryptoAlgorithmIdentifier::SHA_1: case CryptoAlgorithmIdentifier::SHA_224: case CryptoAlgorithmIdentifier::SHA_256: case CryptoAlgorithmIdentifier::SHA_384: case CryptoAlgorithmIdentifier::SHA_512: result = makeUnique(params); break; default: return Exception { NotSupportedError }; } break; case Operations::GenerateKey: switch (*identifier) { case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: case CryptoAlgorithmIdentifier::RSA_PSS: case CryptoAlgorithmIdentifier::RSA_OAEP: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto hashIdentifier = toHashIdentifier(state, params.hash); if (hashIdentifier.hasException()) return hashIdentifier.releaseException(); params.hashIdentifier = hashIdentifier.releaseReturnValue(); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::AES_CTR: case CryptoAlgorithmIdentifier::AES_CBC: case CryptoAlgorithmIdentifier::AES_GCM: case CryptoAlgorithmIdentifier::AES_CFB: case CryptoAlgorithmIdentifier::AES_KW: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::HMAC: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto hashIdentifier = toHashIdentifier(state, params.hash); if (hashIdentifier.hasException()) return hashIdentifier.releaseException(); params.hashIdentifier = hashIdentifier.releaseReturnValue(); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::ECDSA: case CryptoAlgorithmIdentifier::ECDH: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } default: return Exception { NotSupportedError }; } break; case Operations::DeriveBits: switch (*identifier) { case CryptoAlgorithmIdentifier::ECDH: { // Remove this hack once https://bugs.webkit.org/show_bug.cgi?id=169333 is fixed. JSValue nameValue = value.get()->get(&state, Identifier::fromString(vm, "name")); JSValue publicValue = value.get()->get(&state, Identifier::fromString(vm, "public")); JSObject* newValue = constructEmptyObject(&state); newValue->putDirect(vm, Identifier::fromString(vm, "name"), nameValue); newValue->putDirect(vm, Identifier::fromString(vm, "publicKey"), publicValue); auto params = convertDictionary(state, newValue); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::HKDF: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto hashIdentifier = toHashIdentifier(state, params.hash); if (hashIdentifier.hasException()) return hashIdentifier.releaseException(); params.hashIdentifier = hashIdentifier.releaseReturnValue(); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::PBKDF2: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto hashIdentifier = toHashIdentifier(state, params.hash); if (hashIdentifier.hasException()) return hashIdentifier.releaseException(); params.hashIdentifier = hashIdentifier.releaseReturnValue(); result = makeUnique(params); break; } default: return Exception { NotSupportedError }; } break; case Operations::ImportKey: switch (*identifier) { case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: result = makeUnique(params); break; case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: case CryptoAlgorithmIdentifier::RSA_PSS: case CryptoAlgorithmIdentifier::RSA_OAEP: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto hashIdentifier = toHashIdentifier(state, params.hash); if (hashIdentifier.hasException()) return hashIdentifier.releaseException(); params.hashIdentifier = hashIdentifier.releaseReturnValue(); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::AES_CTR: case CryptoAlgorithmIdentifier::AES_CBC: case CryptoAlgorithmIdentifier::AES_GCM: case CryptoAlgorithmIdentifier::AES_CFB: case CryptoAlgorithmIdentifier::AES_KW: result = makeUnique(params); break; case CryptoAlgorithmIdentifier::HMAC: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto hashIdentifier = toHashIdentifier(state, params.hash); if (hashIdentifier.hasException()) return hashIdentifier.releaseException(); params.hashIdentifier = hashIdentifier.releaseReturnValue(); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::ECDSA: case CryptoAlgorithmIdentifier::ECDH: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::HKDF: case CryptoAlgorithmIdentifier::PBKDF2: result = makeUnique(params); break; default: return Exception { NotSupportedError }; } break; case Operations::WrapKey: case Operations::UnwrapKey: switch (*identifier) { case CryptoAlgorithmIdentifier::AES_KW: result = makeUnique(params); break; default: return Exception { NotSupportedError }; } break; case Operations::GetKeyLength: switch (*identifier) { case CryptoAlgorithmIdentifier::AES_CTR: case CryptoAlgorithmIdentifier::AES_CBC: case CryptoAlgorithmIdentifier::AES_GCM: case CryptoAlgorithmIdentifier::AES_CFB: case CryptoAlgorithmIdentifier::AES_KW: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::HMAC: { auto params = convertDictionary(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); auto hashIdentifier = toHashIdentifier(state, params.hash); if (hashIdentifier.hasException()) return hashIdentifier.releaseException(); params.hashIdentifier = hashIdentifier.releaseReturnValue(); result = makeUnique(params); break; } case CryptoAlgorithmIdentifier::HKDF: case CryptoAlgorithmIdentifier::PBKDF2: result = makeUnique(params); break; default: return Exception { NotSupportedError }; } break; } result->identifier = *identifier; return result; } static CryptoKeyUsageBitmap toCryptoKeyUsageBitmap(CryptoKeyUsage usage) { switch (usage) { case CryptoKeyUsage::Encrypt: return CryptoKeyUsageEncrypt; case CryptoKeyUsage::Decrypt: return CryptoKeyUsageDecrypt; case CryptoKeyUsage::Sign: return CryptoKeyUsageSign; case CryptoKeyUsage::Verify: return CryptoKeyUsageVerify; case CryptoKeyUsage::DeriveKey: return CryptoKeyUsageDeriveKey; case CryptoKeyUsage::DeriveBits: return CryptoKeyUsageDeriveBits; case CryptoKeyUsage::WrapKey: return CryptoKeyUsageWrapKey; case CryptoKeyUsage::UnwrapKey: return CryptoKeyUsageUnwrapKey; } RELEASE_ASSERT_NOT_REACHED(); } static CryptoKeyUsageBitmap toCryptoKeyUsageBitmap(const Vector& usages) { CryptoKeyUsageBitmap result = 0; // Maybe we shouldn't silently bypass duplicated usages? for (auto usage : usages) result |= toCryptoKeyUsageBitmap(usage); return result; } // Maybe we want more specific error messages? static void rejectWithException(Ref&& passedPromise, ExceptionCode ec) { switch (ec) { case NotSupportedError: passedPromise->reject(ec, "The algorithm is not supported"_s); return; case SyntaxError: passedPromise->reject(ec, "A required parameter was missing or out-of-range"_s); return; case InvalidStateError: passedPromise->reject(ec, "The requested operation is not valid for the current state of the provided key"_s); return; case InvalidAccessError: passedPromise->reject(ec, "The requested operation is not valid for the provided key"_s); return; case UnknownError: passedPromise->reject(ec, "The operation failed for an unknown transient reason (e.g. out of memory)"_s); return; case DataError: passedPromise->reject(ec, "Data provided to an operation does not meet requirements"_s); return; case OperationError: passedPromise->reject(ec, "The operation failed for an operation-specific reason"_s); return; default: break; } ASSERT_NOT_REACHED(); } static void normalizeJsonWebKey(JsonWebKey& webKey) { // Maybe we shouldn't silently bypass duplicated usages? webKey.usages = webKey.key_ops ? toCryptoKeyUsageBitmap(webKey.key_ops.value()) : 0; } // FIXME: This returns an std::optional and takes a promise, rather than returning an // ExceptionOr and letting the caller handle the promise, to work around an issue where // Variant types (which KeyData is) in ExceptionOr<> cause compile issues on some platforms. This // should be resolved by adopting a standards compliant std::variant (see https://webkit.org/b/175583) static std::optional toKeyData(SubtleCrypto::KeyFormat format, SubtleCrypto::KeyDataVariant&& keyDataVariant, Ref& promise) { switch (format) { case SubtleCrypto::KeyFormat::Spki: case SubtleCrypto::KeyFormat::Pkcs8: case SubtleCrypto::KeyFormat::Raw: return WTF::switchOn(keyDataVariant, [&promise] (JsonWebKey&) -> std::optional { promise->reject(Exception { TypeError }); return std::nullopt; }, [] (auto& bufferSource) -> std::optional { return KeyData { Vector { static_cast(bufferSource->data()), bufferSource->byteLength() } }; } ); case SubtleCrypto::KeyFormat::Jwk: return WTF::switchOn(keyDataVariant, [] (JsonWebKey& webKey) -> std::optional { normalizeJsonWebKey(webKey); return KeyData { webKey }; }, [&promise] (auto&) -> std::optional { promise->reject(Exception { TypeError }); return std::nullopt; } ); } RELEASE_ASSERT_NOT_REACHED(); } static Vector copyToVector(BufferSource&& data) { return { data.data(), data.length() }; } static bool isSupportedExportKey(CryptoAlgorithmIdentifier identifier) { switch (identifier) { case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: case CryptoAlgorithmIdentifier::RSA_PSS: case CryptoAlgorithmIdentifier::RSA_OAEP: case CryptoAlgorithmIdentifier::AES_CTR: case CryptoAlgorithmIdentifier::AES_CBC: case CryptoAlgorithmIdentifier::AES_GCM: case CryptoAlgorithmIdentifier::AES_CFB: case CryptoAlgorithmIdentifier::AES_KW: case CryptoAlgorithmIdentifier::HMAC: case CryptoAlgorithmIdentifier::ECDSA: case CryptoAlgorithmIdentifier::ECDH: return true; default: return false; } } RefPtr getPromise(DeferredPromise* index, WeakPtr subtleCryptoWeakPointer) { if (subtleCryptoWeakPointer) return subtleCryptoWeakPointer->m_pendingPromises.take(index); return nullptr; } static std::unique_ptr crossThreadCopyImportParams(const CryptoAlgorithmParameters& importParams) { switch (importParams.parametersClass()) { case CryptoAlgorithmParameters::Class::None: { auto result = makeUnique(); result->identifier = importParams.identifier; return result; } case CryptoAlgorithmParameters::Class::EcKeyParams: return makeUnique(crossThreadCopy(downcast(importParams))); case CryptoAlgorithmParameters::Class::HmacKeyParams: return makeUnique(crossThreadCopy(downcast(importParams))); case CryptoAlgorithmParameters::Class::RsaHashedImportParams: return makeUnique(crossThreadCopy(downcast(importParams))); default: ASSERT_NOT_REACHED(); return nullptr; } } void SubtleCrypto::addAuthenticatedEncryptionWarningIfNecessary(CryptoAlgorithmIdentifier algorithmIdentifier) { if (algorithmIdentifier == CryptoAlgorithmIdentifier::AES_CBC || algorithmIdentifier == CryptoAlgorithmIdentifier::AES_CTR) scriptExecutionContext()->addConsoleMessage(MessageSource::Security, MessageLevel::Warning, "AES-CBC and AES-CTR do not provide authentication by default, and implementing it manually can result in minor, but serious mistakes. We recommended using authenticated encryption like AES-GCM to protect against chosen-ciphertext attacks."); } // MARK: - Exposed functions. void SubtleCrypto::encrypt(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref&& promise) { addAuthenticatedEncryptionWarningIfNecessary(key.algorithmIdentifier()); auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Encrypt); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); auto data = copyToVector(WTFMove(dataBufferSource)); if (params->identifier != key.algorithmIdentifier()) { promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s); return; } if (!key.allows(CryptoKeyUsageEncrypt)) { promise->reject(InvalidAccessError, "CryptoKey doesn't support encryption"_s); return; } auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](const Vector& cipherText) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), cipherText.data(), cipherText.size()); }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; algorithm->encrypt(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); } void SubtleCrypto::decrypt(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref&& promise) { addAuthenticatedEncryptionWarningIfNecessary(key.algorithmIdentifier()); auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Decrypt); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); auto data = copyToVector(WTFMove(dataBufferSource)); if (params->identifier != key.algorithmIdentifier()) { promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s); return; } if (!key.allows(CryptoKeyUsageDecrypt)) { promise->reject(InvalidAccessError, "CryptoKey doesn't support decryption"_s); return; } auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](const Vector& plainText) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), plainText.data(), plainText.size()); }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; algorithm->decrypt(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); } void SubtleCrypto::sign(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref&& promise) { auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Sign); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); auto data = copyToVector(WTFMove(dataBufferSource)); if (params->identifier != key.algorithmIdentifier()) { promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s); return; } if (!key.allows(CryptoKeyUsageSign)) { promise->reject(InvalidAccessError, "CryptoKey doesn't support signing"_s); return; } auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](const Vector& signature) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), signature.data(), signature.size()); }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; algorithm->sign(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); } void SubtleCrypto::verify(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& signatureBufferSource, BufferSource&& dataBufferSource, Ref&& promise) { auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Verify); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); auto signature = copyToVector(WTFMove(signatureBufferSource)); auto data = copyToVector(WTFMove(dataBufferSource)); if (params->identifier != key.algorithmIdentifier()) { promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s); return; } if (!key.allows(CryptoKeyUsageVerify)) { promise->reject(InvalidAccessError, "CryptoKey doesn't support verification"_s); return; } auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](bool result) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) promise->resolve(result); }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; algorithm->verify(*params, key, WTFMove(signature), WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); } void SubtleCrypto::digest(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algorithmIdentifier, BufferSource&& dataBufferSource, Ref&& promise) { auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Digest); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); auto data = copyToVector(WTFMove(dataBufferSource)); auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](const Vector& digest) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), digest.data(), digest.size()); }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; algorithm->digest(WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); } void SubtleCrypto::generateKey(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algorithmIdentifier, bool extractable, Vector&& keyUsages, Ref&& promise) { auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::GenerateKey); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages); auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](KeyOrKeyPair&& keyOrKeyPair) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { WTF::switchOn(keyOrKeyPair, [&promise] (RefPtr& key) { if ((key->type() == CryptoKeyType::Private || key->type() == CryptoKeyType::Secret) && !key->usagesBitmap()) { rejectWithException(promise.releaseNonNull(), SyntaxError); return; } promise->resolve>(*key); }, [&promise] (CryptoKeyPair& keyPair) { if (!keyPair.privateKey->usagesBitmap()) { rejectWithException(promise.releaseNonNull(), SyntaxError); return; } promise->resolve>(keyPair); } ); } }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; // The 26 January 2017 version of the specification suggests we should perform the following task asynchronously // regardless what kind of keys it produces: https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-generateKey // That's simply not efficient for AES, HMAC and EC keys. Therefore, we perform it as an async task only for RSA keys. algorithm->generateKey(*params, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext()); } void SubtleCrypto::deriveKey(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& baseKey, AlgorithmIdentifier&& derivedKeyType, bool extractable, Vector&& keyUsages, Ref&& promise) { auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::DeriveBits); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); auto importParamsOrException = normalizeCryptoAlgorithmParameters(state, derivedKeyType, Operations::ImportKey); if (importParamsOrException.hasException()) { promise->reject(importParamsOrException.releaseException()); return; } auto importParams = importParamsOrException.releaseReturnValue(); auto getLengthParamsOrException = normalizeCryptoAlgorithmParameters(state, derivedKeyType, Operations::GetKeyLength); if (getLengthParamsOrException.hasException()) { promise->reject(getLengthParamsOrException.releaseException()); return; } auto getLengthParams = getLengthParamsOrException.releaseReturnValue(); auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages); if (params->identifier != baseKey.algorithmIdentifier()) { promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s); return; } if (!baseKey.allows(CryptoKeyUsageDeriveKey)) { promise->reject(InvalidAccessError, "CryptoKey doesn't support CryptoKey derivation"_s); return; } auto getLengthAlgorithm = CryptoAlgorithmRegistry::singleton().create(getLengthParams->identifier); auto result = getLengthAlgorithm->getKeyLength(*getLengthParams); if (result.hasException()) { promise->reject(result.releaseException().code(), "Cannot get key length from derivedKeyType"_s); return; } size_t length = result.releaseReturnValue(); auto importAlgorithm = CryptoAlgorithmRegistry::singleton().create(importParams->identifier); auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer, importAlgorithm = WTFMove(importAlgorithm), importParams = crossThreadCopyImportParams(*importParams), extractable, keyUsagesBitmap](const Vector& derivedKey) mutable { // FIXME: https://bugs.webkit.org/show_bug.cgi?id=169395 KeyData data = derivedKey; auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) { rejectWithException(promise.releaseNonNull(), SyntaxError); return; } promise->resolve>(key); } }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; importAlgorithm->importKey(SubtleCrypto::KeyFormat::Raw, WTFMove(data), *importParams, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback)); }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; algorithm->deriveBits(*params, baseKey, length, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); } void SubtleCrypto::deriveBits(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& baseKey, unsigned length, Ref&& promise) { auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::DeriveBits); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); if (params->identifier != baseKey.algorithmIdentifier()) { promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s); return; } if (!baseKey.allows(CryptoKeyUsageDeriveBits)) { promise->reject(InvalidAccessError, "CryptoKey doesn't support bits derivation"_s); return; } auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](const Vector& derivedKey) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), derivedKey.data(), derivedKey.size()); }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; algorithm->deriveBits(*params, baseKey, length, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); } void SubtleCrypto::importKey(JSC::JSGlobalObject& state, KeyFormat format, KeyDataVariant&& keyDataVariant, AlgorithmIdentifier&& algorithmIdentifier, bool extractable, Vector&& keyUsages, Ref&& promise) { auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::ImportKey); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException()); return; } auto params = paramsOrException.releaseReturnValue(); auto keyDataOrNull = toKeyData(format, WTFMove(keyDataVariant), promise); if (!keyDataOrNull) { // When toKeyData, it means the promise has been rejected, and we should return. return; } auto keyData = *keyDataOrNull; auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages); auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) { rejectWithException(promise.releaseNonNull(), SyntaxError); return; } promise->resolve>(key); } }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously: // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously. algorithm->importKey(format, WTFMove(keyData), *params, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback)); } void SubtleCrypto::exportKey(KeyFormat format, CryptoKey& key, Ref&& promise) { if (!isSupportedExportKey(key.algorithmIdentifier())) { promise->reject(Exception { NotSupportedError }); return; } if (!key.extractable()) { promise->reject(InvalidAccessError, "The CryptoKey is nonextractable"_s); return; } auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer](SubtleCrypto::KeyFormat format, KeyData&& key) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { switch (format) { case SubtleCrypto::KeyFormat::Spki: case SubtleCrypto::KeyFormat::Pkcs8: case SubtleCrypto::KeyFormat::Raw: { Vector& rawKey = WTF::get>(key); fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), rawKey.data(), rawKey.size()); return; } case SubtleCrypto::KeyFormat::Jwk: promise->resolve>(WTFMove(WTF::get(key))); return; } ASSERT_NOT_REACHED(); } }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously: // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-exportKey // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously. algorithm->exportKey(format, key, WTFMove(callback), WTFMove(exceptionCallback)); } void SubtleCrypto::wrapKey(JSC::JSGlobalObject& state, KeyFormat format, CryptoKey& key, CryptoKey& wrappingKey, AlgorithmIdentifier&& wrapAlgorithmIdentifier, Ref&& promise) { bool isEncryption = false; auto wrapParamsOrException = normalizeCryptoAlgorithmParameters(state, wrapAlgorithmIdentifier, Operations::WrapKey); if (wrapParamsOrException.hasException()) { ASSERT(wrapParamsOrException.exception().code() != ExistingExceptionError); wrapParamsOrException = normalizeCryptoAlgorithmParameters(state, wrapAlgorithmIdentifier, Operations::Encrypt); if (wrapParamsOrException.hasException()) { promise->reject(wrapParamsOrException.releaseException()); return; } isEncryption = true; } auto wrapParams = wrapParamsOrException.releaseReturnValue(); if (wrapParams->identifier != wrappingKey.algorithmIdentifier()) { promise->reject(InvalidAccessError, "Wrapping CryptoKey doesn't match AlgorithmIdentifier"_s); return; } if (!wrappingKey.allows(CryptoKeyUsageWrapKey)) { promise->reject(InvalidAccessError, "Wrapping CryptoKey doesn't support wrapKey operation"_s); return; } if (!isSupportedExportKey(key.algorithmIdentifier())) { promise->reject(Exception { NotSupportedError }); return; } if (!key.extractable()) { promise->reject(InvalidAccessError, "The CryptoKey is nonextractable"_s); return; } auto exportAlgorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); auto wrapAlgorithm = CryptoAlgorithmRegistry::singleton().create(wrappingKey.algorithmIdentifier()); auto context = scriptExecutionContext(); auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer, wrapAlgorithm, wrappingKey = makeRef(wrappingKey), wrapParams = WTFMove(wrapParams), isEncryption, context, workQueue = m_workQueue](SubtleCrypto::KeyFormat format, KeyData&& key) mutable { if (subtleCryptoWeakPointer) { if (auto promise = subtleCryptoWeakPointer->m_pendingPromises.get(index)) { Vector bytes; switch (format) { case SubtleCrypto::KeyFormat::Spki: case SubtleCrypto::KeyFormat::Pkcs8: case SubtleCrypto::KeyFormat::Raw: bytes = WTF::get>(key); break; case SubtleCrypto::KeyFormat::Jwk: { // FIXME: Converting to JS just to JSON-Stringify seems inefficient. We should find a way to go directly from the struct to JSON. auto jwk = toJS>(*(promise->globalObject()), *(promise->globalObject()), WTFMove(WTF::get(key))); String jwkString = JSONStringify(promise->globalObject(), jwk, 0); CString jwkUtf8String = jwkString.utf8(StrictConversion); bytes.append(jwkUtf8String.data(), jwkUtf8String.length()); } } auto callback = [index, subtleCryptoWeakPointer](const Vector& wrappedKey) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), wrappedKey.data(), wrappedKey.size()); }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; if (!isEncryption) { // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously: // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-wrapKey // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously. wrapAlgorithm->wrapKey(wrappingKey.get(), WTFMove(bytes), WTFMove(callback), WTFMove(exceptionCallback)); return; } // The following operation should be performed asynchronously. wrapAlgorithm->encrypt(*wrapParams, WTFMove(wrappingKey), WTFMove(bytes), WTFMove(callback), WTFMove(exceptionCallback), *context, workQueue); } } }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; // The following operation should be performed synchronously. exportAlgorithm->exportKey(format, key, WTFMove(callback), WTFMove(exceptionCallback)); } void SubtleCrypto::unwrapKey(JSC::JSGlobalObject& state, KeyFormat format, BufferSource&& wrappedKeyBufferSource, CryptoKey& unwrappingKey, AlgorithmIdentifier&& unwrapAlgorithmIdentifier, AlgorithmIdentifier&& unwrappedKeyAlgorithmIdentifier, bool extractable, Vector&& keyUsages, Ref&& promise) { auto wrappedKey = copyToVector(WTFMove(wrappedKeyBufferSource)); bool isDecryption = false; auto unwrapParamsOrException = normalizeCryptoAlgorithmParameters(state, unwrapAlgorithmIdentifier, Operations::UnwrapKey); if (unwrapParamsOrException.hasException()) { unwrapParamsOrException = normalizeCryptoAlgorithmParameters(state, unwrapAlgorithmIdentifier, Operations::Decrypt); if (unwrapParamsOrException.hasException()) { promise->reject(unwrapParamsOrException.releaseException()); return; } isDecryption = true; } auto unwrapParams = unwrapParamsOrException.releaseReturnValue(); auto unwrappedKeyAlgorithmOrException = normalizeCryptoAlgorithmParameters(state, unwrappedKeyAlgorithmIdentifier, Operations::ImportKey); if (unwrappedKeyAlgorithmOrException.hasException()) { promise->reject(unwrappedKeyAlgorithmOrException.releaseException()); return; } auto unwrappedKeyAlgorithm = unwrappedKeyAlgorithmOrException.releaseReturnValue(); auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages); if (unwrapParams->identifier != unwrappingKey.algorithmIdentifier()) { promise->reject(InvalidAccessError, "Unwrapping CryptoKey doesn't match unwrap AlgorithmIdentifier"_s); return; } if (!unwrappingKey.allows(CryptoKeyUsageUnwrapKey)) { promise->reject(InvalidAccessError, "Unwrapping CryptoKey doesn't support unwrapKey operation"_s); return; } auto importAlgorithm = CryptoAlgorithmRegistry::singleton().create(unwrappedKeyAlgorithm->identifier); if (UNLIKELY(!importAlgorithm)) { promise->reject(Exception { NotSupportedError }); return; } auto unwrapAlgorithm = CryptoAlgorithmRegistry::singleton().create(unwrappingKey.algorithmIdentifier()); if (UNLIKELY(!unwrapAlgorithm)) { promise->reject(Exception { NotSupportedError }); return; } auto index = promise.ptr(); m_pendingPromises.add(index, WTFMove(promise)); auto subtleCryptoWeakPointer = makeWeakPtr(*this); auto callback = [index, subtleCryptoWeakPointer, format, importAlgorithm, unwrappedKeyAlgorithm = crossThreadCopyImportParams(*unwrappedKeyAlgorithm), extractable, keyUsagesBitmap](const Vector& bytes) mutable { if (subtleCryptoWeakPointer) { if (auto promise = subtleCryptoWeakPointer->m_pendingPromises.get(index)) { KeyData keyData; switch (format) { case SubtleCrypto::KeyFormat::Spki: case SubtleCrypto::KeyFormat::Pkcs8: case SubtleCrypto::KeyFormat::Raw: keyData = bytes; break; case SubtleCrypto::KeyFormat::Jwk: { auto& state = *(promise->globalObject()); auto& vm = state.vm(); auto scope = DECLARE_THROW_SCOPE(vm); String jwkString(bytes.data(), bytes.size()); JSLockHolder locker(vm); auto jwkObject = JSONParse(&state, jwkString); if (!jwkObject) { promise->reject(DataError, "WrappedKey cannot be converted to a JSON object"_s); return; } auto jwk = convert>(state, jwkObject); RETURN_IF_EXCEPTION(scope, void()); normalizeJsonWebKey(jwk); keyData = jwk; break; } } auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) { rejectWithException(promise.releaseNonNull(), SyntaxError); return; } promise->resolve>(key); } }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; // The following operation should be performed synchronously. importAlgorithm->importKey(format, WTFMove(keyData), *unwrappedKeyAlgorithm, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback)); } } }; auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { if (auto promise = getPromise(index, subtleCryptoWeakPointer)) rejectWithException(promise.releaseNonNull(), ec); }; if (!isDecryption) { // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously: // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-unwrapKey // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously. unwrapAlgorithm->unwrapKey(unwrappingKey, WTFMove(wrappedKey), WTFMove(callback), WTFMove(exceptionCallback)); return; } unwrapAlgorithm->decrypt(*unwrapParams, unwrappingKey, WTFMove(wrappedKey), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); } } #endif