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; }
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; }
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; }
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; }
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; }
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(); }
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; }