diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 5a8ea53f8a6..a4ecc48763c 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -344,6 +345,20 @@ JS::ThrowCompletionOr> RsaOaepParams::from_value( return adopt_own(*new RsaOaepParams { name, move(label) }); } +EcKeyGenParams::~EcKeyGenParams() = default; + +JS::ThrowCompletionOr> EcKeyGenParams::from_value(JS::VM& vm, JS::Value value) +{ + auto& object = value.as_object(); + + auto name_value = TRY(object.get("name")); + auto name = TRY(name_value.to_string(vm)); + + auto curve_value = TRY(object.get("namedCurve")); + auto curve = TRY(curve_value.to_string(vm)); + + return adopt_own(*new EcKeyGenParams { name, curve }); +} // https://w3c.github.io/webcrypto/#rsa-oaep-operations WebIDL::ExceptionOr> RSAOAEP::encrypt(AlgorithmParams const& params, JS::NonnullGCPtr key, ByteBuffer const& plaintext) @@ -927,4 +942,105 @@ WebIDL::ExceptionOr> SHA::digest(AlgorithmPara return JS::ArrayBuffer::create(m_realm, result_buffer.release_value()); } +// https://w3c.github.io/webcrypto/#ecdsa-operations +WebIDL::ExceptionOr, JS::NonnullGCPtr>> ECDSA::generate_key(AlgorithmParams const& params, bool extractable, Vector const& key_usages) +{ + // 1. If usages contains a value which is not one of "sign" or "verify", then throw a SyntaxError. + for (auto const& usage : key_usages) { + if (usage != Bindings::KeyUsage::Sign && usage != Bindings::KeyUsage::Verify) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + + auto const& normalized_algorithm = static_cast(params); + + // 2. If the namedCurve member of normalizedAlgorithm is "P-256", "P-384" or "P-521": + // Generate an Elliptic Curve key pair, as defined in [RFC6090] + // with domain parameters for the curve identified by the namedCurve member of normalizedAlgorithm. + Variant curve; + if (normalized_algorithm.named_curve.is_one_of("P-256"sv, "P-384"sv, "P-521"sv)) { + if (normalized_algorithm.named_curve.equals_ignoring_ascii_case("P-256"sv)) + curve = ::Crypto::Curves::SECP256r1 {}; + + if (normalized_algorithm.named_curve.equals_ignoring_ascii_case("P-384"sv)) + curve = ::Crypto::Curves::SECP384r1 {}; + + // FIXME: Support P-521 + if (normalized_algorithm.named_curve.equals_ignoring_ascii_case("P-521"sv)) + return WebIDL::NotSupportedError::create(m_realm, "'P-521' is not supported yet"_fly_string); + } else { + // If the namedCurve member of normalizedAlgorithm is a value specified in an applicable specification: + // Perform the ECDSA generation steps specified in that specification, + // passing in normalizedAlgorithm and resulting in an elliptic curve key pair. + + // Otherwise: throw a NotSupportedError + return WebIDL::NotSupportedError::create(m_realm, "Only 'P-256', 'P-384' and 'P-521' is supported"_fly_string); + } + + // NOTE: Spec jumps to 6 here for some reason + // 6. If performing the key generation operation results in an error, then throw an OperationError. + auto maybe_private_key_data = curve.visit( + [](Empty const&) -> ErrorOr { return Error::from_string_view("noop error"sv); }, + [](auto instance) { return instance.generate_private_key(); }); + + if (maybe_private_key_data.is_error()) + return WebIDL::OperationError::create(m_realm, "Failed to create valid crypto instance"_fly_string); + + auto private_key_data = maybe_private_key_data.release_value(); + + auto maybe_public_key_data = curve.visit( + [](Empty const&) -> ErrorOr { return Error::from_string_view("noop error"sv); }, + [&](auto instance) { return instance.generate_public_key(private_key_data); }); + + if (maybe_public_key_data.is_error()) + return WebIDL::OperationError::create(m_realm, "Failed to create valid crypto instance"_fly_string); + + auto public_key_data = maybe_public_key_data.release_value(); + + // 7. Let algorithm be a new EcKeyAlgorithm object. + auto algorithm = EcKeyAlgorithm::create(m_realm); + + // 8. Set the name attribute of algorithm to "ECDSA". + algorithm->set_name("ECDSA"_string); + + // 9. Set the namedCurve attribute of algorithm to equal the namedCurve member of normalizedAlgorithm. + algorithm->set_named_curve(normalized_algorithm.named_curve); + + // 10. Let publicKey be a new CryptoKey representing the public key of the generated key pair. + auto public_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key_data }); + + // 11. Set the [[type]] internal slot of publicKey to "public" + public_key->set_type(Bindings::KeyType::Public); + + // 12. Set the [[algorithm]] internal slot of publicKey to algorithm. + public_key->set_algorithm(algorithm); + + // 13. Set the [[extractable]] internal slot of publicKey to true. + public_key->set_extractable(true); + + // 14. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages and [ "verify" ]. + public_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Verify } })); + + // 15. Let privateKey be a new CryptoKey representing the private key of the generated key pair. + auto private_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key_data }); + + // 16. Set the [[type]] internal slot of privateKey to "private" + private_key->set_type(Bindings::KeyType::Private); + + // 17. Set the [[algorithm]] internal slot of privateKey to algorithm. + private_key->set_algorithm(algorithm); + + // 18. Set the [[extractable]] internal slot of privateKey to extractable. + private_key->set_extractable(extractable); + + // 19. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "sign" ]. + private_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Sign } })); + + // 20. Let result be a new CryptoKeyPair dictionary. + // 21. Set the publicKey attribute of result to be publicKey. + // 22. Set the privateKey attribute of result to be privateKey. + // 23. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL]. + return Variant, JS::NonnullGCPtr> { CryptoKeyPair::create(m_realm, public_key, private_key) }; +} + } diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index 0ac9df25674..5f287baa300 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -21,6 +21,7 @@ namespace Web::Crypto { using AlgorithmIdentifier = Variant, String>; using HashAlgorithmIdentifier = AlgorithmIdentifier; +using NamedCurve = String; using KeyDataType = Variant, Bindings::JsonWebKey>; // https://w3c.github.io/webcrypto/#algorithm-overview @@ -116,6 +117,20 @@ struct RsaOaepParams : public AlgorithmParams { static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); }; +// https://w3c.github.io/webcrypto/#dfn-EcKeyGenParams +struct EcKeyGenParams : public AlgorithmParams { + virtual ~EcKeyGenParams() override; + + EcKeyGenParams(String name, NamedCurve named_curve) + : AlgorithmParams(move(name)) + , named_curve(move(named_curve)) + { + } + + NamedCurve named_curve; + + static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); +}; class AlgorithmMethods { public: @@ -212,6 +227,19 @@ private: } }; +class ECDSA : public AlgorithmMethods { +public: + virtual WebIDL::ExceptionOr, JS::NonnullGCPtr>> generate_key(AlgorithmParams const&, bool, Vector const&) override; + + static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new ECDSA(realm)); } + +private: + explicit ECDSA(JS::Realm& realm) + : AlgorithmMethods(realm) + { + } +}; + ErrorOr base64_url_uint_encode(::Crypto::UnsignedBigInteger); WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Realm&, String const& base64_url_string); diff --git a/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp index 170e4f39032..754d7370d76 100644 --- a/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.cpp @@ -16,6 +16,7 @@ namespace Web::Crypto { JS_DEFINE_ALLOCATOR(KeyAlgorithm); JS_DEFINE_ALLOCATOR(RsaKeyAlgorithm); JS_DEFINE_ALLOCATOR(RsaHashedKeyAlgorithm); +JS_DEFINE_ALLOCATOR(EcKeyAlgorithm); template static JS::ThrowCompletionOr impl_from(JS::VM& vm, StringView Name) @@ -119,6 +120,34 @@ JS_DEFINE_NATIVE_FUNCTION(RsaKeyAlgorithm::public_exponent_getter) return impl->public_exponent(); } +JS::NonnullGCPtr EcKeyAlgorithm::create(JS::Realm& realm) +{ + return realm.heap().allocate(realm, realm); +} + +EcKeyAlgorithm::EcKeyAlgorithm(JS::Realm& realm) + : KeyAlgorithm(realm) +{ +} + +void EcKeyAlgorithm::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + + define_native_accessor(realm, "namedCurve", named_curve_getter, {}, JS::Attribute::Enumerable | JS::Attribute::Configurable); +} + +JS_DEFINE_NATIVE_FUNCTION(EcKeyAlgorithm::named_curve_getter) +{ + auto* impl = TRY(impl_from(vm, "EcKeyAlgorithm"sv)); + return JS::PrimitiveString::create(vm, impl->named_curve()); +} + +void EcKeyAlgorithm::visit_edges(Visitor& visitor) +{ + Base::visit_edges(visitor); +} + JS::NonnullGCPtr RsaHashedKeyAlgorithm::create(JS::Realm& realm) { return realm.heap().allocate(realm, realm); diff --git a/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.h index 857ef715251..e806797bd7b 100644 --- a/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/KeyAlgorithms.h @@ -96,4 +96,29 @@ private: HashAlgorithmIdentifier m_hash; }; +// https://w3c.github.io/webcrypto/#EcKeyAlgorithm-dictionary +class EcKeyAlgorithm : public KeyAlgorithm { + JS_OBJECT(EcKeyAlgorithm, KeyAlgorithm); + JS_DECLARE_ALLOCATOR(EcKeyAlgorithm); + +public: + static JS::NonnullGCPtr create(JS::Realm&); + + virtual ~EcKeyAlgorithm() override = default; + + NamedCurve named_curve() const { return m_named_curve; } + void set_named_curve(NamedCurve named_curve) { m_named_curve = named_curve; } + +protected: + EcKeyAlgorithm(JS::Realm&); + + virtual void initialize(JS::Realm&) override; + virtual void visit_edges(Visitor&) override; + +private: + JS_DECLARE_NATIVE_FUNCTION(named_curve_getter); + + NamedCurve m_named_curve; +}; + } diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index e0af98a4a25..bafd5abf325 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -575,6 +575,9 @@ SupportedAlgorithmsMap supported_algorithms() define_an_algorithm("encrypt"_string, "RSA-OAEP"_string); define_an_algorithm("decrypt"_string, "RSA-OAEP"_string); + // https://w3c.github.io/webcrypto/#ecdsa + define_an_algorithm("generateKey"_string, "ECDSA"_string); + return internal_object; }