From 8c5e5adc8a25dbe45a4d26e48da799b86a379489 Mon Sep 17 00:00:00 2001 From: stelar7 Date: Sat, 26 Oct 2024 19:57:59 +0200 Subject: [PATCH] LibWeb: Implement X25519.deriveBits --- .../LibWeb/Crypto/CryptoAlgorithms.cpp | 88 +++++++++++++++++++ .../LibWeb/Crypto/CryptoAlgorithms.h | 27 ++++++ .../Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 3 + 3 files changed, 118 insertions(+) diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 45a77e5cfe6..31d18bd027e 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -504,6 +505,27 @@ JS::ThrowCompletionOr> AesDerivedKeyParams::from_ return adopt_own(*new AesDerivedKeyParams { name, length }); } +EcdhKeyDerivePrams::~EcdhKeyDerivePrams() = default; + +JS::ThrowCompletionOr> EcdhKeyDerivePrams::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 key_value = TRY(object.get("public")); + auto key_object = TRY(key_value.to_object(vm)); + + if (!is(*key_object)) { + return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "CryptoKey"); + } + + auto& key = verify_cast(*key_object); + + return adopt_own(*new EcdhKeyDerivePrams { name, key }); +} + // https://w3c.github.io/webcrypto/#rsa-oaep-operations WebIDL::ExceptionOr> RSAOAEP::encrypt(AlgorithmParams const& params, JS::NonnullGCPtr key, ByteBuffer const& plaintext) { @@ -2306,4 +2328,70 @@ WebIDL::ExceptionOr PBKDF2::get_key_length(AlgorithmParams const&) return JS::js_null(); } +// https://wicg.github.io/webcrypto-secure-curves/#x25519-operations +WebIDL::ExceptionOr> X25519::derive_bits(AlgorithmParams const& params, JS::NonnullGCPtr key, Optional length_optional) +{ + auto& realm = *m_realm; + auto const& normalized_algorithm = static_cast(params); + + // 1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError. + if (key->type() != Bindings::KeyType::Private) + return WebIDL::InvalidAccessError::create(realm, "Key is not a private key"_string); + + // 2. Let publicKey be the public member of normalizedAlgorithm. + auto& public_key = normalized_algorithm.public_key; + + // 3. If the [[type]] internal slot of publicKey is not "public", then throw an InvalidAccessError. + if (public_key->type() != Bindings::KeyType::Public) + return WebIDL::InvalidAccessError::create(realm, "Public key is not a public key"_string); + + // 4. If the name attribute of the [[algorithm]] internal slot of publicKey is not equal to + // the name property of the [[algorithm]] internal slot of key, then throw an InvalidAccessError. + auto& internal_algorithm = static_cast(*key->algorithm()); + auto& public_internal_algorithm = static_cast(*public_key->algorithm()); + if (internal_algorithm.name() != public_internal_algorithm.name()) + return WebIDL::InvalidAccessError::create(realm, "Algorithm mismatch"_string); + + // 5. Let secret be the result of performing the X25519 function specified in [RFC7748] Section 5 with + // key as the X25519 private key k and + // the X25519 public key represented by the [[handle]] internal slot of publicKey as the X25519 public key u. + auto private_key = key->handle().get(); + auto public_key_data = public_key->handle().get(); + + ::Crypto::Curves::X25519 curve; + auto maybe_secret = curve.compute_coordinate(private_key, public_key_data); + if (maybe_secret.is_error()) + return WebIDL::OperationError::create(realm, "Failed to compute secret"_string); + + auto secret = maybe_secret.release_value(); + + // 6. If secret is the all-zero value, then throw a OperationError. + // This check must be performed in constant-time, as per [RFC7748] Section 6.1. + // NOTE: The check may be performed by ORing all the bytes together and checking whether the result is zero, + // as this eliminates standard side-channels in software implementations. + auto or_bytes = 0; + for (auto byte : secret.bytes()) { + or_bytes |= byte; + } + + if (or_bytes == 0) + return WebIDL::OperationError::create(realm, "Secret is the all-zero value"_string); + + // 7. If length is null: Return secret + if (!length_optional.has_value()) { + auto result = TRY_OR_THROW_OOM(realm.vm(), ByteBuffer::copy(secret)); + return JS::ArrayBuffer::create(realm, move(result)); + } + + // Otherwise: If the length of secret in bits is less than length: throw an OperationError. + auto length = length_optional.value(); + if (secret.size() * 8 < length) + return WebIDL::OperationError::create(realm, "Secret is too short"_string); + + // Otherwise: Return an octet string containing the first length bits of secret. + auto slice = TRY_OR_THROW_OOM(realm.vm(), secret.slice(0, length / 8)); + auto result = TRY_OR_THROW_OOM(realm.vm(), ByteBuffer::copy(slice)); + return JS::ArrayBuffer::create(realm, move(result)); +} + } diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index f6920edf1aa..91375caa1dd 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -436,6 +436,33 @@ private: } }; +class X25519 : public AlgorithmMethods { +public: + virtual WebIDL::ExceptionOr> derive_bits(AlgorithmParams const&, JS::NonnullGCPtr, Optional) override; + + static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new X25519(realm)); } + +private: + explicit X25519(JS::Realm& realm) + : AlgorithmMethods(realm) + { + } +}; + +struct EcdhKeyDerivePrams : public AlgorithmParams { + virtual ~EcdhKeyDerivePrams() override; + + EcdhKeyDerivePrams(String name, CryptoKey& public_key) + : AlgorithmParams(move(name)) + , public_key(public_key) + { + } + + JS::NonnullGCPtr public_key; + + static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); +}; + ErrorOr base64_url_uint_encode(::Crypto::UnsignedBigInteger); WebIDL::ExceptionOr base64_url_bytes_decode(JS::Realm&, String const& base64_url_string); WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Realm&, String const& base64_url_string); diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index 03c9f1a51f1..6e15920cdfd 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -811,6 +811,9 @@ SupportedAlgorithmsMap supported_algorithms() define_an_algorithm("verify"_string, "Ed25519"_string); define_an_algorithm("generateKey"_string, "Ed25519"_string); + // https://wicg.github.io/webcrypto-secure-curves/#x25519 + define_an_algorithm("deriveBits"_string, "X25519"_string); + return internal_object; }