JSValue JSWebKitSubtleCrypto::digest(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 2)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    auto algorithm = createAlgorithmFromJSValue(state, scope, state.uncheckedArgument(0));
    RETURN_IF_EXCEPTION(scope, { });

    auto parameters = JSCryptoAlgorithmDictionary::createParametersForDigest(state, scope, algorithm->identifier(), state.uncheckedArgument(0));
    RETURN_IF_EXCEPTION(scope, { });

    auto data = cryptoOperationDataFromJSValue(state, scope, state.uncheckedArgument(1));
    RETURN_IF_EXCEPTION(scope, { });

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();
    auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
        fulfillPromiseWithArrayBuffer(wrapper.releaseNonNull(), result.data(), result.size());
    };
    auto failureCallback = [wrapper]() mutable {
        wrapper->reject(); // FIXME: This should reject with an Exception.
    };

    auto result = algorithm->digest(*parameters, data, WTFMove(successCallback), WTFMove(failureCallback));
    if (result.hasException()) {
        propagateException(state, scope, result.releaseException());
        return { };
    }

    return promise;
}
Esempio n. 2
0
bool JSStorage::putDelegate(ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot&, bool& putResult)
{
    VM& vm = state->vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    // Only perform the custom put if the object doesn't have a native property by this name.
    // Since hasProperty() would end up calling canGetItemsForName() and be fooled, we need to check
    // the native property slots manually.
    PropertySlot slot { this, PropertySlot::InternalMethodType::GetOwnProperty };

    JSValue prototype = this->getPrototypeDirect();
    if (prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot))
        return false;

    if (propertyName.isSymbol())
        return false;

    String stringValue = value.toWTFString(state);
    RETURN_IF_EXCEPTION(scope, true);

    auto setItemResult = wrapped().setItem(propertyNameToString(propertyName), stringValue);
    if (setItemResult.hasException()) {
        propagateException(*state, scope, setItemResult.releaseException());
        return true;
    }

    putResult = true;
    return true;
}
JSValue JSWebKitSubtleCrypto::verify(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 4)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(0));
    ASSERT(scope.exception() || algorithm);
    if (!algorithm)
        return jsUndefined();

    auto parameters = JSCryptoAlgorithmDictionary::createParametersForVerify(&state, algorithm->identifier(), state.uncheckedArgument(0));
    ASSERT(scope.exception() || parameters);
    if (!parameters)
        return jsUndefined();

    RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
    if (!key)
        return throwTypeError(&state, scope);

    if (!key->allows(CryptoKeyUsageVerify)) {
        wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'verify'"));
        setDOMException(&state, NOT_SUPPORTED_ERR);
        return jsUndefined();
    }

    CryptoOperationData signature;
    auto success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(2), signature);
    ASSERT(scope.exception() || success);
    if (!success)
        return jsUndefined();

    CryptoOperationData data;
    success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(3), data);
    ASSERT(scope.exception() || success);
    if (!success)
        return jsUndefined();

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();
    auto successCallback = [wrapper](bool result) mutable {
        wrapper->resolve(result);
    };
    auto failureCallback = [wrapper]() mutable {
        wrapper->reject(nullptr);
    };

    auto result = algorithm->verify(*parameters, *key, signature, data, WTFMove(successCallback), WTFMove(failureCallback));
    if (result.hasException()) {
        propagateException(state, scope, result.releaseException());
        return { };
    }

    return promise;
}
static void importKey(ExecState& state, CryptoKeyFormat keyFormat, CryptoOperationData data, RefPtr<CryptoAlgorithm> algorithm, RefPtr<CryptoAlgorithmParametersDeprecated> parameters, bool extractable, CryptoKeyUsageBitmap keyUsages, CryptoAlgorithm::KeyCallback callback, CryptoAlgorithm::VoidCallback failureCallback)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    std::unique_ptr<CryptoKeySerialization> keySerialization;
    switch (keyFormat) {
    case CryptoKeyFormat::Raw:
        keySerialization = CryptoKeySerializationRaw::create(data);
        break;
    case CryptoKeyFormat::JWK: {
        String jwkString = String::fromUTF8(data.first, data.second);
        if (jwkString.isNull()) {
            throwTypeError(&state, scope, ASCIILiteral("JWK JSON serialization is not valid UTF-8"));
            return;
        }
        keySerialization = std::make_unique<JSCryptoKeySerializationJWK>(&state, jwkString);
        RETURN_IF_EXCEPTION(scope, void());
        break;
    }
    default:
        throwTypeError(&state, scope, ASCIILiteral("Unsupported key format for import"));
        return;
    }

    ASSERT(keySerialization);

    std::optional<CryptoAlgorithmPair> reconciledResult = keySerialization->reconcileAlgorithm(algorithm.get(), parameters.get());
    if (!reconciledResult) {
        if (!scope.exception())
            throwTypeError(&state, scope, ASCIILiteral("Algorithm specified in key is not compatible with one passed to importKey as argument"));
        return;
    }
    RETURN_IF_EXCEPTION(scope, void());

    algorithm = reconciledResult->algorithm;
    parameters = reconciledResult->parameters;
    if (!algorithm) {
        throwTypeError(&state, scope, ASCIILiteral("Neither key nor function argument has crypto algorithm specified"));
        return;
    }
    ASSERT(parameters);

    keySerialization->reconcileExtractable(extractable);
    RETURN_IF_EXCEPTION(scope, void());

    keySerialization->reconcileUsages(keyUsages);
    RETURN_IF_EXCEPTION(scope, void());

    auto keyData = keySerialization->keyData();
    RETURN_IF_EXCEPTION(scope, void());

    propagateException(state, scope, algorithm->importKey(*parameters, *keyData, extractable, keyUsages, WTFMove(callback), WTFMove(failureCallback)));
}
JSValue JSWebKitSubtleCrypto::generateKey(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 1)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(0));
    ASSERT(scope.exception() || algorithm);
    if (!algorithm)
        return jsUndefined();

    auto parameters = JSCryptoAlgorithmDictionary::createParametersForGenerateKey(&state, algorithm->identifier(), state.uncheckedArgument(0));
    ASSERT(scope.exception() || parameters);
    if (!parameters)
        return jsUndefined();

    bool extractable = false;
    if (state.argumentCount() >= 2) {
        extractable = state.uncheckedArgument(1).toBoolean(&state);
        RETURN_IF_EXCEPTION(scope, JSValue());
    }

    CryptoKeyUsageBitmap keyUsages = 0;
    if (state.argumentCount() >= 3) {
        auto success = cryptoKeyUsagesFromJSValue(state, state.argument(2), keyUsages);
        ASSERT(scope.exception() || success);
        if (!success)
            return jsUndefined();
    }

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();
    auto successCallback = [wrapper](CryptoKey* key, CryptoKeyPair* keyPair) mutable {
        ASSERT(key || keyPair);
        ASSERT(!key || !keyPair);
        if (key)
            wrapper->resolve(key);
        else
            wrapper->resolve(keyPair);
    };
    auto failureCallback = [wrapper]() mutable {
        wrapper->reject(nullptr);
    };

    auto result = algorithm->generateKey(*parameters, extractable, keyUsages, WTFMove(successCallback), WTFMove(failureCallback), *scriptExecutionContextFromExecState(&state));
    if (result.hasException()) {
        propagateException(state, scope, result.releaseException());
        return { };
    }

    return promise;
}
Esempio n. 6
0
void JSStorage::getOwnPropertyNames(JSObject* object, ExecState* state, PropertyNameArray& propertyNames, EnumerationMode mode)
{
    VM& vm = state->vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    auto& thisObject = *jsCast<JSStorage*>(object);
    auto lengthResult = thisObject.wrapped().length();
    if (lengthResult.hasException()) {
        propagateException(*state, scope, lengthResult.releaseException());
        return;
    }
    unsigned length = lengthResult.releaseReturnValue();
    for (unsigned i = 0; i < length; ++i) {
        auto keyResult = thisObject.wrapped().key(i);
        if (keyResult.hasException()) {
            propagateException(*state, scope, lengthResult.releaseException());
            return;
        }
        propertyNames.add(Identifier::fromString(state, keyResult.releaseReturnValue()));
    }
        
    Base::getOwnPropertyNames(&thisObject, state, propertyNames, mode);
}
bool JSTestNamedDeleterThrowingException::deleteProperty(JSCell* cell, ExecState* state, PropertyName propertyName)
{
    auto& thisObject = *jsCast<JSTestNamedDeleterThrowingException*>(cell);
    auto& impl = thisObject.wrapped();
    if (isVisibleNamedProperty<OverrideBuiltins::No>(*state, thisObject, propertyName)) {
        auto result = impl.deleteNamedProperty(propertyNameToString(propertyName));
        if (result.hasException()) {
            auto throwScope = DECLARE_THROW_SCOPE(state->vm());
            propagateException(*state, throwScope, result.releaseException());
            return true;
        }

        return result.releaseReturnValue();
    }
    return JSObject::deleteProperty(cell, state, propertyName);
}
JSValue JSWebKitSubtleCrypto::generateKey(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 1)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    auto algorithm = createAlgorithmFromJSValue(state, scope, state.uncheckedArgument(0));
    RETURN_IF_EXCEPTION(scope, { });

    auto parameters = JSCryptoAlgorithmDictionary::createParametersForGenerateKey(state, scope, algorithm->identifier(), state.uncheckedArgument(0));
    RETURN_IF_EXCEPTION(scope, { });

    bool extractable = state.argument(1).toBoolean(&state);
    RETURN_IF_EXCEPTION(scope, { });

    CryptoKeyUsageBitmap keyUsages = 0;
    if (state.argumentCount() >= 3) {
        keyUsages = cryptoKeyUsagesFromJSValue(state, scope, state.uncheckedArgument(2));
        RETURN_IF_EXCEPTION(scope, { });
    }

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();
    auto successCallback = [wrapper](KeyOrKeyPair&& keyOrKeyPair) mutable {
        WTF::switchOn(keyOrKeyPair,
            [&wrapper] (RefPtr<CryptoKey>& key) {
                wrapper->resolve<IDLInterface<CryptoKey>>(*key);
            },
            [&wrapper] (CryptoKeyPair& keyPair) {
                wrapper->resolve<IDLDictionary<CryptoKeyPair>>(keyPair);
            }
        );
    };
    auto failureCallback = [wrapper]() mutable {
        wrapper->reject(); // FIXME: This should reject with an Exception.
    };

    auto result = algorithm->generateKey(*parameters, extractable, keyUsages, WTFMove(successCallback), WTFMove(failureCallback), *scriptExecutionContextFromExecState(&state));
    if (result.hasException()) {
        propagateException(state, scope, result.releaseException());
        return { };
    }

    return promise;
}
JSValue JSWebKitSubtleCrypto::verify(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 4)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    auto algorithm = createAlgorithmFromJSValue(state, scope, state.uncheckedArgument(0));
    RETURN_IF_EXCEPTION(scope, { });

    auto parameters = JSCryptoAlgorithmDictionary::createParametersForVerify(state, scope, algorithm->identifier(), state.uncheckedArgument(0));
    RETURN_IF_EXCEPTION(scope, { });

    RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(vm, state.uncheckedArgument(1));
    if (!key)
        return throwTypeError(&state, scope);

    if (!key->allows(CryptoKeyUsageVerify)) {
        wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'verify'"));
        throwNotSupportedError(state, scope);
        return jsUndefined();
    }

    auto signature = cryptoOperationDataFromJSValue(state, scope, state.uncheckedArgument(2));
    RETURN_IF_EXCEPTION(scope, { });

    auto data = cryptoOperationDataFromJSValue(state, scope, state.uncheckedArgument(3));
    RETURN_IF_EXCEPTION(scope, { });

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();
    auto successCallback = [wrapper](bool result) mutable {
        wrapper->resolve<IDLBoolean>(result);
    };
    auto failureCallback = [wrapper]() mutable {
        wrapper->reject(); // FIXME: This should reject with an Exception.
    };

    auto result = algorithm->verify(*parameters, *key, signature, data, WTFMove(successCallback), WTFMove(failureCallback));
    if (result.hasException()) {
        propagateException(state, scope, result.releaseException());
        return { };
    }

    return promise;
}
Esempio n. 10
0
bool JSStorage::nameGetter(ExecState* state, PropertyName propertyName, JSValue& value)
{
    if (propertyName.isSymbol())
        return false;

    auto item = wrapped().getItem(propertyNameToString(propertyName));
    if (item.hasException()) {
        auto& vm = state->vm();
        auto scope = DECLARE_THROW_SCOPE(vm);
        propagateException(*state, scope, item.releaseException());
        return false;
    }

    auto string = item.releaseReturnValue();
    if (string.isNull())
        return false;

    value = jsStringWithCache(state, string);
    return true;
}
Esempio n. 11
0
bool JSStorage::deleteProperty(JSCell* cell, ExecState* state, PropertyName propertyName)
{
    auto& thisObject = *jsCast<JSStorage*>(cell);

    // Only perform the custom delete if the object doesn't have a native property by this name.
    // Since hasProperty() would end up calling canGetItemsForName() and be fooled, we need to check
    // the native property slots manually.
    PropertySlot slot(&thisObject, PropertySlot::InternalMethodType::GetOwnProperty);

    JSValue prototype = thisObject.getPrototypeDirect();
    if (prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot))
        return Base::deleteProperty(&thisObject, state, propertyName);

    if (propertyName.isSymbol())
        return Base::deleteProperty(&thisObject, state, propertyName);

    VM& vm = state->vm();
    auto scope = DECLARE_THROW_SCOPE(vm);
    propagateException(*state, scope, thisObject.wrapped().removeItem(propertyNameToString(propertyName)));
    return true;
}
Esempio n. 12
0
static JSValue handlePostMessage(DOMWindow& impl, ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (UNLIKELY(state.argumentCount() < 2))
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    Vector<RefPtr<MessagePort>> messagePorts;
    Vector<RefPtr<JSC::ArrayBuffer>> arrayBuffers;

    // This function has variable arguments and can be:
    // Per current spec:
    //   postMessage(message, targetOrigin)
    //   postMessage(message, targetOrigin, {sequence of transferrables})
    // Legacy non-standard implementations in webkit allowed:
    //   postMessage(message, {sequence of transferrables}, targetOrigin);
    int targetOriginArgIndex = 1;
    if (state.argumentCount() > 2) {
        int transferablesArgIndex = 2;
        if (state.uncheckedArgument(2).isString()) {
            targetOriginArgIndex = 2;
            transferablesArgIndex = 1;
        }
        extractTransferables(state, state.argument(transferablesArgIndex), messagePorts, arrayBuffers);
    }
    RETURN_IF_EXCEPTION(scope, JSValue());

    auto message = SerializedScriptValue::create(state, state.uncheckedArgument(0), messagePorts, WTFMove(arrayBuffers));
    RETURN_IF_EXCEPTION(scope, JSValue());

    String targetOrigin = convert<IDLNullable<IDLUSVString>>(state, state.uncheckedArgument(targetOriginArgIndex));
    RETURN_IF_EXCEPTION(scope, JSValue());

    propagateException(state, scope, impl.postMessage(message.releaseNonNull(), WTFMove(messagePorts), targetOrigin, callerDOMWindow(&state)));

    return jsUndefined();
}
Esempio n. 13
0
static void JSMediaDevicesGetUserMediaPromiseFunction(ExecState& state, Ref<DeferredPromise>&& promise)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (UNLIKELY(state.argumentCount() < 1)) {
        throwVMError(&state, scope, createNotEnoughArgumentsError(&state));
        return;
    }

    auto constraintsDictionary = Dictionary(&state, state.uncheckedArgument(0));

    MediaTrackConstraintSetMap mandatoryAudioConstraints;
    Vector<MediaTrackConstraintSetMap> advancedAudioConstraints;
    bool areAudioConstraintsValid = false;

    Dictionary audioConstraintsDictionary;
    if (constraintsDictionary.get("audio", audioConstraintsDictionary) && !audioConstraintsDictionary.isUndefinedOrNull()) {
        parseMediaConstraintsDictionary(audioConstraintsDictionary, mandatoryAudioConstraints, advancedAudioConstraints);
        areAudioConstraintsValid = true;
    } else
        constraintsDictionary.get("audio", areAudioConstraintsValid);

    MediaTrackConstraintSetMap mandatoryVideoConstraints;
    Vector<MediaTrackConstraintSetMap> advancedVideoConstraints;
    bool areVideoConstraintsValid = false;

    Dictionary videoConstraintsDictionary;
    if (constraintsDictionary.get("video", videoConstraintsDictionary) && !videoConstraintsDictionary.isUndefinedOrNull()) {
        parseMediaConstraintsDictionary(videoConstraintsDictionary, mandatoryVideoConstraints, advancedVideoConstraints);
        areVideoConstraintsValid = true;
    } else
        constraintsDictionary.get("video", areVideoConstraintsValid);

    auto audioConstraints = MediaConstraintsImpl::create(WTFMove(mandatoryAudioConstraints), WTFMove(advancedAudioConstraints), areAudioConstraintsValid);
    auto videoConstraints = MediaConstraintsImpl::create(WTFMove(mandatoryVideoConstraints), WTFMove(advancedVideoConstraints), areVideoConstraintsValid);
    propagateException(state, scope, castThisValue<JSMediaDevices>(state).wrapped().getUserMedia(WTFMove(audioConstraints), WTFMove(videoConstraints), WTFMove(promise)));
}
JSValue JSWebKitSubtleCrypto::unwrapKey(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 5)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    auto keyFormat = cryptoKeyFormatFromJSValue(state, scope, state.uncheckedArgument(0));
    RETURN_IF_EXCEPTION(scope, { });

    auto wrappedKeyData = cryptoOperationDataFromJSValue(state, scope, state.uncheckedArgument(1));
    RETURN_IF_EXCEPTION(scope, { });

    RefPtr<CryptoKey> unwrappingKey = JSCryptoKey::toWrapped(vm, state.uncheckedArgument(2));
    if (!unwrappingKey)
        return throwTypeError(&state, scope);

    if (!unwrappingKey->allows(CryptoKeyUsageUnwrapKey)) {
        wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'unwrapKey'"));
        throwNotSupportedError(state, scope);
        return jsUndefined();
    }

    auto unwrapAlgorithm = createAlgorithmFromJSValue(state, scope, state.uncheckedArgument(3));
    RETURN_IF_EXCEPTION(scope, { });

    auto unwrapAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForDecrypt(state, scope, unwrapAlgorithm->identifier(), state.uncheckedArgument(3));
    RETURN_IF_EXCEPTION(scope, { });

    RefPtr<CryptoAlgorithm> unwrappedKeyAlgorithm;
    RefPtr<CryptoAlgorithmParametersDeprecated> unwrappedKeyAlgorithmParameters;
    if (!state.uncheckedArgument(4).isNull()) {
        unwrappedKeyAlgorithm = createAlgorithmFromJSValue(state, scope, state.uncheckedArgument(4));
        RETURN_IF_EXCEPTION(scope, { });

        unwrappedKeyAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(state, scope, unwrappedKeyAlgorithm->identifier(), state.uncheckedArgument(4));
        RETURN_IF_EXCEPTION(scope, { });
    }

    bool extractable = state.argument(5).toBoolean(&state);
    RETURN_IF_EXCEPTION(scope, { });

    CryptoKeyUsageBitmap keyUsages = 0;
    if (state.argumentCount() >= 7) {
        keyUsages = cryptoKeyUsagesFromJSValue(state, scope, state.uncheckedArgument(6));
        RETURN_IF_EXCEPTION(scope, { });
    }

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();
    Strong<JSDOMGlobalObject> domGlobalObject(state.vm(), globalObject());

    auto decryptSuccessCallback = [domGlobalObject, keyFormat, unwrappedKeyAlgorithm, unwrappedKeyAlgorithmParameters, extractable, keyUsages, wrapper](const Vector<uint8_t>& result) mutable {
        auto importSuccessCallback = [wrapper](CryptoKey& key) mutable {
            wrapper->resolve<IDLInterface<CryptoKey>>(key);
        };
        auto importFailureCallback = [wrapper]() mutable {
            wrapper->reject(); // FIXME: This should reject with an Exception.
        };

        VM& vm = domGlobalObject->vm();
        auto scope = DECLARE_CATCH_SCOPE(vm);

        ExecState& state = *domGlobalObject->globalExec();
        WebCore::importKey(state, keyFormat, std::make_pair(result.data(), result.size()), unwrappedKeyAlgorithm, unwrappedKeyAlgorithmParameters, extractable, keyUsages, WTFMove(importSuccessCallback), WTFMove(importFailureCallback));
        if (UNLIKELY(scope.exception())) {
            // FIXME: Report exception details to console, and possibly to calling script once there is a standardized way to pass errors to WebCrypto promise reject functions.
            scope.clearException();
            wrapper->reject(); // FIXME: This should reject with an Exception.
        }
    };

    auto decryptFailureCallback = [wrapper]() mutable {
        wrapper->reject(); // FIXME: This should reject with an Exception.
    };

    auto result = unwrapAlgorithm->decryptForUnwrapKey(*unwrapAlgorithmParameters, *unwrappingKey, wrappedKeyData, WTFMove(decryptSuccessCallback), WTFMove(decryptFailureCallback));
    if (result.hasException()) {
        propagateException(state, scope, result.releaseException());
        return { };
    }

    return promise;
}