diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index fd7b198de6..cd33e36529 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCES Bindings/WindowObject.cpp Bindings/Wrappable.cpp Crypto/Crypto.cpp + Crypto/SubtleCrypto.cpp CSS/Serialize.cpp CSS/CSSConditionRule.cpp CSS/CSSGroupingRule.cpp @@ -364,6 +365,7 @@ function(libweb_js_wrapper class) endfunction() libweb_js_wrapper(Crypto/Crypto) +libweb_js_wrapper(Crypto/SubtleCrypto) libweb_js_wrapper(CSS/CSSRule) libweb_js_wrapper(CSS/CSSRuleList) libweb_js_wrapper(CSS/CSSStyleDeclaration) diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp new file mode 100644 index 0000000000..a77ac50bce --- /dev/null +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::Crypto { + +JS::Promise* SubtleCrypto::digest(String const& algorithm, JS::Handle const& data) +{ + auto& global_object = wrapper()->global_object(); + + // 1. Let algorithm be the algorithm parameter passed to the digest() method. + + // 2. Let data be the result of getting a copy of the bytes held by the data parameter passed to the digest() method. + auto data_buffer = Bindings::IDL::get_buffer_source_copy(*data.cell()); + if (!data_buffer.has_value()) { + auto* error = wrap(wrapper()->global_object(), DOM::OperationError::create("Failed to copy bytes from ArrayBuffer")); + auto* promise = JS::Promise::create(global_object); + promise->reject(error); + return promise; + } + + // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "digest". + // FIXME: This is way more generic than it needs to be right now, so we simplify it. + ::Crypto::Hash::HashKind hash_kind; + if (algorithm.equals_ignoring_case("SHA-1"sv)) { + hash_kind = ::Crypto::Hash::HashKind::SHA1; + } else if (algorithm.equals_ignoring_case("SHA-256"sv)) { + hash_kind = ::Crypto::Hash::HashKind::SHA256; + } else if (algorithm.equals_ignoring_case("SHA-384"sv)) { + hash_kind = ::Crypto::Hash::HashKind::SHA384; + } else if (algorithm.equals_ignoring_case("SHA-512"sv)) { + hash_kind = ::Crypto::Hash::HashKind::SHA512; + } + // 4. If an error occurred, return a Promise rejected with normalizedAlgorithm. + else { + auto* error = wrap(wrapper()->global_object(), DOM::NotSupportedError::create(String::formatted("Invalid hash function '{}'", algorithm))); + auto* promise = JS::Promise::create(global_object); + promise->reject(error); + return promise; + } + + // 5. Let promise be a new Promise. + auto* promise = JS::Promise::create(global_object); + + // 6. Return promise and perform the remaining steps in parallel. + // FIXME: We don't have a good abstraction for this yet, so we do it in sync. + + // 7. If the following steps or referenced procedures say to throw an error, reject promise with the returned error and then terminate the algorithm. + + // 8. Let result be the result of performing the digest operation specified by normalizedAlgorithm using algorithm, with data as message. + ::Crypto::Hash::Manager hash; + hash.initialize(hash_kind); + hash.update(*data_buffer); + auto digest = hash.digest(); + auto const* digest_data = digest.immutable_data(); + auto result_buffer = ByteBuffer::create_zeroed(hash.digest_size()); + if (!result_buffer.has_value()) { + auto* error = wrap(wrapper()->global_object(), DOM::OperationError::create("Failed to create result buffer")); + promise->reject(error); + return promise; + } + for (size_t i = 0; i < hash.digest_size(); ++i) + (*result_buffer)[i] = digest_data[i]; + + auto* result = JS::ArrayBuffer::create(global_object, result_buffer.release_value()); + + // 9. Resolve promise with result. + promise->fulfill(result); + return promise; +} + +} diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h index 7d3b0d7d31..24f762919c 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h @@ -6,6 +6,7 @@ #pragma once +#include #include namespace Web::Crypto { @@ -21,6 +22,8 @@ public: return adopt_ref(*new SubtleCrypto()); } + JS::Promise* digest(String const& algorithm, JS::Handle const& data); + private: SubtleCrypto() = default; }; diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl index e0e9d5489c..99f867b2c6 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl @@ -1,3 +1,5 @@ [SecureContext,Exposed=(Window,Worker)] interface SubtleCrypto { + // FIXME: Add support for AlgorithmIdentifier ("typedef (object or DOMString)") + Promise digest(DOMString algorithm, BufferSource data); };