diff --git a/Tests/LibWeb/Text/expected/Crypto/SubtleCrypto-deriveBits.txt b/Tests/LibWeb/Text/expected/Crypto/SubtleCrypto-deriveBits.txt new file mode 100644 index 00000000000..b7e19348c3f --- /dev/null +++ b/Tests/LibWeb/Text/expected/Crypto/SubtleCrypto-deriveBits.txt @@ -0,0 +1 @@ +Derived bits OK diff --git a/Tests/LibWeb/Text/input/Crypto/SubtleCrypto-deriveBits.html b/Tests/LibWeb/Text/input/Crypto/SubtleCrypto-deriveBits.html new file mode 100644 index 00000000000..91e6ae7157a --- /dev/null +++ b/Tests/LibWeb/Text/input/Crypto/SubtleCrypto-deriveBits.html @@ -0,0 +1,54 @@ + + diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 276788e4f36..e501127acfa 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -7,9 +7,13 @@ #include #include #include +#include #include #include #include +#include +#include +#include #include #include #include @@ -235,16 +239,9 @@ JS::ThrowCompletionOr> PBKDF2Params::from_value(J auto iterations = TRY(iterations_value.to_u32(vm)); auto hash_value = TRY(object.get("hash")); - auto hash = Variant { Empty {} }; - if (hash_value.is_string()) { - auto hash_string = TRY(hash_value.to_string(vm)); - hash = HashAlgorithmIdentifier { hash_string }; - } else { - auto hash_object = TRY(hash_value.to_object(vm)); - hash = HashAlgorithmIdentifier { hash_object }; - } + auto hash = TRY(hash_value.to_string(vm)); - return adopt_own(*new PBKDF2Params { name, salt, iterations, hash.downcast() }); + return adopt_own(*new PBKDF2Params { name, salt, iterations, hash }); } RsaKeyGenParams::~RsaKeyGenParams() = default; @@ -1342,4 +1339,62 @@ WebIDL::ExceptionOr ED25519::verify([[maybe_unused]] AlgorithmParams return JS::Value(result); } +WebIDL::ExceptionOr> PBKDF2::derive_bits(AlgorithmParams const& params, JS::NonnullGCPtr key, u32 length) +{ + auto& realm = m_realm; + auto const& normalized_algorithm = static_cast(params); + + // 1. If length is null or zero, or is not a multiple of 8, then throw an OperationError. + if (length == 0 || length % 8 != 0) + return WebIDL::OperationError::create(realm, "Length must be greater than 0 and divisible by 8"_fly_string); + + // 2. If the iterations member of normalizedAlgorithm is zero, then throw an OperationError. + if (normalized_algorithm.iterations == 0) + return WebIDL::OperationError::create(realm, "Iterations must be greater than 0"_fly_string); + + // 3. Let prf be the MAC Generation function described in Section 4 of [FIPS-198-1] using the hash function described by the hash member of normalizedAlgorithm. + auto const& hash_algorithm = TRY(normalized_algorithm.hash.visit( + [](String const& name) -> JS::ThrowCompletionOr { return name; }, + [&](JS::Handle const& obj) -> JS::ThrowCompletionOr { + auto name_property = TRY(obj->get("name")); + return name_property.to_string(m_realm.vm()); })); + + // 4. Let result be the result of performing the PBKDF2 operation defined in Section 5.2 of [RFC8018] + // using prf as the pseudo-random function, PRF, + // the password represented by [[handle]] internal slot of key as the password, P, + // the contents of the salt attribute of normalizedAlgorithm as the salt, S, + // the value of the iterations attribute of normalizedAlgorithm as the iteration count, c, + // and length divided by 8 as the intended key length, dkLen. + ErrorOr result = Error::from_string_view("noop error"sv); + + auto password = key->handle().visit( + [](ByteBuffer data) -> ByteBuffer { + return data; + }, + [](auto) -> ByteBuffer { VERIFY_NOT_REACHED(); }); + + auto salt = normalized_algorithm.salt; + auto iterations = normalized_algorithm.iterations; + auto derived_key_length_bytes = length / 8; + + if (hash_algorithm.equals_ignoring_ascii_case("SHA-1"sv)) { + result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA1>>(password, salt, iterations, derived_key_length_bytes); + } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-256"sv)) { + result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA256>>(password, salt, iterations, derived_key_length_bytes); + } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-384"sv)) { + result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA384>>(password, salt, iterations, derived_key_length_bytes); + } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-512"sv)) { + result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA512>>(password, salt, iterations, derived_key_length_bytes); + } else { + return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", hash_algorithm))); + } + + // 5. If the key derivation operation fails, then throw an OperationError. + if (result.is_error()) + return WebIDL::OperationError::create(realm, "Failed to derive key"_fly_string); + + // 6. Return result + return JS::ArrayBuffer::create(realm, result.release_value()); +} + } diff --git a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index 68cbb56596b..ffcd20aa638 100644 --- a/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -230,6 +230,7 @@ private: class PBKDF2 : public AlgorithmMethods { public: virtual WebIDL::ExceptionOr> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector const&) override; + virtual WebIDL::ExceptionOr> derive_bits(AlgorithmParams const&, JS::NonnullGCPtr, u32) override; static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new PBKDF2(realm)); } diff --git a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index 6dfa986f470..074f97e04c2 100644 --- a/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -676,7 +676,7 @@ SupportedAlgorithmsMap supported_algorithms() // https://w3c.github.io/webcrypto/#pbkdf2 define_an_algorithm("importKey"_string, "PBKDF2"_string); - // FIXME: define_an_algorithm("deriveBits"_string, "PBKDF2"_string, "Pbkdf2Params"_string); + define_an_algorithm("deriveBits"_string, "PBKDF2"_string); // FIXME: define_an_algorithm("get key length"_string, "PBKDF2"_string, ""_string); // https://w3c.github.io/webcrypto/#rsa-oaep