mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
LibWeb: Implement generateKey for ECDSA
This commit is contained in:
parent
cfae6523be
commit
41449814db
Notes:
sideshowbarker
2024-07-17 07:06:47 +09:00
Author: https://github.com/stelar7 Commit: https://github.com/SerenityOS/serenity/commit/41449814db Pull-request: https://github.com/SerenityOS/serenity/pull/23737 Reviewed-by: https://github.com/ADKaster ✅ Reviewed-by: https://github.com/trflynn89
5 changed files with 201 additions and 0 deletions
|
@ -7,6 +7,7 @@
|
|||
#include <AK/Base64.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
#include <LibCrypto/Curves/SECPxxxr1.h>
|
||||
#include <LibCrypto/Hash/HashManager.h>
|
||||
#include <LibCrypto/PK/RSA.h>
|
||||
#include <LibJS/Runtime/ArrayBuffer.h>
|
||||
|
@ -344,6 +345,20 @@ JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> RsaOaepParams::from_value(
|
|||
|
||||
return adopt_own<AlgorithmParams>(*new RsaOaepParams { name, move(label) });
|
||||
}
|
||||
EcKeyGenParams::~EcKeyGenParams() = default;
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> 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<AlgorithmParams>(*new EcKeyGenParams { name, curve });
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcrypto/#rsa-oaep-operations
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> RSAOAEP::encrypt(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, ByteBuffer const& plaintext)
|
||||
|
@ -927,4 +942,105 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> SHA::digest(AlgorithmPara
|
|||
return JS::ArrayBuffer::create(m_realm, result_buffer.release_value());
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcrypto/#ecdsa-operations
|
||||
WebIDL::ExceptionOr<Variant<JS::NonnullGCPtr<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>>> ECDSA::generate_key(AlgorithmParams const& params, bool extractable, Vector<Bindings::KeyUsage> 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<EcKeyGenParams const&>(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<Empty, ::Crypto::Curves::SECP256r1, ::Crypto::Curves::SECP384r1> 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<ByteBuffer> { 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<ByteBuffer> { 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<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>> { CryptoKeyPair::create(m_realm, public_key, private_key) };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace Web::Crypto {
|
|||
|
||||
using AlgorithmIdentifier = Variant<JS::Handle<JS::Object>, String>;
|
||||
using HashAlgorithmIdentifier = AlgorithmIdentifier;
|
||||
using NamedCurve = String;
|
||||
using KeyDataType = Variant<JS::Handle<WebIDL::BufferSource>, Bindings::JsonWebKey>;
|
||||
|
||||
// https://w3c.github.io/webcrypto/#algorithm-overview
|
||||
|
@ -116,6 +117,20 @@ struct RsaOaepParams : public AlgorithmParams {
|
|||
|
||||
static JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> 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<NonnullOwnPtr<AlgorithmParams>> from_value(JS::VM&, JS::Value);
|
||||
};
|
||||
|
||||
class AlgorithmMethods {
|
||||
public:
|
||||
|
@ -212,6 +227,19 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class ECDSA : public AlgorithmMethods {
|
||||
public:
|
||||
virtual WebIDL::ExceptionOr<Variant<JS::NonnullGCPtr<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>>> generate_key(AlgorithmParams const&, bool, Vector<Bindings::KeyUsage> const&) override;
|
||||
|
||||
static NonnullOwnPtr<AlgorithmMethods> create(JS::Realm& realm) { return adopt_own(*new ECDSA(realm)); }
|
||||
|
||||
private:
|
||||
explicit ECDSA(JS::Realm& realm)
|
||||
: AlgorithmMethods(realm)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
ErrorOr<String> base64_url_uint_encode(::Crypto::UnsignedBigInteger);
|
||||
WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Realm&, String const& base64_url_string);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Web::Crypto {
|
|||
JS_DEFINE_ALLOCATOR(KeyAlgorithm);
|
||||
JS_DEFINE_ALLOCATOR(RsaKeyAlgorithm);
|
||||
JS_DEFINE_ALLOCATOR(RsaHashedKeyAlgorithm);
|
||||
JS_DEFINE_ALLOCATOR(EcKeyAlgorithm);
|
||||
|
||||
template<typename T>
|
||||
static JS::ThrowCompletionOr<T*> 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> EcKeyAlgorithm::create(JS::Realm& realm)
|
||||
{
|
||||
return realm.heap().allocate<EcKeyAlgorithm>(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<EcKeyAlgorithm>(vm, "EcKeyAlgorithm"sv));
|
||||
return JS::PrimitiveString::create(vm, impl->named_curve());
|
||||
}
|
||||
|
||||
void EcKeyAlgorithm::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
}
|
||||
|
||||
JS::NonnullGCPtr<RsaHashedKeyAlgorithm> RsaHashedKeyAlgorithm::create(JS::Realm& realm)
|
||||
{
|
||||
return realm.heap().allocate<RsaHashedKeyAlgorithm>(realm, realm);
|
||||
|
|
|
@ -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<EcKeyAlgorithm> 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -575,6 +575,9 @@ SupportedAlgorithmsMap supported_algorithms()
|
|||
define_an_algorithm<RSAOAEP, RsaOaepParams>("encrypt"_string, "RSA-OAEP"_string);
|
||||
define_an_algorithm<RSAOAEP, RsaOaepParams>("decrypt"_string, "RSA-OAEP"_string);
|
||||
|
||||
// https://w3c.github.io/webcrypto/#ecdsa
|
||||
define_an_algorithm<ECDSA, EcKeyGenParams>("generateKey"_string, "ECDSA"_string);
|
||||
|
||||
return internal_object;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue