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