LibWeb: Implement skeleton of SubtleCrypto.deriveKey

This commit is contained in:
stelar7 2024-03-27 20:51:18 +01:00 committed by Andreas Kling
parent abf55fe33d
commit 9f3c3925e0
Notes: sideshowbarker 2024-07-17 00:57:24 +09:00
5 changed files with 101 additions and 5 deletions

View file

@ -1339,12 +1339,14 @@ WebIDL::ExceptionOr<JS::Value> ED25519::verify([[maybe_unused]] AlgorithmParams
return JS::Value(result);
}
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> PBKDF2::derive_bits(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, u32 length)
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> PBKDF2::derive_bits(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, Optional<u32> length_optional)
{
auto& realm = m_realm;
auto const& normalized_algorithm = static_cast<PBKDF2Params const&>(params);
// 1. If length is null or zero, or is not a multiple of 8, then throw an OperationError.
auto length = length_optional.value_or(0);
if (length == 0 || length % 8 != 0)
return WebIDL::OperationError::create(realm, "Length must be greater than 0 and divisible by 8"_fly_string);

View file

@ -177,7 +177,7 @@ public:
return WebIDL::NotSupportedError::create(m_realm, "digest is not supported"_fly_string);
}
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> derive_bits(AlgorithmParams const&, JS::NonnullGCPtr<CryptoKey>, u32)
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> derive_bits(AlgorithmParams const&, JS::NonnullGCPtr<CryptoKey>, Optional<u32>)
{
return WebIDL::NotSupportedError::create(m_realm, "deriveBits is not supported"_fly_string);
}
@ -235,7 +235,7 @@ private:
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;
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> derive_bits(AlgorithmParams const&, JS::NonnullGCPtr<CryptoKey>, u32) override;
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> derive_bits(AlgorithmParams const&, JS::NonnullGCPtr<CryptoKey>, Optional<u32>) override;
virtual WebIDL::ExceptionOr<JS::Value> get_key_length(AlgorithmParams const&) override;
static NonnullOwnPtr<AlgorithmMethods> create(JS::Realm& realm) { return adopt_own(*new PBKDF2(realm)); }

View file

@ -631,6 +631,99 @@ JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> SubtleCrypto::derive_bits(A
return verify_cast<JS::Promise>(*promise->promise());
}
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> SubtleCrypto::derive_key(AlgorithmIdentifier algorithm, JS::NonnullGCPtr<CryptoKey> base_key, AlgorithmIdentifier derived_key_type, bool extractable, Vector<Bindings::KeyUsage> key_usages)
{
auto& realm = this->realm();
auto& vm = this->vm();
// 1. Let algorithm, baseKey, derivedKeyType, extractable and usages be the algorithm, baseKey, derivedKeyType, extractable and keyUsages parameters passed to the deriveKey() method, respectively.
// 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "deriveBits".
auto normalized_algorithm = normalize_an_algorithm(realm, algorithm, "deriveBits"_string);
// 3. 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());
// 4. Let normalizedDerivedKeyAlgorithmImport be the result of normalizing an algorithm, with alg set to derivedKeyType and op set to "importKey".
auto normalized_derived_key_algorithm_import = normalize_an_algorithm(realm, derived_key_type, "importKey"_string);
// 5. If an error occurred, return a Promise rejected with normalizedDerivedKeyAlgorithmImport.
if (normalized_derived_key_algorithm_import.is_error())
return WebIDL::create_rejected_promise_from_exception(realm, normalized_derived_key_algorithm_import.release_error());
// 6. Let normalizedDerivedKeyAlgorithmLength be the result of normalizing an algorithm, with alg set to derivedKeyType and op set to "get key length".
auto normalized_derived_key_algorithm_length = normalize_an_algorithm(realm, derived_key_type, "get key length"_string);
// 7. If an error occurred, return a Promise rejected with normalizedDerivedKeyAlgorithmLength.
if (normalized_derived_key_algorithm_length.is_error())
return WebIDL::create_rejected_promise_from_exception(realm, normalized_derived_key_algorithm_length.release_error());
// 8. Let promise be a new Promise.
auto promise = WebIDL::create_promise(realm);
// 9. Return promise and perform the remaining steps in parallel.
Platform::EventLoopPlugin::the().deferred_invoke([&realm, &vm, normalized_algorithm = normalized_algorithm.release_value(), promise, normalized_derived_key_algorithm_import = normalized_derived_key_algorithm_import.release_value(), normalized_derived_key_algorithm_length = normalized_derived_key_algorithm_length.release_value(), base_key = move(base_key), extractable, key_usages = move(key_usages)]() -> void {
HTML::TemporaryExecutionContext context(Bindings::host_defined_environment_settings_object(realm), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
// 10. If the following steps or referenced procedures say to throw an error, reject promise with the returned error and then terminate the algorithm.
// 11. If the name member of normalizedAlgorithm is not equal to the name attribute of the [[algorithm]] internal slot of baseKey then throw an InvalidAccessError.
if (normalized_algorithm.parameter->name != base_key->algorithm_name()) {
WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Algorithm mismatch"_fly_string));
return;
}
// 12. If the [[usages]] internal slot of baseKey does not contain an entry that is "deriveKey", then throw an InvalidAccessError.
if (!base_key->internal_usages().contains_slow(Bindings::KeyUsage::Derivekey)) {
WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Key does not support deriving keys"_fly_string));
return;
}
// 13. Let length be the result of performing the get key length algorithm specified by normalizedDerivedKeyAlgorithmLength using derivedKeyType.
auto length_result = normalized_derived_key_algorithm_length.methods->get_key_length(*normalized_derived_key_algorithm_length.parameter);
if (length_result.is_error()) {
WebIDL::reject_promise(realm, promise, Bindings::dom_exception_to_throw_completion(realm.vm(), length_result.release_error()).release_value().value());
return;
}
auto length_raw_value = length_result.release_value();
Optional<u32> length = {};
if (length_raw_value.is_number()) {
auto maybe_length = length_raw_value.to_u32(vm);
if (!maybe_length.has_value()) {
WebIDL::reject_promise(realm, promise, maybe_length.release_error().release_value().value());
return;
}
length = maybe_length.value();
}
// 14. Let secret be the result of performing the derive bits operation specified by normalizedAlgorithm using key, algorithm and length.
auto secret = normalized_algorithm.methods->derive_bits(*normalized_algorithm.parameter, base_key, length);
if (secret.is_error()) {
WebIDL::reject_promise(realm, promise, Bindings::dom_exception_to_throw_completion(realm.vm(), secret.release_error()).release_value().value());
return;
}
// 15. Let result be the result of performing the import key operation specified by normalizedDerivedKeyAlgorithmImport using "raw" as format, secret as keyData, derivedKeyType as algorithm and using extractable and usages.
auto result = normalized_derived_key_algorithm_import.methods->import_key(*normalized_derived_key_algorithm_import.parameter, Bindings::KeyFormat::Raw, secret.release_value()->buffer(), extractable, key_usages);
if (result.is_error()) {
WebIDL::reject_promise(realm, promise, Bindings::dom_exception_to_throw_completion(realm.vm(), result.release_error()).release_value().value());
return;
}
// 16. If the [[type]] internal slot of result is "secret" or "private" and usages is empty, then throw a SyntaxError.
if ((result.release_value()->type() == Bindings::KeyType::Secret || result.release_value()->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;
}
// 17. Resolve promise with result.
WebIDL::resolve_promise(realm, promise, result.release_value());
});
return verify_cast<JS::Promise>(*promise->promise());
}
SupportedAlgorithmsMap& supported_algorithms_internal()
{
static SupportedAlgorithmsMap s_supported_algorithms;

View file

@ -36,6 +36,7 @@ public:
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> generate_key(AlgorithmIdentifier algorithm, bool extractable, Vector<Bindings::KeyUsage> key_usages);
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> derive_bits(AlgorithmIdentifier algorithm, JS::NonnullGCPtr<CryptoKey> base_key, u32 length);
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> derive_key(AlgorithmIdentifier algorithm, JS::NonnullGCPtr<CryptoKey> base_key, AlgorithmIdentifier derived_key_type, bool extractable, Vector<Bindings::KeyUsage> key_usages);
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> import_key(Bindings::KeyFormat format, KeyDataType key_data, AlgorithmIdentifier algorithm, bool extractable, Vector<Bindings::KeyUsage> key_usages);
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> export_key(Bindings::KeyFormat format, JS::NonnullGCPtr<CryptoKey> key);

View file

@ -52,8 +52,8 @@ interface SubtleCrypto {
Promise<any> digest(AlgorithmIdentifier algorithm, BufferSource data);
Promise<any> generateKey(AlgorithmIdentifier algorithm, boolean extractable, sequence<KeyUsage> keyUsages);
// FIXME: Promise<any> deriveKey(AlgorithmIdentifier algorithm, CryptoKey baseKey, AlgorithmIdentifier derivedKeyType, boolean extractable, sequence<KeyUsage> keyUsages );
Promise<any> generateKey(AlgorithmIdentifier algorithm, boolean extractable, sequence<KeyUsage> keyUsages);
Promise<any> deriveKey(AlgorithmIdentifier algorithm, CryptoKey baseKey, AlgorithmIdentifier derivedKeyType, boolean extractable, sequence<KeyUsage> keyUsages);
Promise<ArrayBuffer> deriveBits(AlgorithmIdentifier algorithm, CryptoKey baseKey, unsigned long length);
Promise<CryptoKey> importKey(KeyFormat format, (BufferSource or JsonWebKey) keyData, AlgorithmIdentifier algorithm, boolean extractable, sequence<KeyUsage> keyUsages);