// 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 <limits.h>
#include <stddef.h>
#include <stdint.h>

#include <array>
#include <memory>
#include <utility>

#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
#include "components/webcrypto/status.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"

namespace webcrypto {

namespace {

// Creates an AES-CBC algorithm.
blink::WebCryptoAlgorithm CreateAesCbcAlgorithm(
    const std::vector<uint8_t>& iv) {
  return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
      blink::kWebCryptoAlgorithmIdAesCbc, new blink::WebCryptoAesCbcParams(iv));
}

blink::WebCryptoAlgorithm CreateAesCbcKeyGenAlgorithm(
    uint16_t key_length_bits) {
  return CreateAesKeyGenAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc,
                                  key_length_bits);
}

blink::WebCryptoKey GetTestAesCbcKey() {
  const std::string key_hex = "2b7e151628aed2a6abf7158809cf4f3c";
  blink::WebCryptoKey key = ImportSecretKeyFromRaw(
      HexStringToBytes(key_hex),
      CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
      blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt);

  // Verify exported raw key is identical to the imported data
  std::vector<uint8_t> raw_key;
  EXPECT_EQ(Status::Success(),
            ExportKey(blink::kWebCryptoKeyFormatRaw, key, &raw_key));
  EXPECT_BYTES_EQ_HEX(key_hex, raw_key);
  return key;
}

blink::WebCryptoKey ImportRawKey(const std::vector<uint8_t>& key_bytes) {
  return ImportSecretKeyFromRaw(
      key_bytes, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
      blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt);
}

std::vector<uint8_t> EncryptOrDie(blink::WebCryptoKey key,
                                  const std::vector<uint8_t>& iv,
                                  const std::vector<uint8_t>& plaintext) {
  std::vector<uint8_t> output;
  Status status = Encrypt(CreateAesCbcAlgorithm(iv), key, plaintext, &output);
  CHECK(status.IsSuccess());
  return output;
}

std::vector<uint8_t> DecryptOrDie(blink::WebCryptoKey key,
                                  const std::vector<uint8_t>& iv,
                                  const std::vector<uint8_t>& ciphertext) {
  std::vector<uint8_t> output;
  Status status = Decrypt(CreateAesCbcAlgorithm(iv), key, ciphertext, &output);
  CHECK(status.IsSuccess());
  return output;
}

std::string EncryptMustFail(blink::WebCryptoKey key,
                            const std::vector<uint8_t>& iv,
                            const std::vector<uint8_t>& plaintext) {
  std::vector<uint8_t> output;
  Status status = Encrypt(CreateAesCbcAlgorithm(iv), key, plaintext, &output);
  CHECK(!status.IsSuccess());
  return StatusToString(status);
}

std::string DecryptMustFail(blink::WebCryptoKey key,
                            const std::vector<uint8_t>& iv,
                            const std::vector<uint8_t>& ciphertext) {
  std::vector<uint8_t> output;
  Status status = Decrypt(CreateAesCbcAlgorithm(iv), key, ciphertext, &output);
  CHECK(!status.IsSuccess());
  return StatusToString(status);
}

class WebCryptoAesCbcTest : public WebCryptoTestBase {};

TEST_F(WebCryptoAesCbcTest, ExportKeyUnsupportedFormat) {
  std::vector<uint8_t> output;

  // Fail exporting the key in SPKI and PKCS#8 formats (not allowed for secret
  // keys).
  EXPECT_EQ(
      Status::ErrorUnsupportedExportKeyFormat(),
      ExportKey(blink::kWebCryptoKeyFormatSpki, GetTestAesCbcKey(), &output));
  EXPECT_EQ(
      Status::ErrorUnsupportedExportKeyFormat(),
      ExportKey(blink::kWebCryptoKeyFormatPkcs8, GetTestAesCbcKey(), &output));
}

struct AesCbcKnownAnswer {
  const char* key;
  const char* iv;
  const char* plaintext;
  const char* ciphertext;
};

constexpr auto kAesCbcKnownAnswers = std::to_array<AesCbcKnownAnswer>({
    // F.2.1 (CBC-AES128.Encrypt)
    // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
    {"2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090a0b0c0d0e0f",
     "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a"
     "35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
     // Added a padding block: encryption of {0x10, 0x10, ... 0x10}) (not given
     // by the NIST test vector)
     "7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e"
     "3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a78cb82807230e1321d3"
     "fae00d18cc2012"},

    // F.2.6 CBC-AES256.Decrypt [*]
    // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
    //
    // [*] Truncated 3 bytes off the plain text, so block 4 differs from the
    // NIST vector.
    {"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
     "000102030405060708090a0b0c0d0e0f",
     // Truncated the last block to make it more interesting.
     "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a"
     "35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be6",
     // Last block differs from source vector (due to truncation)
     "f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a"
     "9d9bacfa530e26304231461c9aaf02a6a54e9e242ccbf48c59daca6"},

    // Taken from encryptor_unittest.cc (EncryptorTest.EmptyEncrypt())
    {"3132383d5369787465656e4279746573", "5377656574205369787465656e204956", "",
     "8518b8878d34e7185e300d0fcc426396"},
});

TEST_F(WebCryptoAesCbcTest, KnownAnswers) {
  for (const auto& test : kAesCbcKnownAnswers) {
    SCOPED_TRACE(&test - &kAesCbcKnownAnswers[0]);
    auto key = HexStringToBytes(test.key);
    auto iv = HexStringToBytes(test.iv);
    auto plaintext = HexStringToBytes(test.plaintext);
    auto ciphertext = HexStringToBytes(test.ciphertext);

    blink::WebCryptoKey imported_key = ImportRawKey(key);
    EXPECT_EQ(EncryptOrDie(imported_key, iv, plaintext), ciphertext);
    EXPECT_EQ(DecryptOrDie(imported_key, iv, ciphertext), plaintext);
  }
}

TEST_F(WebCryptoAesCbcTest, IVIsWrongSize) {
  auto key = ImportRawKey(HexStringToBytes("3132383d5369787465656e4279746573"));
  auto plaintext = HexStringToBytes("0000");
  auto ciphertext = HexStringToBytes("8518b8878d34e7185e300d0fcc426396");

  auto short_iv = HexStringToBytes("5300");
  auto long_iv =
      HexStringToBytes("5377656574205369787465656e2049560000000000000000");

  const std::string error =
      "OperationError: The \"iv\" has an unexpected length -- must be 16 bytes";

  EXPECT_EQ(EncryptMustFail(key, short_iv, plaintext), error);
  EXPECT_EQ(EncryptMustFail(key, long_iv, plaintext), error);

  EXPECT_EQ(DecryptMustFail(key, short_iv, ciphertext), error);
  EXPECT_EQ(DecryptMustFail(key, long_iv, ciphertext), error);
}

TEST_F(WebCryptoAesCbcTest, ImportShortKey) {
  auto algo = CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc);
  auto key_bytes = HexStringToBytes("31");
  blink::WebCryptoKey unused_key;
  Status status = ImportKey(
      blink::kWebCryptoKeyFormatRaw, key_bytes, algo, true,
      blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
      &unused_key);
  EXPECT_EQ(StatusToString(status),
            "DataError: AES key data must be 128 or 256 bits");
}

TEST_F(WebCryptoAesCbcTest, ImportKeyWrongFormat) {
  auto algo = CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc);
  auto key_bytes = HexStringToBytes("3132383d5369787465656e4279746573");

  auto try_format = [=](blink::WebCryptoKeyFormat format) {
    blink::WebCryptoKey unused_key;
    Status status = ImportKey(
        format, key_bytes, algo, true,
        blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
        &unused_key);
    EXPECT_EQ(StatusToString(status),
              "NotSupported: Unsupported import key format for algorithm");
  };

  try_format(blink::kWebCryptoKeyFormatSpki);
  try_format(blink::kWebCryptoKeyFormatPkcs8);
}

TEST_F(WebCryptoAesCbcTest, ImportAes192Key) {
  auto algo = CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc);
  auto key_bytes =
      HexStringToBytes("5377656574205369787465656e2049560000000000000000");
  blink::WebCryptoKey unused_key;
  Status status = ImportKey(
      blink::kWebCryptoKeyFormatRaw, key_bytes, algo, true,
      blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
      &unused_key);
  EXPECT_EQ(StatusToString(status),
            "OperationError: 192-bit AES keys are not supported");
}

TEST_F(WebCryptoAesCbcTest, DecryptTruncatedCiphertextFails) {
  auto key = ImportRawKey(HexStringToBytes("2b7e151628aed2a6abf7158809cf4f3c"));
  auto iv = HexStringToBytes("000102030405060708090a0b0c0d0e0f");

  std::vector<uint8_t> plaintext(64, 0);
  auto ciphertext = EncryptOrDie(key, iv, plaintext);

  ASSERT_EQ(plaintext, DecryptOrDie(key, iv, ciphertext));

  // Drop 3 bytes at the end:
  ciphertext.resize(ciphertext.size() - 3);
  EXPECT_EQ(DecryptMustFail(key, iv, ciphertext), "OperationError");

  // Drop the rest of the trailing block:
  ciphertext.resize(ciphertext.size() - 13);
  EXPECT_EQ(DecryptMustFail(key, iv, ciphertext), "OperationError");

  // And try an empty ciphertext:
  ciphertext.clear();
  EXPECT_EQ(DecryptMustFail(key, iv, ciphertext), "OperationError");
}

struct JwkImportFailureTest {
  const char* jwk;
  const char* error;
};

const JwkImportFailureTest kJwkImportFailureTests[] = {
    {R"({
        "kty": "oct",
        "k": "GADWrMRHwQfoNaXU5fZvTg",
        "key_ops": [ "encrypt", "decrypt", "encrypt" ]
      })",
     "DataError: The \"key_ops\" member of the JWK dictionary contains "
     "duplicate usages."},
    {R"({
        "kty": "oct",
        "k": "GADWrMRHwQfoNaXU5fZvTg",
        "key_ops": [ "foopy", "decrypt", "foopy" ]
    })",
     "DataError: The \"key_ops\" member of the JWK dictionary contains "
     "duplicate usages."},
    {R"({
        "kty": "oct",
        "alg": "A127CBC",
        "k": "GADWrMRHwQfoNaXU5fZvTg"
    })",
     "DataError: The JWK \"alg\" member was inconsistent with that specified "
     "by the Web Crypto call"},
    {R"({
        "kty": "foo",
        "k": "GADWrMRHwQfoNaXU5fZvTg"
      })",
     "DataError: The JWK \"kty\" member was not \"oct\""},
    {R"({
        "k": "GADWrMRHwQfoNaXU5fZvTg"
      })",
     "DataError: The required JWK member \"kty\" was missing"},
    {R"({
        "kty": 0.1,
        "k": "GADWrMRHwQfoNaXU5fZvTg"
      })",
     "DataError: The JWK member \"kty\" must be a string"},
    {R"({
        "kty": "oct",
        "use": "foo",
        "k": "GADWrMRHwQfoNaXU5fZvTg"
      })",
     "DataError: The JWK \"use\" member could not be parsed"},
    {R"({
        "kty": "oct",
        "use": true,
        "k": "GADWrMRHwQfoNaXU5fZvTg"
      })",
     "DataError: The JWK member \"use\" must be a string"},
    {R"({
        "kty": "oct",
        "k": "GADWrMRHwQfoNaXU5fZvTg",
        "ext": 0
      })",
     "DataError: The JWK member \"ext\" must be a boolean"},
    {R"({
        "kty": "oct",
        "k": "GADWrMRHwQfoNaXU5fZvTg",
        "key_ops": true
      })",
     "DataError: The JWK member \"key_ops\" must be a list"},
    {R"({
        "kty": "oct",
        "k": "GADWrMRHwQfoNaXU5fZvTg",
        "key_ops": ["encrypt", 3]
      })",
     "DataError: The JWK member \"key_ops[1]\" must be a string"},
    {R"({
        "kty": "oct"
      })",
     "DataError: The required JWK member \"k\" was missing"},
    {R"({
        "kty": "oct",
        "k": "Qk3f0DsytU8lfza2au #$% Htaw2xpop9GYyTuH0p5GghxTI="
      })",
     "DataError: The JWK member \"k\" could not be base64url decoded or "
     "contained padding"},
    {R"({
        "kty": "oct",
        "k": ""
      })",
     "DataError: AES key data must be 128 or 256 bits"},
    {R"({
        "kty": "oct",
        "alg": "A128CBC",
        "k": ""
      })",
     "DataError: The JWK \"k\" member did not include the right length of key "
     "data for the given algorithm."},
    {R"({
        "kty": "oct",
        "alg": "A128CBC",
        "k": "AVj42h0Y5aqGtE3yluKL"
      })",
     "DataError: The JWK \"k\" member did not include the right length of key "
     "data for the given algorithm."},
    {R"({
        "kty": "oct",
        "alg": "A128CBC",
        "k": "dGhpcyAgaXMgIDI0ICBieXRlcyBsb25n"
      })",
     "DataError: The JWK \"k\" member did not include the right length of key "
     "data for the given algorithm."},
    {R"({
        "kty": "oct",
        "alg": "A192CBC",
        "k": "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh"
      })",
     "OperationError: 192-bit AES keys are not supported"},
};

TEST_F(WebCryptoAesCbcTest, JwkImportFailures) {
  for (const auto& test : kJwkImportFailureTests) {
    blink::WebCryptoKey unused_key;
    std::string jwk = test.jwk;
    std::vector<uint8_t> jwk_bytes(jwk.begin(), jwk.end());
    Status status = ImportKey(
        blink::kWebCryptoKeyFormatJwk, jwk_bytes,
        CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
        blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
        &unused_key);
    ASSERT_TRUE(!status.IsSuccess());
    EXPECT_EQ(StatusToString(status), test.error);
  }
}

// TODO(eroman): Do this same test for AES-GCM, AES-KW, AES-CTR ?
TEST_F(WebCryptoAesCbcTest, GenerateKeyIsRandom) {
  // Check key generation for each allowed key length.
  std::vector<blink::WebCryptoAlgorithm> algorithm;
  for (uint16_t key_length : {128, 256}) {
    blink::WebCryptoKey key;

    std::vector<std::vector<uint8_t>> keys;
    std::vector<uint8_t> key_bytes;

    // Generate a small sample of keys.
    for (int j = 0; j < 16; ++j) {
      ASSERT_EQ(Status::Success(),
                GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(key_length), true,
                                  blink::kWebCryptoKeyUsageEncrypt, &key));
      EXPECT_TRUE(key.Handle());
      EXPECT_EQ(blink::kWebCryptoKeyTypeSecret, key.GetType());
      ASSERT_EQ(Status::Success(),
                ExportKey(blink::kWebCryptoKeyFormatRaw, key, &key_bytes));
      EXPECT_EQ(key_bytes.size() * 8,
                key.Algorithm().AesParams()->LengthBits());
      keys.push_back(key_bytes);
    }
    // Ensure all entries in the key sample set are unique. This is a simplistic
    // estimate of whether the generated keys appear random.
    EXPECT_FALSE(CopiesExist(keys));
  }
}

TEST_F(WebCryptoAesCbcTest, GenerateKeyBadLength) {
  blink::WebCryptoKey key;
  for (uint16_t key_length : {0, 127, 257}) {
    SCOPED_TRACE(key_length);
    EXPECT_EQ(Status::ErrorGenerateAesKeyLength(),
              GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(key_length), true,
                                blink::kWebCryptoKeyUsageEncrypt, &key));
  }
}

TEST_F(WebCryptoAesCbcTest, ImportKeyEmptyUsage) {
  blink::WebCryptoKey key;
  ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
            ImportKey(blink::kWebCryptoKeyFormatRaw, std::vector<uint8_t>(16),
                      CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
                      0, &key));
}

// If key_ops is specified but empty, no key usages are allowed for the key.
TEST_F(WebCryptoAesCbcTest, ImportKeyJwkEmptyKeyOps) {
  blink::WebCryptoKey key;
  base::DictValue dict;
  dict.Set("kty", "oct");
  dict.Set("ext", false);
  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
  dict.Set("key_ops", base::ListValue());

  // The JWK does not contain encrypt usages.
  EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(),
            ImportKeyJwkFromDict(
                dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                false, blink::kWebCryptoKeyUsageEncrypt, &key));

  // The JWK does not contain sign usage (nor is it applicable).
  EXPECT_EQ(Status::ErrorCreateKeyBadUsages(),
            ImportKeyJwkFromDict(
                dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                false, blink::kWebCryptoKeyUsageSign, &key));
}

// If key_ops is missing, then any key usages can be specified.
TEST_F(WebCryptoAesCbcTest, ImportKeyJwkNoKeyOps) {
  blink::WebCryptoKey key;
  base::DictValue dict;
  dict.Set("kty", "oct");
  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");

  EXPECT_EQ(Status::Success(),
            ImportKeyJwkFromDict(
                dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                false, blink::kWebCryptoKeyUsageEncrypt, &key));

  EXPECT_EQ(blink::kWebCryptoKeyUsageEncrypt, key.Usages());

  // The JWK does not contain sign usage (nor is it applicable).
  EXPECT_EQ(Status::ErrorCreateKeyBadUsages(),
            ImportKeyJwkFromDict(
                dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                false, blink::kWebCryptoKeyUsageVerify, &key));
}

TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsEncryptDecrypt) {
  blink::WebCryptoKey key;
  base::DictValue dict;
  dict.Set("kty", "oct");
  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
  base::ListValue* key_ops = dict.EnsureList("key_ops");

  key_ops->Append("encrypt");

  EXPECT_EQ(Status::Success(),
            ImportKeyJwkFromDict(
                dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                false, blink::kWebCryptoKeyUsageEncrypt, &key));

  EXPECT_EQ(blink::kWebCryptoKeyUsageEncrypt, key.Usages());

  key_ops->Append("decrypt");

  EXPECT_EQ(Status::Success(),
            ImportKeyJwkFromDict(
                dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                false, blink::kWebCryptoKeyUsageDecrypt, &key));

  EXPECT_EQ(blink::kWebCryptoKeyUsageDecrypt, key.Usages());

  EXPECT_EQ(
      Status::Success(),
      ImportKeyJwkFromDict(
          dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), false,
          blink::kWebCryptoKeyUsageDecrypt | blink::kWebCryptoKeyUsageEncrypt,
          &key));

  EXPECT_EQ(blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
            key.Usages());
}

// Test failure if input usage is NOT a strict subset of the JWK usage.
TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsNotSuperset) {
  blink::WebCryptoKey key;
  base::DictValue dict;
  dict.Set("kty", "oct");
  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
  base::ListValue key_ops;
  key_ops.Append("encrypt");
  dict.Set("key_ops", std::move(key_ops));

  EXPECT_EQ(
      Status::ErrorJwkKeyopsInconsistent(),
      ImportKeyJwkFromDict(
          dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), false,
          blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
          &key));
}

TEST_F(WebCryptoAesCbcTest, ImportKeyJwkUseEnc) {
  blink::WebCryptoKey key;
  base::DictValue dict;
  dict.Set("kty", "oct");
  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");

  // Test JWK composite use 'enc' usage
  dict.Set("alg", "A128CBC");
  dict.Set("use", "enc");
  EXPECT_EQ(
      Status::Success(),
      ImportKeyJwkFromDict(
          dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), false,
          blink::kWebCryptoKeyUsageDecrypt | blink::kWebCryptoKeyUsageEncrypt |
              blink::kWebCryptoKeyUsageWrapKey |
              blink::kWebCryptoKeyUsageUnwrapKey,
          &key));
  EXPECT_EQ(
      blink::kWebCryptoKeyUsageDecrypt | blink::kWebCryptoKeyUsageEncrypt |
          blink::kWebCryptoKeyUsageWrapKey | blink::kWebCryptoKeyUsageUnwrapKey,
      key.Usages());
}

TEST_F(WebCryptoAesCbcTest, ImportJwkUnknownKeyOps) {
  blink::WebCryptoKey key;
  const std::string jwk =
      R"({
            "kty": "oct",
            "k": "GADWrMRHwQfoNaXU5fZvTg",
            "key_ops": ["foo", "bar", "encrypt", "decrypt"]
        })";

  EXPECT_EQ(Status::Success(),
            ImportKey(blink::kWebCryptoKeyFormatJwk, base::as_byte_span(jwk),
                      CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                      false, blink::kWebCryptoKeyUsageEncrypt, &key));
}

TEST_F(WebCryptoAesCbcTest, ImportJwkInvalidJson) {
  blink::WebCryptoKey key;
  // Fail on empty JSON.
  EXPECT_EQ(Status::ErrorJwkNotDictionary(),
            ImportKey(blink::kWebCryptoKeyFormatJwk, {},
                      CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                      false, blink::kWebCryptoKeyUsageEncrypt, &key));

  // Fail on invalid JSON.
  const std::string bad_json = R"({ "kty": "oct", "alg": "HS256", "use": )";
  EXPECT_EQ(
      Status::ErrorJwkNotDictionary(),
      ImportKey(blink::kWebCryptoKeyFormatJwk, base::as_byte_span(bad_json),
                CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), false,
                blink::kWebCryptoKeyUsageEncrypt, &key));
}

// Fail on inconsistent key_ops - asking for "encrypt" however JWK contains
// only "foo".
TEST_F(WebCryptoAesCbcTest, ImportJwkKeyOpsLacksUsages) {
  blink::WebCryptoKey key;

  base::DictValue dict;
  dict.Set("kty", "oct");
  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");

  base::ListValue key_ops;
  key_ops.Append("foo");
  dict.Set("key_ops", std::move(key_ops));
  EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(),
            ImportKeyJwkFromDict(
                dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
                false, blink::kWebCryptoKeyUsageEncrypt, &key));
}

TEST_F(WebCryptoAesCbcTest, ImportExportJwk) {
  const blink::WebCryptoAlgorithm algorithm =
      CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc);

  // AES-CBC 128
  ImportExportJwkSymmetricKey(
      128, algorithm,
      blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
      "A128CBC");

  // AES-CBC 256
  ImportExportJwkSymmetricKey(256, algorithm, blink::kWebCryptoKeyUsageDecrypt,
                              "A256CBC");

  // Large usage value
  ImportExportJwkSymmetricKey(
      256, algorithm,
      blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt |
          blink::kWebCryptoKeyUsageWrapKey | blink::kWebCryptoKeyUsageUnwrapKey,
      "A256CBC");
}

// 192-bit AES is intentionally unsupported (http://crbug.com/533699).
TEST_F(WebCryptoAesCbcTest, GenerateAesCbc192) {
  blink::WebCryptoKey key;
  Status status = GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(192), true,
                                    blink::kWebCryptoKeyUsageEncrypt, &key);
  ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status);
}

// 192-bit AES is intentionally unsupported (http://crbug.com/533699).
TEST_F(WebCryptoAesCbcTest, UnwrapAesCbc192) {
  std::vector<uint8_t> wrapping_key_data(16, 0);
  std::vector<uint8_t> wrapped_key = HexStringToBytes(
      "1A07ACAB6C906E50883173C29441DB1DE91D34F45C435B5F99C822867FB3956F");

  blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw(
      wrapping_key_data, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesKw),
      blink::kWebCryptoKeyUsageUnwrapKey);

  blink::WebCryptoKey unwrapped_key;
  ASSERT_EQ(Status::ErrorAes192BitUnsupported(),
            UnwrapKey(blink::kWebCryptoKeyFormatRaw, wrapped_key, wrapping_key,
                      CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesKw),
                      CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
                      blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
}

// Try importing an AES-CBC key with unsupported key usages using raw
// format. AES-CBC keys support the following usages:
//   'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'
TEST_F(WebCryptoAesCbcTest, ImportKeyBadUsage_Raw) {
  const blink::WebCryptoAlgorithm algorithm =
      CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc);

  const blink::WebCryptoKeyUsageMask kBadUsages[] = {
      blink::kWebCryptoKeyUsageSign,
      blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageDecrypt,
      blink::kWebCryptoKeyUsageDeriveBits,
      blink::kWebCryptoKeyUsageUnwrapKey | blink::kWebCryptoKeyUsageVerify,
  };

  std::vector<uint8_t> key_bytes(16);

  for (auto usage : kBadUsages) {
    SCOPED_TRACE(usage);

    blink::WebCryptoKey key;
    ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
              ImportKey(blink::kWebCryptoKeyFormatRaw, key_bytes, algorithm,
                        true, usage, &key));
  }
}

// Generate an AES-CBC key with invalid usages. AES-CBC supports:
//   'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'
TEST_F(WebCryptoAesCbcTest, GenerateKeyBadUsages) {
  const blink::WebCryptoKeyUsageMask kBadUsages[] = {
      blink::kWebCryptoKeyUsageSign,
      blink::kWebCryptoKeyUsageVerify,
      blink::kWebCryptoKeyUsageDecrypt | blink::kWebCryptoKeyUsageVerify,
  };

  for (auto usage : kBadUsages) {
    SCOPED_TRACE(usage);

    blink::WebCryptoKey key;

    ASSERT_EQ(
        Status::ErrorCreateKeyBadUsages(),
        GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), true, usage, &key));
  }
}

// Generate an AES-CBC key with no usages.
TEST_F(WebCryptoAesCbcTest, GenerateKeyEmptyUsages) {
  blink::WebCryptoKey key;

  ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
            GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), true, 0, &key));
}

// Generate an AES-CBC key and an RSA key pair. Use the AES-CBC key to wrap the
// key pair (using SPKI format for public key, PKCS8 format for private key).
// Then unwrap the wrapped key pair and verify that the key data is the same.
TEST_F(WebCryptoAesCbcTest, WrapUnwrapRoundtripSpkiPkcs8) {
  // Generate the wrapping key.
  blink::WebCryptoKey wrapping_key;
  ASSERT_EQ(Status::Success(),
            GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), true,
                              blink::kWebCryptoKeyUsageWrapKey |
                                  blink::kWebCryptoKeyUsageUnwrapKey,
                              &wrapping_key));

  // Generate an RSA key pair to be wrapped.
  const unsigned int modulus_length = 2048;
  const std::vector<uint8_t> public_exponent = HexStringToBytes("010001");

  blink::WebCryptoKey public_key;
  blink::WebCryptoKey private_key;
  ASSERT_EQ(Status::Success(),
            GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm(
                                blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
                                blink::kWebCryptoAlgorithmIdSha256,
                                modulus_length, public_exponent),
                            true, blink::kWebCryptoKeyUsageSign, &public_key,
                            &private_key));

  // Export key pair as SPKI + PKCS8
  std::vector<uint8_t> public_key_spki;
  ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatSpki,
                                         public_key, &public_key_spki));

  std::vector<uint8_t> private_key_pkcs8;
  ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatPkcs8,
                                         private_key, &private_key_pkcs8));

  // Wrap the key pair.
  blink::WebCryptoAlgorithm wrap_algorithm =
      CreateAesCbcAlgorithm(std::vector<uint8_t>(16, 0));

  std::vector<uint8_t> wrapped_public_key;
  ASSERT_EQ(Status::Success(),
            WrapKey(blink::kWebCryptoKeyFormatSpki, public_key, wrapping_key,
                    wrap_algorithm, &wrapped_public_key));

  std::vector<uint8_t> wrapped_private_key;
  ASSERT_EQ(Status::Success(),
            WrapKey(blink::kWebCryptoKeyFormatPkcs8, private_key, wrapping_key,
                    wrap_algorithm, &wrapped_private_key));

  // Unwrap the key pair.
  blink::WebCryptoAlgorithm rsa_import_algorithm =
      CreateRsaHashedImportAlgorithm(
          blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
          blink::kWebCryptoAlgorithmIdSha256);

  blink::WebCryptoKey unwrapped_public_key;

  ASSERT_EQ(Status::Success(),
            UnwrapKey(blink::kWebCryptoKeyFormatSpki, wrapped_public_key,
                      wrapping_key, wrap_algorithm, rsa_import_algorithm, true,
                      blink::kWebCryptoKeyUsageVerify, &unwrapped_public_key));

  blink::WebCryptoKey unwrapped_private_key;

  ASSERT_EQ(Status::Success(),
            UnwrapKey(blink::kWebCryptoKeyFormatPkcs8, wrapped_private_key,
                      wrapping_key, wrap_algorithm, rsa_import_algorithm, true,
                      blink::kWebCryptoKeyUsageSign, &unwrapped_private_key));

  // Export unwrapped key pair as SPKI + PKCS8
  std::vector<uint8_t> unwrapped_public_key_spki;
  ASSERT_EQ(Status::Success(),
            ExportKey(blink::kWebCryptoKeyFormatSpki, unwrapped_public_key,
                      &unwrapped_public_key_spki));

  std::vector<uint8_t> unwrapped_private_key_pkcs8;
  ASSERT_EQ(Status::Success(),
            ExportKey(blink::kWebCryptoKeyFormatPkcs8, unwrapped_private_key,
                      &unwrapped_private_key_pkcs8));

  EXPECT_EQ(public_key_spki, unwrapped_public_key_spki);
  EXPECT_EQ(private_key_pkcs8, unwrapped_private_key_pkcs8);

  EXPECT_NE(public_key_spki, wrapped_public_key);
  EXPECT_NE(private_key_pkcs8, wrapped_private_key);
}

}  // namespace

}  // namespace webcrypto
