Serenity Operating System
at master 102 lines 4.1 kB view raw
1/* 2 * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibCrypto/Hash/HashManager.h> 8#include <LibJS/Runtime/ArrayBuffer.h> 9#include <LibJS/Runtime/Promise.h> 10#include <LibWeb/Bindings/Intrinsics.h> 11#include <LibWeb/Crypto/SubtleCrypto.h> 12#include <LibWeb/WebIDL/AbstractOperations.h> 13#include <LibWeb/WebIDL/ExceptionOr.h> 14 15namespace Web::Crypto { 16 17WebIDL::ExceptionOr<JS::NonnullGCPtr<SubtleCrypto>> SubtleCrypto::create(JS::Realm& realm) 18{ 19 return MUST_OR_THROW_OOM(realm.heap().allocate<SubtleCrypto>(realm, realm)); 20} 21 22SubtleCrypto::SubtleCrypto(JS::Realm& realm) 23 : PlatformObject(realm) 24{ 25} 26 27SubtleCrypto::~SubtleCrypto() = default; 28 29JS::ThrowCompletionOr<void> SubtleCrypto::initialize(JS::Realm& realm) 30{ 31 MUST_OR_THROW_OOM(Base::initialize(realm)); 32 set_prototype(&Bindings::ensure_web_prototype<Bindings::SubtleCryptoPrototype>(realm, "SubtleCrypto")); 33 34 return {}; 35} 36 37// https://w3c.github.io/webcrypto/#dfn-SubtleCrypto-method-digest 38JS::NonnullGCPtr<JS::Promise> SubtleCrypto::digest(String const& algorithm, JS::Handle<JS::Object> const& data) 39{ 40 auto& realm = this->realm(); 41 42 // 1. Let algorithm be the algorithm parameter passed to the digest() method. 43 44 // 2. Let data be the result of getting a copy of the bytes held by the data parameter passed to the digest() method. 45 auto data_buffer_or_error = WebIDL::get_buffer_source_copy(*data.cell()); 46 if (data_buffer_or_error.is_error()) { 47 auto error = WebIDL::OperationError::create(realm, "Failed to copy bytes from ArrayBuffer"); 48 auto promise = JS::Promise::create(realm); 49 promise->reject(error.ptr()); 50 return promise; 51 } 52 auto& data_buffer = data_buffer_or_error.value(); 53 54 // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "digest". 55 // FIXME: This is way more generic than it needs to be right now, so we simplify it. 56 ::Crypto::Hash::HashKind hash_kind; 57 auto algorithm_as_string_view = algorithm.bytes_as_string_view(); 58 if (algorithm_as_string_view.equals_ignoring_ascii_case("SHA-1"sv)) { 59 hash_kind = ::Crypto::Hash::HashKind::SHA1; 60 } else if (algorithm_as_string_view.equals_ignoring_ascii_case("SHA-256"sv)) { 61 hash_kind = ::Crypto::Hash::HashKind::SHA256; 62 } else if (algorithm_as_string_view.equals_ignoring_ascii_case("SHA-384"sv)) { 63 hash_kind = ::Crypto::Hash::HashKind::SHA384; 64 } else if (algorithm_as_string_view.equals_ignoring_ascii_case("SHA-512"sv)) { 65 hash_kind = ::Crypto::Hash::HashKind::SHA512; 66 } 67 // 4. If an error occurred, return a Promise rejected with normalizedAlgorithm. 68 else { 69 auto error = WebIDL::NotSupportedError::create(realm, DeprecatedString::formatted("Invalid hash function '{}'", algorithm)); 70 auto promise = JS::Promise::create(realm); 71 promise->reject(error.ptr()); 72 return promise; 73 } 74 75 // 5. Let promise be a new Promise. 76 auto promise = JS::Promise::create(realm); 77 78 // 6. Return promise and perform the remaining steps in parallel. 79 // FIXME: We don't have a good abstraction for this yet, so we do it in sync. 80 81 // 7. If the following steps or referenced procedures say to throw an error, reject promise with the returned error and then terminate the algorithm. 82 83 // 8. Let result be the result of performing the digest operation specified by normalizedAlgorithm using algorithm, with data as message. 84 ::Crypto::Hash::Manager hash { hash_kind }; 85 hash.update(data_buffer); 86 87 auto digest = hash.digest(); 88 auto result_buffer = ByteBuffer::copy(digest.immutable_data(), hash.digest_size()); 89 if (result_buffer.is_error()) { 90 auto error = WebIDL::OperationError::create(realm, "Failed to create result buffer"); 91 promise->reject(error.ptr()); 92 return promise; 93 } 94 95 auto result = JS::ArrayBuffer::create(realm, result_buffer.release_value()); 96 97 // 9. Resolve promise with result. 98 promise->fulfill(result); 99 return promise; 100} 101 102}