mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-04 05:20:30 +00:00
LibWeb: Implement import_key
for PBKDF2
Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
This commit is contained in:
parent
7bc7e2a48f
commit
423e77d5c3
Notes:
sideshowbarker
2024-07-17 02:21:14 +09:00
Author: https://github.com/stelar7 Commit: https://github.com/SerenityOS/serenity/commit/423e77d5c3 Pull-request: https://github.com/SerenityOS/serenity/pull/22346 Reviewed-by: https://github.com/ADKaster
3 changed files with 141 additions and 1 deletions
|
@ -184,6 +184,88 @@ JS::NonnullGCPtr<JS::Promise> SubtleCrypto::digest(AlgorithmIdentifier const& al
|
|||
return verify_cast<JS::Promise>(*promise->promise());
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-importKey
|
||||
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> SubtleCrypto::import_key(Bindings::KeyFormat format, KeyDataType key_data, AlgorithmIdentifier algorithm, bool extractable, Vector<Bindings::KeyUsage> key_usages)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// 1. Let format, algorithm, extractable and usages, be the format, algorithm, extractable
|
||||
// and key_usages parameters passed to the importKey() method, respectively.
|
||||
|
||||
Variant<ByteBuffer, Bindings::JsonWebKey, Empty> real_key_data;
|
||||
// 2. If format is equal to the string "raw", "pkcs8", or "spki":
|
||||
if (format == Bindings::KeyFormat::Raw || format == Bindings::KeyFormat::Pkcs8 || format == Bindings::KeyFormat::Spki) {
|
||||
// 1. If the keyData parameter passed to the importKey() method is a JsonWebKey dictionary, throw a TypeError.
|
||||
if (key_data.has<Bindings::JsonWebKey>()) {
|
||||
return realm.vm().throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BufferSource");
|
||||
}
|
||||
|
||||
// 2. Let keyData be the result of getting a copy of the bytes held by the keyData parameter passed to the importKey() method.
|
||||
real_key_data = MUST(WebIDL::get_buffer_source_copy(*key_data.get<JS::Handle<WebIDL::BufferSource>>()->raw_object()));
|
||||
}
|
||||
|
||||
if (format == Bindings::KeyFormat::Jwk) {
|
||||
// 1. If the keyData parameter passed to the importKey() method is not a JsonWebKey dictionary, throw a TypeError.
|
||||
if (!key_data.has<Bindings::JsonWebKey>()) {
|
||||
return realm.vm().throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "JsonWebKey");
|
||||
}
|
||||
|
||||
// 2. Let keyData be the keyData parameter passed to the importKey() method.
|
||||
real_key_data = key_data.get<Bindings::JsonWebKey>();
|
||||
}
|
||||
|
||||
// NOTE: The spec jumps to 5 here for some reason?
|
||||
// 5. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "importKey".
|
||||
auto normalized_algorithm = normalize_an_algorithm(algorithm, "importKey"_string);
|
||||
|
||||
// 6. If an error occurred, return a Promise rejected with normalizedAlgorithm.
|
||||
if (normalized_algorithm.is_error()) {
|
||||
auto promise = WebIDL::create_rejected_promise(realm, normalized_algorithm.release_error().release_value().value());
|
||||
return verify_cast<JS::Promise>(*promise->promise());
|
||||
}
|
||||
|
||||
// 7. Let promise be a new Promise.
|
||||
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 {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
auto result = maybe_result.release_value();
|
||||
|
||||
// 11. If the [[type]] internal slot of result is "secret" or "private" and usages is empty, then throw a SyntaxError.
|
||||
if ((result->type() == Bindings::KeyType::Secret || result->type() == Bindings::KeyType::Private) && key_usages.is_empty()) {
|
||||
WebIDL::reject_promise(realm, promise, WebIDL::SyntaxError::create(realm, "usages must not be empty"_fly_string));
|
||||
return;
|
||||
}
|
||||
|
||||
// 12. Set the [[extractable]] internal slot of result to extractable.
|
||||
result->set_extractable(extractable);
|
||||
|
||||
// 13. Set the [[usages]] internal slot of result to the normalized value of usages.
|
||||
// FIXME: result->set_usages(key_usages);
|
||||
|
||||
// 14. Resolve promise with result.
|
||||
WebIDL::resolve_promise(realm, promise, result);
|
||||
});
|
||||
|
||||
return verify_cast<JS::Promise>(*promise->promise());
|
||||
}
|
||||
|
||||
SubtleCrypto::SupportedAlgorithmsMap& SubtleCrypto::supported_algorithms_internal()
|
||||
{
|
||||
static SubtleCrypto::SupportedAlgorithmsMap s_supported_algorithms;
|
||||
|
@ -227,6 +309,11 @@ SubtleCrypto::SupportedAlgorithmsMap SubtleCrypto::supported_algorithms()
|
|||
define_an_algorithm("digest"_string, "SHA-384"_string, ""_string);
|
||||
define_an_algorithm("digest"_string, "SHA-512"_string, ""_string);
|
||||
|
||||
// https://w3c.github.io/webcrypto/#pbkdf2
|
||||
define_an_algorithm("importKey"_string, "PBKDF2"_string, ""_string);
|
||||
// FIXME: define_an_algorithm("deriveBits"_string, "PBKDF2"_string, "Pbkdf2Params"_string);
|
||||
// FIXME: define_an_algorithm("get key length"_string, "PBKDF2"_string, ""_string);
|
||||
|
||||
return internal_object;
|
||||
}
|
||||
|
||||
|
@ -245,4 +332,47 @@ void SubtleCrypto::define_an_algorithm(String op, String algorithm, String type)
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2023, stelar7 <dudedbz@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -8,9 +9,12 @@
|
|||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/Bindings/SubtleCryptoPrototype.h>
|
||||
#include <LibWeb/Crypto/CryptoBindings.h>
|
||||
#include <LibWeb/Crypto/CryptoKey.h>
|
||||
|
||||
namespace Web::Crypto {
|
||||
|
||||
|
@ -19,6 +23,7 @@ class SubtleCrypto final : public 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>;
|
||||
|
||||
public:
|
||||
|
@ -27,6 +32,7 @@ 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);
|
||||
|
||||
private:
|
||||
explicit SubtleCrypto(JS::Realm&);
|
||||
|
@ -34,6 +40,8 @@ private:
|
|||
|
||||
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);
|
||||
|
||||
static SubtleCrypto::SupportedAlgorithmsMap& supported_algorithms_internal();
|
||||
static SubtleCrypto::SupportedAlgorithmsMap supported_algorithms();
|
||||
static void define_an_algorithm(String op, String algorithm, String type);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import <Crypto/CryptoKey.idl>
|
||||
|
||||
typedef (object or DOMString) AlgorithmIdentifier;
|
||||
|
||||
dictionary Algorithm {
|
||||
|
@ -54,7 +56,7 @@ interface SubtleCrypto {
|
|||
// FIXME: Promise<any> deriveKey(AlgorithmIdentifier algorithm, CryptoKey baseKey, AlgorithmIdentifier derivedKeyType, boolean extractable, sequence<KeyUsage> keyUsages );
|
||||
// FIXME: Promise<ArrayBuffer> deriveBits(AlgorithmIdentifier algorithm, CryptoKey baseKey, unsigned long length);
|
||||
|
||||
// FIXME: Promise<CryptoKey> importKey(KeyFormat format, (BufferSource or JsonWebKey) keyData, AlgorithmIdentifier algorithm, boolean extractable, sequence<KeyUsage> keyUsages);
|
||||
Promise<CryptoKey> importKey(KeyFormat format, (BufferSource or JsonWebKey) keyData, AlgorithmIdentifier algorithm, boolean extractable, sequence<KeyUsage> keyUsages);
|
||||
// FIXME: Promise<any> exportKey(KeyFormat format, CryptoKey key);
|
||||
|
||||
// FIXME: Promise<any> wrapKey(KeyFormat format, CryptoKey key, CryptoKey wrappingKey, AlgorithmIdentifier wrapAlgorithm);
|
||||
|
|
Loading…
Reference in a new issue