From 5a8b0a261042189291794546a3cc855bc639d249 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 25 Nov 2024 11:18:05 +0100 Subject: [PATCH] LibWeb: Implement the generateKey algorithm for X448 --- Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp | 71 +++++++++++++++++++ Libraries/LibWeb/Crypto/CryptoAlgorithms.h | 16 +++++ Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 2 +- .../generateKey/failures_X448.https.any.txt | 68 +++++++++--------- 4 files changed, 122 insertions(+), 35 deletions(-) diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 3ab0a37f0ad..f61f8df5923 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -2,6 +2,7 @@ * Copyright (c) 2024, Andrew Kaster * Copyright (c) 2024, stelar7 * Copyright (c) 2024, Jelle Raaijmakers + * Copyright (c) 2024, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -3749,6 +3751,75 @@ WebIDL::ExceptionOr> X25519::export_key(Bindings::KeyFormat return GC::Ref { *result }; } +// https://wicg.github.io/webcrypto-secure-curves/#x448-operations +WebIDL::ExceptionOr, GC::Ref>> X448::generate_key( + AlgorithmParams const&, + bool extractable, + Vector const& usages) +{ + // 1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a SyntaxError. + for (auto const& usage : usages) { + if (usage != Bindings::KeyUsage::Derivekey && usage != Bindings::KeyUsage::Derivebits) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + + // 2. Generate an X448 key pair, with the private key being 56 random bytes, and the public key being X448(a, 5), as defined in [RFC7748], section 6.2. + ::Crypto::Curves::X448 curve; + auto maybe_private_key = curve.generate_private_key(); + if (maybe_private_key.is_error()) + return WebIDL::OperationError::create(m_realm, "Failed to generate private key"_string); + auto private_key_data = maybe_private_key.release_value(); + auto maybe_public_key = curve.generate_public_key(private_key_data); + if (maybe_public_key.is_error()) + return WebIDL::OperationError::create(m_realm, "Failed to generate public key"_string); + auto public_key_data = maybe_public_key.release_value(); + + // 3. Let algorithm be a new KeyAlgorithm object. + auto algorithm = KeyAlgorithm::create(m_realm); + + // 4. Set the name attribute of algorithm to "X448". + algorithm->set_name("X448"_string); + + // 5. Let publicKey be a new CryptoKey associated with the relevant global object of this [HTML], and representing the public key of the generated key pair. + auto public_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key_data }); + + // 6. Set the [[type]] internal slot of publicKey to "public" + public_key->set_type(Bindings::KeyType::Public); + + // 7. Set the [[algorithm]] internal slot of publicKey to algorithm. + public_key->set_algorithm(algorithm); + + // 8. Set the [[extractable]] internal slot of publicKey to true. + public_key->set_extractable(true); + + // 9. Set the [[usages]] internal slot of publicKey to be the empty list. + public_key->set_usages({}); + + // 10. Let privateKey be a new CryptoKey associated with the relevant global object of this [HTML], and representing the private key of the generated key pair. + auto private_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key_data }); + + // 11. Set the [[type]] internal slot of privateKey to "private" + private_key->set_type(Bindings::KeyType::Private); + + // 12. Set the [[algorithm]] internal slot of privateKey to algorithm. + private_key->set_algorithm(algorithm); + + // 13. Set the [[extractable]] internal slot of privateKey to extractable. + private_key->set_extractable(extractable); + + // 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "deriveKey", "deriveBits" ]. + private_key->set_usages(usage_intersection(usages, { { Bindings::KeyUsage::Derivekey, Bindings::KeyUsage::Derivebits } })); + + // 15. Let result be a new CryptoKeyPair dictionary. + // 16. Set the publicKey attribute of result to be publicKey. + // 17. Set the privateKey attribute of result to be privateKey. + auto result = CryptoKeyPair::create(m_realm, public_key, private_key); + + // 18. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL]. + return Variant, GC::Ref> { result }; +} + static WebIDL::ExceptionOr hmac_calculate_message_digest(JS::Realm& realm, GC::Ptr hash, ReadonlyBytes key, ReadonlyBytes message) { auto calculate_digest = [&]() -> ByteBuffer { diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index 92d539f4383..b89521345ca 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -543,6 +543,22 @@ private: } }; +class X448 : public AlgorithmMethods { +public: + // FIXME: virtual WebIDL::ExceptionOr> derive_bits(AlgorithmParams const&, GC::Ref, Optional) override; + virtual WebIDL::ExceptionOr, GC::Ref>> generate_key(AlgorithmParams const&, bool, Vector const&) override; + // FIXME: virtual WebIDL::ExceptionOr> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector const&) override; + // FIXME: virtual WebIDL::ExceptionOr> export_key(Bindings::KeyFormat, GC::Ref) override; + + static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new X448(realm)); } + +private: + explicit X448(JS::Realm& realm) + : AlgorithmMethods(realm) + { + } +}; + class HMAC : public AlgorithmMethods { public: virtual WebIDL::ExceptionOr> sign(AlgorithmParams const&, GC::Ref, ByteBuffer const&) override; diff --git a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index cdd021a0808..d005213cf1f 100644 --- a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -874,7 +874,7 @@ SupportedAlgorithmsMap const& supported_algorithms() // https://wicg.github.io/webcrypto-secure-curves/#x448-registration // FIXME: define_an_algorithm("deriveBits"_string, "X448"_string); - // FIXME: define_an_algorithm("generateKey"_string, "X448"_string); + define_an_algorithm("generateKey"_string, "X448"_string); // FIXME: define_an_algorithm("importKey"_string, "X448"_string); // FIXME: define_an_algorithm("exportKey"_string, "X448"_string); diff --git a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_X448.https.any.txt b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_X448.https.any.txt index 1a5b81b00ba..9f3ce7de4f8 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_X448.https.any.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_X448.https.any.txt @@ -6,8 +6,8 @@ Rerun Found 392 tests -332 Pass -60 Fail +364 Pass +28 Fail Details Result Test Name MessagePass Bad algorithm: generateKey(AES, false, [decrypt]) Pass Bad algorithm: generateKey(AES, true, [decrypt]) @@ -369,35 +369,35 @@ Pass Empty algorithm: generateKey({}, false, [decrypt, sign, deriveBits, decrypt Pass Empty algorithm: generateKey({}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) Pass Empty algorithm: generateKey({}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) Pass Empty algorithm: generateKey({}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) -Fail Bad usages: generateKey({name: X448}, true, [encrypt]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encrypt]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, encrypt]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt]) -Fail Bad usages: generateKey({name: X448}, true, [decrypt]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, decrypt]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decrypt]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, decrypt]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt]) -Fail Bad usages: generateKey({name: X448}, true, [sign]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, sign]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, sign]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, sign]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign]) -Fail Bad usages: generateKey({name: X448}, true, [verify]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, verify]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, verify]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, verify]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify]) -Fail Bad usages: generateKey({name: X448}, true, [wrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, wrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, wrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, wrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [unwrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, unwrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, unwrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [deriveBits, unwrapKey]) -Fail Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey]) -Fail Empty usages: generateKey({name: X448}, false, []) -Fail Empty usages: generateKey({name: X448}, true, []) \ No newline at end of file +Pass Bad usages: generateKey({name: X448}, true, [encrypt]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encrypt]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, encrypt]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt]) +Pass Bad usages: generateKey({name: X448}, true, [decrypt]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, decrypt]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decrypt]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, decrypt]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt]) +Pass Bad usages: generateKey({name: X448}, true, [sign]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, sign]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, sign]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, sign]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign]) +Pass Bad usages: generateKey({name: X448}, true, [verify]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, verify]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, verify]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, verify]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify]) +Pass Bad usages: generateKey({name: X448}, true, [wrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, wrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, wrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, wrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [unwrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, unwrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, unwrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [deriveBits, unwrapKey]) +Pass Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey]) +Pass Empty usages: generateKey({name: X448}, false, []) +Pass Empty usages: generateKey({name: X448}, true, []) \ No newline at end of file