mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-07 20:31:04 +03:00
LibWeb: Refactor SubtleCrypto to allow adding more algorithms easier
This patch throws away some of the spec suggestions for how to implement the normalize_algorithm AO and uses a new pattern that we can actually extend in our C++. Also update CryptoKey to store the key data.
This commit is contained in:
parent
644e764620
commit
2d59d6c98c
Notes:
sideshowbarker
2024-07-16 23:51:07 +09:00
Author: https://github.com/ADKaster Commit: https://github.com/SerenityOS/serenity/commit/2d59d6c98c Pull-request: https://github.com/SerenityOS/serenity/pull/23532
@ -4,6 +4,8 @@ source_set("Crypto") {
|
||||
sources = [
|
||||
"Crypto.cpp",
|
||||
"Crypto.h",
|
||||
"CryptoAlgorithms.cpp",
|
||||
"CryptoAlgorithms.h",
|
||||
"CryptoBindings.cpp",
|
||||
"CryptoBindings.h",
|
||||
"CryptoKey.cpp",
|
||||
|
@ -25,6 +25,7 @@ set(SOURCES
|
||||
Bindings/PlatformObject.cpp
|
||||
Clipboard/Clipboard.cpp
|
||||
Crypto/Crypto.cpp
|
||||
Crypto/CryptoAlgorithms.cpp
|
||||
Crypto/CryptoBindings.cpp
|
||||
Crypto/CryptoKey.cpp
|
||||
Crypto/SubtleCrypto.cpp
|
||||
|
128
Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp
Normal file
128
Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCrypto/Hash/HashManager.h>
|
||||
#include <LibJS/Runtime/ArrayBuffer.h>
|
||||
#include <LibJS/Runtime/DataView.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibWeb/Crypto/CryptoAlgorithms.h>
|
||||
|
||||
namespace Web::Crypto {
|
||||
|
||||
// Out of line to ensure this class has a key function
|
||||
AlgorithmMethods::~AlgorithmMethods() = default;
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> AlgorithmParams::from_value(JS::VM& vm, JS::Value value)
|
||||
{
|
||||
auto& object = value.as_object();
|
||||
|
||||
auto name = TRY(object.get("name"));
|
||||
auto name_string = TRY(name.to_string(vm));
|
||||
|
||||
return adopt_own(*new AlgorithmParams { .name = name_string });
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> PBKDF2Params::from_value(JS::VM& vm, JS::Value value)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
auto& object = value.as_object();
|
||||
|
||||
auto name_value = TRY(object.get("name"));
|
||||
auto name = TRY(name_value.to_string(vm));
|
||||
|
||||
auto salt_value = TRY(object.get("salt"));
|
||||
JS::Handle<WebIDL::BufferSource> salt;
|
||||
|
||||
if (!salt_value.is_object() || !(is<JS::TypedArrayBase>(salt_value.as_object()) || is<JS::ArrayBuffer>(salt_value.as_object()) || is<JS::DataView>(salt_value.as_object())))
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BufferSource");
|
||||
|
||||
salt = JS::make_handle(vm.heap().allocate<WebIDL::BufferSource>(realm, salt_value.as_object()));
|
||||
|
||||
auto iterations_value = TRY(object.get("iterations"));
|
||||
auto iterations = TRY(iterations_value.to_u32(vm));
|
||||
|
||||
auto hash_value = TRY(object.get("hash"));
|
||||
auto hash = Variant<Empty, HashAlgorithmIdentifier> { Empty {} };
|
||||
if (hash_value.is_string()) {
|
||||
auto hash_string = TRY(hash_value.to_string(vm));
|
||||
hash = HashAlgorithmIdentifier { hash_string };
|
||||
} else {
|
||||
auto hash_object = TRY(hash_value.to_object(vm));
|
||||
hash = HashAlgorithmIdentifier { hash_object };
|
||||
}
|
||||
|
||||
return adopt_own<AlgorithmParams>(*new PBKDF2Params { { name }, salt, iterations, hash.downcast<HashAlgorithmIdentifier>() });
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<CryptoKey>> PBKDF2::import_key(AlgorithmParams const&, Bindings::KeyFormat format, CryptoKey::InternalKeyData key_data, bool extractable, Vector<Bindings::KeyUsage> const& key_usages)
|
||||
{
|
||||
// 1. If format is not "raw", throw a NotSupportedError
|
||||
if (format != Bindings::KeyFormat::Raw) {
|
||||
return WebIDL::NotSupportedError::create(m_realm, "Only raw format is supported"_fly_string);
|
||||
}
|
||||
|
||||
// 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError.
|
||||
for (auto& usage : key_usages) {
|
||||
if (usage != Bindings::KeyUsage::Derivekey && usage != Bindings::KeyUsage::Derivebits) {
|
||||
return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage))));
|
||||
}
|
||||
}
|
||||
|
||||
// 3. If extractable is not false, then throw a SyntaxError.
|
||||
if (extractable)
|
||||
return WebIDL::SyntaxError::create(m_realm, "extractable must be false"_fly_string);
|
||||
|
||||
// 4. Let key be a new CryptoKey representing keyData.
|
||||
auto key = CryptoKey::create(m_realm, move(key_data));
|
||||
|
||||
// 5. Set the [[type]] internal slot of key to "secret".
|
||||
key->set_type(Bindings::KeyType::Secret);
|
||||
|
||||
// 6. Set the [[extractable]] internal slot of key to false.
|
||||
key->set_extractable(false);
|
||||
|
||||
// 7. Let algorithm be a new KeyAlgorithm object.
|
||||
auto algorithm = Bindings::KeyAlgorithm::create(m_realm);
|
||||
|
||||
// 8. Set the name attribute of algorithm to "PBKDF2".
|
||||
algorithm->set_name("PBKDF2"_string);
|
||||
|
||||
// 9. Set the [[algorithm]] internal slot of key to algorithm.
|
||||
key->set_algorithm(algorithm);
|
||||
|
||||
// 10. Return key.
|
||||
return key;
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> SHA::digest(AlgorithmParams const& algorithm, ByteBuffer const& data)
|
||||
{
|
||||
auto& algorithm_name = algorithm.name;
|
||||
|
||||
::Crypto::Hash::HashKind hash_kind;
|
||||
if (algorithm_name.equals_ignoring_ascii_case("SHA-1"sv)) {
|
||||
hash_kind = ::Crypto::Hash::HashKind::SHA1;
|
||||
} else if (algorithm_name.equals_ignoring_ascii_case("SHA-256"sv)) {
|
||||
hash_kind = ::Crypto::Hash::HashKind::SHA256;
|
||||
} else if (algorithm_name.equals_ignoring_ascii_case("SHA-384"sv)) {
|
||||
hash_kind = ::Crypto::Hash::HashKind::SHA384;
|
||||
} else if (algorithm_name.equals_ignoring_ascii_case("SHA-512"sv)) {
|
||||
hash_kind = ::Crypto::Hash::HashKind::SHA512;
|
||||
} else {
|
||||
return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", algorithm_name)));
|
||||
}
|
||||
|
||||
::Crypto::Hash::Manager hash { hash_kind };
|
||||
hash.update(data);
|
||||
|
||||
auto digest = hash.digest();
|
||||
auto result_buffer = ByteBuffer::copy(digest.immutable_data(), hash.digest_size());
|
||||
if (result_buffer.is_error())
|
||||
return WebIDL::OperationError::create(m_realm, "Failed to create result buffer"_fly_string);
|
||||
|
||||
return JS::ArrayBuffer::create(m_realm, result_buffer.release_value());
|
||||
}
|
||||
|
||||
}
|
93
Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h
Normal file
93
Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/EnumBits.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibWeb/Bindings/SubtleCryptoPrototype.h>
|
||||
#include <LibWeb/Crypto/CryptoBindings.h>
|
||||
#include <LibWeb/Crypto/CryptoKey.h>
|
||||
#include <LibWeb/WebIDL/Buffers.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::Crypto {
|
||||
|
||||
using KeyDataType = Variant<JS::Handle<WebIDL::BufferSource>, Bindings::JsonWebKey>;
|
||||
using AlgorithmIdentifier = Variant<JS::Handle<JS::Object>, String>;
|
||||
using HashAlgorithmIdentifier = AlgorithmIdentifier;
|
||||
|
||||
// https://w3c.github.io/webcrypto/#algorithm-overview
|
||||
|
||||
struct AlgorithmParams {
|
||||
String name;
|
||||
|
||||
static JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> from_value(JS::VM&, JS::Value);
|
||||
};
|
||||
|
||||
// https://w3c.github.io/webcrypto/#pbkdf2-params
|
||||
struct PBKDF2Params : public AlgorithmParams {
|
||||
JS::Handle<WebIDL::BufferSource> salt;
|
||||
u32 iterations;
|
||||
HashAlgorithmIdentifier hash;
|
||||
|
||||
static JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> from_value(JS::VM&, JS::Value);
|
||||
};
|
||||
|
||||
class AlgorithmMethods {
|
||||
public:
|
||||
virtual ~AlgorithmMethods();
|
||||
|
||||
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> digest(AlgorithmParams const&, ByteBuffer const&)
|
||||
{
|
||||
return WebIDL::NotSupportedError::create(m_realm, "digest is not supported"_fly_string);
|
||||
}
|
||||
|
||||
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<CryptoKey>> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector<Bindings::KeyUsage> const&)
|
||||
{
|
||||
return WebIDL::NotSupportedError::create(m_realm, "importKey is not supported"_fly_string);
|
||||
}
|
||||
|
||||
static NonnullOwnPtr<AlgorithmMethods> create(JS::Realm& realm) { return adopt_own(*new AlgorithmMethods(realm)); }
|
||||
|
||||
protected:
|
||||
explicit AlgorithmMethods(JS::Realm& realm)
|
||||
: m_realm(realm)
|
||||
{
|
||||
}
|
||||
|
||||
JS::Realm& m_realm;
|
||||
};
|
||||
|
||||
class PBKDF2 : public AlgorithmMethods {
|
||||
public:
|
||||
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<CryptoKey>> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector<Bindings::KeyUsage> const&) override;
|
||||
|
||||
static NonnullOwnPtr<AlgorithmMethods> create(JS::Realm& realm) { return adopt_own(*new PBKDF2(realm)); }
|
||||
|
||||
private:
|
||||
explicit PBKDF2(JS::Realm& realm)
|
||||
: AlgorithmMethods(realm)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class SHA : public AlgorithmMethods {
|
||||
public:
|
||||
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> digest(AlgorithmParams const&, ByteBuffer const&) override;
|
||||
|
||||
static NonnullOwnPtr<AlgorithmMethods> create(JS::Realm& realm) { return adopt_own(*new SHA(realm)); }
|
||||
|
||||
private:
|
||||
explicit SHA(JS::Realm& realm)
|
||||
: AlgorithmMethods(realm)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -6,6 +6,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibWeb/WebIDL/Buffers.h>
|
||||
|
||||
// FIXME: Generate these from IDL
|
||||
@ -40,11 +44,6 @@ struct JsonWebKey {
|
||||
Optional<String> k;
|
||||
};
|
||||
|
||||
// https://w3c.github.io/webcrypto/#dfn-Algorithm
|
||||
struct Algorithm {
|
||||
String name;
|
||||
};
|
||||
|
||||
// https://w3c.github.io/webcrypto/#key-algorithm-dictionary
|
||||
class KeyAlgorithm : public JS::Object {
|
||||
JS_OBJECT(KeyAlgorithm, Object);
|
||||
@ -66,11 +65,4 @@ private:
|
||||
String m_name;
|
||||
};
|
||||
|
||||
// https://w3c.github.io/webcrypto/#pbkdf2-params
|
||||
struct Pbkdf2Params {
|
||||
JS::Handle<WebIDL::BufferSource> salt;
|
||||
u32 iterations;
|
||||
Variant<JS::Handle<JS::Object>, String> hash;
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -1,28 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2023, stelar7 <dudedbz@gmail.com>
|
||||
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Memory.h>
|
||||
#include <LibWeb/Crypto/CryptoKey.h>
|
||||
|
||||
namespace Web::Crypto {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(CryptoKey);
|
||||
|
||||
JS::NonnullGCPtr<CryptoKey> CryptoKey::create(JS::Realm& realm)
|
||||
JS::NonnullGCPtr<CryptoKey> CryptoKey::create(JS::Realm& realm, InternalKeyData key_data)
|
||||
{
|
||||
return realm.heap().allocate<CryptoKey>(realm, realm);
|
||||
return realm.heap().allocate<CryptoKey>(realm, realm, move(key_data));
|
||||
}
|
||||
|
||||
CryptoKey::CryptoKey(JS::Realm& realm)
|
||||
CryptoKey::CryptoKey(JS::Realm& realm, InternalKeyData key_data)
|
||||
: PlatformObject(realm)
|
||||
, m_algorithm(Object::create(realm, nullptr))
|
||||
, m_usages(Object::create(realm, nullptr))
|
||||
, m_key_data(move(key_data))
|
||||
{
|
||||
}
|
||||
|
||||
CryptoKey::~CryptoKey() = default;
|
||||
CryptoKey::~CryptoKey()
|
||||
{
|
||||
m_key_data.visit(
|
||||
[](ByteBuffer& data) { secure_zero(data.data(), data.size()); },
|
||||
[](auto& data) { secure_zero(reinterpret_cast<u8*>(&data), sizeof(data)); });
|
||||
}
|
||||
|
||||
void CryptoKey::initialize(JS::Realm& realm)
|
||||
{
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <LibWeb/Bindings/CryptoKeyPrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/Crypto/CryptoBindings.h>
|
||||
|
||||
namespace Web::Crypto {
|
||||
|
||||
@ -19,7 +20,9 @@ class CryptoKey final : public Bindings::PlatformObject {
|
||||
JS_DECLARE_ALLOCATOR(CryptoKey);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static JS::NonnullGCPtr<CryptoKey> create(JS::Realm&);
|
||||
using InternalKeyData = Variant<ByteBuffer, Bindings::JsonWebKey>;
|
||||
|
||||
[[nodiscard]] static JS::NonnullGCPtr<CryptoKey> create(JS::Realm&, InternalKeyData);
|
||||
|
||||
virtual ~CryptoKey() override;
|
||||
|
||||
@ -34,7 +37,7 @@ public:
|
||||
void set_usages(JS::NonnullGCPtr<Object> usages) { m_usages = move(usages); }
|
||||
|
||||
private:
|
||||
explicit CryptoKey(JS::Realm&);
|
||||
CryptoKey(JS::Realm&, InternalKeyData);
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
@ -42,6 +45,8 @@ private:
|
||||
bool m_extractable { false };
|
||||
JS::NonnullGCPtr<Object> m_algorithm;
|
||||
JS::NonnullGCPtr<Object> m_usages;
|
||||
|
||||
InternalKeyData m_key_data;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -41,17 +41,17 @@ void SubtleCrypto::initialize(JS::Realm& realm)
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcrypto/#dfn-normalize-an-algorithm
|
||||
JS::ThrowCompletionOr<Bindings::Algorithm> SubtleCrypto::normalize_an_algorithm(AlgorithmIdentifier const& algorithm, String operation)
|
||||
WebIDL::ExceptionOr<SubtleCrypto::NormalizedAlgorithmAndParameter> SubtleCrypto::normalize_an_algorithm(AlgorithmIdentifier const& algorithm, String operation)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
auto& vm = this->vm();
|
||||
|
||||
// If alg is an instance of a DOMString:
|
||||
if (algorithm.has<String>()) {
|
||||
// Return the result of running the normalize an algorithm algorithm,
|
||||
// with the alg set to a new Algorithm dictionary whose name attribute is alg, and with the op set to op.
|
||||
auto dictionary = JS::make_handle(JS::Object::create(realm, realm.intrinsics().object_prototype()));
|
||||
TRY(dictionary->create_data_property("name", JS::PrimitiveString::create(realm.vm(), algorithm.get<String>())));
|
||||
TRY(dictionary->create_data_property("op", JS::PrimitiveString::create(realm.vm(), operation)));
|
||||
TRY(dictionary->create_data_property("name", JS::PrimitiveString::create(vm, algorithm.get<String>())));
|
||||
|
||||
return normalize_an_algorithm(dictionary, operation);
|
||||
}
|
||||
@ -65,49 +65,38 @@ JS::ThrowCompletionOr<Bindings::Algorithm> SubtleCrypto::normalize_an_algorithm(
|
||||
|
||||
// 2. Let initialAlg be the result of converting the ECMAScript object represented by alg to
|
||||
// the IDL dictionary type Algorithm, as defined by [WebIDL].
|
||||
// FIXME: How do we turn this into an "Algorithm" in a nice way?
|
||||
// NOTE: For now, we just use the object as-is.
|
||||
auto initial_algorithm = algorithm.get<JS::Handle<JS::Object>>();
|
||||
|
||||
// 3. If an error occurred, return the error and terminate this algorithm.
|
||||
auto has_name = TRY(initial_algorithm->has_property("name"));
|
||||
if (!has_name) {
|
||||
return realm.vm().throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Algorithm");
|
||||
}
|
||||
// Note: We're not going to bother creating an Algorithm object, all we want is the name attribute so that we can
|
||||
// fetch the actual algorithm factory from the registeredAlgorithms map.
|
||||
auto initial_algorithm = TRY(algorithm.get<JS::Handle<JS::Object>>()->get("name"));
|
||||
|
||||
// 4. Let algName be the value of the name attribute of initialAlg.
|
||||
auto algorithm_name = TRY(TRY(initial_algorithm->get("name")).to_string(realm.vm()));
|
||||
auto algorithm_name = TRY(initial_algorithm.to_string(vm));
|
||||
|
||||
String desired_type;
|
||||
RegisteredAlgorithm desired_type;
|
||||
|
||||
// 5. If registeredAlgorithms contains a key that is a case-insensitive string match for algName:
|
||||
if (registered_algorithms.contains(algorithm_name)) {
|
||||
if (auto it = registered_algorithms.find(algorithm_name); it != registered_algorithms.end()) {
|
||||
// 1. Set algName to the value of the matching key.
|
||||
auto it = registered_algorithms.find(algorithm_name);
|
||||
algorithm_name = (*it).key;
|
||||
|
||||
// 2. Let desiredType be the IDL dictionary type stored at algName in registeredAlgorithms.
|
||||
desired_type = (*it).value;
|
||||
desired_type = it->value;
|
||||
} else {
|
||||
// Otherwise:
|
||||
// Return a new NotSupportedError and terminate this algorithm.
|
||||
// FIXME: This should be a DOMException
|
||||
return realm.vm().throw_completion<JS::TypeError>(JS::ErrorType::NotImplemented, algorithm_name);
|
||||
return WebIDL::NotSupportedError::create(realm, MUST(String::formatted("Algorithm '{}' is not supported", algorithm_name)));
|
||||
}
|
||||
|
||||
// 8. Let normalizedAlgorithm be the result of converting the ECMAScript object represented by alg
|
||||
// to the IDL dictionary type desiredType, as defined by [WebIDL].
|
||||
// FIXME: Should IDL generate a struct for each of these?
|
||||
Bindings::Algorithm normalized_algorithm;
|
||||
|
||||
// 9. Set the name attribute of normalizedAlgorithm to algName.
|
||||
normalized_algorithm.name = algorithm_name;
|
||||
|
||||
// 10. If an error occurred, return the error and terminate this algorithm.
|
||||
|
||||
// FIXME: 11. Let dictionaries be a list consisting of the IDL dictionary type desiredType
|
||||
// 11. Let dictionaries be a list consisting of the IDL dictionary type desiredType
|
||||
// and all of desiredType's inherited dictionaries, in order from least to most derived.
|
||||
// FIXME: 12. For each dictionary dictionary in dictionaries:
|
||||
// 12. For each dictionary dictionary in dictionaries:
|
||||
// Note: All of these steps are handled by the create_methods and parameter_from_value methods.
|
||||
auto methods = desired_type.create_methods(realm);
|
||||
auto parameter = TRY(desired_type.parameter_from_value(vm, algorithm.get<JS::Handle<JS::Object>>()));
|
||||
auto normalized_algorithm = NormalizedAlgorithmAndParameter { move(methods), move(parameter) };
|
||||
|
||||
// 13. Return normalizedAlgorithm.
|
||||
return normalized_algorithm;
|
||||
@ -145,36 +134,15 @@ JS::NonnullGCPtr<JS::Promise> SubtleCrypto::digest(AlgorithmIdentifier const& al
|
||||
// FIXME: Need spec reference to https://webidl.spec.whatwg.org/#reject
|
||||
|
||||
// 8. Let result be the result of performing the digest operation specified by normalizedAlgorithm using algorithm, with data as message.
|
||||
auto algorithm_name = algorithm_object.name;
|
||||
auto result = algorithm_object.methods->digest(*algorithm_object.parameter, data_buffer);
|
||||
|
||||
::Crypto::Hash::HashKind hash_kind;
|
||||
if (algorithm_name.equals_ignoring_ascii_case("SHA-1"sv)) {
|
||||
hash_kind = ::Crypto::Hash::HashKind::SHA1;
|
||||
} else if (algorithm_name.equals_ignoring_ascii_case("SHA-256"sv)) {
|
||||
hash_kind = ::Crypto::Hash::HashKind::SHA256;
|
||||
} else if (algorithm_name.equals_ignoring_ascii_case("SHA-384"sv)) {
|
||||
hash_kind = ::Crypto::Hash::HashKind::SHA384;
|
||||
} else if (algorithm_name.equals_ignoring_ascii_case("SHA-512"sv)) {
|
||||
hash_kind = ::Crypto::Hash::HashKind::SHA512;
|
||||
} else {
|
||||
WebIDL::reject_promise(realm, promise, WebIDL::NotSupportedError::create(realm, MUST(String::formatted("Invalid hash function '{}'", algorithm_name))));
|
||||
if (result.is_exception()) {
|
||||
WebIDL::reject_promise(realm, promise, Bindings::dom_exception_to_throw_completion(realm.vm(), result.release_error()).release_value().value());
|
||||
return;
|
||||
}
|
||||
|
||||
::Crypto::Hash::Manager hash { hash_kind };
|
||||
hash.update(data_buffer);
|
||||
|
||||
auto digest = hash.digest();
|
||||
auto result_buffer = ByteBuffer::copy(digest.immutable_data(), hash.digest_size());
|
||||
if (result_buffer.is_error()) {
|
||||
WebIDL::reject_promise(realm, promise, WebIDL::OperationError::create(realm, "Failed to create result buffer"_fly_string));
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = JS::ArrayBuffer::create(realm, result_buffer.release_value());
|
||||
|
||||
// 9. Resolve promise with result.
|
||||
WebIDL::resolve_promise(realm, promise, result);
|
||||
WebIDL::resolve_promise(realm, promise, result.release_value());
|
||||
});
|
||||
|
||||
return verify_cast<JS::Promise>(*promise->promise());
|
||||
@ -222,19 +190,14 @@ JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> SubtleCrypto::import_key(Bi
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// 8. Return promise and perform the remaining steps in parallel.
|
||||
Platform::EventLoopPlugin::the().deferred_invoke([&realm, this, real_key_data = move(real_key_data), normalized_algorithm = normalized_algorithm.release_value(), promise, format, extractable, key_usages = move(key_usages), algorithm = move(algorithm)]() -> void {
|
||||
Platform::EventLoopPlugin::the().deferred_invoke([&realm, real_key_data = move(real_key_data), normalized_algorithm = normalized_algorithm.release_value(), promise, format, extractable, key_usages = move(key_usages), algorithm = move(algorithm)]() -> void {
|
||||
HTML::TemporaryExecutionContext context(Bindings::host_defined_environment_settings_object(realm), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
|
||||
// 9. If the following steps or referenced procedures say to throw an error, reject promise with the returned error and then terminate the algorithm.
|
||||
|
||||
// 10. Let result be the CryptoKey object that results from performing the import key operation
|
||||
// specified by normalizedAlgorithm using keyData, algorithm, format, extractable and usages.
|
||||
if (normalized_algorithm.name != "PBKDF2"sv) {
|
||||
WebIDL::reject_promise(realm, promise, WebIDL::NotSupportedError::create(realm, MUST(String::formatted("Invalid algorithm '{}'", normalized_algorithm.name))));
|
||||
return;
|
||||
}
|
||||
|
||||
auto maybe_result = pbkdf2_import_key(real_key_data, algorithm, format, extractable, key_usages);
|
||||
auto maybe_result = normalized_algorithm.methods->import_key(*normalized_algorithm.parameter, format, real_key_data.downcast<CryptoKey::InternalKeyData>(), extractable, key_usages);
|
||||
if (maybe_result.is_error()) {
|
||||
WebIDL::reject_promise(realm, promise, Bindings::dom_exception_to_throw_completion(realm.vm(), maybe_result.release_error()).release_value().value());
|
||||
return;
|
||||
@ -298,13 +261,13 @@ SubtleCrypto::SupportedAlgorithmsMap SubtleCrypto::supported_algorithms()
|
||||
|
||||
// https://w3c.github.io/webcrypto/#algorithm-conventions
|
||||
// https://w3c.github.io/webcrypto/#sha
|
||||
define_an_algorithm("digest"_string, "SHA-1"_string, ""_string);
|
||||
define_an_algorithm("digest"_string, "SHA-256"_string, ""_string);
|
||||
define_an_algorithm("digest"_string, "SHA-384"_string, ""_string);
|
||||
define_an_algorithm("digest"_string, "SHA-512"_string, ""_string);
|
||||
define_an_algorithm<SHA>("digest"_string, "SHA-1"_string);
|
||||
define_an_algorithm<SHA>("digest"_string, "SHA-256"_string);
|
||||
define_an_algorithm<SHA>("digest"_string, "SHA-384"_string);
|
||||
define_an_algorithm<SHA>("digest"_string, "SHA-512"_string);
|
||||
|
||||
// https://w3c.github.io/webcrypto/#pbkdf2
|
||||
define_an_algorithm("importKey"_string, "PBKDF2"_string, ""_string);
|
||||
define_an_algorithm<PBKDF2>("importKey"_string, "PBKDF2"_string);
|
||||
// FIXME: define_an_algorithm("deriveBits"_string, "PBKDF2"_string, "Pbkdf2Params"_string);
|
||||
// FIXME: define_an_algorithm("get key length"_string, "PBKDF2"_string, ""_string);
|
||||
|
||||
@ -312,7 +275,8 @@ SubtleCrypto::SupportedAlgorithmsMap SubtleCrypto::supported_algorithms()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcrypto/#concept-define-an-algorithm
|
||||
void SubtleCrypto::define_an_algorithm(String op, String algorithm, String type)
|
||||
template<typename Methods, typename Param>
|
||||
void SubtleCrypto::define_an_algorithm(AK::String op, AK::String algorithm)
|
||||
{
|
||||
auto& internal_object = supported_algorithms_internal();
|
||||
|
||||
@ -322,51 +286,8 @@ void SubtleCrypto::define_an_algorithm(String op, String algorithm, String type)
|
||||
auto registered_algorithms = maybe_registered_algorithms.value();
|
||||
|
||||
// 2. Set the alg key of registeredAlgorithms to the IDL dictionary type type.
|
||||
registered_algorithms.set(algorithm, type);
|
||||
registered_algorithms.set(algorithm, RegisteredAlgorithm { &Methods::create, &Param::from_value });
|
||||
internal_object.set(op, registered_algorithms);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcrypto/#pbkdf2-operations
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<CryptoKey>> SubtleCrypto::pbkdf2_import_key([[maybe_unused]] Variant<ByteBuffer, Bindings::JsonWebKey, Empty> key_data, [[maybe_unused]] AlgorithmIdentifier algorithm_parameter, Bindings::KeyFormat format, bool extractable, Vector<Bindings::KeyUsage> key_usages)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// 1. If format is not "raw", throw a NotSupportedError
|
||||
if (format != Bindings::KeyFormat::Raw) {
|
||||
return WebIDL::NotSupportedError::create(realm, "Only raw format is supported"_fly_string);
|
||||
}
|
||||
|
||||
// 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError.
|
||||
for (auto& usage : key_usages) {
|
||||
if (usage != Bindings::KeyUsage::Derivekey && usage != Bindings::KeyUsage::Derivebits) {
|
||||
return WebIDL::SyntaxError::create(realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage))));
|
||||
}
|
||||
}
|
||||
|
||||
// 3. If extractable is not false, then throw a SyntaxError.
|
||||
if (extractable)
|
||||
return WebIDL::SyntaxError::create(realm, "extractable must be false"_fly_string);
|
||||
|
||||
// 4. Let key be a new CryptoKey representing keyData.
|
||||
auto key = CryptoKey::create(realm);
|
||||
|
||||
// 5. Set the [[type]] internal slot of key to "secret".
|
||||
key->set_type(Bindings::KeyType::Secret);
|
||||
|
||||
// 6. Set the [[extractable]] internal slot of key to false.
|
||||
key->set_extractable(false);
|
||||
|
||||
// 7. Let algorithm be a new KeyAlgorithm object.
|
||||
auto algorithm = Bindings::KeyAlgorithm::create(realm);
|
||||
|
||||
// 8. Set the name attribute of algorithm to "PBKDF2".
|
||||
algorithm->set_name("PBKDF2"_string);
|
||||
|
||||
// 9. Set the [[algorithm]] internal slot of key to algorithm.
|
||||
key->set_algorithm(algorithm);
|
||||
|
||||
// 10. Return key.
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/Bindings/SubtleCryptoPrototype.h>
|
||||
#include <LibWeb/Crypto/CryptoAlgorithms.h>
|
||||
#include <LibWeb/Crypto/CryptoBindings.h>
|
||||
#include <LibWeb/Crypto/CryptoKey.h>
|
||||
|
||||
@ -22,9 +23,11 @@ class SubtleCrypto final : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(SubtleCrypto, Bindings::PlatformObject);
|
||||
JS_DECLARE_ALLOCATOR(SubtleCrypto);
|
||||
|
||||
using SupportedAlgorithmsMap = HashMap<String, HashMap<String, String, AK::ASCIICaseInsensitiveStringTraits>>;
|
||||
using KeyDataType = Variant<JS::Handle<WebIDL::BufferSource>, Bindings::JsonWebKey>;
|
||||
using AlgorithmIdentifier = Variant<JS::Handle<JS::Object>, String>;
|
||||
struct RegisteredAlgorithm {
|
||||
NonnullOwnPtr<AlgorithmMethods> (*create_methods)(JS::Realm&) = nullptr;
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> (*parameter_from_value)(JS::VM&, JS::Value) = nullptr;
|
||||
};
|
||||
using SupportedAlgorithmsMap = HashMap<String, HashMap<String, RegisteredAlgorithm, AK::ASCIICaseInsensitiveStringTraits>>;
|
||||
|
||||
public:
|
||||
[[nodiscard]] static JS::NonnullGCPtr<SubtleCrypto> create(JS::Realm&);
|
||||
@ -32,19 +35,24 @@ public:
|
||||
virtual ~SubtleCrypto() override;
|
||||
|
||||
JS::NonnullGCPtr<JS::Promise> digest(AlgorithmIdentifier const& algorithm, JS::Handle<WebIDL::BufferSource> const& data);
|
||||
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> import_key(Bindings::KeyFormat format, KeyDataType keyData, AlgorithmIdentifier algorithm, bool extractable, Vector<Bindings::KeyUsage> keyUsages);
|
||||
|
||||
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> import_key(Bindings::KeyFormat format, KeyDataType key_data, AlgorithmIdentifier algorithm, bool extractable, Vector<Bindings::KeyUsage> key_usages);
|
||||
|
||||
private:
|
||||
explicit SubtleCrypto(JS::Realm&);
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
||||
JS::ThrowCompletionOr<Bindings::Algorithm> normalize_an_algorithm(AlgorithmIdentifier const& algorithm, String operation);
|
||||
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<CryptoKey>> pbkdf2_import_key(Variant<ByteBuffer, Bindings::JsonWebKey, Empty> key_data, AlgorithmIdentifier algorithm, Bindings::KeyFormat format, bool extractable, Vector<Bindings::KeyUsage> usages);
|
||||
struct NormalizedAlgorithmAndParameter {
|
||||
NonnullOwnPtr<AlgorithmMethods> methods;
|
||||
NonnullOwnPtr<AlgorithmParams> parameter;
|
||||
};
|
||||
WebIDL::ExceptionOr<NormalizedAlgorithmAndParameter> normalize_an_algorithm(AlgorithmIdentifier const& algorithm, String operation);
|
||||
|
||||
static SubtleCrypto::SupportedAlgorithmsMap& supported_algorithms_internal();
|
||||
static SubtleCrypto::SupportedAlgorithmsMap supported_algorithms();
|
||||
static void define_an_algorithm(String op, String algorithm, String type);
|
||||
|
||||
template<typename Methods, typename Param = AlgorithmParams>
|
||||
static void define_an_algorithm(String op, String algorithm);
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user