diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 877b7d954f1..e5964508c04 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -2041,6 +2041,72 @@ WebIDL::ExceptionOr> AesGcm::encrypt(Algorithm return JS::ArrayBuffer::create(m_realm, ciphertext); } +WebIDL::ExceptionOr> AesGcm::decrypt(AlgorithmParams const& params, JS::NonnullGCPtr key, ByteBuffer const& ciphertext) +{ + auto const& normalized_algorithm = static_cast(params); + + // 1. If the tagLength member of normalizedAlgorithm is not present: Let tagLength be 128. + u32 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); + + // 2. If ciphertext has a length less than tagLength bits, then throw an OperationError. + if (ciphertext.size() < tag_length / 8) + return WebIDL::OperationError::create(m_realm, "Invalid ciphertext length"_string); + + // FIXME: 3. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes, then throw an OperationError. + + // FIXME: 4. If the additionalData member of normalizedAlgorithm is present and has a length greater than 2^64 - 1 bytes, then throw an OperationError. + + // 5. Let tag be the last tagLength bits of ciphertext. + auto tag_bits = tag_length / 8; + auto tag = TRY_OR_THROW_OOM(m_realm->vm(), ciphertext.slice(ciphertext.size() - tag_bits, tag_bits)); + + // 6. Let actualCiphertext be the result of removing the last tagLength bits from ciphertext. + auto actual_ciphertext = TRY_OR_THROW_OOM(m_realm->vm(), ciphertext.slice(0, ciphertext.size() - tag_bits)); + + // 7. 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 {}); + + // 8. Perform the Authenticated Decryption Function described in Section 7.2 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, + // the contents of actualCiphertext as the input ciphertext, C + // and the contents of tag as the authentication tag, T. + 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::Decryption); + ByteBuffer plaintext = TRY_OR_THROW_OOM(m_realm->vm(), ByteBuffer::create_zeroed(actual_ciphertext.size())); + [[maybe_unused]] Bytes plaintext_span = plaintext.bytes(); + [[maybe_unused]] Bytes actual_ciphertext_span = actual_ciphertext.bytes(); + [[maybe_unused]] Bytes tag_span = tag.bytes(); + + // FIXME: auto result = cipher.decrypt(ciphertext, plaintext_span, normalized_algorithm.iv, additional_data, tag_span); + auto result = ::Crypto::VerificationConsistency::Inconsistent; + + // If the result of the algorithm is the indication of inauthenticity, "FAIL": throw an OperationError + if (result == ::Crypto::VerificationConsistency::Inconsistent) + return WebIDL::OperationError::create(m_realm, "Decryption failed"_string); + + // Otherwise: Let plaintext be the output P of the Authenticated Decryption Function. + + // 9. Return the result of creating an ArrayBuffer containing plaintext. + return JS::ArrayBuffer::create(m_realm, plaintext); +} + // 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 4ef1a08092e..8520367d3a5 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -386,6 +386,7 @@ public: 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; + virtual WebIDL::ExceptionOr> decrypt(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 7189bf7dd89..da5c8db7945 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -789,6 +789,7 @@ SupportedAlgorithmsMap supported_algorithms() define_an_algorithm("importKey"_string, "AES-GCM"_string); define_an_algorithm("exportKey"_string, "AES-GCM"_string); define_an_algorithm("encrypt"_string, "AES-GCM"_string); + define_an_algorithm("decrypt"_string, "AES-GCM"_string); // https://w3c.github.io/webcrypto/#hkdf define_an_algorithm("importKey"_string, "HKDF"_string);