chromium/chrome/browser/ash/platform_keys/platform_keys_service_browsertest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/browser/ash/platform_keys/platform_keys_service.h"

#include <memory>
#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/ash/login/test/device_state_mixin.h"
#include "chrome/browser/ash/login/test/login_manager_mixin.h"
#include "chrome/browser/ash/login/test/scoped_policy_update.h"
#include "chrome/browser/ash/login/test/user_policy_mixin.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service_test_util.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chrome/browser/net/nss_service.h"
#include "chrome/browser/net/nss_service_factory.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ash/scoped_test_system_nss_key_slot_mixin.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/chaps_util/test_util.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
#include "components/policy/core/common/policy_switches.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "crypto/nss_key_util.h"
#include "crypto/scoped_nss_types.h"
#include "crypto/sha2.h"
#include "crypto/signature_verifier.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util_nss.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/constants/pkcs11_custom_attributes.h"

namespace ash::platform_keys {
namespace {

using ::chromeos::platform_keys::HashAlgorithm;
using ::chromeos::platform_keys::KeyAttributeType;
using ::chromeos::platform_keys::Status;
using ::chromeos::platform_keys::TokenId;

constexpr char kTestUserEmail[] = "[email protected]";
constexpr char kTestAffiliationId[] = "test_affiliation_id";
const std::vector<uint8_t> kTestingData = {9, 8, 7, 6, 5, 0, 1, 2, 3};
const std::vector<uint8_t> kSymId = {1, 2, 3, 0, 1, 2, 3};
const int kDefaultSymKeySize = 32;
const unsigned long kDefaultSymSignatureSize = 32;

enum class ProfileToUse {
  // A Profile that belongs to a user that is not affiliated with the device (no
  // access to the system token).
  kUnaffiliatedUserProfile,
  // A Profile that belongs to a user that is affiliated with the device (access
  // to the system token).
  kAffiliatedUserProfile,
  // The sign-in screen profile.
  kSigninProfile
};

// Describes a test configuration for the test suite that runs one test per
// profile.
struct TestConfigPerProfile {
  // The profile for which PlatformKeysService should be tested.
  ProfileToUse profile_to_use;

  // The token IDs that are expected to be available for |profile_to_use|.
  std::vector<TokenId> token_ids;
};

// Describes a test configuration for the test suite that runs one test per
// (profile, token) combination.
struct TestConfigPerToken {
  // The profile for which PlatformKeysService should be tested.
  ProfileToUse profile_to_use;

  // The token ID to perform the tests on.
  TokenId token_id;
};

// Returns |hash| prefixed with DER-encoded PKCS#1 DigestInfo with
// AlgorithmIdentifier=id-sha256.
// This is useful for testing PlatformKeysService::SignRSAPKCS1Raw which only
// appends PKCS#1 v1.5 padding before signing.
std::vector<uint8_t> PrependSHA256DigestInfo(base::span<const uint8_t> hash) {
  // DER-encoded PKCS#1 DigestInfo "prefix" with
  // AlgorithmIdentifier=id-sha256.
  // The encoding is taken from https://tools.ietf.org/html/rfc3447#page-43
  const std::vector<uint8_t> kDigestInfoSha256DerData = {
      0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
      0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};

  std::vector<uint8_t> result;
  result.reserve(kDigestInfoSha256DerData.size() + hash.size());

  result.insert(result.end(), kDigestInfoSha256DerData.begin(),
                kDigestInfoSha256DerData.end());
  result.insert(result.end(), hash.begin(), hash.end());
  return result;
}

std::string BytesToStr(const std::vector<uint8_t>& val) {
  return std::string(val.begin(), val.end());
}

}  // namespace

class PlatformKeysServiceBrowserTestBase
    : public MixinBasedInProcessBrowserTest {
 public:
  PlatformKeysServiceBrowserTestBase() = default;
  PlatformKeysServiceBrowserTestBase(
      const PlatformKeysServiceBrowserTestBase& other) = delete;
  PlatformKeysServiceBrowserTestBase& operator=(
      const PlatformKeysServiceBrowserTestBase& other) = delete;
  ~PlatformKeysServiceBrowserTestBase() override = default;

  void SetUpInProcessBrowserTestFixture() override {
    MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture();

    // Call |Request*PolicyUpdate| even if not setting affiliation IDs so
    // (empty) policy blobs are prepared in FakeSessionManagerClient.
    auto user_policy_update = user_policy_mixin_.RequestPolicyUpdate();
    auto device_policy_update = device_state_mixin_.RequestDevicePolicyUpdate();
    if (GetProfileToUse() == ProfileToUse::kAffiliatedUserProfile) {
      device_policy_update->policy_data()->add_device_affiliation_ids(
          kTestAffiliationId);
      user_policy_update->policy_data()->add_user_affiliation_ids(
          kTestAffiliationId);
    }

    PlatformKeysServiceFactory::GetInstance()->SetTestingMode(true);
  }

  void SetUpOnMainThread() override {
    MixinBasedInProcessBrowserTest::SetUpOnMainThread();

    if (GetProfileToUse() == ProfileToUse::kSigninProfile) {
      profile_ = ProfileHelper::GetSigninProfile();
    } else {
      ASSERT_TRUE(login_manager_mixin_.LoginAndWaitForActiveSession(
          LoginManagerMixin::CreateDefaultUserContext(test_user_info_)));
      profile_ = ProfileManager::GetActiveUserProfile();

      base::RunLoop loop;
      NssServiceFactory::GetForContext(profile_)
          ->UnsafelyGetNSSCertDatabaseForTesting(
              base::BindOnce(&PlatformKeysServiceBrowserTestBase::SetUserSlot,
                             base::Unretained(this), loop.QuitClosure()));
      loop.Run();
    }
    ASSERT_TRUE(profile_);

    platform_keys_service_ =
        PlatformKeysServiceFactory::GetForBrowserContext(profile_);
    ASSERT_TRUE(platform_keys_service_);
  }

  void TearDownOnMainThread() override {
    MixinBasedInProcessBrowserTest::TearDownOnMainThread();
  }

 protected:
  virtual ProfileToUse GetProfileToUse() = 0;

  PlatformKeysService* platform_keys_service() {
    return platform_keys_service_;
  }

  // Returns the slot to be used depending on |token_id|.
  PK11SlotInfo* GetSlot(TokenId token_id) {
    switch (token_id) {
      case TokenId::kSystem:
        return system_nss_key_slot_mixin_.slot();
      case TokenId::kUser:
        return user_slot_.get();
    }
  }

  // Generates a key pair in the given |token_id| using platform keys service
  // and returns the SubjectPublicKeyInfo string encoded in DER format.
  std::vector<uint8_t> GenerateKeyPair(TokenId token_id,
                                       unsigned int key_size) {
    base::test::TestFuture<std::vector<uint8_t>,
                           chromeos::platform_keys::Status>
        generate_key_waiter;
    platform_keys_service()->GenerateRSAKey(token_id, key_size,
                                            /*sw_backed=*/false,
                                            generate_key_waiter.GetCallback());
    EXPECT_TRUE(generate_key_waiter.Wait());
    return std::get<std::vector<uint8_t>>(generate_key_waiter.Take());
  }

  // Generates a key pair with a default size in the given |token_id| using
  // platform keys service and returns the SubjectPublicKeyInfo string encoded
  // in DER format.
  std::vector<uint8_t> GenerateKeyPair(TokenId token_id) {
    const unsigned int kDefaultKeySize = 2048;
    return GenerateKeyPair(token_id, kDefaultKeySize);
  }

  // Imports the certificate and key described by the |cert_filename| and
  // |key_filename| files in |source_dir| into the Token |token_id|, then stores
  // the resulting certificate in *|out_cert| and the SPKI of the public key in
  // *|out_spki_der|. Should be wrapped in ASSERT_NO_FATAL_FAILURE.
  void ImportCertAndKey(TokenId token_id,
                        const base::FilePath& source_dir,
                        const std::string& cert_filename,
                        const std::string& key_filename,
                        net::ScopedCERTCertificate* out_cert,
                        std::vector<uint8_t>* out_spki_der) {
    // Import testing key pair and certificate.
    {
      base::ScopedAllowBlockingForTesting allow_io;
      net::ImportClientCertAndKeyFromFile(
          source_dir, cert_filename, key_filename, GetSlot(token_id), out_cert);
    }
    CERTCertificate* cert = out_cert->get();
    ASSERT_TRUE(cert);
    ASSERT_GT(cert->derPublicKey.len, 0U);
    *out_spki_der =
        std::vector<uint8_t>(cert->derPublicKey.data,
                             cert->derPublicKey.data + cert->derPublicKey.len);
  }

 private:
  void SetUserSlot(base::OnceClosure done_callback, net::NSSCertDatabase* db) {
    user_slot_ = db->GetPrivateSlot();
    std::move(done_callback).Run();
  }

  const AccountId test_user_account_id_ = AccountId::FromUserEmailGaiaId(
      kTestUserEmail,
      signin::GetTestGaiaIdForEmail(kTestUserEmail));
  const LoginManagerMixin::TestUserInfo test_user_info_{test_user_account_id_};

  ScopedTestSystemNSSKeySlotMixin system_nss_key_slot_mixin_{&mixin_host_};
  LoginManagerMixin login_manager_mixin_{&mixin_host_, {test_user_info_}};

  DeviceStateMixin device_state_mixin_{
      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
  UserPolicyMixin user_policy_mixin_{&mixin_host_, test_user_account_id_};

  // Unowned pointer to the profile selected by the current TestConfig.
  // Valid after SetUpOnMainThread().
  raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr;
  // Unowned pointer to the PlatformKeysService for |profile_|. Valid after
  // SetUpOnMainThread().
  raw_ptr<PlatformKeysService, DanglingUntriaged> platform_keys_service_ =
      nullptr;
  // The private slot for the profile under test. This should be null if the
  // test parameter mandates testing with the sign-in profile.
  crypto::ScopedPK11Slot user_slot_;
};

class PlatformKeysServicePerProfileBrowserTest
    : public PlatformKeysServiceBrowserTestBase,
      public ::testing::WithParamInterface<TestConfigPerProfile> {
 public:
  PlatformKeysServicePerProfileBrowserTest() = default;
  PlatformKeysServicePerProfileBrowserTest(
      const PlatformKeysServicePerProfileBrowserTest& other) = delete;
  PlatformKeysServicePerProfileBrowserTest& operator=(
      const PlatformKeysServicePerProfileBrowserTest& other) = delete;
  ~PlatformKeysServicePerProfileBrowserTest() override = default;

 protected:
  ProfileToUse GetProfileToUse() override { return GetParam().profile_to_use; }
};

using CallbackIfCallableBrowserTest = InProcessBrowserTest;

// Tests that GetTokens() is callable and returns the expected tokens.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerProfileBrowserTest, GetTokens) {
  test_util::GetTokensExecutionWaiter get_tokens_waiter;
  platform_keys_service()->GetTokens(get_tokens_waiter.GetCallback());
  ASSERT_TRUE(get_tokens_waiter.Wait());

  EXPECT_EQ(get_tokens_waiter.status(), Status::kSuccess);
  EXPECT_THAT(get_tokens_waiter.token_ids(),
              ::testing::UnorderedElementsAreArray(GetParam().token_ids));
}

// Generates a key pair in tokens accessible from the profile under test and
// retrieves them.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerProfileBrowserTest, GetAllKeys) {
  // Generate key pair in every token.
  std::map<TokenId, std::vector<uint8_t>> token_key_map;
  for (TokenId token_id : GetParam().token_ids) {
    const std::vector<uint8_t> public_key_spki_der = GenerateKeyPair(token_id);
    ASSERT_FALSE(public_key_spki_der.empty());
    token_key_map[token_id] = public_key_spki_der;
  }

  // Only keys in the requested token should be retrieved.
  for (TokenId token_id : GetParam().token_ids) {
    base::test::TestFuture<std::vector<std::vector<uint8_t>>, Status>
        get_all_keys_waiter;
    platform_keys_service()->GetAllKeys(token_id,
                                        get_all_keys_waiter.GetCallback());
    ASSERT_TRUE(get_all_keys_waiter.Wait());

    EXPECT_EQ(get_all_keys_waiter.Get<Status>(), Status::kSuccess);
    const auto& public_keys =
        get_all_keys_waiter.Get<std::vector<std::vector<uint8_t>>>();
    ASSERT_EQ(public_keys.size(), 1U);
    EXPECT_EQ(public_keys[0], token_key_map[token_id]);
  }
}

// Imports the same key into all tokens. Verifies that key attributes are stored
// per-token and don't leak between tokens.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerProfileBrowserTest,
                       KeyAttributesPerToken) {
  // Import the same key pair + cert in every token, remember its SPKI.
  std::vector<uint8_t> spki_der;
  for (TokenId token_id : GetParam().token_ids) {
    net::ScopedCERTCertificate cert;
    std::vector<uint8_t> current_spki_der;
    ASSERT_NO_FATAL_FAILURE(
        ImportCertAndKey(token_id, net::GetTestCertsDirectory(), "client_1.pem",
                         "client_1.pk8", &cert, &current_spki_der));
    // The SPKI must be the same on every slot, because the same key was
    // imported.
    if (!spki_der.empty()) {
      EXPECT_EQ(current_spki_der, spki_der);
      continue;
    }
    spki_der = current_spki_der;
  }

  // Set an attribute for the key on each token.
  const KeyAttributeType kAttributeType =
      KeyAttributeType::kCertificateProvisioningId;
  std::map<TokenId, std::vector<uint8_t>> token_to_value;
  for (TokenId token_id : GetParam().token_ids) {
    token_to_value[token_id] = {1, 2, 3, 4, 5, static_cast<uint8_t>(token_id)};

    // Set key attribute.
    base::test::TestFuture<Status> set_attr_waiter;
    platform_keys_service()->SetAttributeForKey(
        token_id, spki_der, kAttributeType, token_to_value[token_id],
        set_attr_waiter.GetCallback());
    ASSERT_TRUE(set_attr_waiter.Wait());
    EXPECT_EQ(set_attr_waiter.Get<Status>(), Status::kSuccess);
  }

  // Verify the token-specific attribute value for the key on each token.
  for (TokenId token_id : GetParam().token_ids) {
    // Get key attribute.
    base::test::TestFuture<std::optional<std::vector<uint8_t>>, Status>
        get_attr_waiter;
    platform_keys_service()->GetAttributeForKey(
        token_id, spki_der, kAttributeType, get_attr_waiter.GetCallback());
    ASSERT_TRUE(get_attr_waiter.Wait());

    EXPECT_EQ(get_attr_waiter.Get<Status>(), Status::kSuccess);
    const std::optional<std::vector<uint8_t>>& attr = get_attr_waiter.Get<0>();
    ASSERT_TRUE(attr.has_value());
    EXPECT_EQ(attr.value(), token_to_value[token_id]);
  }
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerProfileBrowserTest,
                       IsKeyOnTokenWhenNot) {
  // More than one available token are needed to test this.
  if (GetParam().token_ids.size() < 2) {
    return;
  }

  const TokenId token_id_1 = GetParam().token_ids[0];
  const TokenId token_id_2 = GetParam().token_ids[1];

  const std::vector<uint8_t> public_key = GenerateKeyPair(token_id_1);

  test_util::IsKeyOnTokenExecutionWaiter is_key_on_token_waiter;
  platform_keys_service()->IsKeyOnToken(token_id_2, std::move(public_key),
                                        is_key_on_token_waiter.GetCallback());
  ASSERT_TRUE(is_key_on_token_waiter.Wait());

  EXPECT_EQ(is_key_on_token_waiter.status(), Status::kSuccess);
  ASSERT_TRUE(is_key_on_token_waiter.on_slot().has_value());
  EXPECT_FALSE(is_key_on_token_waiter.on_slot().value());
}

INSTANTIATE_TEST_SUITE_P(
    AllSupportedProfileTypes,
    PlatformKeysServicePerProfileBrowserTest,
    ::testing::Values(
        TestConfigPerProfile{ProfileToUse::kSigninProfile, {TokenId::kSystem}},
        TestConfigPerProfile{ProfileToUse::kUnaffiliatedUserProfile,
                             {TokenId::kUser}},
        TestConfigPerProfile{ProfileToUse::kAffiliatedUserProfile,
                             {TokenId::kUser, TokenId::kSystem}},
        TestConfigPerProfile{ProfileToUse::kAffiliatedUserProfile,
                             {TokenId::kSystem, TokenId::kUser}}));

class PlatformKeysServicePerTokenBrowserTest
    : public PlatformKeysServiceBrowserTestBase,
      public ::testing::WithParamInterface<TestConfigPerToken> {
 public:
  PlatformKeysServicePerTokenBrowserTest() = default;
  PlatformKeysServicePerTokenBrowserTest(
      const PlatformKeysServicePerTokenBrowserTest& other) = delete;
  PlatformKeysServicePerTokenBrowserTest& operator=(
      const PlatformKeysServicePerTokenBrowserTest& other) = delete;
  ~PlatformKeysServicePerTokenBrowserTest() override = default;

 protected:
  ProfileToUse GetProfileToUse() override { return GetParam().profile_to_use; }
};

// Generates a Rsa key pair and tests signing using that key pair.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       GenerateRsaAndSign) {
  const std::vector<uint8_t> kDataToSign({1, 2, 3, 4, 5});
  const unsigned int kKeySize = 2048;
  const HashAlgorithm kHashAlgorithm = HashAlgorithm::HASH_ALGORITHM_SHA256;
  const crypto::SignatureVerifier::SignatureAlgorithm kSignatureAlgorithm =
      crypto::SignatureVerifier::RSA_PKCS1_SHA256;

  const TokenId token_id = GetParam().token_id;
  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateRSAKey(token_id, kKeySize,
                                          /*sw_backed=*/false,
                                          generate_key_waiter.GetCallback());
  ASSERT_TRUE(generate_key_waiter.Wait());
  EXPECT_EQ(generate_key_waiter.Get<Status>(), Status::kSuccess);

  const std::vector<uint8_t> public_key_spki_der =
      generate_key_waiter.Get<std::vector<uint8_t>>();
  EXPECT_FALSE(public_key_spki_der.empty());

  base::test::TestFuture<std::vector<uint8_t>, Status> sign_waiter;
  platform_keys_service()->SignRsaPkcs1(token_id, kDataToSign,
                                        public_key_spki_der, kHashAlgorithm,
                                        sign_waiter.GetCallback());
  ASSERT_TRUE(sign_waiter.Wait());
  EXPECT_EQ(sign_waiter.Get<Status>(), Status::kSuccess);

  crypto::SignatureVerifier signature_verifier;
  ASSERT_TRUE(signature_verifier.VerifyInit(
      kSignatureAlgorithm,
      base::as_bytes(base::make_span(sign_waiter.Get<std::vector<uint8_t>>())),
      base::as_bytes(base::make_span(public_key_spki_der))));
  signature_verifier.VerifyUpdate(base::as_bytes(base::make_span(kDataToSign)));
  EXPECT_TRUE(signature_verifier.VerifyFinal());
}

class RunLoopQuiter {
 public:
  explicit RunLoopQuiter(base::RunLoop* rl) : runloop_(rl) {}
  void Quit() { runloop_->Quit(); }
  void QuitWithFail() {
    Quit();
    FAIL();
  }
  base::WeakPtr<RunLoopQuiter> GetWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

 private:
  const raw_ptr<base::RunLoop> runloop_;
  base::WeakPtrFactory<RunLoopQuiter> weak_factory_{this};
};

// Verifies that RunCallBackIfCallableElseCleanup will run the cleanup callback
// if the given callback is canceled. RunCallBackIfCallableElseCleanup is
// specially helpful in PlatformKeyService when PKS is asked to generate a key
// and by the time the key is generated the asker is gone.
IN_PROC_BROWSER_TEST_F(CallbackIfCallableBrowserTest,
                       CallCleanUpWhenCallBackIsCanceled) {
  base::RunLoop loop;
  base::OnceCallback<void()> canceled_cb, quit_loop_cb;

  RunLoopQuiter main_quiter(&loop);
  quit_loop_cb = base::BindOnce(&RunLoopQuiter::Quit, main_quiter.GetWeakPtr());

  {
    RunLoopQuiter canceled_quiter(&loop);
    // If the canceled_cb runs then test will fail.
    canceled_cb = base::BindOnce(&RunLoopQuiter::QuitWithFail,
                                 canceled_quiter.GetWeakPtr());
  }

  RunCallBackIfCallableElseRunCleanUp(std::move(canceled_cb),
                                      std::move(quit_loop_cb));
  loop.Run();
}

// Generates a Rsa key pair and tests signing using the SignRSAPKCS1Raw
// function.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       GenerateRsaAndSignRaw) {
  const unsigned int kKeySize = 2048;
  const TokenId token_id = GetParam().token_id;

  // SignRSAPKCS1Raw only performs PKCS#1.5 padding. To get a correct PKCS#1
  // signature of |kDataToSign|, it is necessary to pass
  // (DigestInfo + hash(kDataToSign)) to SignRSAPKCS1Raw, where DigestInfo
  // describes the hash function.
  const std::vector<uint8_t> kDataToSign({1, 2, 3, 4, 5});
  const std::array<uint8_t, crypto::kSHA256Length> kDataToSignHash =
      crypto::SHA256Hash(kDataToSign);
  const std::vector<uint8_t> kDigestInfoAndDataToSignHash =
      PrependSHA256DigestInfo(kDataToSignHash);

  const crypto::SignatureVerifier::SignatureAlgorithm kSignatureAlgorithm =
      crypto::SignatureVerifier::RSA_PKCS1_SHA256;

  const std::vector<uint8_t> public_key_spki_der =
      GenerateKeyPair(token_id, kKeySize);

  base::test::TestFuture<std::vector<uint8_t>, Status> sign_waiter;
  platform_keys_service()->SignRSAPKCS1Raw(
      token_id, kDigestInfoAndDataToSignHash, public_key_spki_der,
      sign_waiter.GetCallback());
  ASSERT_TRUE(sign_waiter.Wait());
  EXPECT_EQ(sign_waiter.Get<Status>(), Status::kSuccess);

  crypto::SignatureVerifier signature_verifier;
  ASSERT_TRUE(signature_verifier.VerifyInit(
      kSignatureAlgorithm,
      base::as_bytes(base::make_span(sign_waiter.Get<std::vector<uint8_t>>())),
      base::as_bytes(base::make_span(public_key_spki_der))));
  signature_verifier.VerifyUpdate(kDataToSign);
  EXPECT_TRUE(signature_verifier.VerifyFinal());
}

// Generates a software-backed RSA key pair.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       GenerateRsaSoftwareBacked) {
  const unsigned int kKeySize = 2048;

  // Arrange: Configure the ChapsUtilFactory singleton instance to return fake
  // ChapsUtil instances.
  chromeos::ScopedChapsUtilOverride scoped_chaps_util_override;

  // Act: Generate the key pair.
  const TokenId token_id = GetParam().token_id;
  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateRSAKey(token_id, kKeySize,
                                          /*sw_backed=*/true,
                                          generate_key_waiter.GetCallback());
  ASSERT_TRUE(generate_key_waiter.Wait());
  EXPECT_EQ(generate_key_waiter.Get<Status>(), Status::kSuccess);

  // Assert: Verify that the the returned public key SPKI has been generated
  // through the fake ChapsUtil.
  const std::vector<uint8_t> public_key_spki_der =
      generate_key_waiter.Get<std::vector<uint8_t>>();
  EXPECT_FALSE(public_key_spki_der.empty());

  EXPECT_THAT(scoped_chaps_util_override.generated_key_spkis(),
              ::testing::ElementsAre(BytesToStr(public_key_spki_der)));
}

// Generates a Rsa key pair and tests expected limits of the input length of the
// SignRSAPKCS1Raw function.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       SignRawInputTooLong) {
  const unsigned int kKeySize = 2048;
  const TokenId token_id = GetParam().token_id;

  const std::vector<uint8_t> public_key_spki_der =
      GenerateKeyPair(token_id, kKeySize);

  // SignRSAPKCS1Raw performs PKCS#11 padding which adds at least 11 bytes.
  {
    // An input of |kKeySize in bytes - 11| should be fine.
    std::vector<uint8_t> data_to_sign;
    data_to_sign.resize(kKeySize / 8 - 11);

    base::test::TestFuture<std::vector<uint8_t>, Status> sign_waiter;
    platform_keys_service()->SignRSAPKCS1Raw(
        token_id, data_to_sign, public_key_spki_der, sign_waiter.GetCallback());
    ASSERT_TRUE(sign_waiter.Wait());
    EXPECT_EQ(sign_waiter.Get<Status>(), Status::kSuccess);
  }

  {
    // An input of |kKeySize in bytes - 10| should be too long.
    std::vector<uint8_t> data_to_sign_too_long;
    data_to_sign_too_long.resize(kKeySize / 8 - 10);

    base::test::TestFuture<std::vector<uint8_t>, Status> sign_waiter;
    platform_keys_service()->SignRSAPKCS1Raw(token_id, data_to_sign_too_long,
                                             public_key_spki_der,
                                             sign_waiter.GetCallback());
    ASSERT_TRUE(sign_waiter.Wait());
    EXPECT_EQ(sign_waiter.Get<Status>(), Status::kErrorInputTooLong);
  }
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       SetAndGetKeyAttribute) {
  // The attribute type to be set and retrieved using platform keys service.
  const KeyAttributeType kAttributeType =
      KeyAttributeType::kCertificateProvisioningId;
  const TokenId token_id = GetParam().token_id;
  const std::vector<uint8_t> kAttributeValue = {20, 21, 22, 23, 24};

  // Generate key pair.
  const std::vector<uint8_t> public_key_spki_der = GenerateKeyPair(token_id);
  ASSERT_FALSE(public_key_spki_der.empty());

  // Set key attribute.
  base::test::TestFuture<Status> set_attr_waiter;
  platform_keys_service()->SetAttributeForKey(token_id, public_key_spki_der,
                                              kAttributeType, kAttributeValue,
                                              set_attr_waiter.GetCallback());
  ASSERT_EQ(set_attr_waiter.Get<Status>(), Status::kSuccess);

  // Get key attribute.
  base::test::TestFuture<std::optional<std::vector<uint8_t>>, Status>
      get_attr_waiter;
  platform_keys_service()->GetAttributeForKey(token_id, public_key_spki_der,
                                              kAttributeType,
                                              get_attr_waiter.GetCallback());
  ASSERT_TRUE(get_attr_waiter.Wait());

  EXPECT_EQ(get_attr_waiter.Get<Status>(), Status::kSuccess);
  std::optional<std::vector<uint8_t>> attr = get_attr_waiter.Get<0>();
  ASSERT_TRUE(attr.has_value());
  EXPECT_EQ(attr.value(), kAttributeValue);
}

// TODO(crbug.com/40686352): Add a test for an unset key attribute when
// simulating chaps behavior is possible.

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       GetKeyAttributeForNonExistingKey) {
  const KeyAttributeType kAttributeType =
      KeyAttributeType::kCertificateProvisioningId;
  const TokenId token_id = GetParam().token_id;
  const std::vector<uint8_t> kNonExistingPublicKey = {1, 2, 3};

  // Get key attribute.
  base::test::TestFuture<std::optional<std::vector<uint8_t>>, Status>
      get_attr_waiter;
  platform_keys_service()->GetAttributeForKey(token_id, kNonExistingPublicKey,
                                              kAttributeType,
                                              get_attr_waiter.GetCallback());
  ASSERT_TRUE(get_attr_waiter.Wait());

  EXPECT_NE(get_attr_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_FALSE(get_attr_waiter.Get<std::optional<std::vector<uint8_t>>>());
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       SetKeyAttributeForNonExistingKey) {
  const KeyAttributeType kAttributeType =
      KeyAttributeType::kCertificateProvisioningId;
  const TokenId token_id = GetParam().token_id;
  const std::vector<uint8_t> kAttributeValue = {20, 21, 22, 23, 24};
  const std::vector<uint8_t> kNonExistingPublicKey = {1, 2, 3};

  // Set key attribute.
  base::test::TestFuture<Status> set_attr_waiter;
  platform_keys_service()->SetAttributeForKey(token_id, kNonExistingPublicKey,
                                              kAttributeType, kAttributeValue,
                                              set_attr_waiter.GetCallback());
  ASSERT_TRUE(set_attr_waiter.Wait());

  EXPECT_NE(set_attr_waiter.Get<Status>(), Status::kSuccess);
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       RemoveKeyWithNoMatchingCertificates) {
  const TokenId token_id = GetParam().token_id;

  // Generate first key pair.
  const std::vector<uint8_t> public_key_1 = GenerateKeyPair(token_id);
  ASSERT_FALSE(public_key_1.empty());

  // Generate second key pair.
  const std::vector<uint8_t> public_key_2 = GenerateKeyPair(token_id);
  ASSERT_FALSE(public_key_2.empty());

  auto public_key_bytes_1 = base::as_bytes(base::make_span(public_key_1));
  auto public_key_bytes_2 = base::as_bytes(base::make_span(public_key_2));
  EXPECT_TRUE(crypto::FindNSSKeyFromPublicKeyInfo(public_key_bytes_1));
  EXPECT_TRUE(crypto::FindNSSKeyFromPublicKeyInfo(public_key_bytes_2));

  test_util::RemoveKeyExecutionWaiter remove_key_waiter;
  platform_keys_service()->RemoveKey(token_id, public_key_1,
                                     remove_key_waiter.GetCallback());
  ASSERT_TRUE(remove_key_waiter.Wait());

  EXPECT_EQ(remove_key_waiter.status(), Status::kSuccess);
  EXPECT_FALSE(crypto::FindNSSKeyFromPublicKeyInfo(public_key_bytes_1));
  EXPECT_TRUE(crypto::FindNSSKeyFromPublicKeyInfo(public_key_bytes_2));
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       RemoveKeyWithMatchingCertificate) {
  const TokenId token_id = GetParam().token_id;

  // Assert that there are no certificates before importing.
  test_util::GetCertificatesExecutionWaiter get_certificates_waiter;
  platform_keys_service()->GetCertificates(
      token_id, get_certificates_waiter.GetCallback());
  ASSERT_TRUE(get_certificates_waiter.Wait());
  ASSERT_EQ(get_certificates_waiter.matches().size(), 0U);

  net::ScopedCERTCertificate cert;
  std::vector<uint8_t> public_key;
  ASSERT_NO_FATAL_FAILURE(
      ImportCertAndKey(token_id, net::GetTestCertsDirectory(), "client_1.pem",
                       "client_1.pk8", &cert, &public_key));

  // Assert that the certificate is imported correctly.
  ASSERT_TRUE(cert.get());
  test_util::GetCertificatesExecutionWaiter get_certificates_waiter_2;
  platform_keys_service()->GetCertificates(
      token_id, get_certificates_waiter_2.GetCallback());
  ASSERT_TRUE(get_certificates_waiter_2.Wait());
  ASSERT_EQ(get_certificates_waiter_2.matches().size(), 1U);

  auto public_key_bytes = base::as_bytes(base::make_span(public_key));
  EXPECT_TRUE(crypto::FindNSSKeyFromPublicKeyInfo(public_key_bytes));

  // Try Removing the key pair.
  test_util::RemoveKeyExecutionWaiter remove_key_waiter;
  platform_keys_service()->RemoveKey(token_id, public_key,
                                     remove_key_waiter.GetCallback());
  ASSERT_TRUE(remove_key_waiter.Wait());
  EXPECT_NE(remove_key_waiter.status(), Status::kSuccess);

  // Assert that the certificate is not removed.
  test_util::GetCertificatesExecutionWaiter get_certificates_waiter_3;
  platform_keys_service()->GetCertificates(
      token_id, get_certificates_waiter_3.GetCallback());
  ASSERT_TRUE(get_certificates_waiter_3.Wait());

  net::CertificateList found_certs = get_certificates_waiter_3.matches();
  ASSERT_EQ(found_certs.size(), 1U);
  EXPECT_TRUE(
      net::x509_util::IsSameCertificate(found_certs[0].get(), cert.get()));

  // Assert that the key pair is not deleted.
  EXPECT_TRUE(crypto::FindNSSKeyFromPublicKeyInfo(public_key_bytes));
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       GetAllKeysWhenNoKeysGenerated) {
  const TokenId token_id = GetParam().token_id;
  base::test::TestFuture<std::vector<std::vector<uint8_t>>, Status>
      get_all_keys_waiter;
  platform_keys_service()->GetAllKeys(token_id,
                                      get_all_keys_waiter.GetCallback());
  ASSERT_TRUE(get_all_keys_waiter.Wait());

  EXPECT_EQ(get_all_keys_waiter.Get<Status>(), Status::kSuccess);
  const auto& public_keys =
      get_all_keys_waiter.Get<std::vector<std::vector<uint8_t>>>();
  EXPECT_TRUE(public_keys.empty());
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest, IsKeyOnToken) {
  const TokenId token_id = GetParam().token_id;
  const std::vector<uint8_t> public_key = GenerateKeyPair(token_id);

  test_util::IsKeyOnTokenExecutionWaiter is_key_on_token_waiter;
  platform_keys_service()->IsKeyOnToken(token_id, std::move(public_key),
                                        is_key_on_token_waiter.GetCallback());
  ASSERT_TRUE(is_key_on_token_waiter.Wait());

  EXPECT_EQ(is_key_on_token_waiter.status(), Status::kSuccess);
  ASSERT_TRUE(is_key_on_token_waiter.on_slot().has_value());
  EXPECT_TRUE(is_key_on_token_waiter.on_slot().value());
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       IsKeyOnTokenWhenNoKeysGenerated) {
  const TokenId token_id = GetParam().token_id;

  // A public key for a key that was never generated. (The content is also not
  // realistic.)
  std::vector<uint8_t> bad_key = {1, 2, 3, 4, 5};

  test_util::IsKeyOnTokenExecutionWaiter is_key_on_token_waiter;
  platform_keys_service()->IsKeyOnToken(token_id, std::move(bad_key),
                                        is_key_on_token_waiter.GetCallback());
  ASSERT_TRUE(is_key_on_token_waiter.Wait());

  EXPECT_EQ(is_key_on_token_waiter.status(), Status::kSuccess);
  ASSERT_TRUE(is_key_on_token_waiter.on_slot().has_value());
  EXPECT_FALSE(is_key_on_token_waiter.on_slot().value());
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       GetKeyLocations) {
  const TokenId token_id = GetParam().token_id;

  const std::vector<uint8_t> public_key = GenerateKeyPair(token_id);

  test_util::GetKeyLocationsExecutionWaiter get_key_locations_waiter;
  platform_keys_service()->GetKeyLocations(
      public_key, get_key_locations_waiter.GetCallback());
  ASSERT_TRUE(get_key_locations_waiter.Wait());

  EXPECT_EQ(get_key_locations_waiter.status(), Status::kSuccess);
  ASSERT_EQ(get_key_locations_waiter.key_locations().size(), 1U);
  EXPECT_EQ(get_key_locations_waiter.key_locations()[0], token_id);
}

// Generates symmetric key and uses it to sign with HMAC.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest, SymKeySign) {
  const TokenId token_id = GetParam().token_id;
  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateSymKey(
      token_id, kSymId, kDefaultSymKeySize,
      chromeos::platform_keys::SymKeyType::kHmac,
      generate_key_waiter.GetCallback());
  EXPECT_EQ(generate_key_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_EQ(kSymId, generate_key_waiter.Get<std::vector<uint8_t>>());

  base::test::TestFuture<std::vector<uint8_t>, Status> sign_waiter;
  platform_keys_service()->SignWithSymKey(token_id, kTestingData, kSymId,
                                          sign_waiter.GetCallback());
  EXPECT_EQ(sign_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_EQ(sign_waiter.Get<std::vector<uint8_t>>().size(),
            kDefaultSymSignatureSize);
}

// Cannot sign with invalid/absent key.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       SymKeySignInvalidKey) {
  const TokenId token_id = GetParam().token_id;

  // Should fail because the key doesn't exist.
  base::test::TestFuture<std::vector<uint8_t>, Status> sign_waiter;
  platform_keys_service()->SignWithSymKey(token_id, kTestingData, kSymId,
                                          sign_waiter.GetCallback());
  EXPECT_EQ(sign_waiter.Get<Status>(), Status::kErrorKeyNotFound);
  EXPECT_TRUE(sign_waiter.Get<std::vector<uint8_t>>().empty());

  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateSymKey(
      token_id, kSymId, kDefaultSymKeySize,
      chromeos::platform_keys::SymKeyType::kAesCbc,
      generate_key_waiter.GetCallback());
  EXPECT_EQ(generate_key_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_EQ(kSymId, generate_key_waiter.Get<std::vector<uint8_t>>());

  // Should fail because the key is generated for purposes other than signing.
  base::test::TestFuture<std::vector<uint8_t>, Status> sign_waiter2;
  platform_keys_service()->SignWithSymKey(token_id, kTestingData, kSymId,
                                          sign_waiter2.GetCallback());
  EXPECT_EQ(sign_waiter2.Get<Status>(), Status::kErrorInternal);
  EXPECT_TRUE(sign_waiter2.Get<std::vector<uint8_t>>().empty());
}

// Generates symmetric key and uses it to encrypt/decrypt with AES-CBC.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       SymKeyEncryptDecrypt) {
  const TokenId token_id = GetParam().token_id;
  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateSymKey(
      token_id, kSymId, kDefaultSymKeySize,
      chromeos::platform_keys::SymKeyType::kAesCbc,
      generate_key_waiter.GetCallback());
  EXPECT_EQ(generate_key_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_EQ(kSymId, generate_key_waiter.Get<std::vector<uint8_t>>());

  // Initialization vector must have a length of 16.
  const std::vector<uint8_t> kInitVecIncorrect(/*count=*/7, /*value=*/0);
  base::test::TestFuture<std::vector<uint8_t>, Status> encrypt_waiter;
  platform_keys_service()->EncryptAES(token_id, kTestingData, kSymId, "AES-CBC",
                                      kInitVecIncorrect,
                                      encrypt_waiter.GetCallback());
  EXPECT_EQ(encrypt_waiter.Get<Status>(), Status::kErrorAlgorithmNotSupported);

  const std::vector<uint8_t> kInitVec(/*count=*/16, /*value=*/0);
  base::test::TestFuture<std::vector<uint8_t>, Status> encrypt_waiter2;
  platform_keys_service()->EncryptAES(token_id, kTestingData, kSymId, "AES-CBC",
                                      kInitVec, encrypt_waiter2.GetCallback());
  EXPECT_EQ(encrypt_waiter2.Get<Status>(), Status::kSuccess);

  std::vector<uint8_t> encrypted_data =
      encrypt_waiter2.Get<std::vector<uint8_t>>();
  // Encrypted data's length should be divisible by 16.
  EXPECT_TRUE(encrypted_data.size() % 16 == 0);

  // Decrypting the resulting encrypted data to see if it matches the original
  // data.
  base::test::TestFuture<std::vector<uint8_t>, Status> decrypt_waiter;
  platform_keys_service()->DecryptAES(token_id, encrypted_data, kSymId,
                                      "AES-CBC", kInitVec,
                                      decrypt_waiter.GetCallback());
  EXPECT_EQ(decrypt_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_EQ(kTestingData, decrypt_waiter.Get<std::vector<uint8_t>>());
}

// Cannot encrypt with invalid/absent key.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       SymKeyEncryptInvalidKey) {
  const TokenId token_id = GetParam().token_id;
  // Initialization vector must have a length of 16.
  const std::vector<uint8_t> kInitVec(/*count=*/16, /*value=*/0);

  // Should fail because the key doesn't exist.
  base::test::TestFuture<std::vector<uint8_t>, Status> encrypt_waiter;
  platform_keys_service()->EncryptAES(token_id, kTestingData, kSymId, "AES-CBC",
                                      kInitVec, encrypt_waiter.GetCallback());
  EXPECT_EQ(encrypt_waiter.Get<Status>(), Status::kErrorKeyNotFound);

  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateSymKey(
      token_id, kSymId, kDefaultSymKeySize,
      chromeos::platform_keys::SymKeyType::kHmac,
      generate_key_waiter.GetCallback());
  EXPECT_EQ(generate_key_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_EQ(kSymId, generate_key_waiter.Get<std::vector<uint8_t>>());

  // Should fail because the key is generated for purposes other than
  // encrypting/decrypting.
  base::test::TestFuture<std::vector<uint8_t>, Status> encrypt_waiter2;
  platform_keys_service()->EncryptAES(token_id, kTestingData, kSymId, "AES-CBC",
                                      kInitVec, encrypt_waiter2.GetCallback());
  EXPECT_EQ(encrypt_waiter2.Get<Status>(), Status::kErrorInternal);
}

// Cannot decrypt with invalid/absent key.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest,
                       SymKeyDecryptInvalidKey) {
  const TokenId token_id = GetParam().token_id;
  // Initialization vector must have a length of 16.
  const std::vector<uint8_t> kInitVec(/*count=*/16, /*value=*/0);

  // Should fail because the key doesn't exist.
  base::test::TestFuture<std::vector<uint8_t>, Status> decrypt_waiter;
  platform_keys_service()->DecryptAES(token_id, kTestingData, kSymId, "AES-CBC",
                                      kInitVec, decrypt_waiter.GetCallback());
  EXPECT_EQ(decrypt_waiter.Get<Status>(), Status::kErrorKeyNotFound);

  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateSymKey(
      token_id, kSymId, kDefaultSymKeySize,
      chromeos::platform_keys::SymKeyType::kHmac,
      generate_key_waiter.GetCallback());
  EXPECT_EQ(generate_key_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_EQ(kSymId, generate_key_waiter.Get<std::vector<uint8_t>>());

  // Should fail because the key is generated for purposes other than
  // encrypting/decrypting.
  base::test::TestFuture<std::vector<uint8_t>, Status> decrypt_waiter2;
  platform_keys_service()->DecryptAES(token_id, kTestingData, kSymId, "AES-CBC",
                                      kInitVec, decrypt_waiter2.GetCallback());
  EXPECT_EQ(decrypt_waiter2.Get<Status>(), Status::kErrorInternal);
}

// Generates symmetric key and then removes it.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest, SymRemoveKey) {
  const TokenId token_id = GetParam().token_id;

  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateSymKey(
      token_id, kSymId, kDefaultSymKeySize,
      chromeos::platform_keys::SymKeyType::kAesCbc,
      generate_key_waiter.GetCallback());
  EXPECT_EQ(generate_key_waiter.Get<Status>(), Status::kSuccess);
  EXPECT_EQ(kSymId, generate_key_waiter.Get<std::vector<uint8_t>>());

  // Generating another key with the same key ID should fail.
  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter2;
  platform_keys_service()->GenerateSymKey(
      token_id, kSymId, kDefaultSymKeySize,
      chromeos::platform_keys::SymKeyType::kHmac,
      generate_key_waiter2.GetCallback());
  EXPECT_EQ(generate_key_waiter2.Get<Status>(), Status::kErrorInternal);
  EXPECT_TRUE(generate_key_waiter2.Get<std::vector<uint8_t>>().empty());

  base::test::TestFuture<Status> remove_waiter;
  platform_keys_service()->RemoveSymKey(token_id, kSymId,
                                        remove_waiter.GetCallback());
  EXPECT_EQ(remove_waiter.Get<Status>(), Status::kSuccess);

  // Key is already removed, so this should fail.
  base::test::TestFuture<Status> remove_waiter2;
  platform_keys_service()->RemoveSymKey(token_id, kSymId,
                                        remove_waiter2.GetCallback());
  EXPECT_EQ(remove_waiter2.Get<Status>(), Status::kErrorKeyNotFound);
}

INSTANTIATE_TEST_SUITE_P(
    AllSupportedProfilesAndTokensTypes,
    PlatformKeysServicePerTokenBrowserTest,
    ::testing::Values(TestConfigPerToken{ProfileToUse::kSigninProfile,
                                         TokenId::kSystem},
                      TestConfigPerToken{ProfileToUse::kUnaffiliatedUserProfile,
                                         TokenId::kUser},
                      TestConfigPerToken{ProfileToUse::kAffiliatedUserProfile,
                                         TokenId::kSystem},
                      TestConfigPerToken{ProfileToUse::kAffiliatedUserProfile,
                                         TokenId::kUser}));

// PlatformKeysServicePerUnavailableTokenBrowserTest is essentially the same as
// PlatformKeysServicePerTokenBrowserTest but contains different test cases
// (testing that the token is not available) and runs on a different set of
// (profile, token) pairs accordingly.
using PlatformKeysServicePerUnavailableTokenBrowserTest =
    PlatformKeysServicePerTokenBrowserTest;

// Uses GenerateRSAKey as an example operation that should fail because the
// token is not available.
IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerUnavailableTokenBrowserTest,
                       GenerateRsa) {
  const unsigned int kKeySize = 2048;

  const TokenId token_id = GetParam().token_id;
  base::test::TestFuture<std::vector<uint8_t>, Status> generate_key_waiter;
  platform_keys_service()->GenerateRSAKey(token_id, kKeySize,
                                          /*sw_backed=*/false,
                                          generate_key_waiter.GetCallback());
  ASSERT_TRUE(generate_key_waiter.Wait());
  EXPECT_NE(generate_key_waiter.Get<Status>(), Status::kSuccess);
}

IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerUnavailableTokenBrowserTest,
                       IsKeyOnToken) {
  const TokenId token_id = GetParam().token_id;

  std::vector<uint8_t> some_key = {1, 2, 3, 4, 5};

  test_util::IsKeyOnTokenExecutionWaiter is_key_on_token_waiter;
  platform_keys_service()->IsKeyOnToken(token_id, std::move(some_key),
                                        is_key_on_token_waiter.GetCallback());
  ASSERT_TRUE(is_key_on_token_waiter.Wait());

  EXPECT_NE(is_key_on_token_waiter.status(), Status::kSuccess);
  EXPECT_FALSE(is_key_on_token_waiter.on_slot().has_value());
}

INSTANTIATE_TEST_SUITE_P(
    AllSupportedProfilesAndTokensTypes,
    PlatformKeysServicePerUnavailableTokenBrowserTest,
    ::testing::Values(TestConfigPerToken{ProfileToUse::kSigninProfile,
                                         TokenId::kUser},
                      TestConfigPerToken{ProfileToUse::kUnaffiliatedUserProfile,
                                         TokenId::kSystem}));
}  // namespace ash::platform_keys