// 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
