/* * Copyright (c) 2024, Andrew Kaster * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include namespace Web::Crypto { // Out of line to ensure this class has a key function AlgorithmMethods::~AlgorithmMethods() = default; JS::ThrowCompletionOr> AlgorithmParams::from_value(JS::VM& vm, JS::Value value) { auto& object = value.as_object(); auto name = TRY(object.get("name")); auto name_string = TRY(name.to_string(vm)); return adopt_own(*new AlgorithmParams { .name = name_string }); } JS::ThrowCompletionOr> PBKDF2Params::from_value(JS::VM& vm, JS::Value value) { auto& realm = *vm.current_realm(); auto& object = value.as_object(); auto name_value = TRY(object.get("name")); auto name = TRY(name_value.to_string(vm)); auto salt_value = TRY(object.get("salt")); JS::Handle salt; if (!salt_value.is_object() || !(is(salt_value.as_object()) || is(salt_value.as_object()) || is(salt_value.as_object()))) return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "BufferSource"); salt = JS::make_handle(vm.heap().allocate(realm, salt_value.as_object())); auto iterations_value = TRY(object.get("iterations")); 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 }; } return adopt_own(*new PBKDF2Params { { name }, salt, iterations, hash.downcast() }); } WebIDL::ExceptionOr> PBKDF2::import_key(AlgorithmParams const&, Bindings::KeyFormat format, CryptoKey::InternalKeyData key_data, bool extractable, Vector const& key_usages) { // 1. If format is not "raw", throw a NotSupportedError if (format != Bindings::KeyFormat::Raw) { return WebIDL::NotSupportedError::create(m_realm, "Only raw format is supported"_fly_string); } // 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError. for (auto& usage : key_usages) { if (usage != Bindings::KeyUsage::Derivekey && usage != Bindings::KeyUsage::Derivebits) { return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); } } // 3. If extractable is not false, then throw a SyntaxError. if (extractable) return WebIDL::SyntaxError::create(m_realm, "extractable must be false"_fly_string); // 4. Let key be a new CryptoKey representing keyData. auto key = CryptoKey::create(m_realm, move(key_data)); // 5. Set the [[type]] internal slot of key to "secret". key->set_type(Bindings::KeyType::Secret); // 6. Set the [[extractable]] internal slot of key to false. key->set_extractable(false); // 7. Let algorithm be a new KeyAlgorithm object. auto algorithm = Bindings::KeyAlgorithm::create(m_realm); // 8. Set the name attribute of algorithm to "PBKDF2". algorithm->set_name("PBKDF2"_string); // 9. Set the [[algorithm]] internal slot of key to algorithm. key->set_algorithm(algorithm); // 10. Return key. return key; } WebIDL::ExceptionOr> SHA::digest(AlgorithmParams const& algorithm, ByteBuffer const& data) { auto& algorithm_name = algorithm.name; ::Crypto::Hash::HashKind hash_kind; if (algorithm_name.equals_ignoring_ascii_case("SHA-1"sv)) { hash_kind = ::Crypto::Hash::HashKind::SHA1; } else if (algorithm_name.equals_ignoring_ascii_case("SHA-256"sv)) { hash_kind = ::Crypto::Hash::HashKind::SHA256; } else if (algorithm_name.equals_ignoring_ascii_case("SHA-384"sv)) { hash_kind = ::Crypto::Hash::HashKind::SHA384; } else if (algorithm_name.equals_ignoring_ascii_case("SHA-512"sv)) { hash_kind = ::Crypto::Hash::HashKind::SHA512; } else { return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", algorithm_name))); } ::Crypto::Hash::Manager hash { hash_kind }; hash.update(data); auto digest = hash.digest(); auto result_buffer = ByteBuffer::copy(digest.immutable_data(), hash.digest_size()); if (result_buffer.is_error()) return WebIDL::OperationError::create(m_realm, "Failed to create result buffer"_fly_string); return JS::ArrayBuffer::create(m_realm, result_buffer.release_value()); } }