LibWeb: Implement encapsulateKey method for SubtleCrypto

None of the current algorithms supports the method but the future
post quantum algorithms will do so.
This commit is contained in:
Tete17 2025-11-27 19:16:27 +01:00 committed by Jelle Raaijmakers
parent 42f55c7c97
commit b36a702ad1
Notes: github-actions[bot] 2025-11-27 20:51:16 +00:00
4 changed files with 108 additions and 0 deletions

View file

@ -377,6 +377,11 @@ public:
return WebIDL::NotSupportedError::create(m_realm, "unwwrapKey is not supported"_utf16);
}
virtual WebIDL::ExceptionOr<GC::Ref<EncapsulatedBits>> encapsulate(AlgorithmParams const&, GC::Ref<CryptoKey>)
{
return WebIDL::NotSupportedError::create(m_realm, "encapsulate is not supported"_utf16);
}
static NonnullOwnPtr<AlgorithmMethods> create(JS::Realm& realm) { return adopt_own(*new AlgorithmMethods(realm)); }
protected:

View file

@ -1040,6 +1040,104 @@ GC::Ref<WebIDL::Promise> SubtleCrypto::unwrap_key(Bindings::KeyFormat format, Ke
return promise;
}
// https://wicg.github.io/webcrypto-modern-algos/#dfn-SubtleCrypto-method-encapsulateKey
GC::Ref<WebIDL::Promise> SubtleCrypto::encapsulate_key(AlgorithmIdentifier encapsulation_algorithm, GC::Ref<CryptoKey> encapsulation_key, AlgorithmIdentifier shared_key_algorithm, bool extractable, Vector<Bindings::KeyUsage> key_usages)
{
auto& realm = this->realm();
// 1. Let encapsulationAlgorithm, encapsulationKey, sharedKeyAlgorithm, extractable and usages be the
// encapsulationAlgorithm, encapsulationKey, sharedKeyAlgorithm, extractable and keyUsages parameters passed to
// the encapsulateKey() method, respectively.
// 2. Let normalizedEncapsulationAlgorithm be the result of normalizing an algorithm, with alg set to
// encapsulationAlgorithm and op set to "encapsulate".
auto maybe_normalized_encapsulation_algorithm = normalize_an_algorithm(realm, encapsulation_algorithm, "encapsulate"_string);
// 3. If an error occurred, return a Promise rejected with normalizedEncapsulationAlgorithm.
if (maybe_normalized_encapsulation_algorithm.is_error()) {
return WebIDL::create_rejected_promise_from_exception(realm, maybe_normalized_encapsulation_algorithm.exception());
}
auto normalized_encapsulation_algorithm = maybe_normalized_encapsulation_algorithm.release_value();
// 4. Let normalizedSharedKeyAlgorithm be the result of normalizing an algorithm, with alg
// set to sharedKeyAlgorithm and op set to "importKey".
auto maybe_normalized_shared_key_algorithm = normalize_an_algorithm(realm, shared_key_algorithm, "importKey"_string);
// 5. If an error occurred, return a Promise rejected with normalizedSharedKeyAlgorithm.
if (maybe_normalized_shared_key_algorithm.is_error()) {
return WebIDL::create_rejected_promise_from_exception(realm, maybe_normalized_shared_key_algorithm.exception());
}
auto normalized_shared_key_algorithm = maybe_normalized_shared_key_algorithm.release_value();
// 6. Let realm be the relevant realm of this.
// 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(GC::create_function(realm.heap(), [&realm, normalized_encapsulation_algorithm = move(normalized_encapsulation_algorithm), encapsulation_key = encapsulation_key, promise, normalized_shared_key_algorithm = move(normalized_shared_key_algorithm), extractable, usages = move(key_usages)]() mutable -> void {
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
// 9. If the following steps or referenced procedures say to throw an error, queue a global task on the crypto task
// source, given realm's global object, to reject promise with the returned error; and then terminate the algorithm.
// 10. If the name member of normalizedEncapsulationAlgorithm is not equal to the name attribute of the [[algorithm]]
// internal slot of encapsulationKey then throw an InvalidAccessError.
if (normalized_encapsulation_algorithm.parameter->name != encapsulation_key->algorithm_name()) {
WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Invalid encapsulation key algorithm"_utf16));
return;
}
// 11. If the [[usages]] internal slot of encapsulationKey does not contain an entry that is "encapsulateKey", then
// throw an InvalidAccessError.
if (encapsulation_key->internal_usages().contains_slow(Bindings::KeyUsage::Encapsulatekey)) {
WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Invalid encapsulation key usage"_utf16));
return;
}
// 12. Let encapsulatedBits be the result of performing the encapsulate operation specified by the [[algorithm]]
// internal slot of encapsulationKey using encapsulationKey.
auto maybe_encapsulated_bits = normalized_encapsulation_algorithm.methods->encapsulate(*normalized_encapsulation_algorithm.parameter, encapsulation_key);
if (maybe_encapsulated_bits.is_error()) {
WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), maybe_encapsulated_bits.release_error()).release_value());
return;
}
auto encapsulated_bits = maybe_encapsulated_bits.release_value();
// 13. Let sharedKey be the result of performing the import key operation specified by
// normalizedSharedKeyAlgorithm using "raw-secret" as format, the sharedKey field of encapsulatedBits as
// keyData, sharedKeyAlgorithm as algorithm and using extractable and usages.
auto maybe_shared_key = normalized_shared_key_algorithm.methods->import_key(
*normalized_shared_key_algorithm.parameter,
Bindings::KeyFormat::RawSecret,
encapsulated_bits->shared_key.value(),
extractable,
usages);
if (maybe_shared_key.is_error()) {
WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), maybe_shared_key.release_error()).release_value());
return;
}
auto shared_key = maybe_shared_key.release_value();
// 14. Let encapsulatedKey be a new EncapsulatedKey dictionary with sharedKey set to sharedKey and ciphertext set
// to the ciphertext field of encapsulatedBits.
auto encapsulated_key = EncapsulatedKey { shared_key, encapsulated_bits->ciphertext };
// 15. Queue a global task on the crypto task source, given realm's global object, to perform the remaining steps.
// 16. Let result be the result of converting encapsulatedKey to an ECMAScript Object in realm, as defined by [WebIDL].
auto maybe_result = encapsulated_key.to_object(realm);
if (maybe_result.is_error()) {
WebIDL::reject_promise(realm, promise, maybe_result.release_error().value());
return;
}
auto const result = maybe_result.release_value();
// 17. Resolve promise with result.
WebIDL::resolve_promise(realm, promise, result);
}));
return promise;
}
SupportedAlgorithmsMap& supported_algorithms_internal()
{
static SupportedAlgorithmsMap s_supported_algorithms;

View file

@ -43,6 +43,8 @@ public:
GC::Ref<WebIDL::Promise> wrap_key(Bindings::KeyFormat format, GC::Ref<CryptoKey> key, GC::Ref<CryptoKey> wrapping_key, AlgorithmIdentifier wrap_algorithm);
GC::Ref<WebIDL::Promise> unwrap_key(Bindings::KeyFormat format, KeyDataType wrapped_key, GC::Ref<CryptoKey> unwrapping_key, AlgorithmIdentifier unwrap_algorithm, AlgorithmIdentifier unwrapped_key_algorithm, bool extractable, Vector<Bindings::KeyUsage> key_usages);
GC::Ref<WebIDL::Promise> encapsulate_key(AlgorithmIdentifier encapsulation_algorithm, GC::Ref<CryptoKey> encapsulation_key, AlgorithmIdentifier shared_key_algorithm, bool extractable, Vector<Bindings::KeyUsage> key_usages);
private:
explicit SubtleCrypto(JS::Realm&);
virtual void initialize(JS::Realm&) override;

View file

@ -74,4 +74,7 @@ interface SubtleCrypto {
Promise<any> wrapKey(KeyFormat format, CryptoKey key, CryptoKey wrappingKey, AlgorithmIdentifier wrapAlgorithm);
Promise<CryptoKey> unwrapKey(KeyFormat format, BufferSource wrappedKey, CryptoKey unwrappingKey, AlgorithmIdentifier unwrapAlgorithm, AlgorithmIdentifier unwrappedKeyAlgorithm, boolean extractable, sequence<KeyUsage> keyUsages);
// https://wicg.github.io/webcrypto-modern-algos/#partial-subtlecrypto-interface
Promise<EncapsulatedKey> encapsulateKey(AlgorithmIdentifier encapsulationAlgorithm, CryptoKey encapsulationKey, AlgorithmIdentifier sharedKeyAlgorithm, boolean extractable, sequence<KeyUsage> keyUsages);
};