| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| |
| #include "components/webcrypto/webcrypto_impl.h" |
| |
| #include <limits.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/check_op.h" |
| #include "base/containers/span.h" |
| #include "base/functional/bind.h" |
| #include "base/location.h" |
| #include "base/no_destructor.h" |
| #include "base/strings/string_view_util.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/task/task_runner.h" |
| #include "base/threading/thread.h" |
| #include "base/trace_event/trace_event.h" |
| #include "components/webcrypto/algorithm_dispatch.h" |
| #include "components/webcrypto/encapsulate_result.h" |
| #include "components/webcrypto/generate_key_result.h" |
| #include "components/webcrypto/status.h" |
| #include "third_party/blink/public/platform/web_crypto_key_algorithm.h" |
| #include "third_party/blink/public/platform/web_string.h" |
| #include "third_party/blink/public/web/web_crypto_histograms.h" |
| |
| namespace webcrypto { |
| |
| using webcrypto::Status; |
| |
| namespace { |
| |
| // --------------------- |
| // Threading |
| // --------------------- |
| // |
| // WebCrypto operations are generally fast, but a small handful can be slow. |
| // For instance generating an RSA key can take seconds. |
| // |
| // The strategy used here is to run a worker pool for potentially slow |
| // WebCrypto operations. This same pool is also used by requests started from |
| // Blink Web Workers. |
| // |
| // A few notes to keep in mind: |
| // |
| // * PostTaskAndReply() is not used because of how it handles failures -- it |
| // leaks the callback when failing to post back to the origin thread. |
| // |
| // This is a problem since WebCrypto may be called from WebWorker threads, |
| // which may be aborted at any time. Leaking would be undesirable, and |
| // reachable in practice. |
| // |
| // * blink::WebArrayBuffer is NOT threadsafe, and should therefore be allocated |
| // only on the target Blink thread. |
| // |
| // TODO(eroman): Is there any way around this? Copying the result between |
| // threads is silly. |
| // |
| // * WebCryptoAlgorithm and WebCryptoKey are threadsafe, by virtue of being |
| // immutable. Internally asymmetric WebCryptoKeys wrap BoringSSL's EVP_PKEY. |
| // These are safe to use for BoringSSL operations across threads, provided |
| // the internals of the EVP_PKEY are not mutated (they never should be |
| // following ImportKey()). |
| // |
| // * blink::WebCryptoResult is not threadsafe and should only be operated on |
| // the target Blink thread. HOWEVER, it is safe to delete it from any thread. |
| // This can happen if by the time the operation has completed in the crypto |
| // worker pool, the Blink worker thread that initiated the request is gone. |
| // Posting back to the origin thread will fail, and the WebCryptoResult will |
| // be deleted while running in the crypto worker pool. |
| class CryptoThreadPool { |
| public: |
| CryptoThreadPool() : worker_thread_("WebCrypto") { |
| base::Thread::Options options; |
| options.joinable = false; |
| worker_thread_.StartWithOptions(std::move(options)); |
| } |
| |
| CryptoThreadPool(const CryptoThreadPool&) = delete; |
| CryptoThreadPool& operator=(const CryptoThreadPool&) = delete; |
| |
| static bool PostTask(const base::Location& from_here, base::OnceClosure task); |
| |
| private: |
| // TODO(gab): the pool is currently using a single non-joinable thread to |
| // mimic the old behavior of using a CONTINUE_ON_SHUTDOWN SequencedTaskRunner |
| // on a single-threaded SequencedWorkerPool, but we'd like to consider using |
| // the ThreadPool here and allowing multiple threads (SEQUENCED or even |
| // PARALLEL ExecutionMode: http://crbug.com/623700). |
| base::Thread worker_thread_; |
| }; |
| |
| bool CryptoThreadPool::PostTask(const base::Location& from_here, |
| base::OnceClosure task) { |
| static base::NoDestructor<CryptoThreadPool> crypto_thread_pool; |
| return crypto_thread_pool->worker_thread_.task_runner()->PostTask( |
| from_here, std::move(task)); |
| } |
| |
| void CompleteWithThreadPoolError(blink::WebCryptoResult* result) { |
| result->CompleteWithError(blink::kWebCryptoErrorTypeOperation, |
| "Failed posting to crypto worker pool"); |
| } |
| |
| void CompleteWithError(const Status& status, blink::WebCryptoResult* result) { |
| DCHECK(status.IsError()); |
| |
| result->CompleteWithError(status.error_type(), |
| blink::WebString::FromUtf8(status.error_details())); |
| } |
| |
| void CompleteWithBufferOrError(const Status& status, |
| const std::vector<uint8_t>& buffer, |
| blink::WebCryptoResult* result) { |
| if (status.IsError()) { |
| CompleteWithError(status, result); |
| } else if (buffer.size() > UINT_MAX) { |
| // WebArrayBuffers have a smaller range than std::vector<>, so |
| // theoretically this could overflow. |
| CompleteWithError(Status::ErrorUnexpected(), result); |
| } else { |
| result->CompleteWithBuffer(buffer); |
| } |
| } |
| |
| void CompleteWithKeyOrError(const Status& status, |
| const blink::WebCryptoKey& key, |
| blink::WebCryptoResult* result) { |
| if (status.IsError()) { |
| CompleteWithError(status, result); |
| } else { |
| result->CompleteWithKey(key); |
| } |
| } |
| |
| // -------------------------------------------------------------------- |
| // State |
| // -------------------------------------------------------------------- |
| // |
| // Explicit state classes are used rather than base::Bind{Once,Repeating}. This |
| // is done both for clarity, but also to avoid extraneous allocations for things |
| // like passing buffers and result objects between threads. |
| // |
| // BaseState is the base class common to all of the async operations, and |
| // keeps track of the thread to complete on, the error state, and the |
| // callback into Blink. |
| // |
| // Ownership of the State object is passed between the crypto thread and the |
| // Blink thread. Under normal completion it is destroyed on the Blink thread. |
| // However it may also be destroyed on the crypto thread if the Blink thread |
| // has vanished (which can happen for Blink web worker threads). |
| |
| struct BaseState { |
| BaseState(const blink::WebCryptoResult& result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : origin_thread(task_runner), result(result) {} |
| |
| bool cancelled() { return result.Cancelled(); } |
| |
| scoped_refptr<base::TaskRunner> origin_thread; |
| |
| webcrypto::Status status; |
| blink::WebCryptoResult result; |
| |
| protected: |
| // Since there is no virtual destructor, must not delete directly as a |
| // BaseState. |
| ~BaseState() = default; |
| }; |
| |
| struct GenerateKeyState : public BaseState { |
| GenerateKeyState(const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| const blink::WebCryptoResult& result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : BaseState(result, std::move(task_runner)), |
| algorithm(algorithm), |
| extractable(extractable), |
| usages(usages) {} |
| |
| const blink::WebCryptoAlgorithm algorithm; |
| const bool extractable; |
| const blink::WebCryptoKeyUsageMask usages; |
| |
| webcrypto::GenerateKeyResult generate_key_result; |
| }; |
| |
| struct DeriveBitsState : public BaseState { |
| DeriveBitsState(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& base_key, |
| std::optional<unsigned int> length_bits, |
| const blink::WebCryptoResult& result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : BaseState(result, std::move(task_runner)), |
| algorithm(algorithm), |
| base_key(base_key), |
| length_bits(length_bits) {} |
| |
| const blink::WebCryptoAlgorithm algorithm; |
| const blink::WebCryptoKey base_key; |
| const std::optional<unsigned int> length_bits; |
| |
| std::vector<uint8_t> derived_bytes; |
| }; |
| |
| struct DeriveKeyState : public BaseState { |
| DeriveKeyState(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& base_key, |
| const blink::WebCryptoAlgorithm& import_algorithm, |
| const blink::WebCryptoAlgorithm& key_length_algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| const blink::WebCryptoResult& result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : BaseState(result, std::move(task_runner)), |
| algorithm(algorithm), |
| base_key(base_key), |
| import_algorithm(import_algorithm), |
| key_length_algorithm(key_length_algorithm), |
| extractable(extractable), |
| usages(usages) {} |
| |
| const blink::WebCryptoAlgorithm algorithm; |
| const blink::WebCryptoKey base_key; |
| const blink::WebCryptoAlgorithm import_algorithm; |
| const blink::WebCryptoAlgorithm key_length_algorithm; |
| bool extractable; |
| blink::WebCryptoKeyUsageMask usages; |
| |
| blink::WebCryptoKey derived_key; |
| }; |
| |
| // -------------------------------------------------------------------- |
| // Wrapper functions |
| // -------------------------------------------------------------------- |
| // |
| // * The methods named Do*() run on the crypto thread. |
| // * The methods named Do*Reply() run on the target Blink thread |
| |
| void DoGenerateKeyReply(std::unique_ptr<GenerateKeyState> state) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoGenerateKeyReply"); |
| if (state->status.IsError()) { |
| CompleteWithError(state->status, &state->result); |
| } else { |
| state->generate_key_result.Complete(&state->result); |
| } |
| } |
| |
| void DoGenerateKey(std::unique_ptr<GenerateKeyState> passed_state) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoGenerateKey"); |
| GenerateKeyState* state = passed_state.get(); |
| if (state->cancelled()) |
| return; |
| state->status = |
| webcrypto::GenerateKey(state->algorithm, state->extractable, |
| state->usages, &state->generate_key_result); |
| state->origin_thread->PostTask( |
| FROM_HERE, base::BindOnce(DoGenerateKeyReply, std::move(passed_state))); |
| } |
| |
| void DoDeriveBitsReply(std::unique_ptr<DeriveBitsState> state) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDeriveBitsReply"); |
| if (!state->status.IsError()) { |
| HistogramDeriveBitsTruncation(state->result.GetExecutionContext(), |
| state->length_bits, |
| state->status.warning_type()); |
| } |
| CompleteWithBufferOrError(state->status, state->derived_bytes, |
| &state->result); |
| } |
| |
| void DoDeriveBits(std::unique_ptr<DeriveBitsState> passed_state) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoDeriveBits"); |
| DeriveBitsState* state = passed_state.get(); |
| if (state->cancelled()) |
| return; |
| state->status = |
| webcrypto::DeriveBits(state->algorithm, state->base_key, |
| state->length_bits, &state->derived_bytes); |
| state->origin_thread->PostTask( |
| FROM_HERE, base::BindOnce(DoDeriveBitsReply, std::move(passed_state))); |
| } |
| |
| void DoDeriveKeyReply(std::unique_ptr<DeriveKeyState> state) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDeriveKeyReply"); |
| CompleteWithKeyOrError(state->status, state->derived_key, &state->result); |
| } |
| |
| void DoDeriveKey(std::unique_ptr<DeriveKeyState> passed_state) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoDeriveKey"); |
| DeriveKeyState* state = passed_state.get(); |
| if (state->cancelled()) |
| return; |
| state->status = webcrypto::DeriveKey( |
| state->algorithm, state->base_key, state->import_algorithm, |
| state->key_length_algorithm, state->extractable, state->usages, |
| &state->derived_key); |
| state->origin_thread->PostTask( |
| FROM_HERE, base::BindOnce(DoDeriveKeyReply, std::move(passed_state))); |
| } |
| |
| } // namespace |
| |
| WebCryptoImpl::WebCryptoImpl() { |
| } |
| |
| WebCryptoImpl::~WebCryptoImpl() { |
| } |
| |
| void WebCryptoImpl::Encrypt( |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| std::vector<unsigned char> data, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| DCHECK(!algorithm.IsNull()); |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| std::vector<unsigned char> buffer; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoEncrypt"); |
| status = webcrypto::Encrypt(algorithm, key, data, &buffer); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoEncryptReply"); |
| CompleteWithBufferOrError(status, buffer, &result); |
| } |
| } |
| |
| void WebCryptoImpl::Decrypt( |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| std::vector<unsigned char> data, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| DCHECK(!algorithm.IsNull()); |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| std::vector<unsigned char> buffer; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoDecrypt"); |
| status = webcrypto::Decrypt(algorithm, key, data, &buffer); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDecryptReply"); |
| CompleteWithBufferOrError(status, buffer, &result); |
| } |
| } |
| |
| void WebCryptoImpl::Digest( |
| const blink::WebCryptoAlgorithm& algorithm, |
| std::vector<unsigned char> data, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| DCHECK(!algorithm.IsNull()); |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| std::vector<unsigned char> buffer; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoDigest"); |
| status = webcrypto::Digest(algorithm, data, &buffer); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDigestReply"); |
| CompleteWithBufferOrError(status, buffer, &result); |
| } |
| } |
| |
| void WebCryptoImpl::GenerateKey( |
| const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| DCHECK(!algorithm.IsNull()); |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| blink::WebCryptoAlgorithmId id = algorithm.Id(); |
| if (id == blink::kWebCryptoAlgorithmIdRsaOaep || |
| id == blink::kWebCryptoAlgorithmIdRsaPss || |
| id == blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5) { |
| auto state = std::make_unique<GenerateKeyState>( |
| algorithm, extractable, usages, result, std::move(task_runner)); |
| if (!CryptoThreadPool::PostTask( |
| FROM_HERE, base::BindOnce(DoGenerateKey, std::move(state)))) { |
| CompleteWithThreadPoolError(&result); |
| } |
| return; |
| } |
| |
| webcrypto::Status status; |
| webcrypto::GenerateKeyResult generate_key_result; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoGenerateKey"); |
| status = webcrypto::GenerateKey(algorithm, extractable, usages, |
| &generate_key_result); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoGenerateKeyReply"); |
| if (status.IsError()) { |
| CompleteWithError(status, &result); |
| } else { |
| generate_key_result.Complete(&result); |
| } |
| } |
| } |
| |
| void WebCryptoImpl::ImportKey( |
| blink::WebCryptoKeyFormat format, |
| std::vector<unsigned char> key_data, |
| const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| blink::WebCryptoKey key; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoImportKey"); |
| status = webcrypto::ImportKey(format, key_data, algorithm, extractable, |
| usages, &key); |
| if (status.IsSuccess()) { |
| DCHECK(key.Handle()); |
| DCHECK(!key.Algorithm().IsNull()); |
| DCHECK_EQ(extractable, key.Extractable()); |
| } |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoImportKeyReply"); |
| CompleteWithKeyOrError(status, key, &result); |
| } |
| } |
| |
| void WebCryptoImpl::ExportKey( |
| blink::WebCryptoKeyFormat format, |
| const blink::WebCryptoKey& key, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| std::vector<unsigned char> buffer; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoExportKey"); |
| status = webcrypto::ExportKey(format, key, &buffer); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoExportKeyReply"); |
| if (format != blink::kWebCryptoKeyFormatJwk) { |
| CompleteWithBufferOrError(status, buffer, &result); |
| return; |
| } |
| |
| if (status.IsError()) { |
| CompleteWithError(status, &result); |
| } else { |
| result.CompleteWithJson(base::as_string_view(buffer)); |
| } |
| } |
| } |
| |
| void WebCryptoImpl::Sign( |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| std::vector<unsigned char> data, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| std::vector<unsigned char> buffer; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoSign"); |
| status = webcrypto::Sign(algorithm, key, data, &buffer); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoSignReply"); |
| CompleteWithBufferOrError(status, buffer, &result); |
| } |
| } |
| |
| void WebCryptoImpl::VerifySignature( |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| std::vector<unsigned char> signature, |
| std::vector<unsigned char> data, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| bool verify_result; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoVerify"); |
| status = webcrypto::Verify(algorithm, key, signature, data, &verify_result); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoVerifyReply"); |
| if (status.IsError()) { |
| CompleteWithError(status, &result); |
| } else { |
| result.CompleteWithBoolean(verify_result); |
| } |
| } |
| } |
| |
| void WebCryptoImpl::WrapKey( |
| blink::WebCryptoKeyFormat format, |
| const blink::WebCryptoKey& key, |
| const blink::WebCryptoKey& wrapping_key, |
| const blink::WebCryptoAlgorithm& wrap_algorithm, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| std::vector<unsigned char> buffer; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoWrapKey"); |
| status = |
| webcrypto::WrapKey(format, key, wrapping_key, wrap_algorithm, &buffer); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoWrapKeyReply"); |
| CompleteWithBufferOrError(status, buffer, &result); |
| } |
| } |
| |
| void WebCryptoImpl::UnwrapKey( |
| blink::WebCryptoKeyFormat format, |
| std::vector<unsigned char> wrapped_key, |
| const blink::WebCryptoKey& wrapping_key, |
| const blink::WebCryptoAlgorithm& unwrap_algorithm, |
| const blink::WebCryptoAlgorithm& unwrapped_key_algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| blink::WebCryptoKey unwrapped_key; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoUnwrapKey"); |
| status = webcrypto::UnwrapKey(format, wrapped_key, wrapping_key, |
| unwrap_algorithm, unwrapped_key_algorithm, |
| extractable, usages, &unwrapped_key); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoUnwrapKeyReply"); |
| CompleteWithKeyOrError(status, unwrapped_key, &result); |
| } |
| } |
| |
| void WebCryptoImpl::DeriveBits( |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& base_key, |
| std::optional<unsigned int> length_bits, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| // PBKDF2 can potentially be slow when passed a very large iteration count. |
| if (algorithm.Id() == blink::kWebCryptoAlgorithmIdPbkdf2) { |
| auto state = std::make_unique<DeriveBitsState>( |
| algorithm, base_key, length_bits, result, std::move(task_runner)); |
| if (!CryptoThreadPool::PostTask( |
| FROM_HERE, base::BindOnce(DoDeriveBits, std::move(state)))) { |
| CompleteWithThreadPoolError(&result); |
| } |
| return; |
| } |
| |
| webcrypto::Status status; |
| std::vector<uint8_t> derived_bytes; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDeriveBits"); |
| status = |
| webcrypto::DeriveBits(algorithm, base_key, length_bits, &derived_bytes); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDeriveBitsReply"); |
| if (!status.IsError()) { |
| HistogramDeriveBitsTruncation(result.GetExecutionContext(), length_bits, |
| status.warning_type()); |
| } |
| CompleteWithBufferOrError(status, derived_bytes, &result); |
| } |
| } |
| |
| void WebCryptoImpl::DeriveKey( |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& base_key, |
| const blink::WebCryptoAlgorithm& import_algorithm, |
| const blink::WebCryptoAlgorithm& key_length_algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| // PBKDF2 can potentially be slow when passed a very large iteration count. |
| if (algorithm.Id() == blink::kWebCryptoAlgorithmIdPbkdf2) { |
| auto state = std::make_unique<DeriveKeyState>( |
| algorithm, base_key, import_algorithm, key_length_algorithm, |
| extractable, usages, result, std::move(task_runner)); |
| if (!CryptoThreadPool::PostTask( |
| FROM_HERE, base::BindOnce(DoDeriveKey, std::move(state)))) { |
| CompleteWithThreadPoolError(&result); |
| } |
| return; |
| } |
| |
| webcrypto::Status status; |
| blink::WebCryptoKey derived_key; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoDeriveKey"); |
| status = webcrypto::DeriveKey(algorithm, base_key, import_algorithm, |
| key_length_algorithm, extractable, usages, |
| &derived_key); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDeriveKeyReply"); |
| CompleteWithKeyOrError(status, derived_key, &result); |
| } |
| } |
| |
| void WebCryptoImpl::EncapsulateKey( |
| const blink::WebCryptoAlgorithm& encapsulation_algorithm, |
| const blink::WebCryptoKey& encapsulation_key, |
| const blink::WebCryptoAlgorithm& shared_key_algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| webcrypto::EncapsulateKeyResult encapsulate_key_result; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoEncapsulateKey"); |
| status = webcrypto::EncapsulateKey( |
| encapsulation_algorithm, encapsulation_key, shared_key_algorithm, |
| extractable, usages, &encapsulate_key_result); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoEncapsulateKeyReply"); |
| if (status.IsError()) { |
| CompleteWithError(status, &result); |
| } else { |
| encapsulate_key_result.Complete(&result); |
| } |
| } |
| } |
| |
| void WebCryptoImpl::EncapsulateBits( |
| const blink::WebCryptoAlgorithm& encapsulation_algorithm, |
| const blink::WebCryptoKey& encapsulation_key, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| webcrypto::EncapsulateBitsResult encapsulate_bits_result; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoEncapsulateBits"); |
| status = webcrypto::EncapsulateBits( |
| encapsulation_algorithm, encapsulation_key, &encapsulate_bits_result); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoEncapsulateBitsReply"); |
| if (status.IsError()) { |
| CompleteWithError(status, &result); |
| } else { |
| encapsulate_bits_result.Complete(&result); |
| } |
| } |
| } |
| |
| void WebCryptoImpl::DecapsulateKey( |
| const blink::WebCryptoAlgorithm& decapsulation_algorithm, |
| const blink::WebCryptoKey& decapsulation_key, |
| std::vector<uint8_t> ciphertext, |
| const blink::WebCryptoAlgorithm& shared_key_algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| blink::WebCryptoKey shared_key; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDecapsulateKey"); |
| status = webcrypto::DecapsulateKey( |
| decapsulation_algorithm, decapsulation_key, ciphertext, |
| shared_key_algorithm, extractable, usages, &shared_key); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDecapsulateKeyReply"); |
| CompleteWithKeyOrError(status, shared_key, &result); |
| } |
| } |
| |
| void WebCryptoImpl::DecapsulateBits( |
| const blink::WebCryptoAlgorithm& decapsulation_algorithm, |
| const blink::WebCryptoKey& decapsulation_key, |
| std::vector<uint8_t> ciphertext, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| std::vector<uint8_t> shared_bits; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDecapsulateBits"); |
| status = webcrypto::DecapsulateBits( |
| decapsulation_algorithm, decapsulation_key, ciphertext, &shared_bits); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoDecapsulateBitsReply"); |
| CompleteWithBufferOrError(status, shared_bits, &result); |
| } |
| } |
| |
| void WebCryptoImpl::GetPublicKey( |
| const blink::WebCryptoKey& key, |
| blink::WebCryptoKeyUsageMask usages, |
| blink::WebCryptoResult result, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| if (result.Cancelled()) { |
| return; |
| } |
| |
| webcrypto::Status status; |
| blink::WebCryptoKey public_key; |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoGetPublicKey"); |
| status = webcrypto::GetPublicKey(key, usages, &public_key); |
| } |
| { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "DoGetPublicKeyReply"); |
| CompleteWithKeyOrError(status, public_key, &result); |
| } |
| } |
| |
| bool WebCryptoImpl::Supports(blink::WebCryptoOperation op, |
| const blink::WebCryptoAlgorithm& algorithm, |
| std::optional<unsigned int> length_bits) { |
| return webcrypto::Supports(op, algorithm, length_bits); |
| } |
| |
| bool WebCryptoImpl::DeserializeKeyForClone( |
| const blink::WebCryptoKeyAlgorithm& algorithm, |
| blink::WebCryptoKeyType type, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usages, |
| base::span<const unsigned char> key_data, |
| blink::WebCryptoKey& key) { |
| return webcrypto::DeserializeKeyForClone(algorithm, type, extractable, usages, |
| key_data, &key); |
| } |
| |
| bool WebCryptoImpl::SerializeKeyForClone(const blink::WebCryptoKey& key, |
| std::vector<unsigned char>& key_data) { |
| return webcrypto::SerializeKeyForClone(key, &key_data); |
| } |
| |
| bool WebCryptoImpl::GetKeyLength( |
| const blink::WebCryptoAlgorithm& key_length_algorithm, |
| std::optional<unsigned int>* length_bits) { |
| webcrypto::Status status = |
| webcrypto::GetKeyLength(key_length_algorithm, length_bits); |
| return !status.IsError(); |
| } |
| |
| } // namespace webcrypto |