From 6906b7c1e6b71330ebd58c861d30f5cb18b48c17 Mon Sep 17 00:00:00 2001 From: stelar7 Date: Wed, 27 Mar 2024 01:50:25 +0100 Subject: [PATCH] LibWeb: Implement skeleton of SubtleCrypto.verify --- .../LibWeb/Crypto/CryptoAlgorithms.h | 5 ++ .../Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 64 +++++++++++++++++++ .../Libraries/LibWeb/Crypto/SubtleCrypto.h | 1 + .../Libraries/LibWeb/Crypto/SubtleCrypto.idl | 2 +- 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index df36ee2af35..7819c740383 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -167,6 +167,11 @@ public: return WebIDL::NotSupportedError::create(m_realm, "sign is not supported"_fly_string); } + virtual WebIDL::ExceptionOr> verify(AlgorithmParams const&, JS::NonnullGCPtr, ByteBuffer const&, ByteBuffer const&) + { + return WebIDL::NotSupportedError::create(m_realm, "verify is not supported"_fly_string); + } + virtual WebIDL::ExceptionOr> digest(AlgorithmParams const&, ByteBuffer const&) { return WebIDL::NotSupportedError::create(m_realm, "digest is not supported"_fly_string); diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index 87e355f35da..b9b5110065e 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -520,6 +520,70 @@ JS::ThrowCompletionOr> SubtleCrypto::sign(Algorith return verify_cast(*promise->promise()); } +// https://w3c.github.io/webcrypto/#dfn-SubtleCrypto-method-verify +JS::ThrowCompletionOr> SubtleCrypto::verify(AlgorithmIdentifier const& algorithm, JS::NonnullGCPtr key, JS::Handle const& signature_data, JS::Handle const& data_parameter) +{ + auto& realm = this->realm(); + auto& vm = this->vm(); + // 1. Let algorithm and key be the algorithm and key parameters passed to the verify() method, respectively. + + // 2. Let signature be the result of getting a copy of the bytes held by the signature parameter passed to the verify() method. + auto signature_or_error = WebIDL::get_buffer_source_copy(*signature_data->raw_object()); + if (signature_or_error.is_error()) { + VERIFY(signature_or_error.error().code() == ENOMEM); + return WebIDL::create_rejected_promise_from_exception(realm, vm.throw_completion(vm.error_message(JS::VM::ErrorMessage::OutOfMemory))); + } + auto signature = signature_or_error.release_value(); + + // 3. Let data be the result of getting a copy of the bytes held by the data parameter passed to the verify() method. + auto data_or_error = WebIDL::get_buffer_source_copy(*data_parameter->raw_object()); + if (data_or_error.is_error()) { + VERIFY(data_or_error.error().code() == ENOMEM); + return WebIDL::create_rejected_promise_from_exception(realm, vm.throw_completion(vm.error_message(JS::VM::ErrorMessage::OutOfMemory))); + } + auto data = data_or_error.release_value(); + + // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "verify". + auto normalized_algorithm = normalize_an_algorithm(realm, algorithm, "verify"_string); + + // 5. If an error occurred, return a Promise rejected with normalizedAlgorithm. + if (normalized_algorithm.is_error()) + return WebIDL::create_rejected_promise_from_exception(realm, normalized_algorithm.release_error()); + + // 6. Let promise be a new Promise. + auto promise = WebIDL::create_promise(realm); + + // 7. Return promise and perform the remaining steps in parallel. + Platform::EventLoopPlugin::the().deferred_invoke([&realm, normalized_algorithm = normalized_algorithm.release_value(), promise, key, signature = move(signature), data = move(data)]() -> void { + HTML::TemporaryExecutionContext context(Bindings::host_defined_environment_settings_object(realm), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes); + // 8. If the following steps or referenced procedures say to throw an error, reject promise with the returned error and then terminate the algorithm. + + // 9. If the name member of normalizedAlgorithm is not equal to the name attribute of the [[algorithm]] internal slot of key then throw an InvalidAccessError. + if (normalized_algorithm.parameter->name != key->algorithm_name()) { + WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Algorithm mismatch"_fly_string)); + return; + } + + // 10. If the [[usages]] internal slot of key does not contain an entry that is "verify", then throw an InvalidAccessError. + if (!key->internal_usages().contains_slow(Bindings::KeyUsage::Verify)) { + WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Key does not support verification"_fly_string)); + return; + } + + // 11. Let result be the result of performing the verify operation specified by normalizedAlgorithm using key, algorithm and signature and with data as message. + auto result = normalized_algorithm.methods->verify(*normalized_algorithm.parameter, key, signature, data); + if (result.is_error()) { + WebIDL::reject_promise(realm, promise, Bindings::dom_exception_to_throw_completion(realm.vm(), result.release_error()).release_value().value()); + return; + } + + // 12. Resolve promise with result. + WebIDL::resolve_promise(realm, promise, result.release_value()); + }); + + return verify_cast(*promise->promise()); +} + SupportedAlgorithmsMap& supported_algorithms_internal() { static SupportedAlgorithmsMap s_supported_algorithms; diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h index 6db39dab3ee..de6d89b0618 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h @@ -30,6 +30,7 @@ public: JS::NonnullGCPtr encrypt(AlgorithmIdentifier const& algorithm, JS::NonnullGCPtr key, JS::Handle const& data_parameter); JS::NonnullGCPtr decrypt(AlgorithmIdentifier const& algorithm, JS::NonnullGCPtr key, JS::Handle const& data_parameter); JS::ThrowCompletionOr> sign(AlgorithmIdentifier const& algorithm, JS::NonnullGCPtr key, JS::Handle const& data_parameter); + JS::ThrowCompletionOr> verify(AlgorithmIdentifier const& algorithm, JS::NonnullGCPtr key, JS::Handle const& signature, JS::Handle const& data_parameter); JS::NonnullGCPtr digest(AlgorithmIdentifier const& algorithm, JS::Handle const& data); diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl index d7c457494f2..e6ed6c262f9 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl @@ -48,7 +48,7 @@ interface SubtleCrypto { Promise encrypt(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data); Promise decrypt(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data); Promise sign(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data); - // FIXME: Promise verify(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource signature, BufferSource data); + Promise verify(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource signature, BufferSource data); Promise digest(AlgorithmIdentifier algorithm, BufferSource data);