diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/Crypto/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/Crypto/BUILD.gn index 44dac6f956..72ad8f540e 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/Crypto/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/Crypto/BUILD.gn @@ -6,10 +6,11 @@ source_set("Crypto") { "Crypto.h", "CryptoAlgorithms.cpp", "CryptoAlgorithms.h", - "CryptoBindings.cpp", "CryptoBindings.h", "CryptoKey.cpp", "CryptoKey.h", + "KeyAlgorithms.cpp", + "KeyAlgorithms.h", "SubtleCrypto.cpp", "SubtleCrypto.h", ] diff --git a/Tests/LibWeb/Text/expected/Crypto/SubtleCrypto-generateKey.txt b/Tests/LibWeb/Text/expected/Crypto/SubtleCrypto-generateKey.txt new file mode 100644 index 0000000000..f7fb59aed2 --- /dev/null +++ b/Tests/LibWeb/Text/expected/Crypto/SubtleCrypto-generateKey.txt @@ -0,0 +1,15 @@ +generateKey with RSA-OAEP algorithm +publicKey: [object CryptoKey] +publicKey algorithm: {"name":"RSA-OAEP","modulusLength":512,"publicExponent":{"0":0,"1":1,"2":0},"hash":"SHA-256"} +publicKey type: public +publicKey extractable: true +publicKey usages: encrypt,wrapKey +privateKey: [object CryptoKey] +privateKey algorithm: {"name":"RSA-OAEP","modulusLength":512,"publicExponent":{"0":0,"1":1,"2":0},"hash":"SHA-256"} +privateKey type: private +privateKey extractable: true +privateKey usages: decrypt,unwrapKey +invalid usages throw +Error: [object DOMException] +no usages for private key throws +Error: [object DOMException] diff --git a/Tests/LibWeb/Text/input/Crypto/SubtleCrypto-generateKey.html b/Tests/LibWeb/Text/input/Crypto/SubtleCrypto-generateKey.html new file mode 100644 index 0000000000..5c8847d9bb --- /dev/null +++ b/Tests/LibWeb/Text/input/Crypto/SubtleCrypto-generateKey.html @@ -0,0 +1,66 @@ + + diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index c39a2adff6..6495c098e3 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -26,7 +26,7 @@ set(SOURCES Clipboard/Clipboard.cpp Crypto/Crypto.cpp Crypto/CryptoAlgorithms.cpp - Crypto/CryptoBindings.cpp + Crypto/KeyAlgorithms.cpp Crypto/CryptoKey.cpp Crypto/SubtleCrypto.cpp CSS/Angle.cpp diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index d5f0a4de36..b3d5370c4c 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -4,17 +4,59 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include +#include #include #include #include #include +#include namespace Web::Crypto { +// https://w3c.github.io/webcrypto/#concept-usage-intersection +static Vector usage_intersection(ReadonlySpan a, ReadonlySpan b) +{ + Vector result; + for (auto const& usage : a) { + if (b.contains_slow(usage)) + result.append(usage); + } + quick_sort(result); + return result; +} + // Out of line to ensure this class has a key function AlgorithmMethods::~AlgorithmMethods() = default; +// https://w3c.github.io/webcrypto/#big-integer +static ::Crypto::UnsignedBigInteger big_integer_from_api_big_integer(JS::GCPtr const& big_integer) +{ + static_assert(AK::HostIsLittleEndian, "This method needs special treatment for BE"); + + // The BigInteger typedef is a Uint8Array that holds an arbitrary magnitude unsigned integer + // **in big-endian order**. Values read from the API SHALL have minimal typed array length + // (that is, at most 7 leading zero bits, except the value 0 which shall have length 8 bits). + // The API SHALL accept values with any number of leading zero bits, including the empty array, which represents zero. + + auto const& buffer = big_integer->viewed_array_buffer()->buffer(); + + ::Crypto::UnsignedBigInteger result(0); + if (buffer.size() > 0) { + + // We need to reverse the buffer to get it into little-endian order + Vector reversed_buffer; + reversed_buffer.resize(buffer.size()); + for (size_t i = 0; i < buffer.size(); ++i) { + reversed_buffer[buffer.size() - i - 1] = buffer[i]; + } + + result = ::Crypto::UnsignedBigInteger::import_data(reversed_buffer.data(), reversed_buffer.size()); + } + return result; +} + JS::ThrowCompletionOr> AlgorithmParams::from_value(JS::VM& vm, JS::Value value) { auto& object = value.as_object(); @@ -57,6 +99,126 @@ JS::ThrowCompletionOr> PBKDF2Params::from_value(J return adopt_own(*new PBKDF2Params { { name }, salt, iterations, hash.downcast() }); } +JS::ThrowCompletionOr> RsaKeyGenParams::from_value(JS::VM& vm, JS::Value value) +{ + auto& object = value.as_object(); + + auto name_value = TRY(object.get("name")); + auto name = TRY(name_value.to_string(vm)); + + auto modulus_length_value = TRY(object.get("modulusLength")); + auto modulus_length = TRY(modulus_length_value.to_u32(vm)); + + auto public_exponent_value = TRY(object.get("publicExponent")); + JS::GCPtr public_exponent; + + if (!public_exponent_value.is_object() || !is(public_exponent_value.as_object())) + return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "Uint8Array"); + + public_exponent = static_cast(public_exponent_value.as_object()); + + return adopt_own(*new RsaKeyGenParams { { name }, modulus_length, big_integer_from_api_big_integer(public_exponent) }); +} + +JS::ThrowCompletionOr> RsaHashedKeyGenParams::from_value(JS::VM& vm, JS::Value value) +{ + auto& object = value.as_object(); + + auto name_value = TRY(object.get("name")); + auto name = TRY(name_value.to_string(vm)); + + auto modulus_length_value = TRY(object.get("modulusLength")); + auto modulus_length = TRY(modulus_length_value.to_u32(vm)); + + auto public_exponent_value = TRY(object.get("publicExponent")); + JS::GCPtr public_exponent; + + if (!public_exponent_value.is_object() || !is(public_exponent_value.as_object())) + return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "Uint8Array"); + + public_exponent = static_cast(public_exponent_value.as_object()); + + auto hash_value = TRY(object.get("hash")); + auto hash = Variant { 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(*new RsaHashedKeyGenParams { { { name }, modulus_length, big_integer_from_api_big_integer(public_exponent) }, hash.get() }); +} + +// https://w3c.github.io/webcrypto/#rsa-oaep-operations +WebIDL::ExceptionOr, JS::NonnullGCPtr>> RSAOAEP::generate_key(AlgorithmParams const& params, bool extractable, Vector const& key_usages) +{ + // 1. If usages contains an entry which is not "encrypt", "decrypt", "wrapKey" or "unwrapKey", then throw a SyntaxError. + for (auto const& usage : key_usages) { + if (usage != Bindings::KeyUsage::Encrypt && usage != Bindings::KeyUsage::Decrypt && usage != Bindings::KeyUsage::Wrapkey && usage != Bindings::KeyUsage::Unwrapkey) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + + // 2. Generate an RSA key pair, as defined in [RFC3447], with RSA modulus length equal to the modulusLength member of normalizedAlgorithm + // and RSA public exponent equal to the publicExponent member of normalizedAlgorithm. + // 3. If performing the operation results in an error, then throw an OperationError. + auto const& normalized_algorithm = static_cast(params); + auto key_pair = ::Crypto::PK::RSA::generate_key_pair(normalized_algorithm.modulus_length, normalized_algorithm.public_exponent); + + // 4. Let algorithm be a new RsaHashedKeyAlgorithm object. + auto algorithm = RsaHashedKeyAlgorithm::create(m_realm); + + // 5. Set the name attribute of algorithm to "RSA-OAEP". + algorithm->set_name("RSA-OAEP"_string); + + // 6. Set the modulusLength attribute of algorithm to equal the modulusLength member of normalizedAlgorithm. + algorithm->set_modulus_length(normalized_algorithm.modulus_length); + + // 7. Set the publicExponent attribute of algorithm to equal the publicExponent member of normalizedAlgorithm. + TRY(algorithm->set_public_exponent(normalized_algorithm.public_exponent)); + + // 8. Set the hash attribute of algorithm to equal the hash member of normalizedAlgorithm. + algorithm->set_hash(normalized_algorithm.hash); + + // 9. Let publicKey be a new CryptoKey representing the public key of the generated key pair. + auto public_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { key_pair.public_key }); + + // 10. Set the [[type]] internal slot of publicKey to "public" + public_key->set_type(Bindings::KeyType::Public); + + // 11. Set the [[algorithm]] internal slot of publicKey to algorithm. + public_key->set_algorithm(algorithm); + + // 12. Set the [[extractable]] internal slot of publicKey to true. + public_key->set_extractable(true); + + // 13. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages and [ "encrypt", "wrapKey" ]. + public_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Encrypt, Bindings::KeyUsage::Wrapkey } })); + + // 14. Let privateKey be a new CryptoKey representing the private key of the generated key pair. + auto private_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { key_pair.private_key }); + + // 15. Set the [[type]] internal slot of privateKey to "private" + private_key->set_type(Bindings::KeyType::Private); + + // 16. Set the [[algorithm]] internal slot of privateKey to algorithm. + private_key->set_algorithm(algorithm); + + // 17. Set the [[extractable]] internal slot of privateKey to extractable. + private_key->set_extractable(extractable); + + // 18. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "decrypt", "unwrapKey" ]. + private_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Decrypt, Bindings::KeyUsage::Unwrapkey } })); + + // 19. Let result be a new CryptoKeyPair dictionary. + // 20. Set the publicKey attribute of result to be publicKey. + // 21. Set the privateKey attribute of result to be privateKey. + // 22. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL]. + return Variant, JS::NonnullGCPtr> { CryptoKeyPair::create(m_realm, public_key, private_key) }; +} + WebIDL::ExceptionOr> PBKDF2::import_key(AlgorithmParams const&, Bindings::KeyFormat format, CryptoKey::InternalKeyData key_data, bool extractable, Vector const& key_usages) { // 1. If format is not "raw", throw a NotSupportedError @@ -85,7 +247,7 @@ WebIDL::ExceptionOr> PBKDF2::import_key(AlgorithmPar key->set_extractable(false); // 7. Let algorithm be a new KeyAlgorithm object. - auto algorithm = Bindings::KeyAlgorithm::create(m_realm); + auto algorithm = KeyAlgorithm::create(m_realm); // 8. Set the name attribute of algorithm to "PBKDF2". algorithm->set_name("PBKDF2"_string); diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index b8075b9696..0ab4297a34 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -18,12 +19,11 @@ namespace Web::Crypto { -using KeyDataType = Variant, Bindings::JsonWebKey>; using AlgorithmIdentifier = Variant, String>; using HashAlgorithmIdentifier = AlgorithmIdentifier; +using KeyDataType = Variant, Bindings::JsonWebKey>; // https://w3c.github.io/webcrypto/#algorithm-overview - struct AlgorithmParams { String name; @@ -39,6 +39,22 @@ struct PBKDF2Params : public AlgorithmParams { static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); }; +// https://w3c.github.io/webcrypto/#dfn-RsaKeyGenParams +struct RsaKeyGenParams : public AlgorithmParams { + u32 modulus_length; + // NOTE that the raw data is going to be in Big Endian u8[] format + ::Crypto::UnsignedBigInteger public_exponent; + + static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); +}; + +// https://w3c.github.io/webcrypto/#dfn-RsaHashedKeyGenParams +struct RsaHashedKeyGenParams : public RsaKeyGenParams { + HashAlgorithmIdentifier hash; + + static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); +}; + class AlgorithmMethods { public: virtual ~AlgorithmMethods(); @@ -69,6 +85,19 @@ protected: JS::Realm& m_realm; }; +class RSAOAEP : public AlgorithmMethods { +public: + virtual WebIDL::ExceptionOr, JS::NonnullGCPtr>> generate_key(AlgorithmParams const&, bool, Vector const&) override; + + static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new RSAOAEP(realm)); } + +private: + explicit RSAOAEP(JS::Realm& realm) + : AlgorithmMethods(realm) + { + } +}; + class PBKDF2 : public AlgorithmMethods { public: virtual WebIDL::ExceptionOr> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector const&) override; diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoBindings.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoBindings.cpp deleted file mode 100644 index a04c8e5da0..0000000000 --- a/Userland/Libraries/LibWeb/Crypto/CryptoBindings.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2023, stelar7 - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Web::Bindings { - -JS_DEFINE_ALLOCATOR(KeyAlgorithm); - -JS::NonnullGCPtr KeyAlgorithm::create(JS::Realm& realm) -{ - return realm.heap().allocate(realm, realm); -} - -KeyAlgorithm::KeyAlgorithm(JS::Realm& realm) - : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype()) -{ -} - -void KeyAlgorithm::initialize(JS::Realm& realm) -{ - define_native_accessor(realm, "name", name_getter, {}, JS::Attribute::Enumerable | JS::Attribute::Configurable); - Base::initialize(realm); -} - -static JS::ThrowCompletionOr impl_from(JS::VM& vm) -{ - auto this_value = vm.this_value(); - JS::Object* this_object = nullptr; - if (this_value.is_nullish()) - this_object = &vm.current_realm()->global_object(); - else - this_object = TRY(this_value.to_object(vm)); - - if (!is(this_object)) - return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "KeyAlgorithm"); - return static_cast(this_object); -} - -JS_DEFINE_NATIVE_FUNCTION(KeyAlgorithm::name_getter) -{ - auto* impl = TRY(impl_from(vm)); - auto name = TRY(throw_dom_exception_if_needed(vm, [&] { return impl->name(); })); - return JS::PrimitiveString::create(vm, name); -} - -} diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoBindings.h b/Userland/Libraries/LibWeb/Crypto/CryptoBindings.h index edf766c6f0..437f308714 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoBindings.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoBindings.h @@ -44,25 +44,4 @@ struct JsonWebKey { Optional k; }; -// https://w3c.github.io/webcrypto/#key-algorithm-dictionary -class KeyAlgorithm : public JS::Object { - JS_OBJECT(KeyAlgorithm, Object); - JS_DECLARE_ALLOCATOR(KeyAlgorithm); - -public: - static JS::NonnullGCPtr create(JS::Realm&); - virtual ~KeyAlgorithm() override = default; - - String const& name() const { return m_name; } - void set_name(String name) { m_name = move(name); } - -private: - KeyAlgorithm(JS::Realm&); - virtual void initialize(JS::Realm&) override; - - JS_DECLARE_NATIVE_FUNCTION(name_getter); - - String m_name; -}; - -}; +} diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoKey.h b/Userland/Libraries/LibWeb/Crypto/CryptoKey.h index a601f556bb..66d76e0c17 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoKey.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoKey.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -20,7 +21,7 @@ class CryptoKey final : public Bindings::PlatformObject { JS_DECLARE_ALLOCATOR(CryptoKey); public: - using InternalKeyData = Variant; + using InternalKeyData = Variant, ::Crypto::PK::RSAPrivateKey<>>; [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&, InternalKeyData); diff --git a/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp new file mode 100644 index 0000000000..0d95ee4143 --- /dev/null +++ b/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023, stelar7 + * Copyright (c) 2024, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Web::Crypto { + +JS_DEFINE_ALLOCATOR(KeyAlgorithm); +JS_DEFINE_ALLOCATOR(RsaKeyAlgorithm); +JS_DEFINE_ALLOCATOR(RsaHashedKeyAlgorithm); + +template +static JS::ThrowCompletionOr impl_from(JS::VM& vm, StringView Name) +{ + auto this_value = vm.this_value(); + JS::Object* this_object = nullptr; + if (this_value.is_nullish()) + this_object = &vm.current_realm()->global_object(); + else + this_object = TRY(this_value.to_object(vm)); + + if (!is(this_object)) + return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, Name); + return static_cast(this_object); +} + +JS::NonnullGCPtr KeyAlgorithm::create(JS::Realm& realm) +{ + return realm.heap().allocate(realm, realm); +} + +KeyAlgorithm::KeyAlgorithm(JS::Realm& realm) + : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype()) + , m_realm(realm) +{ +} + +void KeyAlgorithm::initialize(JS::Realm& realm) +{ + define_native_accessor(realm, "name", name_getter, {}, JS::Attribute::Enumerable | JS::Attribute::Configurable); + Base::initialize(realm); +} + +JS_DEFINE_NATIVE_FUNCTION(KeyAlgorithm::name_getter) +{ + auto* impl = TRY(impl_from(vm, "KeyAlgorithm"sv)); + auto name = TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return impl->name(); })); + return JS::PrimitiveString::create(vm, name); +} + +JS::NonnullGCPtr RsaKeyAlgorithm::create(JS::Realm& realm) +{ + return realm.heap().allocate(realm, realm); +} + +RsaKeyAlgorithm::RsaKeyAlgorithm(JS::Realm& realm) + : KeyAlgorithm(realm) + , m_public_exponent(MUST(JS::Uint8Array::create(realm, 0))) +{ +} + +void RsaKeyAlgorithm::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + + define_native_accessor(realm, "modulusLength", modulus_length_getter, {}, JS::Attribute::Enumerable | JS::Attribute::Configurable); + define_native_accessor(realm, "publicExponent", public_exponent_getter, {}, JS::Attribute::Enumerable | JS::Attribute::Configurable); +} + +void RsaKeyAlgorithm::visit_edges(Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_public_exponent); +} + +WebIDL::ExceptionOr RsaKeyAlgorithm::set_public_exponent(::Crypto::UnsignedBigInteger exponent) +{ + static_assert(AK::HostIsLittleEndian, "This code assumes a little endian host"); + + auto& realm = this->realm(); + auto& vm = this->vm(); + + auto bytes = TRY_OR_THROW_OOM(vm, ByteBuffer::create_uninitialized(exponent.trimmed_byte_length())); + + bool const strip_leading_zeroes = true; + auto data_size = exponent.export_data(bytes.span(), strip_leading_zeroes); + + // The BigInteger typedef from the WebCrypto spec requires the bytes in the Uint8Array be ordered in Big Endian + + Vector byte_swapped_data; + byte_swapped_data.ensure_capacity(data_size); + for (size_t i = 0; i < data_size; ++i) + byte_swapped_data.append(bytes[data_size - i - 1]); + + m_public_exponent = TRY(JS::Uint8Array::create(realm, byte_swapped_data.size())); + m_public_exponent->viewed_array_buffer()->buffer().overwrite(0, byte_swapped_data.data(), byte_swapped_data.size()); + + return {}; +} + +JS_DEFINE_NATIVE_FUNCTION(RsaKeyAlgorithm::modulus_length_getter) +{ + auto* impl = TRY(impl_from(vm, "RsaKeyAlgorithm"sv)); + return JS::Value(impl->modulus_length()); +} + +JS_DEFINE_NATIVE_FUNCTION(RsaKeyAlgorithm::public_exponent_getter) +{ + auto* impl = TRY(impl_from(vm, "RsaKeyAlgorithm"sv)); + return impl->public_exponent(); +} + +JS::NonnullGCPtr RsaHashedKeyAlgorithm::create(JS::Realm& realm) +{ + return realm.heap().allocate(realm, realm); +} + +RsaHashedKeyAlgorithm::RsaHashedKeyAlgorithm(JS::Realm& realm) + : RsaKeyAlgorithm(realm) + , m_hash(String {}) +{ +} + +void RsaHashedKeyAlgorithm::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + + define_native_accessor(realm, "hash", hash_getter, {}, JS::Attribute::Enumerable | JS::Attribute::Configurable); +} + +JS_DEFINE_NATIVE_FUNCTION(RsaHashedKeyAlgorithm::hash_getter) +{ + auto* impl = TRY(impl_from(vm, "RsaHashedKeyAlgorithm"sv)); + auto hash = TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return impl->hash(); })); + return hash.visit( + [&](String const& hash_string) -> JS::Value { + return JS::PrimitiveString::create(vm, hash_string); + }, + [&](JS::Handle const& hash) -> JS::Value { + return hash; + }); +} + +} diff --git a/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.h new file mode 100644 index 0000000000..857ef71525 --- /dev/null +++ b/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023, stelar7 + * Copyright (c) 2024, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Web::Crypto { + +// https://w3c.github.io/webcrypto/#key-algorithm-dictionary +class KeyAlgorithm : public JS::Object { + JS_OBJECT(KeyAlgorithm, Object); + JS_DECLARE_ALLOCATOR(KeyAlgorithm); + +public: + static JS::NonnullGCPtr create(JS::Realm&); + virtual ~KeyAlgorithm() override = default; + + String const& name() const { return m_name; } + void set_name(String name) { m_name = move(name); } + + JS::Realm& realm() const { return m_realm; } + +protected: + KeyAlgorithm(JS::Realm&); + + virtual void initialize(JS::Realm&) override; + +private: + JS_DECLARE_NATIVE_FUNCTION(name_getter); + + String m_name; + JS::Realm& m_realm; +}; + +// https://w3c.github.io/webcrypto/#RsaKeyAlgorithm-dictionary +class RsaKeyAlgorithm : public KeyAlgorithm { + JS_OBJECT(RsaKeyAlgorithm, KeyAlgorithm); + JS_DECLARE_ALLOCATOR(RsaKeyAlgorithm); + +public: + static JS::NonnullGCPtr create(JS::Realm&); + + virtual ~RsaKeyAlgorithm() override = default; + + u32 modulus_length() const { return m_modulus_length; } + void set_modulus_length(u32 modulus_length) { m_modulus_length = modulus_length; } + + JS::NonnullGCPtr public_exponent() const { return m_public_exponent; } + void set_public_exponent(JS::NonnullGCPtr public_exponent) { m_public_exponent = public_exponent; } + WebIDL::ExceptionOr set_public_exponent(::Crypto::UnsignedBigInteger); + +protected: + RsaKeyAlgorithm(JS::Realm&); + + virtual void initialize(JS::Realm&) override; + virtual void visit_edges(Visitor&) override; + +private: + JS_DECLARE_NATIVE_FUNCTION(modulus_length_getter); + JS_DECLARE_NATIVE_FUNCTION(public_exponent_getter); + + u32 m_modulus_length { 0 }; + JS::NonnullGCPtr m_public_exponent; +}; + +// https://w3c.github.io/webcrypto/#RsaHashedKeyAlgorithm-dictionary +class RsaHashedKeyAlgorithm : public RsaKeyAlgorithm { + JS_OBJECT(RsaHashedKeyAlgorithm, RsaKeyAlgorithm); + JS_DECLARE_ALLOCATOR(RsaHashedKeyAlgorithm); + +public: + static JS::NonnullGCPtr create(JS::Realm&); + + virtual ~RsaHashedKeyAlgorithm() override = default; + + HashAlgorithmIdentifier const& hash() const { return m_hash; } + void set_hash(HashAlgorithmIdentifier hash) { m_hash = move(hash); } + +protected: + RsaHashedKeyAlgorithm(JS::Realm&); + + virtual void initialize(JS::Realm&) override; + +private: + JS_DECLARE_NATIVE_FUNCTION(hash_getter); + + HashAlgorithmIdentifier m_hash; +}; + +} diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index 1ba79fe7e3..00c3b90e3e 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -337,6 +337,10 @@ SubtleCrypto::SupportedAlgorithmsMap SubtleCrypto::supported_algorithms() // FIXME: define_an_algorithm("deriveBits"_string, "PBKDF2"_string, "Pbkdf2Params"_string); // FIXME: define_an_algorithm("get key length"_string, "PBKDF2"_string, ""_string); + // https://w3c.github.io/webcrypto/#rsa-oaep + define_an_algorithm("generateKey"_string, "RSA-OAEP"_string); + // FIXME: encrypt, decrypt, importKey, exportKey + return internal_object; }