diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/bindings/js/JSSubtleCryptoCustom.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/bindings/js/JSSubtleCryptoCustom.cpp')
-rw-r--r-- | Source/WebCore/bindings/js/JSSubtleCryptoCustom.cpp | 1113 |
1 files changed, 1113 insertions, 0 deletions
diff --git a/Source/WebCore/bindings/js/JSSubtleCryptoCustom.cpp b/Source/WebCore/bindings/js/JSSubtleCryptoCustom.cpp new file mode 100644 index 000000000..86b95d71a --- /dev/null +++ b/Source/WebCore/bindings/js/JSSubtleCryptoCustom.cpp @@ -0,0 +1,1113 @@ +/* + * Copyright (C) 2016 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 "JSSubtleCrypto.h" + +#if ENABLE(SUBTLE_CRYPTO) + +#include "CryptoAlgorithm.h" +#include "CryptoAlgorithmRegistry.h" +#include "JSAesCbcParams.h" +#include "JSAesKeyGenParams.h" +#include "JSCryptoAlgorithmParameters.h" +#include "JSCryptoKey.h" +#include "JSCryptoKeyPair.h" +#include "JSDOMPromise.h" +#include "JSDOMWrapper.h" +#include "JSHmacKeyParams.h" +#include "JSJsonWebKey.h" +#include "JSRsaHashedImportParams.h" +#include "JSRsaHashedKeyGenParams.h" +#include "JSRsaKeyGenParams.h" +#include "JSRsaOaepParams.h" +#include "ScriptState.h" +#include <runtime/Error.h> +#include <runtime/JSArray.h> +#include <runtime/JSONObject.h> + +using namespace JSC; + +namespace WebCore { + +enum class Operations { + Encrypt, + Decrypt, + Sign, + Verify, + Digest, + DeriveKey, + DeriveBits, + GenerateKey, + ImportKey, + WrapKey, + UnwrapKey +}; + +static std::unique_ptr<CryptoAlgorithmParameters> normalizeCryptoAlgorithmParameters(ExecState&, JSValue, Operations); + +static CryptoAlgorithmIdentifier toHashIdentifier(ExecState& state, JSValue value) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto digestParams = normalizeCryptoAlgorithmParameters(state, value, Operations::Digest); + RETURN_IF_EXCEPTION(scope, { }); + return digestParams->identifier; +} + +static std::unique_ptr<CryptoAlgorithmParameters> normalizeCryptoAlgorithmParameters(ExecState& state, JSValue value, Operations operation) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (value.isString()) { + JSObject* newParams = constructEmptyObject(&state); + newParams->putDirect(vm, Identifier::fromString(&vm, "name"), value); + return normalizeCryptoAlgorithmParameters(state, newParams, operation); + } + + if (value.isObject()) { + auto params = convertDictionary<CryptoAlgorithmParameters>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + + auto identifier = CryptoAlgorithmRegistry::singleton().identifier(params.name); + if (UNLIKELY(!identifier)) { + throwNotSupportedError(state, scope); + return nullptr; + } + + std::unique_ptr<CryptoAlgorithmParameters> result; + switch (operation) { + case Operations::Encrypt: + case Operations::Decrypt: + switch (*identifier) { + case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: + result = std::make_unique<CryptoAlgorithmParameters>(params); + break; + case CryptoAlgorithmIdentifier::RSA_OAEP: { + auto params = convertDictionary<CryptoAlgorithmRsaOaepParams>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + result = std::make_unique<CryptoAlgorithmRsaOaepParams>(params); + break; + } + case CryptoAlgorithmIdentifier::AES_CBC: { + auto params = convertDictionary<CryptoAlgorithmAesCbcParams>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + result = std::make_unique<CryptoAlgorithmAesCbcParams>(params); + break; + } + default: + throwNotSupportedError(state, scope); + return nullptr; + } + break; + case Operations::Sign: + case Operations::Verify: + switch (*identifier) { + case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: + case CryptoAlgorithmIdentifier::HMAC: + result = std::make_unique<CryptoAlgorithmParameters>(params); + break; + default: + throwNotSupportedError(state, scope); + return nullptr; + } + 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 = std::make_unique<CryptoAlgorithmParameters>(params); + break; + default: + throwNotSupportedError(state, scope); + return nullptr; + } + break; + case Operations::DeriveKey: + case Operations::DeriveBits: + throwNotSupportedError(state, scope); + return nullptr; + case Operations::GenerateKey: + switch (*identifier) { + case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: { + auto params = convertDictionary<CryptoAlgorithmRsaKeyGenParams>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + result = std::make_unique<CryptoAlgorithmRsaKeyGenParams>(params); + break; + } + case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: + case CryptoAlgorithmIdentifier::RSA_PSS: + case CryptoAlgorithmIdentifier::RSA_OAEP: { + auto params = convertDictionary<CryptoAlgorithmRsaHashedKeyGenParams>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + params.hashIdentifier = toHashIdentifier(state, params.hash); + RETURN_IF_EXCEPTION(scope, nullptr); + result = std::make_unique<CryptoAlgorithmRsaHashedKeyGenParams>(params); + break; + } + case CryptoAlgorithmIdentifier::AES_CTR: + case CryptoAlgorithmIdentifier::AES_CBC: + case CryptoAlgorithmIdentifier::AES_CMAC: + case CryptoAlgorithmIdentifier::AES_GCM: + case CryptoAlgorithmIdentifier::AES_CFB: + case CryptoAlgorithmIdentifier::AES_KW: { + auto params = convertDictionary<CryptoAlgorithmAesKeyGenParams>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + result = std::make_unique<CryptoAlgorithmAesKeyGenParams>(params); + break; + } + case CryptoAlgorithmIdentifier::HMAC: { + auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + params.hashIdentifier = toHashIdentifier(state, params.hash); + RETURN_IF_EXCEPTION(scope, nullptr); + result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params); + break; + } + default: + throwNotSupportedError(state, scope); + return nullptr; + } + break; + case Operations::ImportKey: + switch (*identifier) { + case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: + result = std::make_unique<CryptoAlgorithmParameters>(params); + break; + case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: + case CryptoAlgorithmIdentifier::RSA_PSS: + case CryptoAlgorithmIdentifier::RSA_OAEP: { + auto params = convertDictionary<CryptoAlgorithmRsaHashedImportParams>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + params.hashIdentifier = toHashIdentifier(state, params.hash); + RETURN_IF_EXCEPTION(scope, nullptr); + result = std::make_unique<CryptoAlgorithmRsaHashedImportParams>(params); + break; + } + case CryptoAlgorithmIdentifier::AES_CTR: + case CryptoAlgorithmIdentifier::AES_CBC: + case CryptoAlgorithmIdentifier::AES_CMAC: + case CryptoAlgorithmIdentifier::AES_GCM: + case CryptoAlgorithmIdentifier::AES_CFB: + case CryptoAlgorithmIdentifier::AES_KW: + result = std::make_unique<CryptoAlgorithmParameters>(params); + break; + case CryptoAlgorithmIdentifier::HMAC: { + auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value); + RETURN_IF_EXCEPTION(scope, nullptr); + params.hashIdentifier = toHashIdentifier(state, params.hash); + RETURN_IF_EXCEPTION(scope, nullptr); + result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params); + break; + } + default: + throwNotSupportedError(state, scope); + return nullptr; + } + break; + case Operations::WrapKey: + case Operations::UnwrapKey: + switch (*identifier) { + case CryptoAlgorithmIdentifier::AES_KW: + result = std::make_unique<CryptoAlgorithmParameters>(params); + break; + default: + throwNotSupportedError(state, scope); + return nullptr; + } + break; + default: + ASSERT_NOT_REACHED(); + return nullptr; + } + + result->identifier = *identifier; + return result; + } + + throwTypeError(&state, scope, ASCIILiteral("Invalid AlgorithmIdentifier")); + return nullptr; +} + +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; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static CryptoKeyUsageBitmap cryptoKeyUsageBitmapFromJSValue(ExecState& state, JSValue value) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + CryptoKeyUsageBitmap result = 0; + auto usages = convert<IDLSequence<IDLEnumeration<CryptoKeyUsage>>>(state, value); + RETURN_IF_EXCEPTION(scope, 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<DeferredPromise>&& passedPromise, ExceptionCode ec) +{ + switch (ec) { + case NOT_SUPPORTED_ERR: + passedPromise->reject(ec, ASCIILiteral("The algorithm is not supported")); + return; + case SYNTAX_ERR: + passedPromise->reject(ec, ASCIILiteral("A required parameter was missing or out-of-range")); + return; + case INVALID_STATE_ERR: + passedPromise->reject(ec, ASCIILiteral("The requested operation is not valid for the current state of the provided key")); + return; + case INVALID_ACCESS_ERR: + passedPromise->reject(ec, ASCIILiteral("The requested operation is not valid for the provided key")); + return; + case UnknownError: + passedPromise->reject(ec, ASCIILiteral("The operation failed for an unknown transient reason (e.g. out of memory)")); + return; + case DataError: + passedPromise->reject(ec, ASCIILiteral("Data provided to an operation does not meet requirements")); + return; + case OperationError: + passedPromise->reject(ec, ASCIILiteral("The operation failed for an operation-specific reason")); + return; + } + ASSERT_NOT_REACHED(); +} + +static KeyData toKeyData(ExecState& state, SubtleCrypto::KeyFormat format, JSValue value) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + KeyData result; + switch (format) { + case SubtleCrypto::KeyFormat::Spki: + case SubtleCrypto::KeyFormat::Pkcs8: + case SubtleCrypto::KeyFormat::Raw: { + BufferSource bufferSource = convert<IDLBufferSource>(state, value); + RETURN_IF_EXCEPTION(scope, result); + Vector<uint8_t> vector; + vector.append(bufferSource.data(), bufferSource.length()); + result = WTFMove(vector); + return result; + } + case SubtleCrypto::KeyFormat::Jwk: { + result = convertDictionary<JsonWebKey>(state, value); + RETURN_IF_EXCEPTION(scope, result); + CryptoKeyUsageBitmap usages = 0; + if (WTF::get<JsonWebKey>(result).key_ops) { + // Maybe we shouldn't silently bypass duplicated usages? + for (auto usage : WTF::get<JsonWebKey>(result).key_ops.value()) + usages |= toCryptoKeyUsageBitmap(usage); + } + WTF::get<JsonWebKey>(result).usages = usages; + return result; + } + } + ASSERT_NOT_REACHED(); + return result; +} + +static RefPtr<CryptoKey> toCryptoKey(ExecState& state, JSValue value) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + RefPtr<CryptoKey> result = JSCryptoKey::toWrapped(vm, value); + if (!result) { + throwTypeError(&state, scope, ASCIILiteral("Invalid CryptoKey")); + return nullptr; + } + return result; +} + +static Vector<uint8_t> toVector(ExecState& state, JSValue value) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + BufferSource data = convert<IDLBufferSource>(state, value); + RETURN_IF_EXCEPTION(scope, { }); + Vector<uint8_t> dataVector; + dataVector.append(data.data(), data.length()); + + return dataVector; +} + +static void supportExportKeyThrow(ExecState& state, ThrowScope& scope, 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_CMAC: + case CryptoAlgorithmIdentifier::AES_GCM: + case CryptoAlgorithmIdentifier::AES_CFB: + case CryptoAlgorithmIdentifier::AES_KW: + case CryptoAlgorithmIdentifier::HMAC: + return; + default: + throwNotSupportedError(state, scope); + } +} + +static void jsSubtleCryptoFunctionEncryptPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 3)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::Encrypt); + RETURN_IF_EXCEPTION(scope, void()); + + auto key = toCryptoKey(state, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + auto data = toVector(state, state.uncheckedArgument(2)); + RETURN_IF_EXCEPTION(scope, void()); + + if (params->identifier != key->algorithmIdentifier()) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't match AlgorithmIdentifier")); + return; + } + + if (!key->allows(CryptoKeyUsageEncrypt)) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't support encryption")); + return; + } + + auto algorithm = CryptoAlgorithmRegistry::singleton().create(key->algorithmIdentifier()); + if (UNLIKELY(!algorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [capturedPromise = promise.copyRef()](const Vector<uint8_t>& cipherText) mutable { + fulfillPromiseWithArrayBuffer(WTFMove(capturedPromise), cipherText.data(), cipherText.size()); + return; + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), ec); + }; + + auto subtle = jsDynamicDowncast<JSSubtleCrypto*>(vm, state.thisValue()); + ASSERT(subtle); + algorithm->encrypt(WTFMove(params), key.releaseNonNull(), WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state), subtle->wrapped().workQueue()); +} + +static void jsSubtleCryptoFunctionDecryptPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 3)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::Decrypt); + RETURN_IF_EXCEPTION(scope, void()); + + auto key = toCryptoKey(state, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + auto data = toVector(state, state.uncheckedArgument(2)); + RETURN_IF_EXCEPTION(scope, void()); + + if (params->identifier != key->algorithmIdentifier()) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't match AlgorithmIdentifier")); + return; + } + + if (!key->allows(CryptoKeyUsageDecrypt)) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't support decryption")); + return; + } + + auto algorithm = CryptoAlgorithmRegistry::singleton().create(key->algorithmIdentifier()); + if (UNLIKELY(!algorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [capturedPromise = promise.copyRef()](const Vector<uint8_t>& plainText) mutable { + fulfillPromiseWithArrayBuffer(WTFMove(capturedPromise), plainText.data(), plainText.size()); + return; + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), ec); + }; + + auto subtle = jsDynamicDowncast<JSSubtleCrypto*>(vm, state.thisValue()); + ASSERT(subtle); + algorithm->decrypt(WTFMove(params), key.releaseNonNull(), WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state), subtle->wrapped().workQueue()); +} + +static void jsSubtleCryptoFunctionSignPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 3)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::Sign); + RETURN_IF_EXCEPTION(scope, void()); + + auto key = toCryptoKey(state, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + auto data = toVector(state, state.uncheckedArgument(2)); + RETURN_IF_EXCEPTION(scope, void()); + + if (params->identifier != key->algorithmIdentifier()) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't match AlgorithmIdentifier")); + return; + } + + if (!key->allows(CryptoKeyUsageSign)) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't support signing")); + return; + } + + auto algorithm = CryptoAlgorithmRegistry::singleton().create(key->algorithmIdentifier()); + if (UNLIKELY(!algorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [capturedPromise = promise.copyRef()](const Vector<uint8_t>& signature) mutable { + fulfillPromiseWithArrayBuffer(WTFMove(capturedPromise), signature.data(), signature.size()); + return; + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), ec); + }; + + JSSubtleCrypto* subtle = jsDynamicDowncast<JSSubtleCrypto*>(vm, state.thisValue()); + ASSERT(subtle); + algorithm->sign(key.releaseNonNull(), WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state), subtle->wrapped().workQueue()); +} + +static void jsSubtleCryptoFunctionVerifyPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 4)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::Verify); + RETURN_IF_EXCEPTION(scope, void()); + + auto key = toCryptoKey(state, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + auto signature = toVector(state, state.uncheckedArgument(2)); + RETURN_IF_EXCEPTION(scope, void()); + + auto data = toVector(state, state.uncheckedArgument(3)); + RETURN_IF_EXCEPTION(scope, void()); + + if (params->identifier != key->algorithmIdentifier()) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't match AlgorithmIdentifier")); + return; + } + + if (!key->allows(CryptoKeyUsageVerify)) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't support verification")); + return; + } + + auto algorithm = CryptoAlgorithmRegistry::singleton().create(key->algorithmIdentifier()); + if (UNLIKELY(!algorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [capturedPromise = promise.copyRef()](bool result) mutable { + capturedPromise->resolve<IDLBoolean>(result); + return; + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), ec); + }; + + auto subtle = jsDynamicDowncast<JSSubtleCrypto*>(vm, state.thisValue()); + ASSERT(subtle); + algorithm->verify(key.releaseNonNull(), WTFMove(signature), WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state), subtle->wrapped().workQueue()); +} + +static void jsSubtleCryptoFunctionDigestPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 2)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::Digest); + RETURN_IF_EXCEPTION(scope, void()); + + auto data = toVector(state, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); + if (UNLIKELY(!algorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [capturedPromise = promise.copyRef()](const Vector<uint8_t>& digest) mutable { + fulfillPromiseWithArrayBuffer(WTFMove(capturedPromise), digest.data(), digest.size()); + return; + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), ec); + }; + + auto subtle = jsDynamicDowncast<JSSubtleCrypto*>(vm, state.thisValue()); + ASSERT(subtle); + algorithm->digest(WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state), subtle->wrapped().workQueue()); +} + +static void jsSubtleCryptoFunctionDeriveKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 5)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::DeriveKey); + RETURN_IF_EXCEPTION(scope, void()); + + // We should always return a NOT_SUPPORTED_ERR since we currently don't support any algorithms that has deriveKey operation. + ASSERT_NOT_REACHED(); +} + +static void jsSubtleCryptoFunctionDeriveBitsPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 3)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::DeriveBits); + RETURN_IF_EXCEPTION(scope, void()); + + // We should always return a NOT_SUPPORTED_ERR since we currently don't support any algorithms that has deriveBits operation. + ASSERT_NOT_REACHED(); +} + +static void jsSubtleCryptoFunctionGenerateKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 3)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::GenerateKey); + RETURN_IF_EXCEPTION(scope, void()); + + auto extractable = state.uncheckedArgument(1).toBoolean(&state); + RETURN_IF_EXCEPTION(scope, void()); + + auto keyUsages = cryptoKeyUsageBitmapFromJSValue(state, state.uncheckedArgument(2)); + RETURN_IF_EXCEPTION(scope, void()); + + auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); + if (UNLIKELY(!algorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [capturedPromise = promise.copyRef()](KeyOrKeyPair&& keyOrKeyPair) mutable { + WTF::switchOn(keyOrKeyPair, + [&capturedPromise] (RefPtr<CryptoKey>& key) { + if ((key->type() == CryptoKeyType::Private || key->type() == CryptoKeyType::Secret) && !key->usagesBitmap()) { + rejectWithException(WTFMove(capturedPromise), SYNTAX_ERR); + return; + } + capturedPromise->resolve<IDLInterface<CryptoKey>>(*key); + }, + [&capturedPromise] (CryptoKeyPair& keyPair) { + if (!keyPair.privateKey->usagesBitmap()) { + rejectWithException(WTFMove(capturedPromise), SYNTAX_ERR); + return; + } + capturedPromise->resolve<IDLDictionary<CryptoKeyPair>>(keyPair); + } + ); + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), ec); + }; + + // The 11 December 2014 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 and HMAC keys. Therefore, we perform it as an async task conditionally. + algorithm->generateKey(*params, extractable, keyUsages, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state)); +} + +static void jsSubtleCryptoFunctionImportKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 5)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto format = convertEnumeration<SubtleCrypto::KeyFormat>(state, state.uncheckedArgument(0)); + RETURN_IF_EXCEPTION(scope, void()); + + auto keyData = toKeyData(state, format, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(2), Operations::ImportKey); + RETURN_IF_EXCEPTION(scope, void()); + + auto extractable = state.uncheckedArgument(3).toBoolean(&state); + RETURN_IF_EXCEPTION(scope, void()); + + auto keyUsages = cryptoKeyUsageBitmapFromJSValue(state, state.uncheckedArgument(4)); + RETURN_IF_EXCEPTION(scope, void()); + + auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); + if (UNLIKELY(!algorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [capturedPromise = promise.copyRef()](CryptoKey& key) mutable { + if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) { + rejectWithException(WTFMove(capturedPromise), SYNTAX_ERR); + return; + } + capturedPromise->resolve<IDLInterface<CryptoKey>>(key); + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), 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), WTFMove(params), extractable, keyUsages, WTFMove(callback), WTFMove(exceptionCallback)); +} + +static void jsSubtleCryptoFunctionExportKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 2)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto format = convertEnumeration<SubtleCrypto::KeyFormat>(state, state.uncheckedArgument(0)); + RETURN_IF_EXCEPTION(scope, void()); + + auto key = toCryptoKey(state, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + supportExportKeyThrow(state, scope, key->algorithmIdentifier()); + RETURN_IF_EXCEPTION(scope, void()); + + if (!key->extractable()) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("The CryptoKey is nonextractable")); + return; + } + + auto algorithm = CryptoAlgorithmRegistry::singleton().create(key->algorithmIdentifier()); + if (UNLIKELY(!algorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [capturedPromise = promise.copyRef()](SubtleCrypto::KeyFormat format, KeyData&& key) mutable { + switch (format) { + case SubtleCrypto::KeyFormat::Spki: + case SubtleCrypto::KeyFormat::Pkcs8: + case SubtleCrypto::KeyFormat::Raw: { + Vector<uint8_t>& rawKey = WTF::get<Vector<uint8_t>>(key); + fulfillPromiseWithArrayBuffer(WTFMove(capturedPromise), rawKey.data(), rawKey.size()); + return; + } + case SubtleCrypto::KeyFormat::Jwk: + capturedPromise->resolve<IDLDictionary<JsonWebKey>>(WTFMove(WTF::get<JsonWebKey>(key))); + return; + } + ASSERT_NOT_REACHED(); + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), 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.releaseNonNull(), WTFMove(callback), WTFMove(exceptionCallback)); +} + +static void jsSubtleCryptoFunctionWrapKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 4)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto format = convertEnumeration<SubtleCrypto::KeyFormat>(state, state.uncheckedArgument(0)); + RETURN_IF_EXCEPTION(scope, void()); + + auto key = toCryptoKey(state, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + auto wrappingKey = toCryptoKey(state, state.uncheckedArgument(2)); + RETURN_IF_EXCEPTION(scope, void()); + + auto catchScope = DECLARE_CATCH_SCOPE(vm); + bool isEncryption = false; + auto wrapParams = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(3), Operations::WrapKey); + if (catchScope.exception()) { + catchScope.clearException(); + wrapParams = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(3), Operations::Encrypt); + RETURN_IF_EXCEPTION(scope, void()); + isEncryption = true; + } + + if (wrapParams->identifier != wrappingKey->algorithmIdentifier()) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("Wrapping CryptoKey doesn't match AlgorithmIdentifier")); + return; + } + + if (!wrappingKey->allows(CryptoKeyUsageWrapKey)) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("Wrapping CryptoKey doesn't support wrapKey operation")); + return; + } + + supportExportKeyThrow(state, scope, key->algorithmIdentifier()); + RETURN_IF_EXCEPTION(scope, void()); + + if (!key->extractable()) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("The CryptoKey is nonextractable")); + return; + } + + auto exportAlgorithm = CryptoAlgorithmRegistry::singleton().create(key->algorithmIdentifier()); + if (UNLIKELY(!exportAlgorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto wrapAlgorithm = CryptoAlgorithmRegistry::singleton().create(wrappingKey->algorithmIdentifier()); + if (UNLIKELY(!wrapAlgorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto context = scriptExecutionContextFromExecState(&state); + + auto subtle = jsDynamicDowncast<JSSubtleCrypto*>(vm, state.thisValue()); + ASSERT(subtle); + auto& workQueue = subtle->wrapped().workQueue(); + + auto callback = [promise = promise.copyRef(), wrapAlgorithm, wrappingKey = WTFMove(wrappingKey), wrapParams = WTFMove(wrapParams), isEncryption, context, &workQueue](SubtleCrypto::KeyFormat format, KeyData&& key) mutable { + Vector<uint8_t> bytes; + switch (format) { + case SubtleCrypto::KeyFormat::Spki: + case SubtleCrypto::KeyFormat::Pkcs8: + case SubtleCrypto::KeyFormat::Raw: + bytes = WTF::get<Vector<uint8_t>>(key); + break; + case SubtleCrypto::KeyFormat::Jwk: { + auto jwk = toJS<IDLDictionary<JsonWebKey>>(*(promise->globalObject()->globalExec()), *(promise->globalObject()), WTFMove(WTF::get<JsonWebKey>(key))); + String jwkString = JSONStringify(promise->globalObject()->globalExec(), jwk, 0); + CString jwkUtf8String = jwkString.utf8(StrictConversion); + bytes.append(jwkUtf8String.data(), jwkUtf8String.length()); + } + } + + auto callback = [promise = promise.copyRef()](const Vector<uint8_t>& wrappedKey) mutable { + fulfillPromiseWithArrayBuffer(WTFMove(promise), wrappedKey.data(), wrappedKey.size()); + return; + }; + auto exceptionCallback = [promise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(promise), 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.releaseNonNull(), WTFMove(bytes), WTFMove(callback), WTFMove(exceptionCallback)); + return; + } + // The following operation should be performed asynchronously. + wrapAlgorithm->encrypt(WTFMove(wrapParams), wrappingKey.releaseNonNull(), WTFMove(bytes), WTFMove(callback), WTFMove(exceptionCallback), *context, workQueue); + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), ec); + }; + + // The following operation should be performed synchronously. + exportAlgorithm->exportKey(format, key.releaseNonNull(), WTFMove(callback), WTFMove(exceptionCallback)); +} + +static void jsSubtleCryptoFunctionUnwrapKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise) +{ + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 7)) { + promise->reject<IDLAny>(createNotEnoughArgumentsError(&state)); + return; + } + + auto format = convertEnumeration<SubtleCrypto::KeyFormat>(state, state.uncheckedArgument(0)); + RETURN_IF_EXCEPTION(scope, void()); + + auto wrappedKey = toVector(state, state.uncheckedArgument(1)); + RETURN_IF_EXCEPTION(scope, void()); + + auto unwrappingKey = toCryptoKey(state, state.uncheckedArgument(2)); + RETURN_IF_EXCEPTION(scope, void()); + + auto catchScope = DECLARE_CATCH_SCOPE(vm); + bool isDecryption = false; + auto unwrapParams = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(3), Operations::UnwrapKey); + if (catchScope.exception()) { + catchScope.clearException(); + unwrapParams = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(3), Operations::Decrypt); + RETURN_IF_EXCEPTION(scope, void()); + isDecryption = true; + } + + auto unwrappedKeyAlgorithm = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(4), Operations::ImportKey); + RETURN_IF_EXCEPTION(scope, void()); + + auto extractable = state.uncheckedArgument(5).toBoolean(&state); + RETURN_IF_EXCEPTION(scope, void()); + + auto keyUsages = cryptoKeyUsageBitmapFromJSValue(state, state.uncheckedArgument(6)); + RETURN_IF_EXCEPTION(scope, void()); + + if (unwrapParams->identifier != unwrappingKey->algorithmIdentifier()) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("Unwrapping CryptoKey doesn't match unwrap AlgorithmIdentifier")); + return; + } + + if (!unwrappingKey->allows(CryptoKeyUsageUnwrapKey)) { + promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("Unwrapping CryptoKey doesn't support unwrapKey operation")); + return; + } + + auto importAlgorithm = CryptoAlgorithmRegistry::singleton().create(unwrappedKeyAlgorithm->identifier); + if (UNLIKELY(!importAlgorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto unwrapAlgorithm = CryptoAlgorithmRegistry::singleton().create(unwrappingKey->algorithmIdentifier()); + if (UNLIKELY(!unwrapAlgorithm)) { + throwNotSupportedError(state, scope); + return; + } + + auto callback = [promise = promise.copyRef(), format, importAlgorithm, unwrappedKeyAlgorithm = WTFMove(unwrappedKeyAlgorithm), extractable, keyUsages](const Vector<uint8_t>& bytes) mutable { + ExecState& state = *(promise->globalObject()->globalExec()); + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + KeyData keyData; + switch (format) { + case SubtleCrypto::KeyFormat::Spki: + case SubtleCrypto::KeyFormat::Pkcs8: + case SubtleCrypto::KeyFormat::Raw: + keyData = bytes; + break; + case SubtleCrypto::KeyFormat::Jwk: { + String jwkString(reinterpret_cast_ptr<const char*>(bytes.data()), bytes.size()); + JSC::JSLockHolder locker(vm); + auto jwk = JSONParse(&state, jwkString); + if (!jwk) { + promise->reject(DataError, ASCIILiteral("WrappedKey cannot be converted to a JSON object")); + return; + } + keyData = toKeyData(state, format, jwk); + RETURN_IF_EXCEPTION(scope, void()); + } + } + + auto callback = [promise = promise.copyRef()](CryptoKey& key) mutable { + if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) { + rejectWithException(WTFMove(promise), SYNTAX_ERR); + return; + } + promise->resolve<IDLInterface<CryptoKey>>(key); + }; + auto exceptionCallback = [promise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(promise), ec); + }; + + // The following operation should be performed synchronously. + importAlgorithm->importKey(format, WTFMove(keyData), WTFMove(unwrappedKeyAlgorithm), extractable, keyUsages, WTFMove(callback), WTFMove(exceptionCallback)); + }; + auto exceptionCallback = [capturedPromise = WTFMove(promise)](ExceptionCode ec) mutable { + rejectWithException(WTFMove(capturedPromise), 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.releaseNonNull(), WTFMove(wrappedKey), WTFMove(callback), WTFMove(exceptionCallback)); + return; + } + auto subtle = jsDynamicDowncast<JSSubtleCrypto*>(vm, state.thisValue()); + ASSERT(subtle); + // The following operation should be performed asynchronously. + unwrapAlgorithm->decrypt(WTFMove(unwrapParams), unwrappingKey.releaseNonNull(), WTFMove(wrappedKey), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state), subtle->wrapped().workQueue()); +} + +JSValue JSSubtleCrypto::encrypt(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionEncryptPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::decrypt(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionDecryptPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::sign(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionSignPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::verify(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionVerifyPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::digest(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionDigestPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::deriveKey(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionDeriveKeyPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::deriveBits(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionDeriveBitsPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::generateKey(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionGenerateKeyPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::importKey(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionImportKeyPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::exportKey(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionExportKeyPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::wrapKey(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionWrapKeyPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +JSValue JSSubtleCrypto::unwrapKey(ExecState& state) +{ + return callPromiseFunction<jsSubtleCryptoFunctionUnwrapKeyPromise, PromiseExecutionScope::WindowOrWorker>(state); +} + +} // namespace WebCore + +#endif |