From 74403d7f1eff9c91992c9b3567ac40aa9f979f6d Mon Sep 17 00:00:00 2001 From: stelar7 Date: Wed, 30 Oct 2024 23:53:48 +0100 Subject: [PATCH] LibWeb: Implement AES-CTR.encrypt --- .../LibWeb/Crypto/CryptoAlgorithms.cpp | 51 +++++++++++++++++++ .../LibWeb/Crypto/CryptoAlgorithms.h | 17 +++++++ .../Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 1 + 3 files changed, 69 insertions(+) diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index c94d601b171..64e9bc91f6e 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -267,6 +267,26 @@ JS::ThrowCompletionOr> AesCbcParams::from_value(J return adopt_own(*new AesCbcParams { name, iv }); } +AesCtrParams::~AesCtrParams() = default; + +JS::ThrowCompletionOr> AesCtrParams::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("counter")); + 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 length_value = TRY(object.get("length")); + auto length = TRY(length_value.to_u8(vm)); + + return adopt_own(*new AesCtrParams { name, iv, length }); +} + HKDFParams::~HKDFParams() = default; JS::ThrowCompletionOr> HKDFParams::from_value(JS::VM& vm, JS::Value value) @@ -1645,6 +1665,37 @@ WebIDL::ExceptionOr, JS::NonnullGCPtr> AesCtr::encrypt(AlgorithmParams const& params, JS::NonnullGCPtr key, ByteBuffer const& plaintext) +{ + // 1. If the counter member of normalizedAlgorithm does not have length 16 bytes, then throw an OperationError. + auto const& normalized_algorithm = static_cast(params); + auto const& counter = normalized_algorithm.counter; + if (counter.size() != 16) + return WebIDL::OperationError::create(m_realm, "Invalid counter length"_string); + + // 2. If the length member of normalizedAlgorithm is zero or is greater than 128, then throw an OperationError. + auto const& length = normalized_algorithm.length; + if (length == 0 || length > 128) + return WebIDL::OperationError::create(m_realm, "Invalid length"_string); + + // 3. Let ciphertext be the result of performing the CTR Encryption operation described in Section 6.5 of [NIST-SP800-38A] using + // AES as the block cipher, + // the contents of the counter member of normalizedAlgorithm as the initial value of the counter block, + // the length member of normalizedAlgorithm as the input parameter m to the standard counter block incrementing function defined in Appendix B.1 of [NIST-SP800-38A] + // 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::CTRMode cipher(key_bytes, key_length, ::Crypto::Cipher::Intent::Encryption); + ByteBuffer ciphertext = TRY_OR_THROW_OOM(m_realm->vm(), ByteBuffer::create_zeroed(plaintext.size())); + Bytes ciphertext_span = ciphertext.bytes(); + cipher.encrypt(plaintext, ciphertext_span, counter); + + // 4. Return the result of creating an ArrayBuffer containing plaintext. + 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 70a1e6fb6ac..d53c045629b 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -66,6 +66,22 @@ struct AesCbcParams : public AlgorithmParams { static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); }; +// https://w3c.github.io/webcrypto/#dfn-AesCtrParams +struct AesCtrParams : public AlgorithmParams { + virtual ~AesCtrParams() override; + AesCtrParams(String name, ByteBuffer counter, u8 length) + : AlgorithmParams(move(name)) + , counter(move(counter)) + , length(length) + { + } + + ByteBuffer counter; + u8 length; + + static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); +}; + // https://w3c.github.io/webcrypto/#hkdf-params struct HKDFParams : public AlgorithmParams { virtual ~HKDFParams() override; @@ -333,6 +349,7 @@ public: virtual WebIDL::ExceptionOr> export_key(Bindings::KeyFormat, JS::NonnullGCPtr) override; virtual WebIDL::ExceptionOr get_key_length(AlgorithmParams const&) override; virtual WebIDL::ExceptionOr, JS::NonnullGCPtr>> generate_key(AlgorithmParams const&, bool, Vector const&) override; + virtual WebIDL::ExceptionOr> encrypt(AlgorithmParams const&, JS::NonnullGCPtr, ByteBuffer const&) override; static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new AesCtr(realm)); } diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index 5d7aac3be65..38e79e882bb 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -777,6 +777,7 @@ SupportedAlgorithmsMap supported_algorithms() define_an_algorithm("get key length"_string, "AES-CBC"_string); // https://w3c.github.io/webcrypto/#aes-ctr-registration + define_an_algorithm("encrypt"_string, "AES-CTR"_string); define_an_algorithm("importKey"_string, "AES-CTR"_string); define_an_algorithm("exportKey"_string, "AES-CTR"_string); define_an_algorithm("get key length"_string, "AES-CTR"_string);