diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index 00fc33db5f4..f66072946ba 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -106,6 +106,11 @@ class AlgorithmMethods { public: virtual ~AlgorithmMethods(); + virtual WebIDL::ExceptionOr> encrypt(AlgorithmParams const&, JS::NonnullGCPtr, ByteBuffer const&) + { + return WebIDL::NotSupportedError::create(m_realm, "encrypt 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/CryptoKey.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoKey.cpp index c6f8582f314..57902c1e722 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoKey.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoKey.cpp @@ -70,6 +70,15 @@ void CryptoKey::set_usages(Vector usages) }); } +String CryptoKey::algorithm_name() const +{ + if (m_algorithm_name.is_empty()) { + auto name = MUST(m_algorithm->get("name")); + m_algorithm_name = MUST(name.to_string(vm())); + } + return m_algorithm_name; +} + JS::NonnullGCPtr CryptoKeyPair::create(JS::Realm& realm, JS::NonnullGCPtr public_key, JS::NonnullGCPtr private_key) { return realm.heap().allocate(realm, realm, public_key, private_key); diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoKey.h b/Userland/Libraries/LibWeb/Crypto/CryptoKey.h index 2b9fa2bc5b0..c118be95793 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoKey.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoKey.h @@ -44,6 +44,7 @@ public: void set_usages(Vector); InternalKeyData const& handle() const { return m_key_data; } + String algorithm_name() const; virtual StringView interface_name() const override { return "CryptoKey"sv; } virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord& record, bool for_storage, HTML::SerializationMemory&) override; @@ -63,6 +64,7 @@ private: Vector m_key_usages; InternalKeyData m_key_data; // [[handle]] + mutable String m_algorithm_name; }; // https://w3c.github.io/webcrypto/#ref-for-dfn-CryptoKeyPair-2 diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index 863583d6575..db14e38a8f6 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -119,6 +119,63 @@ WebIDL::ExceptionOr normalize_an_algorithm(JS:: return normalized_algorithm; } +// https://w3c.github.io/webcrypto/#dfn-SubtleCrypto-method-encrypt +JS::NonnullGCPtr SubtleCrypto::encrypt(AlgorithmIdentifier const& algorithm, JS::NonnullGCPtr key, 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 encrypt() method, respectively. + + // 2. Let data be the result of getting a copy of the bytes held by the data parameter passed to the encrypt() 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 "encrypt". + auto normalized_algorithm = normalize_an_algorithm(realm, algorithm, "encrypt"_string); + + // 4. 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()); + + // 5. Let promise be a new Promise. + auto promise = WebIDL::create_promise(realm); + + // 6. Return promise and perform the remaining steps in parallel. + + Platform::EventLoopPlugin::the().deferred_invoke([&realm, normalized_algorithm = normalized_algorithm.release_value(), promise, key, data = move(data)]() -> void { + HTML::TemporaryExecutionContext context(Bindings::host_defined_environment_settings_object(realm), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes); + // 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. 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; + } + + // 9. If the [[usages]] internal slot of key does not contain an entry that is "encrypt", then throw an InvalidAccessError. + if (!key->internal_usages().contains_slow(Bindings::KeyUsage::Encrypt)) { + WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Key does not support encryption"_fly_string)); + return; + } + + // 10. Let ciphertext be the result of performing the encrypt operation specified by normalizedAlgorithm using algorithm and key and with data as plaintext. + auto cipher_text = normalized_algorithm.methods->encrypt(*normalized_algorithm.parameter, key, data); + if (cipher_text.is_error()) { + WebIDL::reject_promise(realm, promise, Bindings::dom_exception_to_throw_completion(realm.vm(), cipher_text.release_error()).release_value().value()); + return; + } + + // 9. Resolve promise with ciphertext. + WebIDL::resolve_promise(realm, promise, cipher_text.release_value()); + }); + + return verify_cast(*promise->promise()); +} + // https://w3c.github.io/webcrypto/#dfn-SubtleCrypto-method-digest JS::NonnullGCPtr SubtleCrypto::digest(AlgorithmIdentifier const& algorithm, JS::Handle const& data) { diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h index 13c6b607333..d5dd04342fd 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.h @@ -27,6 +27,8 @@ public: virtual ~SubtleCrypto() override; + JS::NonnullGCPtr encrypt(AlgorithmIdentifier const& algorithm, JS::NonnullGCPtr key, JS::Handle const& data_parameter); + JS::NonnullGCPtr digest(AlgorithmIdentifier const& algorithm, JS::Handle const& data); JS::ThrowCompletionOr> generate_key(AlgorithmIdentifier algorithm, bool extractable, Vector key_usages); diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl index 27643a42b2c..efd4e313031 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.idl @@ -45,7 +45,7 @@ dictionary JsonWebKey // https://w3c.github.io/webcrypto/#subtlecrypto-interface [SecureContext,Exposed=(Window,Worker)] interface SubtleCrypto { - // FIXME: Promise encrypt(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data); + Promise encrypt(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data); // FIXME: Promise decrypt(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data); // FIXME: Promise sign(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data); // FIXME: Promise verify(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource signature, BufferSource data);