diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index d07cfb266be..877b7d954f1 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -296,6 +296,37 @@ JS::ThrowCompletionOr> AesCtrParams::from_value(J return adopt_own(*new AesCtrParams { name, iv, length }); } +AesGcmParams::~AesGcmParams() = default; + +JS::ThrowCompletionOr> AesGcmParams::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 iv_value = TRY(object.get("iv")); + if (!iv_value.is_object() || !(is(iv_value.as_object()) || is(iv_value.as_object()) || is(iv_value.as_object()))) + return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "BufferSource"); + auto iv = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(iv_value.as_object())); + + auto maybe_additional_data = Optional {}; + if (MUST(object.has_property("additionalData"))) { + auto additional_data_value = TRY(object.get("additionalData")); + if (!additional_data_value.is_object() || !(is(additional_data_value.as_object()) || is(additional_data_value.as_object()) || is(additional_data_value.as_object()))) + return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "BufferSource"); + maybe_additional_data = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(additional_data_value.as_object())); + } + + auto maybe_tag_length = Optional {}; + if (MUST(object.has_property("tagLength"))) { + auto tag_length_value = TRY(object.get("tagLength")); + maybe_tag_length = TRY(tag_length_value.to_u8(vm)); + } + + return adopt_own(*new AesGcmParams { name, iv, maybe_additional_data, maybe_tag_length }); +} + HKDFParams::~HKDFParams() = default; JS::ThrowCompletionOr> HKDFParams::from_value(JS::VM& vm, JS::Value value) @@ -1958,6 +1989,58 @@ WebIDL::ExceptionOr> AesGcm::export_key(Bindings::K return JS::NonnullGCPtr { *result }; } +WebIDL::ExceptionOr> AesGcm::encrypt(AlgorithmParams const& params, JS::NonnullGCPtr key, ByteBuffer const& plaintext) +{ + auto const& normalized_algorithm = static_cast(params); + + // FIXME: 1. If plaintext has a length greater than 2^39 - 256 bytes, then throw an OperationError. + + // FIXME: 2. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes, then throw an OperationError. + + // FIXME: 3. If the additionalData member of normalizedAlgorithm is present and has a length greater than 2^64 - 1 bytes, then throw an OperationError. + + // 4. If the tagLength member of normalizedAlgorithm is not present: Let tagLength be 128. + auto tag_length = 0; + auto to_compare_against = Vector { 32, 64, 96, 104, 112, 120, 128 }; + if (!normalized_algorithm.tag_length.has_value()) + tag_length = 128; + + // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128: Let tagLength be equal to the tagLength member of normalizedAlgorithm + else if (to_compare_against.contains_slow(normalized_algorithm.tag_length.value())) + tag_length = normalized_algorithm.tag_length.value(); + + // Otherwise: throw an OperationError. + else + return WebIDL::OperationError::create(m_realm, "Invalid tag length"_string); + + // 5. Let additionalData be the contents of the additionalData member of normalizedAlgorithm if present or the empty octet string otherwise. + auto additional_data = normalized_algorithm.additional_data.value_or(ByteBuffer {}); + + // 6. Let C and T be the outputs that result from performing the Authenticated Encryption Function described in Section 7.1 of [NIST-SP800-38D] using + // AES as the block cipher, + // the contents of the iv member of normalizedAlgorithm as the IV input parameter, + // the contents of additionalData as the A input parameter, + // tagLength as the t pre-requisite + // and the contents of plaintext as the input plaintext. + auto& aes_algorithm = static_cast(*key->algorithm()); + auto key_length = aes_algorithm.length(); + auto key_bytes = key->handle().get(); + + ::Crypto::Cipher::AESCipher::GCMMode cipher(key_bytes, key_length, ::Crypto::Cipher::Intent::Encryption); + ByteBuffer ciphertext = TRY_OR_THROW_OOM(m_realm->vm(), ByteBuffer::create_zeroed(plaintext.size())); + ByteBuffer tag = TRY_OR_THROW_OOM(m_realm->vm(), ByteBuffer::create_zeroed(tag_length / 8)); + [[maybe_unused]] Bytes ciphertext_span = ciphertext.bytes(); + [[maybe_unused]] Bytes tag_span = tag.bytes(); + + // FIXME: cipher.encrypt(plaintext, ciphertext_span, normalized_algorithm.iv, additional_data, tag_span); + + // 7. Let ciphertext be equal to C | T, where '|' denotes concatenation. + TRY_OR_THROW_OOM(m_realm->vm(), ciphertext.try_append(tag)); + + // 8. Return the result of creating an ArrayBuffer containing ciphertext. + return JS::ArrayBuffer::create(m_realm, ciphertext); +} + // 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 702210eb51f..4ef1a08092e 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -83,6 +83,24 @@ struct AesCtrParams : public AlgorithmParams { static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); }; +// https://w3c.github.io/webcrypto/#dfn-AesGcmParams +struct AesGcmParams : public AlgorithmParams { + virtual ~AesGcmParams() override; + AesGcmParams(String name, ByteBuffer iv, Optional additional_data, Optional tag_length) + : AlgorithmParams(move(name)) + , iv(move(iv)) + , additional_data(move(additional_data)) + , tag_length(tag_length) + { + } + + ByteBuffer iv; + Optional additional_data; + Optional tag_length; + + static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); +}; + // https://w3c.github.io/webcrypto/#hkdf-params struct HKDFParams : public AlgorithmParams { virtual ~HKDFParams() override; @@ -367,6 +385,7 @@ 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; + virtual WebIDL::ExceptionOr> encrypt(AlgorithmParams const&, JS::NonnullGCPtr, ByteBuffer const&) 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 cccf5cb0682..7189bf7dd89 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -788,6 +788,7 @@ SupportedAlgorithmsMap supported_algorithms() 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); + define_an_algorithm("encrypt"_string, "AES-GCM"_string); // https://w3c.github.io/webcrypto/#hkdf define_an_algorithm("importKey"_string, "HKDF"_string);