/*
 * Copyright (C) 2013 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "third_party/blink/renderer/modules/crypto/subtle_crypto.h"

#include "base/check_deref.h"
#include "base/task/single_thread_task_runner.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/web_crypto.h"
#include "third_party/blink/public/platform/web_crypto_algorithm.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
#include "third_party/blink/public/web/web_crypto_histograms.h"
#include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encapsulated_bits.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encapsulated_key.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_json_web_key.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_arraybuffer_arraybufferview_jsonwebkey.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_arraybuffer_jsonwebkey.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_cryptokey_cryptokeypair.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
#include "third_party/blink/renderer/modules/crypto/crypto_key.h"
#include "third_party/blink/renderer/modules/crypto/crypto_result_impl.h"
#include "third_party/blink/renderer/modules/crypto/crypto_utilities.h"
#include "third_party/blink/renderer/modules/crypto/normalize_algorithm.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/json/json_values.h"

namespace blink {

namespace {

// Parses a JsonWebKey dictionary. On success writes the result to
// |jsonUtf8| as a UTF8-encoded JSON octet string and returns true.
// On failure sets an error on |result| and returns false.
//
// Note: The choice of output as an octet string is to facilitate interop
// with the non-JWK formats, but does mean there is a second parsing step.
// This design choice should be revisited after crbug.com/614385).
bool ParseJsonWebKey(const JsonWebKey& key, std::vector<uint8_t>& json_utf8) {
  auto json_object = std::make_unique<JSONObject>();

  if (key.hasKty())
    json_object->SetString("kty", key.kty());
  if (key.hasUse())
    json_object->SetString("use", key.use());
  if (key.hasKeyOps()) {
    auto json_array = std::make_unique<JSONArray>();
    for (auto&& value : key.keyOps())
      json_array->PushString(value);
    json_object->SetArray("key_ops", std::move(json_array));
  }
  if (key.hasAlg())
    json_object->SetString("alg", key.alg());
  if (key.hasExt())
    json_object->SetBoolean("ext", key.ext());

  if (key.hasCrv())
    json_object->SetString("crv", key.crv());
  if (key.hasX())
    json_object->SetString("x", key.x());
  if (key.hasY())
    json_object->SetString("y", key.y());
  if (key.hasD())
    json_object->SetString("d", key.d());
  if (key.hasN())
    json_object->SetString("n", key.n());
  if (key.hasE())
    json_object->SetString("e", key.e());
  if (key.hasP())
    json_object->SetString("p", key.p());
  if (key.hasQ())
    json_object->SetString("q", key.q());
  if (key.hasDp())
    json_object->SetString("dp", key.dp());
  if (key.hasDq())
    json_object->SetString("dq", key.dq());
  if (key.hasQi())
    json_object->SetString("qi", key.qi());
  // TODO(eroman): Parse "oth" (crbug.com/441396)
  if (key.hasK())
    json_object->SetString("k", key.k());
  if (key.hasPub()) {
    json_object->SetString("pub", key.pub());
  }
  if (key.hasPriv()) {
    json_object->SetString("priv", key.priv());
  }

  std::string json = json_object->ToJSONString().Utf8();
  json_utf8 = base::ToVector(base::as_byte_span(json));
  return true;
}

std::optional<WebCryptoOperation> StringToWebCryptoOperation(const String& op) {
  if (op == "encrypt") {
    return kWebCryptoOperationEncrypt;
  }
  if (op == "decrypt") {
    return kWebCryptoOperationDecrypt;
  }
  if (op == "sign") {
    return kWebCryptoOperationSign;
  }
  if (op == "verify") {
    return kWebCryptoOperationVerify;
  }
  if (op == "digest") {
    return kWebCryptoOperationDigest;
  }
  if (op == "generateKey") {
    return kWebCryptoOperationGenerateKey;
  }
  if (op == "importKey") {
    return kWebCryptoOperationImportKey;
  }
  if (op == "deriveKey" || op == "deriveBits") {
    return kWebCryptoOperationDeriveBits;
  }
  if (op == "wrapKey") {
    return kWebCryptoOperationWrapKey;
  }
  if (op == "unwrapKey") {
    return kWebCryptoOperationUnwrapKey;
  }
  if (op == "encapsulateKey" || op == "encapsulateBits") {
    return kWebCryptoOperationEncapsulate;
  }
  if (op == "decapsulateKey" || op == "decapsulateBits") {
    return kWebCryptoOperationDecapsulate;
  }
  if (op == "getPublicKey") {
    return kWebCryptoOperationGetPublicKey;
  }
  return std::nullopt;
}

// Implements
// https://wicg.github.io/webcrypto-modern-algos/#partial-subtlecrypto-interface-checking-support-for-algorithm,
// though for many of the operations we will just return true without calling
// into the algorithm implementation if we do not need to.
bool supportsInternal(ScriptState* script_state,
                      WebCryptoOperation op,
                      const V8AlgorithmIdentifier* algorithm,
                      std::optional<unsigned int> length_bits) {
  WebCryptoAlgorithm normalized_algorithm;
  // 3.3.4.4  Let normalizedAlgorithm be the result of normalizing an algorithm,
  //          with alg set to alg and op set to op.
  if (!NormalizeAlgorithm(script_state->GetIsolate(), algorithm, op,
                          normalized_algorithm, IGNORE_EXCEPTION)) {
    // 3.3.4.5  If an error occurred:
    //
    //          If op is "wrapKey", return the result of checking support for an
    //          algorithm with op set to "encrypt" and alg set to alg.
    //
    //          If op is "unwrapKey", return the result of checking support for
    //          an algorithm with op set to "decrypt" and alg set to alg.
    //
    //           Otherwise, return false.
    if (op == kWebCryptoOperationWrapKey) {
      return supportsInternal(script_state, kWebCryptoOperationEncrypt,
                              algorithm, length_bits);
    } else if (op == kWebCryptoOperationUnwrapKey) {
      return supportsInternal(script_state, kWebCryptoOperationDecrypt,
                              algorithm, length_bits);
    }
    return false;
  }

  // NormalizeAlgorithm already ensures that
  //    (a) we support the algorithm
  //    (b) we support the op on that specific algorithm
  //
  // so we only need to do additional checks there are parameters as part of the
  // algorithm for that operation, or if we have to check the length_bits
  // parameter for deriveBits/deriveKey.
  switch (op) {
    case kWebCryptoOperationEncrypt:
    case kWebCryptoOperationDecrypt:
    case kWebCryptoOperationSign:
    case kWebCryptoOperationVerify:
    case kWebCryptoOperationDigest:
    case kWebCryptoOperationImportKey:
    case kWebCryptoOperationGetKeyLength:
    case kWebCryptoOperationWrapKey:
    case kWebCryptoOperationUnwrapKey:
    case kWebCryptoOperationEncapsulate:
    case kWebCryptoOperationDecapsulate:
    case kWebCryptoOperationGenerateKey:
    case kWebCryptoOperationGetPublicKey:
      if (normalized_algorithm.ParamsType() ==
          kWebCryptoAlgorithmParamsTypeNone) {
        return true;
      }
      break;
    case kWebCryptoOperationDeriveBits:
      break;
  }
  return Platform::Current()->Crypto()->Supports(op, normalized_algorithm,
                                                 length_bits);
}

}  // namespace

SubtleCrypto::SubtleCrypto() = default;

ScriptPromise<DOMArrayBuffer> SubtleCrypto::encrypt(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_algorithm,
    CryptoKey* key,
    const V8BufferSource* raw_data,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-encrypt

  // 14.3.1.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to "encrypt".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationEncrypt, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // 14.3.1.4: Let data be the result of getting a copy of the bytes held by
  //           the data parameter passed to the encrypt method. This must
  //           happen after normalizing the algorithm, since normalization can
  //           run author getters that mutate or detach the data buffer.
  std::vector<uint8_t> data = CopyBytes(raw_data);

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<DOMArrayBuffer>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 14.3.1.8: If the name member of normalizedAlgorithm is not equal to the
  //           name attribute of the [[algorithm]] internal slot of key then
  //           throw an InvalidAccessError.
  //
  // 14.3.1.9: If the [[usages]] internal slot of key does not contain an
  //           entry that is "encrypt", then throw an InvalidAccessError.
  if (!key->CanBeUsedForAlgorithm(normalized_algorithm,
                                  kWebCryptoKeyUsageEncrypt, result))
    return promise;

  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                           normalized_algorithm, key->Key());
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->Encrypt(normalized_algorithm, key->Key(),
                                         std::move(data), result->Result(),
                                         std::move(task_runner));
  return promise;
}

ScriptPromise<DOMArrayBuffer> SubtleCrypto::decrypt(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_algorithm,
    CryptoKey* key,
    const V8BufferSource* raw_data,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-decrypt

  // 14.3.2.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to "decrypt".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationDecrypt, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // 14.3.2.4: Let data be the result of getting a copy of the bytes held by
  //           the data parameter passed to the decrypt method. This must
  //           happen after normalizing the algorithm, since normalization can
  //           run author getters that mutate or detach the data buffer.
  std::vector<uint8_t> data = CopyBytes(raw_data);

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<DOMArrayBuffer>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 14.3.2.8: If the name member of normalizedAlgorithm is not equal to the
  //           name attribute of the [[algorithm]] internal slot of key then
  //           throw an InvalidAccessError.
  //
  // 14.3.2.9: If the [[usages]] internal slot of key does not contain an
  //           entry that is "decrypt", then throw an InvalidAccessError.
  if (!key->CanBeUsedForAlgorithm(normalized_algorithm,
                                  kWebCryptoKeyUsageDecrypt, result))
    return promise;

  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                           normalized_algorithm, key->Key());
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->Decrypt(normalized_algorithm, key->Key(),
                                         std::move(data), result->Result(),
                                         std::move(task_runner));
  return promise;
}

ScriptPromise<DOMArrayBuffer> SubtleCrypto::sign(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_algorithm,
    CryptoKey* key,
    const V8BufferSource* raw_data,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-sign

  // 14.3.3.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to "sign".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationSign, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // 14.3.3.4: Let data be the result of getting a copy of the bytes held by
  //           the data parameter passed to the sign method. This must happen
  //           after normalizing the algorithm, since normalization can run
  //           author getters that mutate or detach the data buffer.
  std::vector<uint8_t> data = CopyBytes(raw_data);

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<DOMArrayBuffer>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 14.3.3.8: If the name member of normalizedAlgorithm is not equal to the
  //           name attribute of the [[algorithm]] internal slot of key then
  //           throw an InvalidAccessError.
  //
  // 14.3.3.9: If the [[usages]] internal slot of key does not contain an
  //           entry that is "sign", then throw an InvalidAccessError.
  if (!key->CanBeUsedForAlgorithm(normalized_algorithm, kWebCryptoKeyUsageSign,
                                  result))
    return promise;

  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                           normalized_algorithm, key->Key());
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->Sign(normalized_algorithm, key->Key(),
                                      std::move(data), result->Result(),
                                      std::move(task_runner));
  return promise;
}

ScriptPromise<IDLBoolean> SubtleCrypto::verifySignature(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_algorithm,
    CryptoKey* key,
    const V8BufferSource* raw_signature,
    const V8BufferSource* raw_data,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-verify

  // 14.3.4.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to "verify".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationVerify, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // 14.3.4.4: Let signature be the result of getting a copy of the bytes
  //           held by the signature parameter passed to the verify method.
  //
  // 14.3.4.5: Let data be the result of getting a copy of the bytes held by
  //           the data parameter passed to the verify method.
  //
  // Both copies must happen after normalizing the algorithm, since
  // normalization can run author getters that mutate or detach these buffers.
  std::vector<uint8_t> signature = CopyBytes(raw_signature);
  std::vector<uint8_t> data = CopyBytes(raw_data);

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<IDLBoolean>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 14.3.4.9: If the name member of normalizedAlgorithm is not equal to the
  //           name attribute of the [[algorithm]] internal slot of key then
  //           throw an InvalidAccessError.
  //
  // 14.3.4.10: If the [[usages]] internal slot of key does not contain an
  //            entry that is "verify", then throw an InvalidAccessError.
  if (!key->CanBeUsedForAlgorithm(normalized_algorithm,
                                  kWebCryptoKeyUsageVerify, result))
    return promise;

  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                           normalized_algorithm, key->Key());
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->VerifySignature(
      normalized_algorithm, key->Key(), std::move(signature), std::move(data),
      result->Result(), std::move(task_runner));
  return promise;
}

ScriptPromise<DOMArrayBuffer> SubtleCrypto::digest(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_algorithm,
    const V8BufferSource* raw_data,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-digest

  // 14.3.5.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to "digest".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationDigest, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // 14.3.5.4: Let data be the result of getting a copy of the bytes held by
  //           the data parameter passed to the digest method. This must
  //           happen after normalizing the algorithm, since normalization can
  //           run author getters that mutate or detach the data buffer.
  std::vector<uint8_t> data = CopyBytes(raw_data);

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<DOMArrayBuffer>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);

  HistogramAlgorithm(ExecutionContext::From(script_state),
                     normalized_algorithm);
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->Digest(normalized_algorithm, std::move(data),
                                        result->Result(),
                                        std::move(task_runner));
  return resolver->Promise();
}

ScriptPromise<V8UnionCryptoKeyOrCryptoKeyPair> SubtleCrypto::generateKey(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_algorithm,
    bool extractable,
    const Vector<String>& raw_key_usages,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-generateKey

  WebCryptoKeyUsageMask key_usages;
  if (!CryptoKey::ParseUsageMask(raw_key_usages, key_usages, exception_state)) {
    return EmptyPromise();
  }

  // 14.3.6.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to
  //           "generateKey".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationGenerateKey, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  auto* resolver = MakeGarbageCollected<
      ScriptPromiseResolver<V8UnionCryptoKeyOrCryptoKeyPair>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // NOTE: Steps (8) and (9) disallow empty usages on secret and private
  // keys. This normative requirement is enforced by the platform
  // implementation in the call below.

  HistogramAlgorithm(ExecutionContext::From(script_state),
                     normalized_algorithm);
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->GenerateKey(normalized_algorithm, extractable,
                                             key_usages, result->Result(),
                                             std::move(task_runner));
  return promise;
}

ScriptPromise<CryptoKey> SubtleCrypto::importKey(
    ScriptState* script_state,
    const String& raw_format,
    const V8UnionBufferSourceOrJsonWebKey* raw_key_data,
    const V8AlgorithmIdentifier* raw_algorithm,
    bool extractable,
    const Vector<String>& raw_key_usages,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-importKey
  WebCryptoKeyFormat format;
  if (!CryptoKey::ParseFormat(raw_format, format, exception_state)) {
    return EmptyPromise();
  }

  WebCryptoKeyUsageMask key_usages;
  if (!CryptoKey::ParseUsageMask(raw_key_usages, key_usages, exception_state)) {
    return EmptyPromise();
  }

  // 14.3.9.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to
  //           "importKey". This must happen before copying the key data,
  //           since normalization can run author getters that mutate or
  //           detach the keyData buffer.
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationImportKey, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // In the case of JWK keyData will hold the UTF8-encoded JSON for the
  // JsonWebKey, otherwise it holds a copy of the BufferSource.
  std::vector<uint8_t> key_data;

  switch (format) {
    // 14.3.9.4: If format is equal to the string "raw", "pkcs8", or "spki":
    //
    //  (1) If the keyData parameter passed to the importKey method is a
    //      JsonWebKey dictionary, throw a TypeError.
    //
    //  (2) Let keyData be the result of getting a copy of the bytes held by
    //      the keyData parameter passed to the importKey method.
    //
    // This section doesn't mention the new key formats added in
    // https://wicg.github.io/webcrypto-modern-algos/#subtlecrypto-interface-keyformat
    // yet; assume for now that they should be treated in the same way.
    case kWebCryptoKeyFormatRaw:
    case kWebCryptoKeyFormatRawPublic:
    case kWebCryptoKeyFormatRawPrivate:
    case kWebCryptoKeyFormatRawSeed:
    case kWebCryptoKeyFormatRawSecret:
    case kWebCryptoKeyFormatPkcs8:
    case kWebCryptoKeyFormatSpki:
      switch (raw_key_data->GetContentType()) {
        case V8UnionBufferSourceOrJsonWebKey::ContentType::kArrayBuffer:
          key_data = CopyBytes(raw_key_data->GetAsArrayBuffer());
          break;
        case V8UnionBufferSourceOrJsonWebKey::ContentType::kArrayBufferView:
          key_data = CopyBytes(raw_key_data->GetAsArrayBufferView().Get());
          break;
        case V8UnionBufferSourceOrJsonWebKey::ContentType::kJsonWebKey:
          exception_state.ThrowTypeError(
              "Key data must be a BufferSource for non-JWK formats");
          return EmptyPromise();
      }
      break;
    // 14.3.9.4: If format is equal to the string "jwk":
    //
    //  (1) If the keyData parameter passed to the importKey method is not a
    //      JsonWebKey dictionary, throw a TypeError.
    //
    //  (2) Let keyData be the keyData parameter passed to the importKey
    //      method.
    case kWebCryptoKeyFormatJwk:
      if (!raw_key_data->IsJsonWebKey()) {
        exception_state.ThrowTypeError(
            "Key data must be an object for JWK import");
        return EmptyPromise();
      }
      if (!ParseJsonWebKey(*raw_key_data->GetAsJsonWebKey(), key_data)) {
        return EmptyPromise();
      }
      break;
  }

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<CryptoKey>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  HistogramAlgorithm(ExecutionContext::From(script_state),
                     normalized_algorithm);
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->ImportKey(
      format, std::move(key_data), normalized_algorithm, extractable,
      key_usages, result->Result(), std::move(task_runner));
  return promise;
}

ScriptPromise<V8UnionArrayBufferOrJsonWebKey> SubtleCrypto::exportKey(
    ScriptState* script_state,
    const String& raw_format,
    CryptoKey* key,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-exportKey

  WebCryptoKeyFormat format;
  if (!CryptoKey::ParseFormat(raw_format, format, exception_state)) {
    return EmptyPromise();
  }

  // 14.3.10.6: If the [[extractable]] internal slot of key is false, then
  //            throw an InvalidAccessError.
  if (!key->extractable()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
                                      "key is not extractable");
    return EmptyPromise();
  }

  auto* resolver = MakeGarbageCollected<
      ScriptPromiseResolver<V8UnionArrayBufferOrJsonWebKey>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  HistogramKey(ExecutionContext::From(script_state), key->Key());
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->ExportKey(format, key->Key(), result->Result(),
                                           std::move(task_runner));
  return promise;
}

ScriptPromise<DOMArrayBuffer> SubtleCrypto::wrapKey(
    ScriptState* script_state,
    const String& raw_format,
    CryptoKey* key,
    CryptoKey* wrapping_key,
    const V8AlgorithmIdentifier* raw_wrap_algorithm,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-wrapKey

  WebCryptoKeyFormat format;
  if (!CryptoKey::ParseFormat(raw_format, format, exception_state)) {
    return EmptyPromise();
  }

  // 14.3.11.2: Let normalizedAlgorithm be the result of normalizing an
  //            algorithm, with alg set to algorithm and op set to "wrapKey".
  //
  // 14.3.11.3: If an error occurred, let normalizedAlgorithm be the result
  //            of normalizing an algorithm, with alg set to algorithm and op
  //            set to "encrypt".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_wrap_algorithm,
                          kWebCryptoOperationWrapKey, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<DOMArrayBuffer>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 14.3.11.9: If the name member of normalizedAlgorithm is not equal to the
  //            name attribute of the [[algorithm]] internal slot of
  //            wrappingKey then throw an InvalidAccessError.
  //
  // 14.3.11.10: If the [[usages]] internal slot of wrappingKey does not
  //             contain an entry that is "wrapKey", then throw an
  //             InvalidAccessError.
  if (!wrapping_key->CanBeUsedForAlgorithm(normalized_algorithm,
                                           kWebCryptoKeyUsageWrapKey, result))
    return promise;

  // TODO(crbug.com/628416): The error from step 11
  // (NotSupportedError) is thrown after step 12 which does not match
  // the spec order.

  // 14.3.11.12: If the [[extractable]] internal slot of key is false, then
  //             throw an InvalidAccessError.
  if (!key->extractable()) {
    result->CompleteWithError(kWebCryptoErrorTypeInvalidAccess,
                              "key is not extractable");
    return promise;
  }

  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                           normalized_algorithm, wrapping_key->Key());
  HistogramKey(ExecutionContext::From(script_state), key->Key());
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->WrapKey(
      format, key->Key(), wrapping_key->Key(), normalized_algorithm,
      result->Result(), std::move(task_runner));
  return promise;
}

ScriptPromise<CryptoKey> SubtleCrypto::unwrapKey(
    ScriptState* script_state,
    const String& raw_format,
    const V8BufferSource* raw_wrapped_key,
    CryptoKey* unwrapping_key,
    const V8AlgorithmIdentifier* raw_unwrap_algorithm,
    const V8AlgorithmIdentifier* raw_unwrapped_key_algorithm,
    bool extractable,
    const Vector<String>& raw_key_usages,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-unwrapKey

  WebCryptoKeyFormat format;
  if (!CryptoKey::ParseFormat(raw_format, format, exception_state)) {
    return EmptyPromise();
  }

  WebCryptoKeyUsageMask key_usages;
  if (!CryptoKey::ParseUsageMask(raw_key_usages, key_usages, exception_state)) {
    return EmptyPromise();
  }

  // 14.3.12.3: Let normalizedAlgorithm be the result of normalizing an
  //            algorithm, with alg set to algorithm and op set to
  //            "unwrapKey".
  //
  // 14.3.12.4: If an error occurred, let normalizedAlgorithm be the result
  //            of normalizing an algorithm, with alg set to algorithm and op
  //            set to "decrypt".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_unwrap_algorithm,
                          kWebCryptoOperationUnwrapKey, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // 14.3.12.6: Let normalizedKeyAlgorithm be the result of normalizing an
  //            algorithm, with alg set to unwrappedKeyAlgorithm and op set
  //            to "importKey".
  WebCryptoAlgorithm normalized_key_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(),
                          raw_unwrapped_key_algorithm,
                          kWebCryptoOperationImportKey,
                          normalized_key_algorithm, exception_state)) {
    return EmptyPromise();
  }

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<CryptoKey>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 14.3.12.2: Let wrappedKey be the result of getting a copy of the bytes
  //            held by the wrappedKey parameter passed to the unwrapKey
  //            method.
  std::vector<uint8_t> wrapped_key = CopyBytes(raw_wrapped_key);

  // 14.3.12.11: If the name member of normalizedAlgorithm is not equal to
  //             the name attribute of the [[algorithm]] internal slot of
  //             unwrappingKey then throw an InvalidAccessError.
  //
  // 14.3.12.12: If the [[usages]] internal slot of unwrappingKey does not
  //             contain an entry that is "unwrapKey", then throw an
  //             InvalidAccessError.
  if (!unwrapping_key->CanBeUsedForAlgorithm(
          normalized_algorithm, kWebCryptoKeyUsageUnwrapKey, result))
    return promise;

  // NOTE: Step (16) disallows empty usages on secret and private keys. This
  // normative requirement is enforced by the platform implementation in the
  // call below.

  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                           normalized_algorithm, unwrapping_key->Key());
  HistogramAlgorithm(ExecutionContext::From(script_state),
                     normalized_key_algorithm);
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->UnwrapKey(
      format, std::move(wrapped_key), unwrapping_key->Key(),
      normalized_algorithm, normalized_key_algorithm, extractable, key_usages,
      result->Result(), std::move(task_runner));
  return promise;
}

ScriptPromise<DOMArrayBuffer> SubtleCrypto::deriveBits(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_algorithm,
    CryptoKey* base_key,
    std::optional<unsigned> length_bits,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-deriveBits

  // 14.3.8.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to
  //           "deriveBits".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationDeriveBits, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // 14.3.8.7: If the name member of normalizedAlgorithm is not equal to the
  //           name attribute of the [[algorithm]] internal slot of baseKey
  //           then throw an InvalidAccessError.
  //
  // 14.3.8.8: If the [[usages]] internal slot of baseKey does not contain an
  //           entry that is "deriveBits", then throw an InvalidAccessError.
  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<DOMArrayBuffer>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  if (!base_key->CanBeUsedForAlgorithm(normalized_algorithm,
                                       kWebCryptoKeyUsageDeriveBits, result)) {
    return promise;
  }

  auto* execution_context = ExecutionContext::From(script_state);
  HistogramAlgorithmAndKey(execution_context, normalized_algorithm,
                           base_key->Key());
  Platform::Current()->Crypto()->DeriveBits(
      normalized_algorithm, base_key->Key(), length_bits, result->Result(),
      execution_context->GetTaskRunner(blink::TaskType::kInternalWebCrypto));

  return promise;
}

ScriptPromise<CryptoKey> SubtleCrypto::deriveKey(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_algorithm,
    CryptoKey* base_key,
    const V8AlgorithmIdentifier* raw_derived_key_type,
    bool extractable,
    const Vector<String>& raw_key_usages,
    ExceptionState& exception_state) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-deriveKey

  WebCryptoKeyUsageMask key_usages;
  if (!CryptoKey::ParseUsageMask(raw_key_usages, key_usages, exception_state)) {
    return EmptyPromise();
  }

  // 14.3.7.2: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to
  //           "deriveBits".
  WebCryptoAlgorithm normalized_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_algorithm,
                          kWebCryptoOperationDeriveBits, normalized_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  // 14.3.7.4: Let normalizedDerivedKeyAlgorithm be the result of normalizing
  //           an algorithm, with alg set to derivedKeyType and op set to
  //           "importKey".
  WebCryptoAlgorithm normalized_derived_key_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_derived_key_type,
                          kWebCryptoOperationImportKey,
                          normalized_derived_key_algorithm, exception_state)) {
    return EmptyPromise();
  }

  // TODO(eroman): The description in the spec needs to be updated as
  // it doesn't describe algorithm normalization for the Get Key
  // Length parameters (https://github.com/w3c/webcrypto/issues/127)
  // For now reference step 10 which is the closest.
  //
  // 14.3.7.10: If the name member of normalizedDerivedKeyAlgorithm does not
  //            identify a registered algorithm that supports the get key length
  //            operation, then throw a NotSupportedError.
  WebCryptoAlgorithm key_length_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_derived_key_type,
                          kWebCryptoOperationGetKeyLength, key_length_algorithm,
                          exception_state)) {
    return EmptyPromise();
  }

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<CryptoKey>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 14.3.7.11: If the name member of normalizedAlgorithm is not equal to the
  //            name attribute of the [[algorithm]] internal slot of baseKey
  //            then throw an InvalidAccessError.
  //
  // 14.3.7.12: If the [[usages]] internal slot of baseKey does not contain
  //            an entry that is "deriveKey", then throw an InvalidAccessError.
  if (!base_key->CanBeUsedForAlgorithm(normalized_algorithm,
                                       kWebCryptoKeyUsageDeriveKey, result))
    return promise;

  // NOTE: Step (16) disallows empty usages on secret and private keys. This
  // normative requirement is enforced by the platform implementation in the
  // call below.

  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                           normalized_algorithm, base_key->Key());
  HistogramAlgorithm(ExecutionContext::From(script_state),
                     normalized_derived_key_algorithm);
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  Platform::Current()->Crypto()->DeriveKey(
      normalized_algorithm, base_key->Key(), normalized_derived_key_algorithm,
      key_length_algorithm, extractable, key_usages, result->Result(),
      std::move(task_runner));
  return promise;
}

ScriptPromise<EncapsulatedKey> SubtleCrypto::encapsulateKey(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_encapsulation_algorithm,
    CryptoKey* encapsulation_key,
    const V8AlgorithmIdentifier* raw_shared_key_algorithm,
    bool extractable,
    const Vector<String>& raw_key_usages,
    ExceptionState& exception_state) {
  // Method described by:
  // https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-encapsulateKey

  WebCryptoKeyUsageMask key_usages;
  if (!CryptoKey::ParseUsageMask(raw_key_usages, key_usages, exception_state)) {
    return EmptyPromise();
  }

  // 3.2.1.2: Let normalizedEncapsulationAlgorithm be the result of normalizing
  //          an algorithm, with alg set to algorithm and op set to
  //          "encapsulate".
  WebCryptoAlgorithm normalized_encapsulation_algorithm;
  if (!NormalizeAlgorithm(
          script_state->GetIsolate(), raw_encapsulation_algorithm,
          kWebCryptoOperationEncapsulate, normalized_encapsulation_algorithm,
          exception_state)) {
    return EmptyPromise();
  }

  // 3.2.1.4: Let normalizedSharedKeyAlgorithm be the result of normalizing an
  //          algorithm, with alg set to sharedKeyAlgorithm and op set to
  //          "importKey".
  WebCryptoAlgorithm normalized_shared_key_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_shared_key_algorithm,
                          kWebCryptoOperationImportKey,
                          normalized_shared_key_algorithm, exception_state)) {
    return EmptyPromise();
  }

  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<EncapsulatedKey>>(
      script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 3.2.1.10: If the name member of normalizedEncapsulationAlgorithm is not
  //           equal to the name attribute of the algorithm internal slot of
  //           encapsulationKey then throw an InvalidAccessError.
  //
  // 3.2.1.11: If the usages internal slot of encapsulationKey does not contain
  //           an entry that is "encapsulateKey", then throw an
  //           InvalidAccessError.
  if (!encapsulation_key->CanBeUsedForAlgorithm(
          normalized_encapsulation_algorithm, kWebCryptoKeyUsageEncapsulateKey,
          result)) {
    return promise;
  }

  auto* execution_context = ExecutionContext::From(script_state);
  HistogramAlgorithmAndKey(execution_context,
                           normalized_encapsulation_algorithm,
                           encapsulation_key->Key());
  HistogramAlgorithm(execution_context, normalized_shared_key_algorithm);

  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      execution_context->GetTaskRunner(blink::TaskType::kInternalWebCrypto);

  Platform::Current()->Crypto()->EncapsulateKey(
      normalized_encapsulation_algorithm, encapsulation_key->Key(),
      normalized_shared_key_algorithm, extractable, key_usages,
      result->Result(), std::move(task_runner));

  return promise;
}

ScriptPromise<EncapsulatedBits> SubtleCrypto::encapsulateBits(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_encapsulation_algorithm,
    CryptoKey* encapsulation_key,
    ExceptionState& exception_state) {
  // Method described by:
  // https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-encapsulateBits

  // 3.2.2.2: Let normalizedEncapsulationAlgorithm be the result of normalizing
  //          an algorithm, with alg set to encapsulationAlgorithm and op set to
  //          "encapsulate".
  WebCryptoAlgorithm normalized_encapsulation_algorithm;
  if (!NormalizeAlgorithm(
          script_state->GetIsolate(), raw_encapsulation_algorithm,
          kWebCryptoOperationEncapsulate, normalized_encapsulation_algorithm,
          exception_state)) {
    return EmptyPromise();
  }

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<EncapsulatedBits>>(
          script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 3.2.2.8: If the name member of normalizedEncapsulationAlgorithm is not
  //          equal to the name attribute of the algorithm internal slot of
  //          encapsulationKey then throw an InvalidAccessError.
  //
  // 3.2.2.9: If the usages internal slot of encapsulationKey does not contain
  //          an entry that is "encapsulateBits", then throw an
  //          InvalidAccessError.
  if (!encapsulation_key->CanBeUsedForAlgorithm(
          normalized_encapsulation_algorithm, kWebCryptoKeyUsageEncapsulateBits,
          result)) {
    return promise;
  }

  auto* execution_context = ExecutionContext::From(script_state);
  HistogramAlgorithmAndKey(execution_context,
                           normalized_encapsulation_algorithm,
                           encapsulation_key->Key());

  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      execution_context->GetTaskRunner(blink::TaskType::kInternalWebCrypto);

  Platform::Current()->Crypto()->EncapsulateBits(
      normalized_encapsulation_algorithm, encapsulation_key->Key(),
      result->Result(), std::move(task_runner));

  return promise;
}

ScriptPromise<CryptoKey> SubtleCrypto::decapsulateKey(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_decapsulation_algorithm,
    CryptoKey* decapsulation_key,
    const V8BufferSource* raw_ciphertext,
    const V8AlgorithmIdentifier* raw_shared_key_algorithm,
    bool extractable,
    const Vector<String>& raw_key_usages,
    ExceptionState& exception_state) {
  // Method described by:
  // https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-decapsulateKey

  WebCryptoKeyUsageMask key_usages;
  if (!CryptoKey::ParseUsageMask(raw_key_usages, key_usages, exception_state)) {
    return EmptyPromise();
  }

  // 3.2.3.2: Let ciphertext be the result of getting a copy of the bytes held
  //          by the ciphertext parameter passed to the decapsulateKey() method.
  std::vector<uint8_t> ciphertext = CopyBytes(raw_ciphertext);

  // 3.2.3.3: Let normalizedAlgorithm be the result of normalizing an
  //          algorithm, with alg set to algorithm and op set to
  //          "decapsulate".
  WebCryptoAlgorithm normalized_decapsulation_algorithm;
  if (!NormalizeAlgorithm(
          script_state->GetIsolate(), raw_decapsulation_algorithm,
          kWebCryptoOperationDecapsulate, normalized_decapsulation_algorithm,
          exception_state)) {
    return EmptyPromise();
  }

  // 3.2.3.5: Let normalizedSharedKeyAlgorithm be the result of normalizing an
  //          algorithm, with alg set to sharedKeyAlgorithm and op set to
  //          "importKey".
  WebCryptoAlgorithm normalized_shared_key_algorithm;
  if (!NormalizeAlgorithm(script_state->GetIsolate(), raw_shared_key_algorithm,
                          kWebCryptoOperationImportKey,
                          normalized_shared_key_algorithm, exception_state)) {
    return EmptyPromise();
  }

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<CryptoKey>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 3.2.3.11: If the name member of normalizedDecapsulationAlgorithm is not
  //           equal to the name attribute of the algorithm internal slot of
  //           decapsulationKey then throw an InvalidAccessError.
  //
  //
  // 3.2.3.12: If the usages internal slot of decapsulationKey does not contain
  //           an entry that is "decapsulateKey", then throw an
  //           InvalidAccessError.
  if (!decapsulation_key->CanBeUsedForAlgorithm(
          normalized_decapsulation_algorithm, kWebCryptoKeyUsageDecapsulateKey,
          result)) {
    return promise;
  }

  auto* execution_context = ExecutionContext::From(script_state);
  HistogramAlgorithmAndKey(execution_context,
                           normalized_decapsulation_algorithm,
                           decapsulation_key->Key());
  HistogramAlgorithm(execution_context, normalized_shared_key_algorithm);

  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      execution_context->GetTaskRunner(blink::TaskType::kInternalWebCrypto);

  Platform::Current()->Crypto()->DecapsulateKey(
      normalized_decapsulation_algorithm, decapsulation_key->Key(),
      std::move(ciphertext), normalized_shared_key_algorithm, extractable,
      key_usages, result->Result(), std::move(task_runner));

  return promise;
}

ScriptPromise<DOMArrayBuffer> SubtleCrypto::decapsulateBits(
    ScriptState* script_state,
    const V8AlgorithmIdentifier* raw_decapsulation_algorithm,
    CryptoKey* decapsulation_key,
    const V8BufferSource* raw_ciphertext,
    ExceptionState& exception_state) {
  // Method described by:
  // https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-decapsulateBits

  // 3.2.4.2: Let ciphertext be the result of getting a copy of the bytes held
  //          by the ciphertext parameter passed to the decapsulateBits()
  //          method.
  std::vector<uint8_t> ciphertext = CopyBytes(raw_ciphertext);

  // 3.2.4.3: Let normalizedDecapsulationAlgorithm be the result of normalizing
  //          an algorithm, with alg set to decapsulationAlgorithm and op set to
  //          "decapsulate".
  WebCryptoAlgorithm normalized_decapsulation_algorithm;
  if (!NormalizeAlgorithm(
          script_state->GetIsolate(), raw_decapsulation_algorithm,
          kWebCryptoOperationDecapsulate, normalized_decapsulation_algorithm,
          exception_state)) {
    return EmptyPromise();
  }

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<DOMArrayBuffer>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  // 3.2.4.9:  If the name member of normalizedDecapsulationAlgorithm is not
  //           equal to the name attribute of the [[algorithm]] internal slot of
  //           decapsulationKey then throw an InvalidAccessError.
  //
  // 3.2.4.10: If the usages internal slot of decapsulationKey does not
  //           contain an entry that is "decapsulateBits", then throw an
  //           InvalidAccessError.
  if (!decapsulation_key->CanBeUsedForAlgorithm(
          normalized_decapsulation_algorithm, kWebCryptoKeyUsageDecapsulateBits,
          result)) {
    return promise;
  }

  auto* execution_context = ExecutionContext::From(script_state);
  HistogramAlgorithmAndKey(execution_context,
                           normalized_decapsulation_algorithm,
                           decapsulation_key->Key());

  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      execution_context->GetTaskRunner(blink::TaskType::kInternalWebCrypto);

  Platform::Current()->Crypto()->DecapsulateBits(
      normalized_decapsulation_algorithm, decapsulation_key->Key(),
      std::move(ciphertext), result->Result(), std::move(task_runner));

  return promise;
}

ScriptPromise<CryptoKey> SubtleCrypto::getPublicKey(
    ScriptState* script_state,
    CryptoKey* key,
    const Vector<String>& raw_key_usages,
    ExceptionState& exception_state) {
  // Method described by:
  // https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey

  if (!WebCryptoAlgorithm::IsAsymmetric(key->Key().Algorithm().Id())) {
    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                      "getPublicKey can only be called with "
                                      "keys from an asymmetric algorithm");
    return EmptyPromise();
  }

  if (key->Key().GetType() != kWebCryptoKeyTypePrivate) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kInvalidAccessError,
        "getPublicKey can only be called with a private key");
    return EmptyPromise();
  }

  WebCryptoKeyUsageMask key_usages;
  if (!CryptoKey::ParseUsageMask(raw_key_usages, key_usages, exception_state)) {
    return EmptyPromise();
  }

  auto* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<CryptoKey>>(script_state);
  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state, resolver);
  auto promise = resolver->Promise();

  auto* execution_context = ExecutionContext::From(script_state);
  HistogramKey(execution_context, key->Key());

  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      execution_context->GetTaskRunner(blink::TaskType::kInternalWebCrypto);

  Platform::Current()->Crypto()->GetPublicKey(
      key->Key(), key_usages, result->Result(), std::move(task_runner));

  return promise;
}

// Defined by
// https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-supports,
// 3.2.6 and for the supports(operation, algorithm, length) section.
bool SubtleCrypto::supports(ScriptState* script_state,
                            const String& operation,
                            const V8AlgorithmIdentifier* algorithm,
                            std::optional<unsigned> length) {
  std::optional<WebCryptoOperation> op = StringToWebCryptoOperation(operation);
  if (!op) {
    return false;
  }

  return supportsInternal(script_state, *op, algorithm, length);
}

// Defined by
// https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-supports,
// 3.2.6 and for the supports(operation, algorithm, additionalAlgorithm)
// section.
bool SubtleCrypto::supports(ScriptState* script_state,
                            const String& operation,
                            const V8AlgorithmIdentifier* algorithm,
                            const V8AlgorithmIdentifier* additional_algorithm) {
  std::optional<WebCryptoOperation> op = StringToWebCryptoOperation(operation);
  if (!op) {
    return false;
  }

  std::optional<unsigned> length_bits = std::nullopt;

  // 3.2.6.2  If operation is "deriveKey", "unwrapKey", "encapsulateKey" or
  //          "decapsulateKey": If the result of checking support for an
  //          algorithm with op set to "importKey" and alg set to
  //          additionalAlgorithm is false, return false.
  //
  // If operation is "wrapKey": If the result of checking support for
  //           an algorithm with op set to "exportKey" and alg set to
  //           additionalAlgorithm is false, return false.
  //
  // importKey and exportKey are always supported, so we skip these checks.

  if (operation == "deriveKey") {
    // 3.2.6.4.1 If the result of checking support for an algorithm with op set
    //           to "get key length" and alg set to additionalAlgorithm is
    //           false, return false.
    if (!supportsInternal(script_state, kWebCryptoOperationGetKeyLength,
                          additional_algorithm, length_bits)) {
      return false;
    }

    // 3.2.6.4.2 Let normalizedAdditionalAlgorithm be the result of normalizing
    //           an algorithm, with alg set to additionalAlgorithm and op set to
    //           "get key length".
    WebCryptoAlgorithm normalized_additional_algorithm;
    if (!NormalizeAlgorithm(script_state->GetIsolate(), additional_algorithm,
                            kWebCryptoOperationGetKeyLength,
                            normalized_additional_algorithm,
                            IGNORE_EXCEPTION)) {
      return false;
    }

    // 3.2.6.4.3 Let length be the result of performing the get key length
    //           algorithm specified by additionalAlgorithm using
    //           normalizedAdditionalAlgorithm.
    if (!Platform::Current()->Crypto()->GetKeyLength(
            normalized_additional_algorithm, &length_bits)) {
      return false;
    }

    // 3.2.6.4.4 Set operation to "deriveBits".
    //
    // op should already be deriveBits
    CHECK(op == kWebCryptoOperationDeriveBits);
  }

  return supportsInternal(script_state, *op, algorithm, length_bits);
}

}  // namespace blink
