From 90ae6c9e2d86dd815bea36006f9e11a9e59a6f88 Mon Sep 17 00:00:00 2001 From: devgianlu Date: Fri, 13 Dec 2024 22:31:38 +0100 Subject: [PATCH] tmp --- Libraries/LibWeb/Crypto/CryptoAlgorithms.h | 10 + Libraries/LibWeb/Crypto/CryptoBindings.cpp | 142 +++++ Libraries/LibWeb/Crypto/CryptoBindings.h | 3 + Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 289 ++++++++++ Libraries/LibWeb/Crypto/SubtleCrypto.h | 3 + Libraries/LibWeb/Crypto/SubtleCrypto.idl | 4 +- .../wrapKey_unwrapKey.https.any.txt | 250 ++++++++ .../wrapKey_unwrapKey.https.any.html | 17 + .../wrapKey_unwrapKey.https.any.js | 536 ++++++++++++++++++ .../wrapKey_unwrapKey_vectors.js | 114 ++++ 10 files changed, 1366 insertions(+), 2 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey_vectors.js diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index 1bad8b7ca9979..9dd1954c78424 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -337,6 +337,16 @@ class AlgorithmMethods { return WebIDL::NotSupportedError::create(m_realm, "getKeyLength is not supported"_string); } + virtual WebIDL::ExceptionOr> wrap_key(AlgorithmParams const&, GC::Ref, ByteBuffer const&) + { + return WebIDL::NotSupportedError::create(m_realm, "wrapKey is not supported"_string); + } + + virtual WebIDL::ExceptionOr> unwrap_key(AlgorithmParams const&, GC::Ref, ByteBuffer const&) + { + return WebIDL::NotSupportedError::create(m_realm, "unwwrapKey is not supported"_string); + } + static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new AlgorithmMethods(realm)); } protected: diff --git a/Libraries/LibWeb/Crypto/CryptoBindings.cpp b/Libraries/LibWeb/Crypto/CryptoBindings.cpp index 2e7e7bde7fd7c..831a8bb97df55 100644 --- a/Libraries/LibWeb/Crypto/CryptoBindings.cpp +++ b/Libraries/LibWeb/Crypto/CryptoBindings.cpp @@ -1,15 +1,91 @@ /* * Copyright (c) 2024, Andrew Kaster + * Copyright (c) 2024, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include #include +#include #include #include +#include +#include namespace Web::Bindings { +#define JWK_PARSE_STRING_PROPERTY(name) \ + if (json_object.has_string(#name##sv)) { \ + key.name = TRY_OR_THROW_OOM(vm, String::from_byte_string(*json_object.get_byte_string(#name##sv))); \ + } + +JS::ThrowCompletionOr JsonWebKey::parse(JS::Realm& realm, ReadonlyBytes data) +{ + auto& vm = realm.vm(); + // 1. Let data be the sequence of bytes to be parsed. + + // 2. Let json be the Unicode string that results from interpreting data according to UTF-8. + // 3. Convert json to UTF-16. + auto json = TRY_OR_THROW_OOM(vm, String::from_utf8(data)); + + // 4. Let result be the object literal that results from executing the JSON.parse internal function + // in the context of a new global object, with text argument set to a JavaScript String containing json. + auto maybe_json_value = JsonValue::from_string(json); + if (maybe_json_value.is_error()) + return vm.throw_completion(JS::ErrorType::JsonMalformed); + + auto json_value = maybe_json_value.release_value(); + if (!json_value.is_object()) { + return vm.throw_completion("JSON value is not an object"_string); + } + + auto& json_object = json_value.as_object(); + + // 5. Let key be the result of converting result to the IDL dictionary type of JsonWebKey. + JsonWebKey key {}; + JWK_PARSE_STRING_PROPERTY(kty); + JWK_PARSE_STRING_PROPERTY(use); + JWK_PARSE_STRING_PROPERTY(alg); + JWK_PARSE_STRING_PROPERTY(crv); + JWK_PARSE_STRING_PROPERTY(x); + JWK_PARSE_STRING_PROPERTY(y); + JWK_PARSE_STRING_PROPERTY(d); + JWK_PARSE_STRING_PROPERTY(n); + JWK_PARSE_STRING_PROPERTY(e); + JWK_PARSE_STRING_PROPERTY(p); + JWK_PARSE_STRING_PROPERTY(q); + JWK_PARSE_STRING_PROPERTY(dp); + JWK_PARSE_STRING_PROPERTY(dq); + JWK_PARSE_STRING_PROPERTY(qi); + JWK_PARSE_STRING_PROPERTY(k); + + key.ext = json_object.get_bool("ext"sv); + + if (json_object.has_array("key_ops"sv)) { + auto key_ops = *json_object.get_array("key_ops"sv); + key.key_ops = Vector {}; + TRY_OR_THROW_OOM(vm, key.key_ops->try_ensure_capacity(key_ops.size())); + + TRY(key_ops.try_for_each([&key, &vm](auto value) -> JS::Completion { + key.key_ops->append(TRY_OR_THROW_OOM(vm, String::from_byte_string(value.as_string()))); + return {}; + })); + } + + if (json_object.has("oth"sv)) + TODO(); + + // 6. If the kty field of key is not defined, then throw a DataError. + if (!key.kty.has_value()) + return vm.throw_completion("kty field is not defined"_string); + + // 7. Return key. + return key; +} + +#undef JWK_PARSE_STRING_PROPERTY JS::ThrowCompletionOr> JsonWebKey::to_object(JS::Realm& realm) { @@ -78,4 +154,70 @@ JS::ThrowCompletionOr> JsonWebKey::to_object(JS::Realm& real return object; } +ErrorOr JsonWebKey::to_json() +{ + auto object = JsonObject {}; + + if (kty.has_value()) + object.set("kty", kty->to_byte_string()); + + if (use.has_value()) + object.set("use", use->to_byte_string()); + + if (key_ops.has_value()) { + JsonArray key_ops_array {}; + for (auto& key_usage : key_ops.value()) + TRY(key_ops_array.append(key_usage.to_byte_string())); + object.set("key_ops", move(key_ops_array)); + } + + if (alg.has_value()) + object.set("alg", alg->to_byte_string()); + + if (ext.has_value()) + object.set("ext", *ext); + + if (crv.has_value()) + object.set("crv", crv->to_byte_string()); + + if (x.has_value()) + object.set("x", x->to_byte_string()); + + if (y.has_value()) + object.set("y", y->to_byte_string()); + + if (d.has_value()) + object.set("d", d->to_byte_string()); + + if (n.has_value()) + object.set("n", n->to_byte_string()); + + if (e.has_value()) + object.set("e", e->to_byte_string()); + + if (p.has_value()) + object.set("p", p->to_byte_string()); + + if (q.has_value()) + object.set("q", q->to_byte_string()); + + if (dp.has_value()) + object.set("dp", dp->to_byte_string()); + + if (dq.has_value()) + object.set("dq", dq->to_byte_string()); + + if (qi.has_value()) + object.set("qi", qi->to_byte_string()); + + if (oth.has_value()) { + TODO(); + } + + if (k.has_value()) + object.set("k", k->to_byte_string()); + + return object; +} + } diff --git a/Libraries/LibWeb/Crypto/CryptoBindings.h b/Libraries/LibWeb/Crypto/CryptoBindings.h index 89e98fdff5c93..4b9262aad5b13 100644 --- a/Libraries/LibWeb/Crypto/CryptoBindings.h +++ b/Libraries/LibWeb/Crypto/CryptoBindings.h @@ -44,6 +44,9 @@ struct JsonWebKey { Optional k; JS::ThrowCompletionOr> to_object(JS::Realm&); + ErrorOr to_json(); // FIXME: not used? + + static JS::ThrowCompletionOr parse(JS::Realm& realm, ReadonlyBytes data); }; } diff --git a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index e032ae579cf88..cf283ec500d69 100644 --- a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -6,9 +6,11 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include +#include #include #include #include @@ -744,6 +746,293 @@ JS::ThrowCompletionOr> SubtleCrypto::derive_key(Algorit return promise; } +// https://w3c.github.io/webcrypto/#SubtleCrypto-method-wrapKey +JS::ThrowCompletionOr> SubtleCrypto::wrap_key(Bindings::KeyFormat format, GC::Ref key, GC::Ref wrapping_key, AlgorithmIdentifier algorithm) +{ + auto& realm = this->realm(); + // 1. Let format, key, wrappingKey and algorithm be the format, key, wrappingKey and wrapAlgorithm parameters passed to the wrapKey() method, respectively. + + StringView operation; + auto normalized_algorithm_or_error = [&]() -> WebIDL::ExceptionOr { + // 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "wrapKey". + auto normalized_algorithm_wrap_key_or_error = normalize_an_algorithm(realm, algorithm, "wrapKey"_string); + + // 3. If an error occurred, let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "encrypt". + // 4. If an error occurred, return a Promise rejected with normalizedAlgorithm. + if (normalized_algorithm_wrap_key_or_error.is_error()) { + auto normalized_algorithm_encrypt_or_error = normalize_an_algorithm(realm, algorithm, "encrypt"_string); + if (normalized_algorithm_encrypt_or_error.is_error()) + return normalized_algorithm_encrypt_or_error.release_error(); + + operation = "encrypt"sv; + return normalized_algorithm_encrypt_or_error.release_value(); + } else { + operation = "wrapKey"sv; + return normalized_algorithm_wrap_key_or_error.release_value(); + } + }(); + if (normalized_algorithm_or_error.is_error()) + return WebIDL::create_rejected_promise_from_exception(realm, normalized_algorithm_or_error.release_error()); + + auto normalized_algorithm = normalized_algorithm_or_error.release_value(); + + // 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(GC::create_function(realm.heap(), [&realm, normalized_algorithm = move(normalized_algorithm), promise, wrapping_key = move(wrapping_key), key = move(key), format, operation]() mutable -> void { + HTML::TemporaryExecutionContext context(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 wrappingKey then throw an InvalidAccessError. + if (normalized_algorithm.parameter->name != wrapping_key->algorithm_name()) { + WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Algorithm mismatch"_string)); + return; + } + + // 9. If the [[usages]] internal slot of wrappingKey does not contain an entry that is "wrapKey", then throw an InvalidAccessError. + if (!wrapping_key->internal_usages().contains_slow(Bindings::KeyUsage::Wrapkey)) { + WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Key does not support wrapping keys"_string)); + return; + } + + // 10. If the algorithm identified by the [[algorithm]] internal slot of key does not support the export key operation, then throw a NotSupportedError. + // Note: Handled by the base AlgorithmMethods implementation + + // 11. If the [[extractable]] internal slot of key is false, then throw an InvalidAccessError. + if (!key->extractable()) { + WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Key is not extractable"_string)); + return; + } + + // 12. Let key be the result of performing the export key operation specified the [[algorithm]] internal slot of key using key and format. + // NOTE: The spec does not mention we need to normalize this, but it's the only way we have to get to export_key. + auto& key_algorithm = verify_cast(*key->algorithm()); + auto normalized_key_algorithm = normalize_an_algorithm(realm, key_algorithm.name(), "exportKey"_string); + if (normalized_key_algorithm.is_error()) { + WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), normalized_key_algorithm.release_error()).release_value().value()); + return; + } + + auto key_data_or_error = normalized_key_algorithm.release_value().methods->export_key(format, key); + if (key_data_or_error.is_error()) { + WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), key_data_or_error.release_error()).release_value().value()); + return; + } + + auto key_data = key_data_or_error.release_value(); + + ByteBuffer bytes; + // 13. If format is equal to the strings "raw", "pkcs8", or "spki": + if (format == Bindings::KeyFormat::Raw || format == Bindings::KeyFormat::Pkcs8 || format == Bindings::KeyFormat::Spki) { + // Set bytes be set to key. + bytes = verify_cast(*key_data).buffer(); + } + + // If format is equal to the string "jwk": + else if (format == Bindings::KeyFormat::Jwk) { + // 1. Convert key to an ECMAScript Object, as specified in [WEBIDL], + // performing the conversion in the context of a new global object. + // 2. Let json be the result of representing key as a UTF-16 string conforming to the JSON grammar; + // for example, by executing the JSON.stringify algorithm specified in [ECMA-262] in the context of a new global object. + auto maybe_json = JS::JSONObject::stringify_impl(realm.vm(), key_data, JS::Value {}, JS::Value {}); + if (maybe_json.is_error()) { + WebIDL::reject_promise(realm, promise, maybe_json.release_error().release_value().value()); + return; + } + + // 3. Let bytes be the result of UTF-8 encoding json. + bytes = maybe_json.release_value()->to_byte_buffer(); + } else { + VERIFY_NOT_REACHED(); + } + + JS::Value result; + // 14. If normalizedAlgorithm supports the wrap key operation: + if (operation == "wrapKey") { + // Let result be the result of performing the wrap key operation specified by normalizedAlgorithm + // using algorithm, wrappingKey as key and bytes as plaintext. + auto result_or_error = normalized_algorithm.methods->wrap_key(*normalized_algorithm.parameter, wrapping_key, bytes); + if (result_or_error.is_error()) { + WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), result_or_error.release_error()).release_value().value()); + return; + } + + result = result_or_error.release_value(); + } + + // Otherwise, if normalizedAlgorithm supports the encrypt operation: + else if (operation == "encrypt") { + // Let result be the result of performing the encrypt operation specified by normalizedAlgorithm + // using algorithm, wrappingKey as key and bytes as plaintext. + auto result_or_error = normalized_algorithm.methods->encrypt(*normalized_algorithm.parameter, wrapping_key, bytes); + if (result_or_error.is_error()) { + WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), result_or_error.release_error()).release_value().value()); + return; + } + + result = result_or_error.release_value(); + } + + // Otherwise: + else { + // throw a NotSupportedError. + WebIDL::reject_promise(realm, promise, WebIDL::NotSupportedError::create(realm, "Algorithm does not support wrapping"_string)); + return; + } + + // 15. Resolve promise with result. + WebIDL::resolve_promise(realm, promise, result); + })); + + return promise; +} + +// https://w3c.github.io/webcrypto/#SubtleCrypto-method-unwrapKey +JS::ThrowCompletionOr> SubtleCrypto::unwrap_key(Bindings::KeyFormat format, KeyDataType wrapped_key, GC::Ref unwrapping_key, AlgorithmIdentifier algorithm, AlgorithmIdentifier unwrapped_key_algorithm, bool extractable, Vector key_usages) +{ + auto& realm = this->realm(); + // 1. Let format, unwrappingKey, algorithm, unwrappedKeyAlgorithm, extractable and usages, be the format, unwrappingKey, unwrapAlgorithm, + // unwrappedKeyAlgorithm, extractable and keyUsages parameters passed to the unwrapKey() method, respectively. + + // 2. Let wrappedKey be the result of getting a copy of the bytes held by the wrappedKey parameter passed to the unwrapKey() method. + auto real_wrapped_key = MUST(WebIDL::get_buffer_source_copy(*wrapped_key.get>()->raw_object())); + + StringView operation; + auto normalized_algorithm_or_error = [&]() -> WebIDL::ExceptionOr { + // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "unwrapKey". + auto normalized_algorithm_unwrap_key_or_error = normalize_an_algorithm(realm, algorithm, "unwrapKey"_string); + + // 4. If an error occurred, let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "decrypt". + // 5. If an error occurred, return a Promise rejected with normalizedAlgorithm. + if (normalized_algorithm_unwrap_key_or_error.is_error()) { + auto normalized_algorithm_decrypt_or_error = normalize_an_algorithm(realm, algorithm, "decrypt"_string); + if (normalized_algorithm_decrypt_or_error.is_error()) + return normalized_algorithm_decrypt_or_error.release_error(); + + operation = "decrypt"sv; + return normalized_algorithm_decrypt_or_error.release_value(); + } else { + operation = "unwrapKey"sv; + return normalized_algorithm_unwrap_key_or_error.release_value(); + } + }(); + if (normalized_algorithm_or_error.is_error()) + return WebIDL::create_rejected_promise_from_exception(realm, normalized_algorithm_or_error.release_error()); + + auto normalized_algorithm = normalized_algorithm_or_error.release_value(); + + // 6. Let normalizedKeyAlgorithm be the result of normalizing an algorithm, with alg set to unwrappedKeyAlgorithm and op set to "importKey". + auto normalized_key_algorithm_or_error = normalize_an_algorithm(realm, unwrapped_key_algorithm, "importKey"_string); + if (normalized_key_algorithm_or_error.is_error()) { + // 7. If an error occurred, return a Promise rejected with normalizedKeyAlgorithm. + return WebIDL::create_rejected_promise_from_exception(realm, normalized_key_algorithm_or_error.release_error()); + } + + auto normalized_key_algorithm = normalized_key_algorithm_or_error.release_value(); + + // 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(GC::create_function(realm.heap(), [&realm, normalized_algorithm = move(normalized_algorithm), promise, unwrapping_key = unwrapping_key, real_wrapped_key = move(real_wrapped_key), operation, format, extractable, key_usages = move(key_usages), normalized_key_algorithm = move(normalized_key_algorithm)]() mutable -> void { + HTML::TemporaryExecutionContext context(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 unwrappingKey then throw an InvalidAccessError. + if (normalized_algorithm.parameter->name != unwrapping_key->algorithm_name()) { + WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Algorithm mismatch"_string)); + return; + } + + // 12. If the [[usages]] internal slot of unwrappingKey does not contain an entry that is "unwrapKey", then throw an InvalidAccessError. + if (!unwrapping_key->internal_usages().contains_slow(Bindings::KeyUsage::Unwrapkey)) { + WebIDL::reject_promise(realm, promise, WebIDL::InvalidAccessError::create(realm, "Key does not support unwrapping keys"_string)); + return; + } + + auto key_or_error = [&]() -> WebIDL::ExceptionOr> { + // 13. If normalizedAlgorithm supports an unwrap key operation: + if (operation == "unwrapKey") { + // Let key be the result of performing the unwrap key operation specified by normalizedAlgorithm + // using algorithm, unwrappingKey as key and wrappedKey as ciphertext. + return normalized_algorithm.methods->unwrap_key(*normalized_algorithm.parameter, unwrapping_key, real_wrapped_key); + } + + // Otherwise, if normalizedAlgorithm supports a decrypt operation: + else if (operation == "decrypt") { + // Let key be the result of performing the decrypt operation specified by normalizedAlgorithm + // using algorithm, unwrappingKey as key and wrappedKey as ciphertext. + return normalized_algorithm.methods->decrypt(*normalized_algorithm.parameter, unwrapping_key, real_wrapped_key); + } + + // Otherwise: + else { + // throw a NotSupportedError. + return WebIDL::NotSupportedError::create(realm, "Algorithm does not support wrapping"_string); + } + }(); + if (key_or_error.is_error()) { + WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), key_or_error.release_error()).release_value().value()); + return; + } + + auto key = key_or_error.release_value(); + + Variant bytes; + + // 14. If format is equal to the strings "raw", "pkcs8", or "spki": + if (format == Bindings::KeyFormat::Raw || format == Bindings::KeyFormat::Pkcs8 || format == Bindings::KeyFormat::Spki) { + // Set bytes be set to key. + bytes = key->buffer(); + } + + // If format is equal to the string "jwk": + else if (format == Bindings::KeyFormat::Jwk) { + // Let bytes be the result of executing the parse a JWK algorithm, with key as the data to be parsed. + auto maybe_parsed = Bindings::JsonWebKey::parse(realm, key->buffer()); + if (maybe_parsed.is_error()) { + WebIDL::reject_promise(realm, promise, maybe_parsed.release_error().release_value().value()); + return; + } + + bytes = maybe_parsed.release_value(); + } else { + VERIFY_NOT_REACHED(); + } + + // 15. Let result be the result of performing the import key operation specified by normalizedKeyAlgorithm + // using unwrappedKeyAlgorithm as algorithm, format, usages and extractable and with bytes as keyData. + auto result_or_error = normalized_key_algorithm.methods->import_key(*normalized_key_algorithm.parameter, format, bytes.downcast(), extractable, key_usages); + if (result_or_error.is_error()) { + WebIDL::reject_promise(realm, promise, Bindings::exception_to_throw_completion(realm.vm(), result_or_error.release_error()).release_value().value()); + return; + } + + auto result = result_or_error.release_value(); + + // 16. 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"_string)); + return; + } + + // 17. Set the [[extractable]] internal slot of result to extractable. + result->set_extractable(extractable); + + // 18. Set the [[usages]] internal slot of result to the normalized value of usages. + normalize_key_usages(key_usages); + result->set_usages(key_usages); + + // 19. Resolve promise with result. + WebIDL::resolve_promise(realm, promise, result); + })); + + return promise; +} + SupportedAlgorithmsMap& supported_algorithms_internal() { static SupportedAlgorithmsMap s_supported_algorithms; diff --git a/Libraries/LibWeb/Crypto/SubtleCrypto.h b/Libraries/LibWeb/Crypto/SubtleCrypto.h index f8ca8e032edaa..c27cdc151e68b 100644 --- a/Libraries/LibWeb/Crypto/SubtleCrypto.h +++ b/Libraries/LibWeb/Crypto/SubtleCrypto.h @@ -40,6 +40,9 @@ class SubtleCrypto final : public Bindings::PlatformObject { JS::ThrowCompletionOr> import_key(Bindings::KeyFormat format, KeyDataType key_data, AlgorithmIdentifier algorithm, bool extractable, Vector key_usages); JS::ThrowCompletionOr> export_key(Bindings::KeyFormat format, GC::Ref key); + JS::ThrowCompletionOr> wrap_key(Bindings::KeyFormat format, GC::Ref key, GC::Ref wrapping_key, AlgorithmIdentifier wrap_algorithm); + JS::ThrowCompletionOr> unwrap_key(Bindings::KeyFormat format, KeyDataType wrapped_key, GC::Ref unwrapping_key, AlgorithmIdentifier unwrap_algorithm, AlgorithmIdentifier unwrapped_key_algorithm, bool extractable, Vector key_usages); + private: explicit SubtleCrypto(JS::Realm&); virtual void initialize(JS::Realm&) override; diff --git a/Libraries/LibWeb/Crypto/SubtleCrypto.idl b/Libraries/LibWeb/Crypto/SubtleCrypto.idl index 2baa4db86e616..bf47ba840d180 100644 --- a/Libraries/LibWeb/Crypto/SubtleCrypto.idl +++ b/Libraries/LibWeb/Crypto/SubtleCrypto.idl @@ -58,6 +58,6 @@ interface SubtleCrypto { Promise importKey(KeyFormat format, (BufferSource or JsonWebKey) keyData, AlgorithmIdentifier algorithm, boolean extractable, sequence keyUsages); Promise exportKey(KeyFormat format, CryptoKey key); - [FIXME] Promise wrapKey(KeyFormat format, CryptoKey key, CryptoKey wrappingKey, AlgorithmIdentifier wrapAlgorithm); - [FIXME] Promise unwrapKey(KeyFormat format, BufferSource wrappedKey, CryptoKey unwrappingKey, AlgorithmIdentifier unwrapAlgorithm, AlgorithmIdentifier unwrappedKeyAlgorithm, boolean extractable, sequence keyUsages); + Promise wrapKey(KeyFormat format, CryptoKey key, CryptoKey wrappingKey, AlgorithmIdentifier wrapAlgorithm); + Promise unwrapKey(KeyFormat format, BufferSource wrappedKey, CryptoKey unwrappingKey, AlgorithmIdentifier unwrapAlgorithm, AlgorithmIdentifier unwrappedKeyAlgorithm, boolean extractable, sequence keyUsages); }; diff --git a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.txt b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.txt new file mode 100644 index 0000000000000..f08f7c144ee98 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.txt @@ -0,0 +1,250 @@ +Harness status: OK + +Found 244 tests + +173 Pass +71 Fail +Pass setup +Pass Can wrap and unwrap RSA-OAEP public key keys using spki and RSA-OAEP +Pass Can wrap and unwrap RSA-OAEP public key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap ECDSA public key keys using spki and RSA-OAEP +Pass Can wrap and unwrap ECDSA public key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap ECDSA private key keys using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap ECDSA private key keys as non-extractable using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap ECDSA private key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap ECDSA private key keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap ECDSA private key non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap ECDH public key keys using spki and RSA-OAEP +Pass Can wrap and unwrap ECDH public key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap ECDH private key keys using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap ECDH private key keys as non-extractable using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap ECDH private key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap ECDH private key keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap ECDH private key non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap Ed25519 public key keys using spki and RSA-OAEP +Pass Can wrap and unwrap Ed25519 public key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap Ed25519 private key keys using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap Ed25519 private key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap Ed25519 private key non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap X25519 public key keys using spki and RSA-OAEP +Pass Can wrap and unwrap X25519 public key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap X25519 private key keys using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap X25519 private key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap X25519 private key keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap X25519 private key non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap X448 public key keys using spki and RSA-OAEP +Pass Can wrap and unwrap X448 public key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap X448 private key keys using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap X448 private key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap X448 private key keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap X448 private key non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap AES-CTR keys using raw and RSA-OAEP +Pass Can wrap and unwrap AES-CTR keys as non-extractable using raw and RSA-OAEP +Pass Can wrap and unwrap AES-CTR keys using jwk and RSA-OAEP +Pass Can wrap and unwrap AES-CTR keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap AES-CTR non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap AES-CBC keys using raw and RSA-OAEP +Pass Can wrap and unwrap AES-CBC keys as non-extractable using raw and RSA-OAEP +Pass Can wrap and unwrap AES-CBC keys using jwk and RSA-OAEP +Pass Can wrap and unwrap AES-CBC keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap AES-CBC non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap AES-GCM keys using raw and RSA-OAEP +Fail Can wrap and unwrap AES-GCM keys as non-extractable using raw and RSA-OAEP +Pass Can wrap and unwrap AES-GCM keys using jwk and RSA-OAEP +Fail Can wrap and unwrap AES-GCM keys as non-extractable using jwk and RSA-OAEP +Fail Can unwrap AES-GCM non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap HMAC keys using raw and RSA-OAEP +Pass Can wrap and unwrap HMAC keys as non-extractable using raw and RSA-OAEP +Pass Can wrap and unwrap HMAC keys using jwk and RSA-OAEP +Pass Can wrap and unwrap HMAC keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap HMAC non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap RSA-OAEP public key keys using spki and AES-CTR +Pass Can wrap and unwrap RSA-OAEP public key keys using jwk and AES-CTR +Pass Can wrap and unwrap RSA-OAEP private key keys using pkcs8 and AES-CTR +Pass Can wrap and unwrap RSA-OAEP private key keys as non-extractable using pkcs8 and AES-CTR +Pass Can wrap and unwrap RSA-OAEP private key keys using jwk and AES-CTR +Pass Can wrap and unwrap RSA-OAEP private key keys as non-extractable using jwk and AES-CTR +Pass Can unwrap RSA-OAEP private key non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap ECDSA public key keys using spki and AES-CTR +Pass Can wrap and unwrap ECDSA public key keys using jwk and AES-CTR +Pass Can wrap and unwrap ECDSA private key keys using pkcs8 and AES-CTR +Pass Can wrap and unwrap ECDSA private key keys as non-extractable using pkcs8 and AES-CTR +Pass Can wrap and unwrap ECDSA private key keys using jwk and AES-CTR +Pass Can wrap and unwrap ECDSA private key keys as non-extractable using jwk and AES-CTR +Pass Can unwrap ECDSA private key non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap ECDH public key keys using spki and AES-CTR +Pass Can wrap and unwrap ECDH public key keys using jwk and AES-CTR +Pass Can wrap and unwrap ECDH private key keys using pkcs8 and AES-CTR +Pass Can wrap and unwrap ECDH private key keys as non-extractable using pkcs8 and AES-CTR +Pass Can wrap and unwrap ECDH private key keys using jwk and AES-CTR +Pass Can wrap and unwrap ECDH private key keys as non-extractable using jwk and AES-CTR +Pass Can unwrap ECDH private key non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap Ed25519 public key keys using spki and AES-CTR +Pass Can wrap and unwrap Ed25519 public key keys using jwk and AES-CTR +Pass Can wrap and unwrap Ed25519 private key keys using pkcs8 and AES-CTR +Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using pkcs8 and AES-CTR +Pass Can wrap and unwrap Ed25519 private key keys using jwk and AES-CTR +Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using jwk and AES-CTR +Pass Can unwrap Ed25519 private key non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap X25519 public key keys using spki and AES-CTR +Pass Can wrap and unwrap X25519 public key keys using jwk and AES-CTR +Pass Can wrap and unwrap X25519 private key keys using pkcs8 and AES-CTR +Pass Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-CTR +Pass Can wrap and unwrap X25519 private key keys using jwk and AES-CTR +Pass Can wrap and unwrap X25519 private key keys as non-extractable using jwk and AES-CTR +Pass Can unwrap X25519 private key non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap X448 public key keys using spki and AES-CTR +Pass Can wrap and unwrap X448 public key keys using jwk and AES-CTR +Pass Can wrap and unwrap X448 private key keys using pkcs8 and AES-CTR +Pass Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CTR +Pass Can wrap and unwrap X448 private key keys using jwk and AES-CTR +Pass Can wrap and unwrap X448 private key keys as non-extractable using jwk and AES-CTR +Pass Can unwrap X448 private key non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap AES-CTR keys using raw and AES-CTR +Pass Can wrap and unwrap AES-CTR keys as non-extractable using raw and AES-CTR +Pass Can wrap and unwrap AES-CTR keys using jwk and AES-CTR +Pass Can wrap and unwrap AES-CTR keys as non-extractable using jwk and AES-CTR +Pass Can unwrap AES-CTR non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap AES-CBC keys using raw and AES-CTR +Pass Can wrap and unwrap AES-CBC keys as non-extractable using raw and AES-CTR +Pass Can wrap and unwrap AES-CBC keys using jwk and AES-CTR +Pass Can wrap and unwrap AES-CBC keys as non-extractable using jwk and AES-CTR +Pass Can unwrap AES-CBC non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap AES-GCM keys using raw and AES-CTR +Fail Can wrap and unwrap AES-GCM keys as non-extractable using raw and AES-CTR +Pass Can wrap and unwrap AES-GCM keys using jwk and AES-CTR +Fail Can wrap and unwrap AES-GCM keys as non-extractable using jwk and AES-CTR +Fail Can unwrap AES-GCM non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap HMAC keys using raw and AES-CTR +Pass Can wrap and unwrap HMAC keys as non-extractable using raw and AES-CTR +Pass Can wrap and unwrap HMAC keys using jwk and AES-CTR +Pass Can wrap and unwrap HMAC keys as non-extractable using jwk and AES-CTR +Pass Can unwrap HMAC non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap RSA-OAEP public key keys using spki and AES-CBC +Pass Can wrap and unwrap RSA-OAEP public key keys using jwk and AES-CBC +Pass Can wrap and unwrap RSA-OAEP private key keys using pkcs8 and AES-CBC +Pass Can wrap and unwrap RSA-OAEP private key keys as non-extractable using pkcs8 and AES-CBC +Pass Can wrap and unwrap RSA-OAEP private key keys using jwk and AES-CBC +Pass Can wrap and unwrap RSA-OAEP private key keys as non-extractable using jwk and AES-CBC +Pass Can unwrap RSA-OAEP private key non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap ECDSA public key keys using spki and AES-CBC +Pass Can wrap and unwrap ECDSA public key keys using jwk and AES-CBC +Pass Can wrap and unwrap ECDSA private key keys using pkcs8 and AES-CBC +Pass Can wrap and unwrap ECDSA private key keys as non-extractable using pkcs8 and AES-CBC +Pass Can wrap and unwrap ECDSA private key keys using jwk and AES-CBC +Pass Can wrap and unwrap ECDSA private key keys as non-extractable using jwk and AES-CBC +Pass Can unwrap ECDSA private key non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap ECDH public key keys using spki and AES-CBC +Pass Can wrap and unwrap ECDH public key keys using jwk and AES-CBC +Pass Can wrap and unwrap ECDH private key keys using pkcs8 and AES-CBC +Pass Can wrap and unwrap ECDH private key keys as non-extractable using pkcs8 and AES-CBC +Pass Can wrap and unwrap ECDH private key keys using jwk and AES-CBC +Pass Can wrap and unwrap ECDH private key keys as non-extractable using jwk and AES-CBC +Pass Can unwrap ECDH private key non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap Ed25519 public key keys using spki and AES-CBC +Pass Can wrap and unwrap Ed25519 public key keys using jwk and AES-CBC +Pass Can wrap and unwrap Ed25519 private key keys using pkcs8 and AES-CBC +Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using pkcs8 and AES-CBC +Pass Can wrap and unwrap Ed25519 private key keys using jwk and AES-CBC +Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using jwk and AES-CBC +Pass Can unwrap Ed25519 private key non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap X25519 public key keys using spki and AES-CBC +Pass Can wrap and unwrap X25519 public key keys using jwk and AES-CBC +Pass Can wrap and unwrap X25519 private key keys using pkcs8 and AES-CBC +Pass Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-CBC +Pass Can wrap and unwrap X25519 private key keys using jwk and AES-CBC +Pass Can wrap and unwrap X25519 private key keys as non-extractable using jwk and AES-CBC +Pass Can unwrap X25519 private key non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap X448 public key keys using spki and AES-CBC +Pass Can wrap and unwrap X448 public key keys using jwk and AES-CBC +Pass Can wrap and unwrap X448 private key keys using pkcs8 and AES-CBC +Pass Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CBC +Pass Can wrap and unwrap X448 private key keys using jwk and AES-CBC +Pass Can wrap and unwrap X448 private key keys as non-extractable using jwk and AES-CBC +Pass Can unwrap X448 private key non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap AES-CTR keys using raw and AES-CBC +Pass Can wrap and unwrap AES-CTR keys as non-extractable using raw and AES-CBC +Pass Can wrap and unwrap AES-CTR keys using jwk and AES-CBC +Pass Can wrap and unwrap AES-CTR keys as non-extractable using jwk and AES-CBC +Pass Can unwrap AES-CTR non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap AES-CBC keys using raw and AES-CBC +Pass Can wrap and unwrap AES-CBC keys as non-extractable using raw and AES-CBC +Pass Can wrap and unwrap AES-CBC keys using jwk and AES-CBC +Pass Can wrap and unwrap AES-CBC keys as non-extractable using jwk and AES-CBC +Pass Can unwrap AES-CBC non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap AES-GCM keys using raw and AES-CBC +Fail Can wrap and unwrap AES-GCM keys as non-extractable using raw and AES-CBC +Pass Can wrap and unwrap AES-GCM keys using jwk and AES-CBC +Fail Can wrap and unwrap AES-GCM keys as non-extractable using jwk and AES-CBC +Fail Can unwrap AES-GCM non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap HMAC keys using raw and AES-CBC +Pass Can wrap and unwrap HMAC keys as non-extractable using raw and AES-CBC +Pass Can wrap and unwrap HMAC keys using jwk and AES-CBC +Pass Can wrap and unwrap HMAC keys as non-extractable using jwk and AES-CBC +Pass Can unwrap HMAC non-extractable keys using jwk and AES-CBC +Fail Can wrap and unwrap RSA-OAEP public key keys using spki and AES-GCM +Fail Can wrap and unwrap RSA-OAEP public key keys using jwk and AES-GCM +Fail Can wrap and unwrap RSA-OAEP private key keys using pkcs8 and AES-GCM +Fail Can wrap and unwrap RSA-OAEP private key keys as non-extractable using pkcs8 and AES-GCM +Fail Can wrap and unwrap RSA-OAEP private key keys using jwk and AES-GCM +Fail Can wrap and unwrap RSA-OAEP private key keys as non-extractable using jwk and AES-GCM +Fail Can unwrap RSA-OAEP private key non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap ECDSA public key keys using spki and AES-GCM +Fail Can wrap and unwrap ECDSA public key keys using jwk and AES-GCM +Fail Can wrap and unwrap ECDSA private key keys using pkcs8 and AES-GCM +Fail Can wrap and unwrap ECDSA private key keys as non-extractable using pkcs8 and AES-GCM +Fail Can wrap and unwrap ECDSA private key keys using jwk and AES-GCM +Fail Can wrap and unwrap ECDSA private key keys as non-extractable using jwk and AES-GCM +Fail Can unwrap ECDSA private key non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap ECDH public key keys using spki and AES-GCM +Fail Can wrap and unwrap ECDH public key keys using jwk and AES-GCM +Fail Can wrap and unwrap ECDH private key keys using pkcs8 and AES-GCM +Fail Can wrap and unwrap ECDH private key keys as non-extractable using pkcs8 and AES-GCM +Fail Can wrap and unwrap ECDH private key keys using jwk and AES-GCM +Fail Can wrap and unwrap ECDH private key keys as non-extractable using jwk and AES-GCM +Fail Can unwrap ECDH private key non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap Ed25519 public key keys using spki and AES-GCM +Fail Can wrap and unwrap Ed25519 public key keys using jwk and AES-GCM +Fail Can wrap and unwrap Ed25519 private key keys using pkcs8 and AES-GCM +Fail Can wrap and unwrap Ed25519 private key keys as non-extractable using pkcs8 and AES-GCM +Fail Can wrap and unwrap Ed25519 private key keys using jwk and AES-GCM +Fail Can wrap and unwrap Ed25519 private key keys as non-extractable using jwk and AES-GCM +Fail Can unwrap Ed25519 private key non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap X25519 public key keys using spki and AES-GCM +Fail Can wrap and unwrap X25519 public key keys using jwk and AES-GCM +Fail Can wrap and unwrap X25519 private key keys using pkcs8 and AES-GCM +Fail Can wrap and unwrap X25519 private key keys as non-extractable using pkcs8 and AES-GCM +Fail Can wrap and unwrap X25519 private key keys using jwk and AES-GCM +Fail Can wrap and unwrap X25519 private key keys as non-extractable using jwk and AES-GCM +Fail Can unwrap X25519 private key non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap X448 public key keys using spki and AES-GCM +Fail Can wrap and unwrap X448 public key keys using jwk and AES-GCM +Fail Can wrap and unwrap X448 private key keys using pkcs8 and AES-GCM +Fail Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-GCM +Fail Can wrap and unwrap X448 private key keys using jwk and AES-GCM +Fail Can wrap and unwrap X448 private key keys as non-extractable using jwk and AES-GCM +Fail Can unwrap X448 private key non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap AES-CTR keys using raw and AES-GCM +Fail Can wrap and unwrap AES-CTR keys as non-extractable using raw and AES-GCM +Fail Can wrap and unwrap AES-CTR keys using jwk and AES-GCM +Fail Can wrap and unwrap AES-CTR keys as non-extractable using jwk and AES-GCM +Fail Can unwrap AES-CTR non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap AES-CBC keys using raw and AES-GCM +Fail Can wrap and unwrap AES-CBC keys as non-extractable using raw and AES-GCM +Fail Can wrap and unwrap AES-CBC keys using jwk and AES-GCM +Fail Can wrap and unwrap AES-CBC keys as non-extractable using jwk and AES-GCM +Fail Can unwrap AES-CBC non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap AES-GCM keys using raw and AES-GCM +Fail Can wrap and unwrap AES-GCM keys as non-extractable using raw and AES-GCM +Fail Can wrap and unwrap AES-GCM keys using jwk and AES-GCM +Fail Can wrap and unwrap AES-GCM keys as non-extractable using jwk and AES-GCM +Fail Can unwrap AES-GCM non-extractable keys using jwk and AES-GCM +Fail Can wrap and unwrap HMAC keys using raw and AES-GCM +Fail Can wrap and unwrap HMAC keys as non-extractable using raw and AES-GCM +Fail Can wrap and unwrap HMAC keys using jwk and AES-GCM +Fail Can wrap and unwrap HMAC keys as non-extractable using jwk and AES-GCM +Fail Can unwrap HMAC non-extractable keys using jwk and AES-GCM \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.html b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.html new file mode 100644 index 0000000000000..326148ab90810 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.html @@ -0,0 +1,17 @@ + + +WebCryptoAPI: wrapKey() and unwrapKey() + + + + + + +
+ diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js new file mode 100644 index 0000000000000..9dd837b3bf60a --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js @@ -0,0 +1,536 @@ +// META: title=WebCryptoAPI: wrapKey() and unwrapKey() +// META: timeout=long +// META: script=../util/helpers.js +// META: script=wrapKey_unwrapKey_vectors.js + +// Tests for wrapKey and unwrapKey round tripping + + var subtle = self.crypto.subtle; + + var wrappers = {}; // Things we wrap (and upwrap) keys with + var keys = {}; // Things to wrap and unwrap + + // There are five algorithms that can be used for wrapKey/unwrapKey. + // Generate one key with typical parameters for each kind. + // + // Note: we don't need cryptographically strong parameters for things + // like IV - just any legal value will do. + var wrappingKeysParameters = [ + { + name: "RSA-OAEP", + importParameters: {name: "RSA-OAEP", hash: "SHA-256"}, + wrapParameters: {name: "RSA-OAEP", label: new Uint8Array(8)} + }, + { + name: "AES-CTR", + importParameters: {name: "AES-CTR", length: 128}, + wrapParameters: {name: "AES-CTR", counter: new Uint8Array(16), length: 64} + }, + { + name: "AES-CBC", + importParameters: {name: "AES-CBC", length: 128}, + wrapParameters: {name: "AES-CBC", iv: new Uint8Array(16)} + }, + { + name: "AES-GCM", + importParameters: {name: "AES-GCM", length: 128}, + wrapParameters: {name: "AES-GCM", iv: new Uint8Array(16), additionalData: new Uint8Array(16), tagLength: 128} + }, + { + name: "AES-KW", + importParameters: {name: "AES-KW", length: 128}, + wrapParameters: {name: "AES-KW"} + } + ]; + + var keysToWrapParameters = [ + {algorithm: {name: "RSASSA-PKCS1-v1_5", hash: "SHA-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "RSA-PSS", hash: "SHA-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "RSA-OAEP", hash: "SHA-256"}, privateUsages: ["decrypt"], publicUsages: ["encrypt"]}, + {algorithm: {name: "ECDSA", namedCurve: "P-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "ECDH", namedCurve: "P-256"}, privateUsages: ["deriveBits"], publicUsages: []}, + {algorithm: {name: "Ed25519" }, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "Ed448" }, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "X25519" }, privateUsages: ["deriveBits"], publicUsages: []}, + {algorithm: {name: "X448" }, privateUsages: ["deriveBits"], publicUsages: []}, + {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-KW", length: 128}, usages: ["wrapKey", "unwrapKey"]}, + {algorithm: {name: "HMAC", length: 128, hash: "SHA-256"}, usages: ["sign", "verify"]} + ]; + + // Import all the keys needed, then iterate over all combinations + // to test wrapping and unwrapping. + promise_test(function() { + return Promise.all([importWrappingKeys(), importKeysToWrap()]) + .then(function(results) { + wrappingKeysParameters.filter((param) => Object.keys(wrappers).includes(param.name)).forEach(function(wrapperParam) { + var wrapper = wrappers[wrapperParam.name]; + keysToWrapParameters.filter((param) => Object.keys(keys).includes(param.algorithm.name)).forEach(function(toWrapParam) { + var keyData = keys[toWrapParam.algorithm.name]; + ["raw", "spki", "pkcs8"].filter((fmt) => Object.keys(keyData).includes(fmt)).forEach(function(keyDataFormat) { + var toWrap = keyData[keyDataFormat]; + [keyDataFormat, "jwk"].forEach(function(format) { + if (wrappingIsPossible(toWrap.originalExport[format], wrapper.parameters.name)) { + testWrapping(wrapper, toWrap, format); + if (canCompareNonExtractableKeys(toWrap.key)) { + testWrappingNonExtractable(wrapper, toWrap, format); + if (format === "jwk") { + testWrappingNonExtractableAsExtractable(wrapper, toWrap); + } + } + } + }); + }); + }); + }); + return Promise.resolve("setup done"); + }, function(err) { + return Promise.reject("setup failed: " + err.name + ': "' + err.message + '"'); + }); + }, "setup"); + + function importWrappingKeys() { + // Using allSettled to skip unsupported test cases. + var promises = []; + wrappingKeysParameters.forEach(function(params) { + if (params.name === "RSA-OAEP") { // we have a key pair, not just a key + var algorithm = {name: "RSA-OAEP", hash: "SHA-256"}; + wrappers[params.name] = {wrappingKey: undefined, unwrappingKey: undefined, parameters: params}; + promises.push(subtle.importKey("spki", wrappingKeyData["RSA"].spki, algorithm, true, ["wrapKey"]) + .then(function(key) { + wrappers["RSA-OAEP"].wrappingKey = key; + })); + promises.push(subtle.importKey("pkcs8", wrappingKeyData["RSA"].pkcs8, algorithm, true, ["unwrapKey"]) + .then(function(key) { + wrappers["RSA-OAEP"].unwrappingKey = key; + })); + } else { + var algorithm = {name: params.name}; + promises.push(subtle.importKey("raw", wrappingKeyData["SYMMETRIC"].raw, algorithm, true, ["wrapKey", "unwrapKey"]) + .then(function(key) { + wrappers[params.name] = {wrappingKey: key, unwrappingKey: key, parameters: params}; + })); + } + }); + // Using allSettled to skip unsupported test cases. + return Promise.allSettled(promises); + } + + async function importAndExport(format, keyData, algorithm, keyUsages, keyType) { + var importedKey; + try { + importedKey = await subtle.importKey(format, keyData, algorithm, true, keyUsages); + keys[algorithm.name][format] = { name: algorithm.name + " " + keyType, algorithm: algorithm, usages: keyUsages, key: importedKey, originalExport: {} }; + } catch (err) { + delete keys[algorithm.name][format]; + throw("Error importing " + algorithm.name + " " + keyType + " key in '" + format + "' -" + err.name + ': "' + err.message + '"'); + }; + try { + var exportedKey = await subtle.exportKey(format, importedKey); + keys[algorithm.name][format].originalExport[format] = exportedKey; + } catch (err) { + delete keys[algorithm.name][format]; + throw("Error exporting " + algorithm.name + " '" + format + "' key -" + err.name + ': "' + err.message + '"'); + }; + try { + var jwkExportedKey = await subtle.exportKey("jwk", importedKey); + keys[algorithm.name][format].originalExport["jwk"] = jwkExportedKey; + } catch (err) { + delete keys[algorithm.name][format]; + throw("Error exporting " + algorithm.name + " '" + format + "' key to 'jwk' -" + err.name + ': "' + err.message + '"'); + }; + } + + function importKeysToWrap() { + var promises = []; + keysToWrapParameters.forEach(function(params) { + if ("publicUsages" in params) { + keys[params.algorithm.name] = {}; + var keyData = toWrapKeyDataFromAlg(params.algorithm.name); + promises.push(importAndExport("spki", keyData.spki, params.algorithm, params.publicUsages, "public key ")); + promises.push(importAndExport("pkcs8", keyData.pkcs8, params.algorithm, params.privateUsages, "private key ")); + } else { + keys[params.algorithm.name] = {}; + promises.push(importAndExport("raw", toWrapKeyData["SYMMETRIC"].raw, params.algorithm, params.usages, "")); + } + }); + // Using allSettled to skip unsupported test cases. + return Promise.allSettled(promises); + } + + // Can we successfully "round-trip" (wrap, then unwrap, a key)? + function testWrapping(wrapper, toWrap, fmt) { + promise_test(async() => { + try { + var wrappedResult = await subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters); + var unwrappedResult = await subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); + assert_goodCryptoKey(unwrappedResult, toWrap.algorithm, true, toWrap.usages, toWrap.key.type); + var roundTripExport = await subtle.exportKey(fmt, unwrappedResult); + assert_true(equalExport(toWrap.originalExport[fmt], roundTripExport), "Post-wrap export matches original export"); + } catch (err) { + if (err instanceof AssertionError) { + throw err; + } + assert_unreached("Round trip for extractable key threw an error - " + err.name + ': "' + err.message + '"'); + } + }, "Can wrap and unwrap " + toWrap.name + "keys using " + fmt + " and " + wrapper.parameters.name); + } + + function testWrappingNonExtractable(wrapper, toWrap, fmt) { + promise_test(async() => { + try { + var wrappedResult = await subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters); + var unwrappedResult = await subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); + assert_goodCryptoKey(unwrappedResult, toWrap.algorithm, false, toWrap.usages, toWrap.key.type); + var result = await equalKeys(toWrap.key, unwrappedResult); + assert_true(result, "Unwrapped key matches original"); + } catch (err) { + if (err instanceof AssertionError) { + throw err; + } + assert_unreached("Round trip for key unwrapped non-extractable threw an error - " + err.name + ': "' + err.message + '"'); + }; + }, "Can wrap and unwrap " + toWrap.name + "keys as non-extractable using " + fmt + " and " + wrapper.parameters.name); + } + + function testWrappingNonExtractableAsExtractable(wrapper, toWrap) { + promise_test(async() => { + var wrappedKey; + try { + var wrappedResult = await wrapAsNonExtractableJwk(toWrap.key,wrapper); + wrappedKey = wrappedResult; + var unwrappedResult = await subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); + assert_false(unwrappedResult.extractable, "Unwrapped key is non-extractable"); + var result = await equalKeys(toWrap.key,unwrappedResult); + assert_true(result, "Unwrapped key matches original"); + } catch (err) { + if (err instanceof AssertionError) { + throw err; + } + assert_unreached("Round trip for non-extractable key threw an error - " + err.name + ': "' + err.message + '"'); + }; + try { + var unwrappedResult = await subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); + assert_unreached("Unwrapping a non-extractable JWK as extractable should fail"); + } catch (err) { + if (err instanceof AssertionError) { + throw err; + } + assert_equals(err.name, "DataError", "Unwrapping a non-extractable JWK as extractable fails with DataError"); + } + }, "Can unwrap " + toWrap.name + "non-extractable keys using jwk and " + wrapper.parameters.name); + } + + // Implement key wrapping by hand to wrap a key as non-extractable JWK + async function wrapAsNonExtractableJwk(key, wrapper) { + var wrappingKey = wrapper.wrappingKey, + encryptKey; + + var jwkWrappingKey = await subtle.exportKey("jwk",wrappingKey); + // Update the key generation parameters to work as key import parameters + var params = Object.create(wrapper.parameters.importParameters); + if(params.name === "AES-KW") { + params.name = "AES-CBC"; + jwkWrappingKey.alg = "A"+params.length+"CBC"; + } else if (params.name === "RSA-OAEP") { + params.modulusLength = undefined; + params.publicExponent = undefined; + } + jwkWrappingKey.key_ops = ["encrypt"]; + var importedWrappingKey = await subtle.importKey("jwk", jwkWrappingKey, params, true, ["encrypt"]); + encryptKey = importedWrappingKey; + var exportedKey = await subtle.exportKey("jwk",key); + exportedKey.ext = false; + var jwk = JSON.stringify(exportedKey) + var result; + if (wrappingKey.algorithm.name === "AES-KW") { + result = await aeskw(encryptKey, str2ab(jwk.slice(0,-1) + " ".repeat(jwk.length%8 ? 8-jwk.length%8 : 0) + "}")); + } else { + result = await subtle.encrypt(wrapper.parameters.wrapParameters,encryptKey,str2ab(jwk)); + } + return result; + } + + + // RSA-OAEP can only wrap relatively small payloads. AES-KW can only + // wrap payloads a multiple of 8 bytes long. + function wrappingIsPossible(exportedKey, algorithmName) { + if ("byteLength" in exportedKey && algorithmName === "AES-KW") { + return exportedKey.byteLength % 8 === 0; + } + + if ("byteLength" in exportedKey && algorithmName === "RSA-OAEP") { + // RSA-OAEP can only encrypt payloads with lengths shorter + // than modulusLength - 2*hashLength - 1 bytes long. For + // a 4096 bit modulus and SHA-256, that comes to + // 4096/8 - 2*(256/8) - 1 = 512 - 2*32 - 1 = 447 bytes. + return exportedKey.byteLength <= 446; + } + + if ("kty" in exportedKey && algorithmName === "AES-KW") { + return JSON.stringify(exportedKey).length % 8 == 0; + } + + if ("kty" in exportedKey && algorithmName === "RSA-OAEP") { + return JSON.stringify(exportedKey).length <= 478; + } + + return true; + } + + + // Helper methods follow: + + // Are two exported keys equal + function equalExport(originalExport, roundTripExport) { + if ("byteLength" in originalExport) { + return equalBuffers(originalExport, roundTripExport); + } else { + return equalJwk(originalExport, roundTripExport); + } + } + + // Are two array buffers the same? + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i x); + } else if (signParams) { + var verifyKey; + var jwkExpectedKey = await subtle.exportKey("jwk",expected); + if (expected.algorithm.name === "RSA-PSS" || expected.algorithm.name === "RSASSA-PKCS1-v1_5") { + ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; }); + } + if (expected.algorithm.name === "ECDSA" || expected.algorithm.name.startsWith("Ed")) { + delete jwkExpectedKey["d"]; + } + jwkExpectedKey.key_ops = ["verify"]; + var expectedVerifyKey = await subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["verify"]); + verifyKey = expectedVerifyKey; + var signature = await subtle.sign(signParams, got, new Uint8Array(32)); + var result = await subtle.verify(signParams, verifyKey, signature, new Uint8Array(32)); + return result; + } else if (wrapParams) { + var aKeyToWrap, wrappedWithExpected; + var key = await subtle.importKey("raw",new Uint8Array(16), "AES-CBC", true, ["encrypt"]) + aKeyToWrap = key; + var wrapResult = await subtle.wrapKey("raw", aKeyToWrap, expected, wrapParams); + wrappedWithExpected = Array.from((new Uint8Array(wrapResult)).values()); + wrapResult = await subtle.wrapKey("raw", aKeyToWrap, got, wrapParams); + var wrappedWithGot = Array.from((new Uint8Array(wrapResult)).values()); + return wrappedWithGot.every((x,i) => x === wrappedWithExpected[i]); + } else if (deriveParams) { + var expectedDerivedBits; + var key = await subtle.generateKey(expected.algorithm, true, ['deriveBits']); + deriveParams.public = key.publicKey; + var result = await subtle.deriveBits(deriveParams, expected, 128); + expectedDerivedBits = Array.from((new Uint8Array(result)).values()); + result = await subtle.deriveBits(deriveParams, got, 128); + var gotDerivedBits = Array.from((new Uint8Array(result)).values()); + return gotDerivedBits.every((x,i) => x === expectedDerivedBits[i]); + } + } + + // Raw AES encryption + async function aes(k, p) { + const ciphertext = await subtle.encrypt({ name: "AES-CBC", iv: new Uint8Array(16) }, k, p); + return ciphertext.slice(0, 16); + } + + // AES Key Wrap + async function aeskw(key, data) { + if (data.byteLength % 8 !== 0) { + throw new Error("AES Key Wrap data must be a multiple of 8 bytes in length"); + } + + var A = Uint8Array.from([0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0, 0, 0, 0, 0, 0, 0, 0]), + Av = new DataView(A.buffer), + R = [], + n = data.byteLength / 8; + + for(var i = 0; i