From 3dd80d2a6e0db87f1bef628a81d4a5a89ac6f7eb Mon Sep 17 00:00:00 2001 From: stelar7 Date: Thu, 31 Oct 2024 16:02:24 +0100 Subject: [PATCH] LibWeb: Implement AES-GCM.exportKey --- .../LibWeb/Crypto/CryptoAlgorithms.cpp | 68 +++++++++++++++++++ .../LibWeb/Crypto/CryptoAlgorithms.h | 1 + .../Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 1 + 3 files changed, 70 insertions(+) diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index dbed1bbf8cd..d07cfb266be 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -1890,6 +1890,74 @@ WebIDL::ExceptionOr> AesGcm::import_key(AlgorithmPar return key; } +WebIDL::ExceptionOr> AesGcm::export_key(Bindings::KeyFormat format, JS::NonnullGCPtr key) +{ + // 1. If the underlying cryptographic key material represented by the [[handle]] internal slot of key cannot be accessed, then throw an OperationError. + // Note: In our impl this is always accessible + + JS::GCPtr result = nullptr; + + // 2. If format is "raw": + if (format == Bindings::KeyFormat::Raw) { + // 1. Let data be the raw octets of the key represented by [[handle]] internal slot of key. + auto data = key->handle().get(); + + // 2. Let result be the result of creating an ArrayBuffer containing data. + result = JS::ArrayBuffer::create(m_realm, data); + } + + // 2. If format is "jwk": + else if (format == Bindings::KeyFormat::Jwk) { + // 1. Let jwk be a new JsonWebKey dictionary. + Bindings::JsonWebKey jwk = {}; + + // 2. Set the kty attribute of jwk to the string "oct". + jwk.kty = "oct"_string; + + // 3. Set the k attribute of jwk to be a string containing the raw octets of the key represented by [[handle]] internal slot of key, + // encoded according to Section 6.4 of JSON Web Algorithms [JWA]. + auto const& key_bytes = key->handle().get(); + jwk.k = TRY_OR_THROW_OOM(m_realm->vm(), encode_base64url(key_bytes, AK::OmitPadding::Yes)); + + // 4. -> If the length attribute of key is 128: + // Set the alg attribute of jwk to the string "A128GCM". + // -> If the length attribute of key is 192: + // Set the alg attribute of jwk to the string "A192GCM". + // -> If the length attribute of key is 256: + // Set the alg attribute of jwk to the string "A256GCM". + auto key_bits = key_bytes.size() * 8; + if (key_bits == 128) { + jwk.alg = "A128GCM"_string; + } else if (key_bits == 192) { + jwk.alg = "A192GCM"_string; + } else if (key_bits == 256) { + jwk.alg = "A256GCM"_string; + } + + // 5. Set the key_ops attribute of jwk to the usages attribute of key. + jwk.key_ops = Vector {}; + jwk.key_ops->ensure_capacity(key->internal_usages().size()); + for (auto const& usage : key->internal_usages()) { + jwk.key_ops->append(Bindings::idl_enum_to_string(usage)); + } + + // 6. Set the ext attribute of jwk to equal the [[extractable]] internal slot of key. + jwk.ext = key->extractable(); + + // 7. Let result be the result of converting jwk to an ECMAScript Object, as defined by [WebIDL]. + result = TRY(jwk.to_object(m_realm)); + } + + // 2. Otherwise: + else { + // 1. throw a NotSupportedError. + return WebIDL::NotSupportedError::create(m_realm, "Cannot export to unsupported format"_string); + } + + // 3. Return result. + return JS::NonnullGCPtr { *result }; +} + // https://w3c.github.io/webcrypto/#hkdf-operations WebIDL::ExceptionOr> HKDF::import_key(AlgorithmParams const&, Bindings::KeyFormat format, CryptoKey::InternalKeyData key_data, bool extractable, Vector const& key_usages) { diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index 06e63105c82..702210eb51f 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -366,6 +366,7 @@ class AesGcm : public AlgorithmMethods { public: virtual WebIDL::ExceptionOr get_key_length(AlgorithmParams const&) override; virtual WebIDL::ExceptionOr> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector const&) override; + virtual WebIDL::ExceptionOr> export_key(Bindings::KeyFormat, JS::NonnullGCPtr) override; static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new AesGcm(realm)); } diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index bf3a38c5989..cccf5cb0682 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -787,6 +787,7 @@ SupportedAlgorithmsMap supported_algorithms() // https://w3c.github.io/webcrypto/#aes-gcm-registration define_an_algorithm("get key length"_string, "AES-GCM"_string); define_an_algorithm("importKey"_string, "AES-GCM"_string); + define_an_algorithm("exportKey"_string, "AES-GCM"_string); // https://w3c.github.io/webcrypto/#hkdf define_an_algorithm("importKey"_string, "HKDF"_string);