chromium/chromeos/components/kcer/kcer_token_impl_unittest.cc

// Copyright 2024 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 "chromeos/components/kcer/kcer_token_impl.h"

#include <string_view>

#include "base/base64.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/gmock_move_support.h"
#include "base/test/test_future.h"
#include "chromeos/components/kcer/chaps/mock_high_level_chaps_client.h"
#include "chromeos/components/kcer/kcer_nss/test_utils.h"
#include "content/public/test/browser_task_environment.h"
#include "net/cert/cert_database.h"
#include "net/cert/x509_util.h"
#include "net/test/cert_builder.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/chaps/dbus-constants.h"

using base::test::RunOnceCallback;
using base::test::RunOnceCallbackRepeatedly;
using testing::_;
using testing::DoAll;
using testing::Invoke;
using ObjectHandle = kcer::SessionChapsClient::ObjectHandle;
using AttributeId = kcer::HighLevelChapsClient::AttributeId;
using testing::UnorderedElementsAre;

namespace kcer::internal {
namespace {

constexpr int kDefaultAttempts = KcerTokenImpl::kDefaultAttempts;

// Base64 encoded RSA modulus for client_1.key.
constexpr char kFakeRsaModulusBase64[] =
    "2vg6u00xUsNQMUZGn7awjPLbj22B+"
    "zU8S3R4pdBdsuBvj765ajdKgeN7UWiiT0BGIRvkZ6aV3jpOvwG4DhVwUAj6FSVjo8cDF93mt60"
    "izOQvcVD/UbJ3/35J4A+huqTqNp2Ip34KSDUA4Rgf6/ASbOvUTYb4HWzeGlewc/"
    "FhmKihdw1YVfg4ucyDjzQvc6GUD3suHbjEtOjhERuzqfHxcLN0KAsxMsFXQG3v0fpuaHoPO+"
    "by1b5jjcA4udpqr+"
    "K4G35fmj4csj86Ed4XSDxItdwEAeYZsiwHEw7b3Im923jS8OfOjV3GIEGXXGy5g5I1UQyuLp4h"
    "OEGkVBnkSQ9vsQ==";
constexpr char kFakeRsaExponentBase64[] = "AQAB";
// The correct id for `kFakeRsaModulusBase64`.
constexpr char kFakeRsaPkcs11IdBase64[] = "PIlO/U4s0iZrIYu7pjwbGzTrWlA=";
// The correct SPKI for `kFakeRsaModulusBase64` and `kFakeRsaExponentBase64`
// pair.
constexpr char kFakeRsaSpkiBase64[] =
    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2vg6u00xUsNQMUZGn7awjPLbj22B+"
    "zU8S3R4pdBdsuBvj765ajdKgeN7UWiiT0BGIRvkZ6aV3jpOvwG4DhVwUAj6FSVjo8cDF93mt60"
    "izOQvcVD/UbJ3/35J4A+huqTqNp2Ip34KSDUA4Rgf6/ASbOvUTYb4HWzeGlewc/"
    "FhmKihdw1YVfg4ucyDjzQvc6GUD3suHbjEtOjhERuzqfHxcLN0KAsxMsFXQG3v0fpuaHoPO+"
    "by1b5jjcA4udpqr+"
    "K4G35fmj4csj86Ed4XSDxItdwEAeYZsiwHEw7b3Im923jS8OfOjV3GIEGXXGy5g5I1UQyuLp4h"
    "OEGkVBnkSQ9vsQIDAQAB";

// DER-encoded ASN.1 octet string containing the public value of an EC key (the
// EC point).
constexpr char kFakeEcPublicValueOctetStringDerBase64[] =
    "BEEE862evgBzpaK6fQ+IuGRLhVtiHE9eg0Qq+auOnkj/"
    "PQsakZK+HwzGhwq9BkcLYwa3J7pY4wVkbR0wKO7u8ql7kw==";
// The correct id for `kFakeEcPublicValueBase64`.
constexpr char kFakeEcPkcs11IdBase64[] = "gE0RjontXMV0CGICnWode163LoU=";
// The correct SPKI for `kFakeEcPublicValueBase64` and `kFakeEcPkcs11IdBase64`
// pair.
constexpr char kFakeEcSpkiBase64[] =
    "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE862evgBzpaK6fQ+IuGRLhVtiHE9eg0Qq+"
    "auOnkj/PQsakZK+HwzGhwq9BkcLYwa3J7pY4wVkbR0wKO7u8ql7kw==";

// Generated by chrome/test/data/policy/test_certs/create_test_certs.sh
constexpr char kFakeCertificate[] =
    "MIIDAzCCAeugAwIBAgIJAKqcrTp/"
    "a0aRMA0GCSqGSIb3DQEBCwUAMCIxIDAeBgNVBAMMF0lzc3VlclN1YmplY3RDb21tb25OYW1lMB"
    "4XDTcwMDEwMjAwNDQyN1oXDTcwMDIwMTAwNDQyN1owHDEaMBgGA1UEAwwRU3ViamVjdENvbW1v"
    "bk5hbWUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC57XcC/"
    "hnVfyqzjoc785VENgZ+DfcCJOYjBFLVkBtKX+sbAyef7X42Jpfb6riB8bCH/"
    "nG9+AGgUySkTHRryQS8STJx00+IolhwDE+l7QoaznID5USkwxhiPOFD+"
    "1bZUF6b7UKISrws7SLSmxEjPuiMc2bDJWmuUMCrhm4mpoG1F43m1iyxtZx+"
    "3eXC1NJ3HvDGSTSlBcRlKdO+glcMf0YAQmFN+py6PXLGq1HbOrh2shnz72N+"
    "34H7jMoulRbA49kkyh0wRbW41s+"
    "yJPG0oOAJzc5jKYBoRM7uDfzoBPbFYclpHbNHxhYbzhjvFVAar+"
    "zhoX5lTwKmG0UhALcsThQ1AgMBAAGjQjBAMB0GA1UdDgQWBBTUNlxLd+brIllUgxM5mFQ/"
    "Zdxl7jAfBgNVHSMEGDAWgBS0pYptJl5osorqt880cBIKOMvfRTANBgkqhkiG9w0BAQsFAAOCAQ"
    "EAQn0F/"
    "Q7QAowOlNwY74cHJRNs4rktcCcV3VODNwSNaXuZtELK2XI9wFweX6szR4EpFbnQnOS+"
    "SE52meaJb5TwqPWvd6ILkbr9XRHBFze296vU81IUrcm0IYu1R4wrguFnGuMLI9fjMOeYOCOXbr"
    "JpYm8QMr3dNnE/QIrz+qZ3PxBI7e/gLTezowfR40/"
    "yfGniCIBjZacKNosXaf2M5ZS7iqqdAQiTaHKp0eTONCBI7OGRKPq7VF+"
    "1qtPl5xE7ffmxf1TbmsDp0UyPy1zlcZUrWHwVjcuNil09wwZfSG1lqLOX6zXdWZKkIJzRlBNXw"
    "6GzDsbwCyiKzpEBk3edl+aNfQ==";

const std::vector<uint8_t>& GetRsaModulus() {
  static std::vector<uint8_t> value =
      base::Base64Decode(kFakeRsaModulusBase64).value();
  return value;
}
const std::vector<uint8_t>& GetRsaPublicExponent() {
  static std::vector<uint8_t> value =
      base::Base64Decode(kFakeRsaExponentBase64).value();
  return value;
}
const Pkcs11Id& GetRsaPkcs11Id() {
  static Pkcs11Id value(base::Base64Decode(kFakeRsaPkcs11IdBase64).value());
  return value;
}
const PublicKeySpki& GetRsaSpki() {
  static PublicKeySpki value(base::Base64Decode(kFakeRsaSpkiBase64).value());
  return value;
}

const std::vector<uint8_t>& GetEcPublicValue() {
  static std::vector<uint8_t> value =
      base::Base64Decode(kFakeEcPublicValueOctetStringDerBase64).value();
  return value;
}
const Pkcs11Id& GetEcPkcs11Id() {
  static Pkcs11Id value(base::Base64Decode(kFakeEcPkcs11IdBase64).value());
  return value;
}
const PublicKeySpki& GetEcSpki() {
  static PublicKeySpki value(base::Base64Decode(kFakeEcSpkiBase64).value());
  return value;
}

const CertDer& GetCertDer() {
  static CertDer value(base::Base64Decode(kFakeCertificate).value());
  return value;
}

bool SpanEqual(base::span<const uint8_t> s1, base::span<const uint8_t> s2) {
  return base::ranges::equal(s1, s2);
}

[[nodiscard]] bool FindAttribute(chaps::AttributeList attrs,
                                 uint32_t attribute_type,
                                 base::span<const uint8_t> value) {
  for (int i = 0; i < attrs.attributes_size(); ++i) {
    const chaps::Attribute& cur_attr = attrs.attributes(i);
    if (cur_attr.type() != attribute_type) {
      continue;
    }
    // There shouldn't be two attributes with the same type and different
    // values, if this one is not the one, return false;
    if (!cur_attr.has_length() || !cur_attr.has_value()) {
      return false;
    }

    return SpanEqual(base::as_byte_span(cur_attr.value()), value);
  }
  return false;
}

// `T` must be a simple type, i.e. no internal pointers, etc.
// `value` must outlive the returned span.
template <typename T>
base::span<const uint8_t> MakeSpan(T* value) {
  return base::as_bytes(base::span<T>(value, /*count=*/1u));
}

void AddAttribute(chaps::AttributeList& attr_list,
                  chromeos::PKCS11_CK_ATTRIBUTE_TYPE type,
                  base::span<const uint8_t> data) {
  chaps::Attribute* new_attr = attr_list.add_attributes();
  new_attr->set_type(type);
  new_attr->set_value(std::string(data.begin(), data.end()));
  new_attr->set_length(data.size());
}

chaps::AttributeList GetRsaKeyAttrs(base::span<const uint8_t> pkcs11_id,
                                    base::span<const uint8_t> modulus,
                                    base::span<const uint8_t> exponent) {
  chaps::AttributeList rsa_attrs;
  AddAttribute(rsa_attrs, chromeos::PKCS11_CKA_ID, pkcs11_id);
  AddAttribute(rsa_attrs, chromeos::PKCS11_CKA_MODULUS, modulus);
  AddAttribute(rsa_attrs, chromeos::PKCS11_CKA_PUBLIC_EXPONENT, exponent);
  return rsa_attrs;
}

chaps::AttributeList GetEcKeyAttrs(base::span<const uint8_t> pkcs11_id,
                                   base::span<const uint8_t> ec_point) {
  chaps::AttributeList ec_attrs;
  AddAttribute(ec_attrs, chromeos::PKCS11_CKA_ID, pkcs11_id);
  AddAttribute(ec_attrs, chromeos::PKCS11_CKA_EC_POINT, ec_point);
  return ec_attrs;
}

chaps::AttributeList GetFakeRsaPublicKeyAttrs() {
  chaps::AttributeList result;
  AddAttribute(result, chromeos::PKCS11_CKA_MODULUS, GetRsaModulus());
  AddAttribute(result, chromeos::PKCS11_CKA_PUBLIC_EXPONENT,
               GetRsaPublicExponent());
  return result;
}

chaps::AttributeList GetFakeEcPublicKeyAttrs() {
  chaps::AttributeList result;
  AddAttribute(result, chromeos::PKCS11_CKA_EC_POINT, GetEcPublicValue());
  return result;
}

chaps::AttributeList GetFakeKeyInfoAttrs(
    bool is_in_software,
    chromeos::PKCS11_CK_KEY_TYPE pkcs_key_type,
    const std::string& label) {
  chaps::AttributeList result;
  AddAttribute(result, chaps::kKeyInSoftwareAttribute,
               MakeSpan(&is_in_software));
  AddAttribute(result, chromeos::PKCS11_CKA_KEY_TYPE, MakeSpan(&pkcs_key_type));
  AddAttribute(result, chromeos::PKCS11_CKA_LABEL, base::as_byte_span(label));
  return result;
}

chaps::AttributeList GetFakeCertAttrs(const Pkcs11Id& pkcs11_id,
                                      const std::string& label,
                                      const CertDer& cert_der) {
  chaps::AttributeList result;
  AddAttribute(result, chromeos::PKCS11_CKA_ID, pkcs11_id.value());
  AddAttribute(result, chromeos::PKCS11_CKA_LABEL, base::as_byte_span(label));
  AddAttribute(result, chromeos::PKCS11_CKA_VALUE, cert_der.value());
  return result;
}

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

class ScopedNotificationsObserver : public net::CertDatabase::Observer {
 public:
  ScopedNotificationsObserver() {
    net::CertDatabase::GetInstance()->AddObserver(this);
  }
  ~ScopedNotificationsObserver() override {
    net::CertDatabase::GetInstance()->RemoveObserver(this);
  }

  void OnClientCertStoreChanged() override { ++counter_; }

  size_t counter_ = 0;
};

class KcerTokenImplTest : public testing::Test {
 public:
  KcerTokenImplTest() : token_(Token::kUser, &chaps_client_) {
    ON_CALL(chaps_client_, FindObjects)
        .WillByDefault(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
                                                    chromeos::PKCS11_CKR_OK));
  }

  void TearDown() override {
    // Check the notifications about cert changes. If a test doesn't configure
    // anything, then by default it is expected to not emit any notifications.
    EXPECT_EQ(notifications_observer_.counter_, expected_notifications_count_);
  }

 protected:
  content::BrowserTaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME,
      base::test::TaskEnvironment::MainThreadType::UI};

  ScopedNotificationsObserver notifications_observer_;
  size_t expected_notifications_count_ = 0;

  SessionChapsClient::SlotId pkcs11_slot_id_{1};
  MockHighLevelChapsClient chaps_client_;
  KcerTokenImpl token_;
};

// Test that GenerateRsaKey can successfully generate a key pair.
TEST_F(KcerTokenImplTest, GenerateRsaKeySuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});
  uint32_t result_code = chromeos::PKCS11_CKR_OK;

  std::vector<uint8_t> mechanism_attrs;
  chaps::AttributeList public_key_attrs;
  EXPECT_CALL(chaps_client_,
              GenerateKeyPair(pkcs11_slot_id_,
                              chromeos::PKCS11_CKM_RSA_PKCS_KEY_PAIR_GEN,
                              mechanism_attrs, _, _, _))
      .WillOnce(DoAll(MoveArg<3>(&public_key_attrs),
                      RunOnceCallback<5>(result_pub_key_handle,
                                         result_priv_key_handle, result_code)));

  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, result_pub_key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(GetFakeRsaPublicKeyAttrs(), result_code));

  chaps::AttributeList pkcs11_id_attrs;
  EXPECT_CALL(chaps_client_,
              SetAttributeValue(pkcs11_slot_id_, key_handles, _, _))
      .WillOnce(
          DoAll(MoveArg<2>(&pkcs11_id_attrs), RunOnceCallback<3>(result_code)));

  RsaModulusLength modulus_length_enum = RsaModulusLength::k2048;
  chromeos::PKCS11_CK_ULONG modulus_length_bits =
      static_cast<uint32_t>(modulus_length_enum);

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateRsaKey(modulus_length_enum,
                        /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_MODULUS_BITS,
                            MakeSpan(&modulus_length_bits)));
  EXPECT_TRUE(FindAttribute(pkcs11_id_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));

  EXPECT_TRUE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get()->GetToken(), Token::kUser);
  EXPECT_TRUE(
      SpanEqual(waiter.Get()->GetPkcs11Id().value(), GetRsaPkcs11Id().value()));
  EXPECT_TRUE(SpanEqual(waiter.Get()->GetSpki().value(), GetRsaSpki().value()));
}

// Test that GenerateRsaKey correctly sets attributes for a software backed key
// pair.
TEST_F(KcerTokenImplTest, GenerateRsaKeySoftwareBacked) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList private_key_attrs;
  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillOnce(DoAll(MoveArg<4>(&private_key_attrs),
                      RunOnceCallback<5>(ObjectHandle(), ObjectHandle(),
                                         chromeos::PKCS11_CKR_GENERAL_ERROR)));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateRsaKey(RsaModulusLength::k2048,
                        /*hardware_backed=*/false, waiter.GetCallback());

  chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
  EXPECT_TRUE(FindAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
                            MakeSpan(&kTrue)));

  // The rest is not important for this test.
  EXPECT_FALSE(waiter.Get().has_value());
}

// Test that GenerateRsaKey correctly fails when the generation of a key pair
// fails.
TEST_F(KcerTokenImplTest, GenerateRsaKeyFailToGenerate) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillOnce(RunOnceCallback<5>(ObjectHandle(), ObjectHandle(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateRsaKey(RsaModulusLength::k2048,
                        /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToGenerateKey);
}

// Test that GenerateRsaKey retries several times when generation of a key pair
// fails with a session error.
TEST_F(KcerTokenImplTest, GenerateRsaKeyRetryGenerateOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(
          ObjectHandle(), ObjectHandle(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateRsaKey(RsaModulusLength::k2048,
                        /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GenerateRsaKey correctly fails when the reading of public key
// attributes fails.
TEST_F(KcerTokenImplTest, GenerateRsaKeyFailToReadPublicKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillOnce(RunOnceCallback<5>(result_pub_key_handle,
                                   result_priv_key_handle,
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));
  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, key_handles, _))
      .WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateRsaKey(RsaModulusLength::k2048,
                        /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToExportPublicKey);
}

// Test that GenerateRsaKey retries several times when reading of public key
// attributes fails with a session error. GenerateRsaKey has to retry all the
// previous methods.
TEST_F(KcerTokenImplTest, GenerateRsaKeyRetryReadAttrsOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(result_pub_key_handle,
                                                   result_priv_key_handle,
                                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(
          chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateRsaKey(RsaModulusLength::k2048,
                        /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GenerateRsaKey correctly fails when the writing of the id on the
// public and private keys fails.
TEST_F(KcerTokenImplTest, GenerateRsaKeyFailToSetId) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillOnce(RunOnceCallback<5>(result_pub_key_handle,
                                   result_priv_key_handle,
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .WillOnce(RunOnceCallback<3>(GetFakeRsaPublicKeyAttrs(),
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handles, _, _))
      .WillOnce(RunOnceCallback<3>(chromeos::PKCS11_CKR_GENERAL_ERROR));
  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, key_handles, _))
      .WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateRsaKey(RsaModulusLength::k2048,
                        /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToWriteAttribute);
}

// Test that GenerateRsaKey retries several times when the writing of the id on
// the public and private keys fails with a session error. GenerateRsaKey has to
// retry all the previous methods.
TEST_F(KcerTokenImplTest, GenerateRsaKeyRetrySetIdOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(result_pub_key_handle,
                                                   result_priv_key_handle,
                                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(GetFakeRsaPublicKeyAttrs(),
                                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handles, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<3>(chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateRsaKey(RsaModulusLength::k2048,
                        /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GenerateEcKey can successfully generate a key pair.
TEST_F(KcerTokenImplTest, GenerateEcKeySuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});
  uint32_t result_code = chromeos::PKCS11_CKR_OK;

  std::vector<uint8_t> mechanism_attrs;
  chaps::AttributeList public_key_attrs;
  EXPECT_CALL(
      chaps_client_,
      GenerateKeyPair(pkcs11_slot_id_, chromeos::PKCS11_CKM_EC_KEY_PAIR_GEN,
                      mechanism_attrs, _, _, _))
      .WillOnce(DoAll(MoveArg<3>(&public_key_attrs),
                      RunOnceCallback<5>(result_pub_key_handle,
                                         result_priv_key_handle, result_code)));

  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, result_pub_key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(GetFakeEcPublicKeyAttrs(), result_code));

  chaps::AttributeList pkcs11_id_attrs;
  EXPECT_CALL(chaps_client_,
              SetAttributeValue(pkcs11_slot_id_, key_handles, _, _))
      .WillOnce(
          DoAll(MoveArg<2>(&pkcs11_id_attrs), RunOnceCallback<3>(result_code)));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateEcKey(EllipticCurve::kP256,
                       /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(pkcs11_id_attrs, chromeos::PKCS11_CKA_ID,
                            GetEcPkcs11Id().value()));

  EXPECT_TRUE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get()->GetToken(), Token::kUser);
  EXPECT_TRUE(
      SpanEqual(waiter.Get()->GetPkcs11Id().value(), GetEcPkcs11Id().value()));
  EXPECT_TRUE(SpanEqual(waiter.Get()->GetSpki().value(), GetEcSpki().value()));
}

// Test that GenerateEcKey correctly sets attributes for a software backed key
// pair.
TEST_F(KcerTokenImplTest, GenerateEcKeySoftwareBacked) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList private_key_attrs;
  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillOnce(DoAll(MoveArg<4>(&private_key_attrs),
                      RunOnceCallback<5>(ObjectHandle(), ObjectHandle(),
                                         chromeos::PKCS11_CKR_GENERAL_ERROR)));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateEcKey(EllipticCurve::kP256,
                       /*hardware_backed=*/false, waiter.GetCallback());

  chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
  EXPECT_TRUE(FindAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
                            MakeSpan(&kTrue)));

  // The rest is not important for this test.
  EXPECT_FALSE(waiter.Get().has_value());
}

// Test that GenerateEcKey correctly fails when the generation of a key pair
// fails.
TEST_F(KcerTokenImplTest, GenerateEcKeyFailToGenerate) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillOnce(RunOnceCallback<5>(ObjectHandle(), ObjectHandle(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateEcKey(EllipticCurve::kP256,
                       /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToGenerateKey);
}

// Test that GenerateEcKey retries several times when generation of a key pair
// fails with a session error.
TEST_F(KcerTokenImplTest, GenerateEcKeyRetryGenerateOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(
          ObjectHandle(), ObjectHandle(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateEcKey(EllipticCurve::kP256,
                       /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GenerateEcKey correctly fails when the reading of public key
// attributes fails.
TEST_F(KcerTokenImplTest, GenerateEcKeyFailToReadPublicKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillOnce(RunOnceCallback<5>(result_pub_key_handle,
                                   result_priv_key_handle,
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));
  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, key_handles, _))
      .WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateEcKey(EllipticCurve::kP256,
                       /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToExportPublicKey);
}

// Test that GenerateEcKey retries several times when reading of public key
// attributes fails with a session error. GenerateEcKey has to retry all the
// previous methods.
TEST_F(KcerTokenImplTest, GenerateEcKeyRetryReadAttrsOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(result_pub_key_handle,
                                                   result_priv_key_handle,
                                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(
          chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateEcKey(EllipticCurve::kP256,
                       /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GenerateEcKey correctly fails when the writing of the id on the
// public and private keys fails.
TEST_F(KcerTokenImplTest, GenerateEcKeyFailToSetId) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillOnce(RunOnceCallback<5>(result_pub_key_handle,
                                   result_priv_key_handle,
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .WillOnce(RunOnceCallback<3>(GetFakeEcPublicKeyAttrs(),
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handles, _, _))
      .WillOnce(RunOnceCallback<3>(chromeos::PKCS11_CKR_GENERAL_ERROR));
  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, key_handles, _))
      .WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateEcKey(EllipticCurve::kP256,
                       /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToWriteAttribute);
}

// Test that GenerateEcKey retries several times when the writing of the id on
// the public and private keys fails with a session error. GenerateEcKey has to
// retry all the previous methods.
TEST_F(KcerTokenImplTest, GenerateEcKeyRetrySetIdOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle result_pub_key_handle{10};
  ObjectHandle result_priv_key_handle{20};
  std::vector<ObjectHandle> key_handles(
      {result_pub_key_handle, result_priv_key_handle});

  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(result_pub_key_handle,
                                                   result_priv_key_handle,
                                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(GetFakeEcPublicKeyAttrs(),
                                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handles, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<3>(chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
  token_.GenerateEcKey(EllipticCurve::kP256,
                       /*hardware_backed=*/true, waiter.GetCallback());

  EXPECT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportKey correctly fails when it's given an invalid key.
TEST_F(KcerTokenImplTest, ImportKeyBadKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  Pkcs8PrivateKeyInfoDer invalid_key({1, 2, 3, 4, 5});

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(invalid_key, import_key_waiter.GetCallback());
  EXPECT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToParseKey);
}

// Test that ImportKey can successfully import an RSA key.
TEST_F(KcerTokenImplTest, ImportKeyRsaSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  // ImportKey() will check whether the key already exists.
  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_key_attrs),
                      RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                         chromeos::PKCS11_CKR_OK)));

  chaps::AttributeList private_key_attrs;
  chaps::AttributeList public_key_attrs;
  EXPECT_CALL(chaps_client_, CreateObject(pkcs11_slot_id_, _, _))
      .WillOnce(
          DoAll(MoveArg<1>(&private_key_attrs),
                RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK)))
      .WillOnce(
          DoAll(MoveArg<1>(&public_key_attrs),
                RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK)));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_TRUE(import_key_waiter.Get().has_value());
  const PublicKey& kcer_public_key = import_key_waiter.Get().value();

  // Check that ImportKey() was looking for the correct existing key.
  const chromeos::PKCS11_CK_OBJECT_CLASS kPrivKeyClass =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&kPrivKeyClass)));
  EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));

  // Check the private key attributes.
  constexpr chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  chromeos::PKCS11_CK_KEY_TYPE key_type = chromeos::PKCS11_CKK_RSA;
  // At the moment of writing these key components were printed from the code
  // under test, i.e. not guaranteed to be correct. The code was also tested
  // against the real Chaps and was able to correctly sign using the imported
  // key client_1.key, so most likely they are correct. Long term this is a
  // regression test.
  std::vector<uint8_t> private_exponent =
      base::Base64Decode(
          "BMvoYMcg2WGDuESZZ5u6nn0eZUlT4329H6ECQzg/KTEvOGydhqUF6eD4B/"
          "vnsZ6POrVFSaZK76EtgukbJUcqcee0b1yljrDyvCXaoojgHjFcaa90HE/"
          "Gvvm++AcXoZfwX826cILQtQK2OCK4EPTDY+U+6LYtaVruZZDTVxgr7V+v4v1EDKEjQc+"
          "Ttupwo6aXSeiTKuqNsXuodoDvcv/"
          "uJgzMDCxi14TZTjaWOz7Xw2JZ+NLbTrsiqTyzmyJousV6/+4sfTYt8/"
          "tz0gMt3Qaddvs+"
          "BpTTrYIKTpsGYwPkKDqUdEkC87OQ6a2mXB1lpA7FMpZiiyJ1HpIXHkd+eLoNkQ==")
          .value();
  std::vector<uint8_t> prime_1 =
      base::Base64Decode(
          "5ULWXU5R7tGCzNlpTkRWD/M8sf/"
          "ZOq12VVWSzQXGWsxFJHIFyezEvKV4nol6OlCRH9DzQgmNtaKDjD6uiyNDTRf8g270/"
          "UYu3kTWUWaLFyA2gde0vd74GeFJK4uySb9wcfldlsJdz9v4NPNLIasfGWbINWkKqSqL/"
          "arGr8lwLhk=")
          .value();
  std::vector<uint8_t> prime_2 =
      base::Base64Decode(
          "9IIdj/q7dI0PUpyfBExsxQF5oWcreJsvrwbhV/"
          "LRbwcsTIlCMc7Jh8QgOrXf+iglj8KKN8YXu6//"
          "jRxBXupPTUdyoKxzqxxXAqjgB1mD7jwimhtMnGxBd7Y87g22VCcx+"
          "FgYEAxLD5cA2BKZEdexV+rCDGgDzEb5TnGUGap10Vk=")
          .value();
  std::vector<uint8_t> exponent_1 =
      base::Base64Decode(
          "urdPnNhPlGAf1jRvNmYjbYQdd562zbo+eMtj7wR4ArUAzujqXAUwSa++Z+fxmxLIzw+/"
          "PpZHSpnb51mZkAodIumZJ3Yzox8Ixs9reQo515DNs7v5IPY6O+GmVQfGIZf/"
          "vWNpXIJaIxK0uHM5SmdywZ5bClzNaO8U6niurrYxXek=")
          .value();
  std::vector<uint8_t> exponent_2 =
      base::Base64Decode(
          "gGh9AgpZvCIAtBAQ6v7/"
          "+I6HxB4clGBbsH3ahoe9OaP4vdEv9Fx3Nlfn3S17DTNcVp2CXTwpZqZNfVwjcKd5Mkqd"
          "hohKzsg5YeoyjWmTgeAPBAPmPhgUYbxRT2vgH13ePmB1cqgiG3PgO5m4zcgLGPLvKfjO"
          "Vc/ISkwXzUraSTE=")
          .value();
  std::vector<uint8_t> coefficient =
      base::Base64Decode(
          "fpgE21erI8NeCrK9GSNm3TUjZlifaIgcGEfINYTmPRCOHMXaQukogBI7W45Oxq6sCE+"
          "7JRNH7wRe7Y5I/"
          "dGhonlu7BRvzIvZvxDTvlYJKn55d1TpmYRh1+cpf7rcj1Aha9MWxERnJsVHi0+"
          "CxAkar8vyM0jEPEb0PQ/UjZnGt90=")
          .value();
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_KEY_TYPE,
                            MakeSpan(&key_type)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_TOKEN,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_SENSITIVE,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EXTRACTABLE,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIVATE,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_UNWRAP,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_DECRYPT,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_SIGN,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(
      private_key_attrs, chromeos::PKCS11_CKA_SIGN_RECOVER, MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_MODULUS,
                            GetRsaModulus()));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  EXPECT_TRUE(FindAttribute(private_key_attrs,
                            chromeos::PKCS11_CKA_PUBLIC_EXPONENT,
                            GetRsaPublicExponent()));
  EXPECT_TRUE(FindAttribute(private_key_attrs,
                            chromeos::PKCS11_CKA_PRIVATE_EXPONENT,
                            private_exponent));
  EXPECT_TRUE(
      FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIME_1, prime_1));
  EXPECT_TRUE(
      FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIME_2, prime_2));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EXPONENT_1,
                            exponent_1));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EXPONENT_2,
                            exponent_2));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_COEFFICIENT,
                            coefficient));

  // Check the public key attributes.
  chromeos::PKCS11_CK_OBJECT_CLASS pub_key_class =
      chromeos::PKCS11_CKO_PUBLIC_KEY;
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&pub_key_class)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_KEY_TYPE,
                            MakeSpan(&key_type)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_TOKEN,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_WRAP,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_ENCRYPT,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_VERIFY,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_MODULUS,
                            GetRsaModulus()));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  EXPECT_TRUE(FindAttribute(public_key_attrs,
                            chromeos::PKCS11_CKA_PUBLIC_EXPONENT,
                            GetRsaPublicExponent()));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chaps::kForceSoftwareAttribute,
                            MakeSpan(&kTrue)));

  // Check returned kcer::PublicKey.
  EXPECT_EQ(kcer_public_key.GetPkcs11Id(), GetRsaPkcs11Id());
  EXPECT_EQ(kcer_public_key.GetSpki(), GetRsaSpki());
  EXPECT_EQ(kcer_public_key.GetToken(), Token::kUser);
}

// Test that ImportKey skips the import and succeeds when it's given an already
// imported RSA key.
TEST_F(KcerTokenImplTest, ImportKeyRsaAlreadyExists) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>{ObjectHandle(1)},
                                   chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_, CreateObject).Times(0);

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  EXPECT_TRUE(import_key_waiter.Get().has_value());
  const PublicKey& kcer_public_key = import_key_waiter.Get().value();
  EXPECT_EQ(kcer_public_key.GetPkcs11Id(), GetRsaPkcs11Id());
  EXPECT_EQ(kcer_public_key.GetSpki(), GetRsaSpki());
  EXPECT_EQ(kcer_public_key.GetToken(), Token::kUser);
}

// Test that ImportKey correctly fails when Chaps fails to search for the
// existing RSA key.
TEST_F(KcerTokenImplTest, ImportKeyRsaFailToSearchExistingKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  EXPECT_CALL(chaps_client_, CreateObject).Times(0);

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that ImportKey retries several times when Chaps fails to search for the
// existing RSA key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyRsaRetryToSearchExistingKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  EXPECT_CALL(chaps_client_, CreateObject).Times(0);

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportKey correctly fails when Chaps fails to create the private
// RSA key.
TEST_F(KcerTokenImplTest, ImportKeyRsaFailToCreatePrivKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, CreateObject)
      .WillOnce(RunOnceCallback<2>(ObjectHandle(0),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  EXPECT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToImportKey);
}

// Test that ImportKey retries several times when Chaps fails to create the
// private RSA key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyRsaRetryToCreatePrivKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
                                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, CreateObject)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          ObjectHandle(0), chromeos::PKCS11_CKR_SESSION_CLOSED));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  EXPECT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportKey correctly fails when Chaps fails to create the public RSA
// key.
TEST_F(KcerTokenImplTest, ImportKeyRsaFailToCreatePubKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK));

  ObjectHandle priv_key_handle(10);
  EXPECT_CALL(chaps_client_, CreateObject)
      .WillOnce(RunOnceCallback<2>(priv_key_handle, chromeos::PKCS11_CKR_OK))
      .WillOnce(RunOnceCallback<2>(ObjectHandle(0),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  EXPECT_CALL(chaps_client_, DestroyObjectsWithRetries(
                                 pkcs11_slot_id_,
                                 std::vector<ObjectHandle>{priv_key_handle}, _))
      .WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToImportKey);
}

// Test that ImportKey retries several times when Chaps fails to create the
// public RSA key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyRsaRetryToCreatePubKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
                                                   chromeos::PKCS11_CKR_OK));

  // Alternates between replying with OK and SESSION_CLOSED to handle
  // alternating calls for private and public keys.
  auto fake_create_objects = [](auto slot_id, auto attrs, auto callback) {
    static bool next_is_pub_key = true;
    bool is_pub_key =
        std::exchange(next_is_pub_key, /*new_value=*/!next_is_pub_key);
    if (is_pub_key) {
      return std::move(callback).Run(ObjectHandle(10), chromeos::PKCS11_CKR_OK);
    } else {
      return std::move(callback).Run(ObjectHandle(0),
                                     chromeos::PKCS11_CKR_SESSION_CLOSED);
    }
  };

  EXPECT_CALL(chaps_client_, CreateObject)
      .Times(2 * kDefaultAttempts)
      .WillRepeatedly(Invoke(fake_create_objects));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportKey can successfully import an EC key.
TEST_F(KcerTokenImplTest, ImportKeyEcSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_key_attrs),
                      RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                         chromeos::PKCS11_CKR_OK)));

  chaps::AttributeList private_key_attrs;
  chaps::AttributeList public_key_attrs;
  EXPECT_CALL(chaps_client_, CreateObject(pkcs11_slot_id_, _, _))
      .WillOnce(
          DoAll(MoveArg<1>(&private_key_attrs),
                RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK)))
      .WillOnce(
          DoAll(MoveArg<1>(&public_key_attrs),
                RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK)));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_TRUE(import_key_waiter.Get().has_value());
  const PublicKey& kcer_public_key = import_key_waiter.Get().value();

  const chromeos::PKCS11_CK_OBJECT_CLASS kPrivKeyClass =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&kPrivKeyClass)));
  EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetEcPkcs11Id().value()));

  // Check the private key attributes.
  constexpr chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  chromeos::PKCS11_CK_KEY_TYPE key_type = chromeos::PKCS11_CKK_EC;
  // At the moment of writing these key components were printed from the code
  // under test, i.e. not guaranteed to be correct. The code was also tested
  // against the real Chaps and was able to correctly sign using the imported
  // key key_usage_p256.key, so most likely they are correct. Long term this is
  // a regression test.
  std::vector<uint8_t> priv_key_bytes =
      base::Base64Decode("eor3507Mmwvc9idKsuXWTudE0GDlVmEc64H7acez5xQ=")
          .value();
  std::vector<uint8_t> ec_params_der =
      base::Base64Decode("BggqhkjOPQMBBw==").value();
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_KEY_TYPE,
                            MakeSpan(&key_type)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_TOKEN,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_SENSITIVE,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EXTRACTABLE,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_SIGN,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(
      private_key_attrs, chromeos::PKCS11_CKA_SIGN_RECOVER, MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_DERIVE,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetEcPkcs11Id().value()));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_VALUE,
                            priv_key_bytes));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EC_POINT,
                            GetEcPublicValue()));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIVATE,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EC_PARAMS,
                            ec_params_der));

  // Check the public key attributes.
  chromeos::PKCS11_CK_OBJECT_CLASS pub_key_class =
      chromeos::PKCS11_CKO_PUBLIC_KEY;
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&pub_key_class)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_KEY_TYPE,
                            MakeSpan(&key_type)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_TOKEN,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_VERIFY,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_DERIVE,
                            MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_EC_PARAMS,
                            ec_params_der));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_EC_POINT,
                            GetEcPublicValue()));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetEcPkcs11Id().value()));
  EXPECT_TRUE(FindAttribute(public_key_attrs, chaps::kForceSoftwareAttribute,
                            MakeSpan(&kTrue)));

  // Check returned kcer::PublicKey.
  EXPECT_EQ(kcer_public_key.GetPkcs11Id(), GetEcPkcs11Id());
  EXPECT_EQ(kcer_public_key.GetSpki(), GetEcSpki());
  EXPECT_EQ(kcer_public_key.GetToken(), Token::kUser);
}

// Test that ImportKey skips the import and succeeds when it's given an already
// imported EC key.
TEST_F(KcerTokenImplTest, ImportKeyEcAlreadyExists) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>{ObjectHandle(1)},
                                   chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_, CreateObject).Times(0);

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  EXPECT_TRUE(import_key_waiter.Get().has_value());
  const PublicKey& kcer_public_key = import_key_waiter.Get().value();
  EXPECT_EQ(kcer_public_key.GetPkcs11Id(), GetEcPkcs11Id());
  EXPECT_EQ(kcer_public_key.GetSpki(), GetEcSpki());
  EXPECT_EQ(kcer_public_key.GetToken(), Token::kUser);
}

// Test that ImportKey correctly fails when Chaps fails to search for the
// existing EC key.
TEST_F(KcerTokenImplTest, ImportKeyEcFailToSearchExistingKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  EXPECT_CALL(chaps_client_, CreateObject).Times(0);

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that ImportKey retries several times when Chaps fails to search for the
// existing EC key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyEcRetryToSearchExistingKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  EXPECT_CALL(chaps_client_, CreateObject).Times(0);

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportKey correctly fails when Chaps fails to create the private EC
// key.
TEST_F(KcerTokenImplTest, ImportKeyEcFailToCreatePrivKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, CreateObject)
      .WillOnce(RunOnceCallback<2>(ObjectHandle(0),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  EXPECT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToImportKey);
}

// Test that ImportKey retries several times when Chaps fails to create the
// private RSA key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyEcRetryToCreatePrivKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
                                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, CreateObject)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          ObjectHandle(0), chromeos::PKCS11_CKR_SESSION_CLOSED));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  EXPECT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportKey correctly fails when Chaps fails to create the public EC
// key.
TEST_F(KcerTokenImplTest, ImportKeyEcFailToCreatePubKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK));

  ObjectHandle priv_key_handle(10);
  EXPECT_CALL(chaps_client_, CreateObject)
      .WillOnce(RunOnceCallback<2>(priv_key_handle, chromeos::PKCS11_CKR_OK))
      .WillOnce(RunOnceCallback<2>(ObjectHandle(0),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  EXPECT_CALL(chaps_client_, DestroyObjectsWithRetries(
                                 pkcs11_slot_id_,
                                 std::vector<ObjectHandle>{priv_key_handle}, _))
      .WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToImportKey);
}

// Test that ImportKey retries several times when Chaps fails to create the
// public EC key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyEcRetryToCreatePubKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
                                                   chromeos::PKCS11_CKR_OK));

  // Alternates between replying with OK and SESSION_CLOSED to handle
  // alternating calls for private and public keys.
  auto fake_create_objects = [](auto slot_id, auto attrs, auto callback) {
    static bool next_is_pub_key = true;
    bool is_pub_key =
        std::exchange(next_is_pub_key, /*new_value=*/!next_is_pub_key);
    if (is_pub_key) {
      return std::move(callback).Run(ObjectHandle(10), chromeos::PKCS11_CKR_OK);
    } else {
      return std::move(callback).Run(ObjectHandle(0),
                                     chromeos::PKCS11_CKR_SESSION_CLOSED);
    }
  };

  EXPECT_CALL(chaps_client_, CreateObject)
      .Times(2 * kDefaultAttempts)
      .WillRepeatedly(Invoke(fake_create_objects));

  std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
  ASSERT_TRUE(key.has_value() && (key->size() > 0));

  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
                   import_key_waiter.GetCallback());
  ASSERT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportCertFromBytes can successfully import a cert.
TEST_F(KcerTokenImplTest, ImportCertFromBytesSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      // Return empty list of existing certs.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK))
      // Simulate that a key for the cert exists.
      .WillOnce(RunOnceCallback<2>(result_handles, chromeos::PKCS11_CKR_OK));

  chaps::AttributeList cert_attrs;
  EXPECT_CALL(chaps_client_, CreateObject(pkcs11_slot_id_, _, _))
      .WillOnce(
          DoAll(MoveArg<1>(&cert_attrs),
                RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK)));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  EXPECT_TRUE(waiter.Get().has_value());

  chromeos::PKCS11_CK_OBJECT_CLASS cert_class =
      chromeos::PKCS11_CKO_CERTIFICATE;
  chromeos::PKCS11_CK_CERTIFICATE_TYPE cert_type = chromeos::PKCS11_CKC_X_509;
  chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
  // The label comes from the client_1.pem file, see the generating script
  // //net/data/ssl/scripts/generate-client-certificates.sh for details.
  const std::string kExpectedLabel = "Client Cert A";

  // Contains "CN=B CA".
  std::vector<uint8_t> issuer_name_der =
      base::Base64Decode("MA8xDTALBgNVBAMMBEIgQ0E=").value();
  // Contains "CN=Client Cert A".
  std::vector<uint8_t> subject_name_der =
      base::Base64Decode("MBgxFjAUBgNVBAMMDUNsaWVudCBDZXJ0IEE=").value();
  // Contains 4096.
  std::vector<uint8_t> serial_number_der =
      base::Base64Decode("AgIQAA==").value();

  EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&cert_class)));
  EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_CERTIFICATE_TYPE,
                            MakeSpan(&cert_type)));
  EXPECT_TRUE(
      FindAttribute(cert_attrs, chromeos::PKCS11_CKA_TOKEN, MakeSpan(&kTrue)));
  EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_LABEL,
                            base::as_byte_span(kExpectedLabel)));
  EXPECT_TRUE(
      FindAttribute(cert_attrs, chromeos::PKCS11_CKA_VALUE, cert.value()));
  EXPECT_TRUE(
      FindAttribute(cert_attrs, chromeos::PKCS11_CKA_ISSUER, issuer_name_der));
  EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_SUBJECT,
                            subject_name_der));
  EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_SERIAL_NUMBER,
                            serial_number_der));
}

// Test that ImportCertFromBytes correctly fails when the key for the cert is
// not present.
TEST_F(KcerTokenImplTest, ImportCertFromBytesKeyNotFound) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      // Return empty list of existing certs.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK))
      // Return empty list of found keys.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kKeyNotFound);
}

// Test that ImportCertFromBytes returns success without importing a cert when
// it already exists.
TEST_F(KcerTokenImplTest, ImportCertFromBytesAlreadyExists) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  // Return some handles to simulate that an existing cert was found.
  std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(result_handles, chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  EXPECT_TRUE(waiter.Get().has_value());
}

// Test that ImportCertFromBytes correctly fails when the cert is invalid.
TEST_F(KcerTokenImplTest, ImportCertFromBytesBadCert) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
  // Corrupt the data.
  cert.value().pop_back();

  std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      // Return empty list of existing certs.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_, CreateObject).Times(0);

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kInvalidCertificate);
}

// Test that ImportCertFromBytes correctly fails when it fails to fetch search
// for the existing cert.
TEST_F(KcerTokenImplTest, ImportCertFromBytesFailToSearchForCert) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that ImportCertFromBytes correctly fails when it fails to fetch search
// for the existing key.
TEST_F(KcerTokenImplTest, ImportCertFromBytesFailToSearchForKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  // Return some handles to simulate that an existing cert was found.
  std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      // Return empty list of existing certs.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK))
      // Return an error when searching for the key.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that ImportCertFromBytes correctly fails when it fails to create a cert
// object.
TEST_F(KcerTokenImplTest, ImportCertFromBytesFailToCreate) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      // Return empty list of existing certs.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK))
      // Simulate that a key for the cert exists.
      .WillOnce(RunOnceCallback<2>(result_handles, chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_, CreateObject)
      .WillOnce(RunOnceCallback<2>(ObjectHandle(0),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToImportCertificate);
}

// Test that ImportCertFromBytes retries several times when Chaps fails to
// search for the cert with a session error.
TEST_F(KcerTokenImplTest, ImportCertFromBytesRetryToSearchForCert) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportCertFromBytes retries several times when Chaps fails to
// search for the key with a session error.
TEST_F(KcerTokenImplTest, ImportCertFromBytesRetryToSearchForKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  // Alternates between replying with OK and SESSION_CLOSED to handle
  // alternating calls for existing certs and keys.
  auto fake_find_objects = [](auto slot_id, auto attrs, auto callback) {
    static bool next_is_cert = true;
    bool is_cert = std::exchange(next_is_cert, /*new_value=*/!next_is_cert);
    if (is_cert) {
      return std::move(callback).Run(std::vector<ObjectHandle>(),
                                     chromeos::PKCS11_CKR_OK);
    } else {
      return std::move(callback).Run(std::vector<ObjectHandle>(),
                                     chromeos::PKCS11_CKR_SESSION_CLOSED);
    }
  };

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(2 * kDefaultAttempts)
      .WillRepeatedly(Invoke(fake_find_objects));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportCertFromBytes retries several times when Chaps fails to
// create a new cert with a session error.
TEST_F(KcerTokenImplTest, ImportCertFromBytesRetryToCreateCert) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
      net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
  ASSERT_TRUE(cert.has_value() && (cert->size() > 0));

  // Alternates between replying with empty and non-empty handles to process
  // alternating calls for existing certs and keys.
  auto fake_find_objects = [](auto slot_id, auto attrs, auto callback) {
    static bool next_is_cert = true;
    bool is_cert = std::exchange(next_is_cert, /*new_value=*/!next_is_cert);
    if (is_cert) {
      return std::move(callback).Run(std::vector<ObjectHandle>(),
                                     chromeos::PKCS11_CKR_OK);
    } else {
      return std::move(callback).Run(std::vector<ObjectHandle>{ObjectHandle(1)},
                                     chromeos::PKCS11_CKR_OK);
    }
  };

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(2 * kDefaultAttempts)
      .WillRepeatedly(Invoke(fake_find_objects));
  EXPECT_CALL(chaps_client_, CreateObject)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          ObjectHandle(0), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ImportPkcs12Cert can successfully import a PKCS#12 file. Most of
// the implementation is shared with KcerTokenImplNss, is covered by the tests
// for it and is not covered again. TODO(miersh): After KcerTokenImplNss is
// removed, those tests should be moved here.
TEST_F(KcerTokenImplTest, ImportPkcs12CertSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(2)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
                                                   chromeos::PKCS11_CKR_OK));

  Pkcs12Blob pkcs12_data(ReadTestFile("client.p12"));
  std::string password("12345");

  chaps::AttributeList private_key_attrs;
  chaps::AttributeList public_key_attrs;
  chaps::AttributeList cert_attrs;
  EXPECT_CALL(chaps_client_, CreateObject(pkcs11_slot_id_, _, _))
      .WillOnce(
          DoAll(MoveArg<1>(&private_key_attrs),
                RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK)))
      .WillOnce(
          DoAll(MoveArg<1>(&public_key_attrs),
                RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK)))
      .WillOnce(
          DoAll(MoveArg<1>(&cert_attrs),
                RunOnceCallback<2>(ObjectHandle(3), chromeos::PKCS11_CKR_OK)));

  base::test::TestFuture<base::expected<void, Error>> import_waiter;
  token_.ImportPkcs12Cert(pkcs12_data, password, /*hardware_backed=*/false,
                          /*mark_as_migrated=*/true,
                          import_waiter.GetCallback());

  EXPECT_TRUE(import_waiter.Get().has_value());
  expected_notifications_count_ = 1;
}

// Test that RemoveKeyAndCerts can successfully remove a key pair and certs by
// PKCS#11 id.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsByIdSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  chaps::AttributeList pkcs11_id_attrs;
  AddAttribute(pkcs11_id_attrs, chromeos::PKCS11_CKA_ID,
               GetRsaPkcs11Id().value());

  // These ids represent all the objects that are related to `public_key` and
  // should be deleted.
  std::vector<ObjectHandle> result_object_list{
      ObjectHandle(10), ObjectHandle(20), ObjectHandle(30)};
  uint32_t result_code = chromeos::PKCS11_CKR_OK;

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(result_object_list, result_code)))
      // Kcer will try to update its cache after a cert removal, prepare a
      // reply.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(), result_code));

  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
      .WillOnce(RunOnceCallback<2>(result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  EXPECT_TRUE(waiter.Get().has_value());
  expected_notifications_count_ = 1;
}

// Test that RemoveKeyAndCerts can successfully remove a key pair and certs by
// SPKI for RSA keys.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsBySpkiRsaSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  // These ids represent all the objects that should be deleted.
  std::vector<ObjectHandle> result_object_list{
      ObjectHandle(10), ObjectHandle(20), ObjectHandle(30)};
  uint32_t result_code = chromeos::PKCS11_CKR_OK;

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(result_object_list, result_code)))
      // Kcer will try to update its cache after a cert removal, prepare a
      // reply.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(), result_code));

  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
      .WillOnce(RunOnceCallback<2>(result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(GetRsaSpki()),
                           waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  EXPECT_TRUE(waiter.Get().has_value());
  expected_notifications_count_ = 1;
}

// Test that RemoveKeyAndCerts can successfully remove a key pair and certs by
// SPKI for EC keys.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsBySpkiEcSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  // These ids represent all the objects that should be deleted.
  std::vector<ObjectHandle> result_object_list{
      ObjectHandle(10), ObjectHandle(20), ObjectHandle(30)};
  uint32_t result_code = chromeos::PKCS11_CKR_OK;

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(result_object_list, result_code)))
      // Kcer will try to update its cache after a cert removal, prepare a
      // reply.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(), result_code));

  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
      .WillOnce(RunOnceCallback<2>(result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(GetEcSpki()), waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
                            GetEcPkcs11Id().value()));
  EXPECT_TRUE(waiter.Get().has_value());
  expected_notifications_count_ = 1;
}

// Test that RemoveKeyAndCerts correctly fails when it cannot recover PKCS#11
// from the provided SPKI.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsBySpkiFail) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  PublicKeySpki bad_spki({1, 2, 3, 4, 5});  // Not a valid SPKI.

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(bad_spki), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToGetPkcs11Id);
}

// Test that RemoveKeyAndCerts correctly fails when the search for objects
// fails.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsFailToSearch) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  std::vector<ObjectHandle> result_object_list{};
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(result_object_list, result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that RemoveKeyAndCerts retries several times when the search for objects
// fails with a session error.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsRetrySearchOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
  uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<2>(result_object_list, result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that RemoveKeyAndCerts correctly fails when the removal of objects
// fails.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsFailToDestroy) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(result_object_list, chromeos::PKCS11_CKR_OK))
      // Kcer will try to update its cache after a cert removal, prepare a reply
      // (even though an error occurred, chaps still might have removed
      // something).
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
      .WillOnce(RunOnceCallback<2>(result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToRemoveObjects);
  expected_notifications_count_ = 1;
}

// Test that RemoveKeyAndCerts retries several times when the removal of objects
// fails with a session error. RemoveKeyAndCerts has to retry all the previous
// methods.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsRetryDestroyOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
  uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
                                                   chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that RemoveCert can successfully remove a cert.
TEST_F(KcerTokenImplTest, RemoveCertSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  scoped_refptr<net::X509Certificate> x509_cert =
      net::X509Certificate::CreateFromBytes(GetCertDer().value());
  scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
      Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);

  // Contains the ids of found certs that should be deleted.
  std::vector<ObjectHandle> cert_handles{ObjectHandle(10)};
  uint32_t result_code = chromeos::PKCS11_CKR_OK;

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(cert_handles, result_code)))
      // Kcer will try to update its cache after a cert removal, prepare a
      // reply.
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(), result_code));

  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, cert_handles, _))
      .WillOnce(RunOnceCallback<2>(result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveCert(cert, waiter.GetCallback());

  EXPECT_TRUE(waiter.Get().has_value());
  CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE;
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&cert_class)));
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_VALUE,
                            GetCertDer().value()));
  expected_notifications_count_ = 1;
}

// Test that RemoveCert correctly fails when the search for objects
// fails.
TEST_F(KcerTokenImplTest, RemoveCertFailToSearch) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  scoped_refptr<net::X509Certificate> x509_cert =
      net::X509Certificate::CreateFromBytes(GetCertDer().value());
  scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
      Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);

  std::vector<ObjectHandle> result_object_list{};
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(result_object_list, result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveCert(cert, waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that RemoveCert retries several times when the search for objects
// fails with a session error.
TEST_F(KcerTokenImplTest, RemoveCertRetrySearchOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  scoped_refptr<net::X509Certificate> x509_cert =
      net::X509Certificate::CreateFromBytes(GetCertDer().value());
  scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
      Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);

  std::vector<ObjectHandle> result_object_list{};
  uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<2>(result_object_list, result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveCert(cert, waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that RemoveCert correctly fails when the removal of objects
// fails.
TEST_F(KcerTokenImplTest, RemoveCertFailToDestroy) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  scoped_refptr<net::X509Certificate> x509_cert =
      net::X509Certificate::CreateFromBytes(GetCertDer().value());
  scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
      Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);

  std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(result_object_list, chromeos::PKCS11_CKR_OK))
      // Kcer will try to update its cache after a cert removal, prepare a reply
      // (even though an error occurred, chaps still might have removed
      // something).
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
      .WillOnce(RunOnceCallback<2>(result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveCert(cert, waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToRemoveObjects);
  expected_notifications_count_ = 1;
}

// Test that RemoveCert retries several times when the removal of objects
// fails with a session error. RemoveCert has to retry all the previous
// methods.
TEST_F(KcerTokenImplTest, RemoveCertRetryDestroyOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  scoped_refptr<net::X509Certificate> x509_cert =
      net::X509Certificate::CreateFromBytes(GetCertDer().value());
  scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
      Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);

  std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
  uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
                                                   chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_,
              DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_code));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.RemoveCert(cert, waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ListKeys can successfully list keys when there are no keys.
TEST_F(KcerTokenImplTest, ListKeysSuccessWithNoKeys) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::vector<ObjectHandle> result_object_list{};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(2)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
                                                   chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  EXPECT_TRUE(waiter.Get().value().empty());
}

// Test that ListKeys can successfully list keys when there is one RSA key.
TEST_F(KcerTokenImplTest, ListKeysSuccessWithOneRsaKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle rsa_handle{1};
  std::vector<ObjectHandle> rsa_handles{rsa_handle};
  std::vector<ObjectHandle> ec_handles{};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(2)
      .WillOnce(RunOnceCallback<2>(rsa_handles, chromeos::PKCS11_CKR_OK))
      .WillOnce(RunOnceCallback<2>(ec_handles, chromeos::PKCS11_CKR_OK));

  chaps::AttributeList rsa_attrs = GetRsaKeyAttrs(
      GetRsaPkcs11Id().value(), GetRsaModulus(), GetRsaPublicExponent());
  EXPECT_CALL(
      chaps_client_,
      GetAttributeValue(pkcs11_slot_id_, rsa_handle,
                        std::vector<AttributeId>{AttributeId::kPkcs11Id,
                                                 AttributeId::kModulus,
                                                 AttributeId::kPublicExponent},
                        _))
      .WillOnce(RunOnceCallback<3>(rsa_attrs, chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  ASSERT_EQ(waiter.Get().value().size(), 1u);
  PublicKey pub_key = waiter.Get().value().front();
  EXPECT_EQ(pub_key.GetPkcs11Id(), GetRsaPkcs11Id());
  EXPECT_EQ(pub_key.GetSpki(), GetRsaSpki());
  EXPECT_EQ(pub_key.GetToken(), Token::kUser);
}

// Test that ListKeys can successfully list keys when there is one EC key.
TEST_F(KcerTokenImplTest, ListKeysSuccessWithOneEcKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle ec_handle{1};
  std::vector<ObjectHandle> rsa_handles{};
  std::vector<ObjectHandle> ec_handles{ec_handle};
  std::vector<ObjectHandle> priv_key_handles{ObjectHandle(2)};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(3)
      .WillOnce(RunOnceCallback<2>(rsa_handles, chromeos::PKCS11_CKR_OK))
      .WillOnce(RunOnceCallback<2>(ec_handles, chromeos::PKCS11_CKR_OK))
      // For each EC handle `token_` will check that the private key exists.
      .WillOnce(RunOnceCallback<2>(priv_key_handles, chromeos::PKCS11_CKR_OK));

  chaps::AttributeList ec_attrs =
      GetEcKeyAttrs(GetEcPkcs11Id().value(), GetEcPublicValue());
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, ec_handle,
                                std::vector<AttributeId>{AttributeId::kPkcs11Id,
                                                         AttributeId::kEcPoint},
                                _))
      .WillOnce(RunOnceCallback<3>(ec_attrs, chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  ASSERT_EQ(waiter.Get().value().size(), 1u);
  PublicKey pub_key = waiter.Get().value().front();
  EXPECT_EQ(pub_key.GetPkcs11Id(), GetEcPkcs11Id());
  EXPECT_EQ(pub_key.GetSpki(), GetEcSpki());
  EXPECT_EQ(pub_key.GetToken(), Token::kUser);
}

// Test that ListKeys can successfully list keys when there is one RSA and one
// EC key.
TEST_F(KcerTokenImplTest, ListKeysSuccessWithTwoKeys) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle rsa_handle{1};
  ObjectHandle ec_handle{2};
  std::vector<ObjectHandle> rsa_handles{rsa_handle};
  std::vector<ObjectHandle> ec_handles{ec_handle};
  std::vector<ObjectHandle> priv_key_handles{ObjectHandle(3)};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(3)
      .WillOnce(RunOnceCallback<2>(rsa_handles, chromeos::PKCS11_CKR_OK))
      .WillOnce(RunOnceCallback<2>(ec_handles, chromeos::PKCS11_CKR_OK))
      // For each EC handle `token_` will check that the private key exists.
      .WillOnce(RunOnceCallback<2>(priv_key_handles, chromeos::PKCS11_CKR_OK));

  chaps::AttributeList rsa_attrs = GetRsaKeyAttrs(
      GetRsaPkcs11Id().value(), GetRsaModulus(), GetRsaPublicExponent());
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, rsa_handle, _, _))
      .WillOnce(RunOnceCallback<3>(rsa_attrs, chromeos::PKCS11_CKR_OK));
  chaps::AttributeList ec_attrs =
      GetEcKeyAttrs(GetEcPkcs11Id().value(), GetEcPublicValue());
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, ec_handle, _, _))
      .WillOnce(RunOnceCallback<3>(ec_attrs, chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  ASSERT_EQ(waiter.Get().value().size(), 2u);
  // The order is not guaranteed, but in practice should be stable.
  PublicKey rsa_pub_key = waiter.Get().value().front();
  PublicKey ec_pub_key = waiter.Get().value().back();
  EXPECT_EQ(rsa_pub_key.GetPkcs11Id(), GetRsaPkcs11Id());
  EXPECT_EQ(rsa_pub_key.GetSpki(), GetRsaSpki());
  EXPECT_EQ(rsa_pub_key.GetToken(), Token::kUser);
  EXPECT_EQ(ec_pub_key.GetPkcs11Id(), GetEcPkcs11Id());
  EXPECT_EQ(ec_pub_key.GetSpki(), GetEcSpki());
  EXPECT_EQ(ec_pub_key.GetToken(), Token::kUser);
}

// Test that ListKeys correctly skips invalid keys.
TEST_F(KcerTokenImplTest, ListKeysBadKeysAreSkipped) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  // Same handles will be returned for RSA and EC keys, that's not realistic,
  // but good enough for the test.
  std::vector<ObjectHandle> result_object_list{ObjectHandle{1},
                                               ObjectHandle{2}};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(2)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
                                                   chromeos::PKCS11_CKR_OK));

  chaps::AttributeList bad_rsa_attrs = GetRsaKeyAttrs(
      GetRsaPkcs11Id().value(),
      std::vector<uint8_t>(GetRsaModulus().begin(), GetRsaModulus().end() - 4),
      GetRsaPublicExponent());
  chaps::AttributeList bad_ec_attrs =
      GetEcKeyAttrs(GetEcPkcs11Id().value(),
                    std::vector<uint8_t>(GetEcPublicValue().begin(),
                                         GetEcPublicValue().end() - 1));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .Times(4)
      .WillOnce(
          RunOnceCallback<3>(chaps::AttributeList(), chromeos::PKCS11_CKR_OK))
      .WillOnce(RunOnceCallback<3>(bad_rsa_attrs, chromeos::PKCS11_CKR_OK))
      .WillOnce(
          RunOnceCallback<3>(chaps::AttributeList(), chromeos::PKCS11_CKR_OK))
      .WillOnce(RunOnceCallback<3>(bad_ec_attrs, chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  EXPECT_TRUE(waiter.Get().value().empty());
}

// Test that ListKeys correctly fails when Chaps fails to find RSA keys.
TEST_F(KcerTokenImplTest, ListKeysFailedToListRsaKeys) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::vector<ObjectHandle> handles{};
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(
          RunOnceCallback<2>(handles, chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that ListKeys correctly fails when Chaps fails to find EC keys.
TEST_F(KcerTokenImplTest, ListKeysFailedToListEcKeys) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::vector<ObjectHandle> handles{};
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(2)
      .WillOnce(RunOnceCallback<2>(handles, chromeos::PKCS11_CKR_OK))
      .WillOnce(
          RunOnceCallback<2>(handles, chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that ListKeys correctly retries when Chaps fails to find RSA keys with a
// session error.
TEST_F(KcerTokenImplTest, ListKeysRetryFindRsaOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::vector<ObjectHandle> handles{};
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          handles, chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ListKeys correctly retries when Chaps fails to retrieve attributes
// for RSA keys with a session error.
TEST_F(KcerTokenImplTest, ListKeysRetryGetRsaOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::vector<ObjectHandle> handles{ObjectHandle(1)};
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<2>(handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(
          chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ListKeys correctly retries when Chaps fails to find EC keys with a
// session error.
TEST_F(KcerTokenImplTest, ListKeysRetryFindEcOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  // Alternates between replying with OK and SESSION_CLOSED to handle
  // alternating calls for RSA and EC keys.
  auto fake_find_objects = [](auto slot_id, auto attrs, auto callback) {
    static bool next_is_rsa = true;
    bool is_rsa = std::exchange(next_is_rsa, /*new_value=*/!next_is_rsa);
    if (is_rsa) {
      return std::move(callback).Run(std::vector<ObjectHandle>(),
                                     chromeos::PKCS11_CKR_OK);
    } else {
      return std::move(callback).Run(std::vector<ObjectHandle>(),
                                     chromeos::PKCS11_CKR_SESSION_CLOSED);
    }
  };

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(2 * kDefaultAttempts)
      .WillRepeatedly(Invoke(fake_find_objects));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ListKeys correctly retries when Chaps fails to retrieve attributes
// for EC keys with a session error.
TEST_F(KcerTokenImplTest, ListKeysRetryGetEcOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  // Alternates between returning no handles and one handle to process
  // alternating calls for RSA and EC keys.
  auto fake_find_objects = [](auto slot_id, auto attrs, auto callback) {
    static bool next_is_rsa = true;
    bool is_rsa = std::exchange(next_is_rsa, /*new_value=*/!next_is_rsa);
    if (is_rsa) {
      return std::move(callback).Run(std::vector<ObjectHandle>(),
                                     chromeos::PKCS11_CKR_OK);
    } else {
      return std::move(callback).Run(std::vector<ObjectHandle>{ObjectHandle(1)},
                                     chromeos::PKCS11_CKR_OK);
    }
  };

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(2 * kDefaultAttempts)
      .WillRepeatedly(Invoke(fake_find_objects));

  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(
          chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
  token_.ListKeys(waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that ListCerts can successfully list certs when there are no certs.
TEST_F(KcerTokenImplTest, ListCertsSuccessWithNoCerts) {
  std::vector<ObjectHandle> result_object_list{};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(1)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
                                                   chromeos::PKCS11_CKR_OK));

  // Initialize late, so cache update can interact with the EXPECTs.
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<
      base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
      waiter;
  token_.ListCerts(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  EXPECT_TRUE(waiter.Get().value().empty());
}

// Test that ListCerts can successfully list certs when there is one cert.
TEST_F(KcerTokenImplTest, ListCertsSuccessWithOneCert) {
  ObjectHandle cert_handle{1};
  std::vector<ObjectHandle> cert_handles{cert_handle};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(cert_handles, chromeos::PKCS11_CKR_OK));

  std::string nickname = "cert_nickname";
  chaps::AttributeList cert_attrs =
      GetFakeCertAttrs(GetRsaPkcs11Id(), nickname, GetCertDer());
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, cert_handle,
                                std::vector<AttributeId>{AttributeId::kPkcs11Id,
                                                         AttributeId::kLabel,
                                                         AttributeId::kValue},
                                _))
      .WillOnce(RunOnceCallback<3>(cert_attrs, chromeos::PKCS11_CKR_OK));

  // Initialize late, so cache update can interact with the EXPECTs.
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<
      base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
      waiter;
  token_.ListCerts(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  ASSERT_EQ(waiter.Get().value().size(), 1u);
  scoped_refptr<const Cert> cert = waiter.Get().value().front();
  EXPECT_EQ(cert->GetPkcs11Id(), GetRsaPkcs11Id());
  EXPECT_EQ(cert->GetNickname(), nickname);
  EXPECT_EQ(cert->GetToken(), Token::kUser);
  EXPECT_TRUE(SpanEqual(
      net::x509_util::CryptoBufferAsSpan(cert->GetX509Cert()->cert_buffer()),
      GetCertDer().value()));
}

// Test that KcerTokenImpl correct updates / doesn't update the cert cache for
// ListCerts().
TEST_F(KcerTokenImplTest, ListCertsCacheUpdate) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  // No certs initially, the cache will be up-to-date and empty.
  {
    base::test::TestFuture<
        base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
        waiter;
    token_.ListCerts(waiter.GetCallback());
    ASSERT_TRUE(waiter.Get().has_value());
    ASSERT_EQ(waiter.Get().value().size(), 0u);
  }

  // Check that the cache doesn't update itself unnecessary.
  EXPECT_CALL(chaps_client_, FindObjects).Times(0);

  // Still no certs, the cache is still empty.
  {
    base::test::TestFuture<
        base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
        waiter;
    token_.ListCerts(waiter.GetCallback());
    ASSERT_TRUE(waiter.Get().has_value());
    ASSERT_EQ(waiter.Get().value().size(), 0u);
  }

  // Prepare fake chaps client to return a cert.
  ObjectHandle cert_handle{1};
  std::vector<ObjectHandle> cert_handles{cert_handle};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(cert_handles, chromeos::PKCS11_CKR_OK));

  std::string nickname = "cert_nickname";
  chaps::AttributeList cert_attrs =
      GetFakeCertAttrs(GetRsaPkcs11Id(), nickname, GetCertDer());
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, cert_handle,
                                std::vector<AttributeId>{AttributeId::kPkcs11Id,
                                                         AttributeId::kLabel,
                                                         AttributeId::kValue},
                                _))
      .WillOnce(RunOnceCallback<3>(cert_attrs, chromeos::PKCS11_CKR_OK));

  // Simulate a notification that certs changed.

  net::CertDatabase::GetInstance()->NotifyObserversClientCertStoreChanged();
  expected_notifications_count_++;
  // CertDatabase sends the notifications asynchronously, so give it a chance to
  // do that.
  task_environment_.RunUntilIdle();

  // Now the cache should update itself again and return one cert.
  {
    base::test::TestFuture<
        base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
        waiter;
    token_.ListCerts(waiter.GetCallback());
    ASSERT_TRUE(waiter.Get().has_value());
    ASSERT_EQ(waiter.Get().value().size(), 1u);
  }
}

// Test that ListCerts can successfully list two certs.
TEST_F(KcerTokenImplTest, ListCertsSuccessWithTwoCerts) {
  ObjectHandle cert_handle_1{1};
  ObjectHandle cert_handle_2{2};
  std::vector<ObjectHandle> cert_handles{cert_handle_1, cert_handle_2};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(cert_handles, chromeos::PKCS11_CKR_OK));

  std::string nickname_1 = "cert_nickname_1";
  chaps::AttributeList cert_attrs_1 =
      GetFakeCertAttrs(GetRsaPkcs11Id(), nickname_1, GetCertDer());
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, cert_handle_1, _, _))
      .WillOnce(RunOnceCallback<3>(cert_attrs_1, chromeos::PKCS11_CKR_OK));

  // The second cert has to be different for Kcer to return them as two separate
  // certs.
  std::unique_ptr<net::CertBuilder> cert_issuer = MakeCertIssuer();
  std::unique_ptr<net::CertBuilder> cert_builder =
      MakeCertBuilder(cert_issuer.get(), GetRsaSpki().value());
  std::string nickname_2 = "cert_nickname_2";
  CertDer cert_der_2(StrToBytes(cert_builder->GetDER()));
  chaps::AttributeList cert_attrs_2 =
      GetFakeCertAttrs(GetEcPkcs11Id(), nickname_2, cert_der_2);
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, cert_handle_2, _, _))
      .WillOnce(RunOnceCallback<3>(cert_attrs_2, chromeos::PKCS11_CKR_OK));

  // Initialize late, so cache update can interact with the EXPECTs.
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<
      base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
      waiter;
  token_.ListCerts(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  ASSERT_EQ(waiter.Get().value().size(), 2u);

  scoped_refptr<const Cert> cert_1 = waiter.Get().value().back();
  scoped_refptr<const Cert> cert_2 = waiter.Get().value().front();
  if (cert_1->GetPkcs11Id() != GetRsaPkcs11Id()) {
    // The order is not guaranteed.
    std::swap(cert_1, cert_2);
  }

  EXPECT_EQ(cert_1->GetPkcs11Id(), GetRsaPkcs11Id());
  EXPECT_EQ(cert_1->GetNickname(), nickname_1);
  EXPECT_EQ(cert_1->GetToken(), Token::kUser);
  EXPECT_TRUE(SpanEqual(
      net::x509_util::CryptoBufferAsSpan(cert_1->GetX509Cert()->cert_buffer()),
      GetCertDer().value()));

  EXPECT_EQ(cert_2->GetPkcs11Id(), GetEcPkcs11Id());
  EXPECT_EQ(cert_2->GetNickname(), nickname_2);
  EXPECT_EQ(cert_2->GetToken(), Token::kUser);
  EXPECT_TRUE(cert_2->GetX509Cert()->EqualsIncludingChain(
      cert_builder->GetX509Certificate().get()));
}

// Test that ListCerts correctly skips invalid certs.
TEST_F(KcerTokenImplTest, ListCertsBadCertsAreSkipped) {
  ObjectHandle cert_handle_1{1};
  ObjectHandle cert_handle_2{2};
  std::vector<ObjectHandle> cert_handles{cert_handle_1, cert_handle_2};
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(RunOnceCallback<2>(cert_handles, chromeos::PKCS11_CKR_OK));

  // Invalid because pkcs11_id is empty.
  chaps::AttributeList cert_attrs_1 =
      GetFakeCertAttrs(Pkcs11Id(), /*label=*/"", GetCertDer());
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, cert_handle_1, _, _))
      .WillOnce(RunOnceCallback<3>(cert_attrs_1, chromeos::PKCS11_CKR_OK));
  // Invalid because cert_der is empty.
  chaps::AttributeList cert_attrs_2 =
      GetFakeCertAttrs(GetEcPkcs11Id(), /*label=*/"", CertDer());
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, cert_handle_2, _, _))
      .WillOnce(RunOnceCallback<3>(cert_attrs_2, chromeos::PKCS11_CKR_OK));

  // Initialize late, so cache update can interact with the EXPECTs.
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<
      base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
      waiter;
  token_.ListCerts(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().value().size(), 0u);
}

// Test that ListCerts correctly fails when Chaps fails to find cert handles.
TEST_F(KcerTokenImplTest, ListCertsFailedToListObjects) {
  std::vector<ObjectHandle> handles{};
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(
          RunOnceCallback<2>(handles, chromeos::PKCS11_CKR_GENERAL_ERROR));

  // Initialize late, so cache update can interact with the EXPECTs.
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<
      base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
      waiter;
  token_.ListCerts(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  EXPECT_TRUE(waiter.Get()->empty());
}

// Test that ListCerts correctly retries when Chaps fails to find cert handles
// with a session error.
TEST_F(KcerTokenImplTest, ListCertsRetryFindOjectsOnSessionError) {
  std::vector<ObjectHandle> handles{};
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          handles, chromeos::PKCS11_CKR_SESSION_CLOSED));

  // Initialize late, so cache update can interact with the EXPECTs.
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<
      base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
      waiter;
  token_.ListCerts(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  EXPECT_TRUE(waiter.Get()->empty());
}

// Test that ListCerts correctly retries when Chaps fails to retrieve attributes
// for a cert with a session error.
TEST_F(KcerTokenImplTest, ListCertsRetryGetCertOnSessionError) {
  std::vector<ObjectHandle> handles{ObjectHandle(1)};
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<2>(handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, GetAttributeValue)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(
          chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  // Initialize late, so cache update can interact with the EXPECTs.
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<
      base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
      waiter;
  token_.ListCerts(waiter.GetCallback());

  ASSERT_TRUE(waiter.Get().has_value());
  EXPECT_TRUE(waiter.Get()->empty());
}

// Test that DoesPrivateKeyExist can successfully check whether a private key
// exists when it exists.
TEST_F(KcerTokenImplTest, DoesPrivateKeyExistKeyExistsSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  std::vector<ObjectHandle> result_object_list{ObjectHandle(10)};
  uint32_t result_code = chromeos::PKCS11_CKR_OK;

  chaps::AttributeList attrs;
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(DoAll(MoveArg<1>(&attrs),
                      RunOnceCallback<2>(result_object_list, result_code)));

  base::test::TestFuture<base::expected<bool, Error>> waiter;
  token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
                             waiter.GetCallback());

  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(
      FindAttribute(attrs, chromeos::PKCS11_CKA_ID, GetRsaPkcs11Id().value()));
  ASSERT_TRUE(waiter.Get().has_value());
  EXPECT_TRUE(waiter.Get().value());
}

// Test that DoesPrivateKeyExist can successfully check whether a private key
// exists when it doesn't exist.
TEST_F(KcerTokenImplTest, DoesPrivateKeyExistKeyDoesNotExistsSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  std::vector<ObjectHandle> result_object_list{};
  uint32_t result_code = chromeos::PKCS11_CKR_OK;

  chaps::AttributeList attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&attrs),
                      RunOnceCallback<2>(result_object_list, result_code)));

  base::test::TestFuture<base::expected<bool, Error>> waiter;
  token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
                             waiter.GetCallback());

  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(
      FindAttribute(attrs, chromeos::PKCS11_CKA_ID, GetRsaPkcs11Id().value()));
  ASSERT_TRUE(waiter.Get<0>().has_value());
  EXPECT_FALSE(waiter.Get<0>().value());
}

// Test that DoesPrivateKeyExist correctly fails when the search for objects
// fails.
TEST_F(KcerTokenImplTest, DoesPrivateKeyExistFailToSearch) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  std::vector<ObjectHandle> result_object_list{};
  uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(1)
      .WillOnce(RunOnceCallback<2>(result_object_list, result_code));

  base::test::TestFuture<base::expected<bool, Error>> waiter;
  token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
                             waiter.GetCallback());

  EXPECT_FALSE(waiter.Get<0>().has_value());
  EXPECT_EQ(waiter.Get<0>().error(), Error::kFailedToSearchForObjects);
}

// Test that DoesPrivateKeyExist retries several times when the search for
// objects fails with a session error.
TEST_F(KcerTokenImplTest, DoesPrivateKeyExistRetryOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  std::vector<ObjectHandle> result_object_list{};
  uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;

  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<2>(result_object_list, result_code));

  base::test::TestFuture<base::expected<bool, Error>> waiter;
  token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
                             waiter.GetCallback());

  EXPECT_FALSE(waiter.Get<0>().has_value());
  EXPECT_EQ(waiter.Get<0>().error(), Error::kPkcs11SessionFailure);
}

// Test that Sign can successfully create a RsaSha1 signature.
TEST_F(KcerTokenImplTest, SignRsaSha1Success) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  // Digest for the same data and algorithm is always the same and was recorded
  // from a working device.
  std::vector<uint8_t> expected_digest =
      base::Base64Decode("MCEwCQYFKw4DAhoFAAQUxTkeMIryW0LVk01qIBo06JjSVcY=")
          .value();
  ObjectHandle expected_key_handle(10);

  std::vector<ObjectHandle> result_object_list({expected_key_handle});
  uint32_t result_code = chromeos::PKCS11_CKR_OK;
  std::vector<uint8_t> result_signature_bytes({11, 12, 13, 14, 15});
  Signature result_signature(result_signature_bytes);

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(result_object_list, result_code)));
  EXPECT_CALL(
      chaps_client_,
      Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS,
           std::vector<uint8_t>(), expected_key_handle, expected_digest, _))
      .WillOnce(DoAll(RunOnceCallback<5>(result_signature_bytes, result_code)));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
              sign_waiter.GetCallback());

  ASSERT_TRUE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().value(), result_signature);
  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
}

// Test that Sign can successfully create a RsaSha256 signature.
TEST_F(KcerTokenImplTest, SignRsaSha256Success) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha256;
  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  // Digest for the same data and algorithm is always the same and was recorded
  // from a working device.
  std::vector<uint8_t> expected_digest =
      base::Base64Decode(
          "MDEwDQYJYIZIAWUDBAIBBQAEIMhI4QE/"
          "nwSp1j+kPOf9SvA1FSx8ZppKQEtnEHzuXy5O")
          .value();
  ObjectHandle expected_key_handle(10);

  std::vector<ObjectHandle> result_object_list({expected_key_handle});
  uint32_t result_code = chromeos::PKCS11_CKR_OK;
  std::vector<uint8_t> result_signature_bytes({11, 12, 13, 14, 15});
  Signature result_signature(result_signature_bytes);

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(result_object_list, result_code)));
  EXPECT_CALL(
      chaps_client_,
      Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS,
           std::vector<uint8_t>(), expected_key_handle, expected_digest, _))
      .WillOnce(DoAll(RunOnceCallback<5>(result_signature_bytes, result_code)));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
              sign_waiter.GetCallback());

  ASSERT_TRUE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().value(), result_signature);
  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
}

// Test that Sign can successfully create a RsaPssSha256 signature.
TEST_F(KcerTokenImplTest, SignRsaPssSha256Success) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  SigningScheme signing_scheme = SigningScheme::kRsaPssRsaeSha256;
  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  // Digest for the same data and algorithm is always the same and was recorded
  // from a working device.
  std::vector<uint8_t> expected_digest =
      base::Base64Decode("yEjhAT+fBKnWP6Q85/1K8DUVLHxmmkpAS2cQfO5fLk4=")
          .value();
  // Mechanism parameters are always the same for a given algorithm.
  std::vector<uint8_t> expected_mechanism_param =
      base::Base64Decode("UAIAAAAAAAACAAAAAAAAACAAAAAAAAAA").value();
  ObjectHandle expected_key_handle(10);

  std::vector<ObjectHandle> result_object_list({expected_key_handle});
  uint32_t result_code = chromeos::PKCS11_CKR_OK;
  std::vector<uint8_t> result_signature_bytes({11, 12, 13, 14, 15});
  Signature result_signature(result_signature_bytes);

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(result_object_list, result_code)));
  EXPECT_CALL(
      chaps_client_,
      Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS_PSS,
           expected_mechanism_param, expected_key_handle, expected_digest, _))
      .WillOnce(DoAll(RunOnceCallback<5>(result_signature_bytes, result_code)));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
              sign_waiter.GetCallback());

  ASSERT_TRUE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().value(), result_signature);
  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
}

// Test that Sign can successfully create a EcSha256 signature.
TEST_F(KcerTokenImplTest, SignEcSha256) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetEcPkcs11Id(), GetEcSpki());
  SigningScheme signing_scheme = SigningScheme::kEcdsaSecp256r1Sha256;
  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  // Digest for the same data and algorithm is always the same and was recorded
  // from a working device.
  std::vector<uint8_t> expected_digest =
      base::Base64Decode("yEjhAT+fBKnWP6Q85/1K8DUVLHxmmkpAS2cQfO5fLk4=")
          .value();
  ObjectHandle expected_key_handle(10);

  std::vector<ObjectHandle> result_object_list({expected_key_handle});
  uint32_t result_code = chromeos::PKCS11_CKR_OK;
  // Signature is different for each key, this one was recorded from a working
  // device.
  std::vector<uint8_t> result_chaps_signature =
      base::Base64Decode(
          "aNhCYZ1TL7eSxbrA6t/+XBAllGfi0zom4Ybo++iwW81Yob2LDKX6OOUX2h661/"
          "INbTVYGDO5kNDqLBc1BUxgkA==")
          .value();
  // `result_chaps_signature` needs to be reencoded by Kcer into a different
  // format, this is the expected result.
  Signature result_signature(
      base::Base64Decode("MEQCIGjYQmGdUy+3ksW6wOrf/"
                         "lwQJZRn4tM6JuGG6PvosFvNAiBYob2LDKX6OOUX2h661/"
                         "INbTVYGDO5kNDqLBc1BUxgkA==")
          .value());

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(result_object_list, result_code)));
  EXPECT_CALL(chaps_client_, Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_ECDSA,
                                  std::vector<uint8_t>(), expected_key_handle,
                                  expected_digest, _))
      .WillOnce(DoAll(RunOnceCallback<5>(result_chaps_signature, result_code)));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
              sign_waiter.GetCallback());

  ASSERT_TRUE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().value(), result_signature);
  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
                            GetEcPkcs11Id().value()));
}

// Test that Sign correctly fails when it fails to find the key.
TEST_F(KcerTokenImplTest, SignFailToSearch) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
              sign_waiter.GetCallback());

  ASSERT_FALSE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that Sign retries several times when the search for the key fails with a
// session error.
TEST_F(KcerTokenImplTest, SignRetrySearchOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
              sign_waiter.GetCallback());

  ASSERT_FALSE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that Sign correctly fails when Chaps fails to sign.
TEST_F(KcerTokenImplTest, SignFailToSign) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(
          RunOnceCallback<2>(std::vector<ObjectHandle>({ObjectHandle(10)}),
                             chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, Sign)
      .WillOnce(RunOnceCallback<5>(std::vector<uint8_t>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
              sign_waiter.GetCallback());

  ASSERT_FALSE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().error(), Error::kFailedToSign);
}

// Test that Sign retries several times when Chaps fails to sign with a session
// error.
TEST_F(KcerTokenImplTest, SignRetrySignOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
  DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>({ObjectHandle(10)}),
          chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, Sign)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(
          std::vector<uint8_t>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
              sign_waiter.GetCallback());

  ASSERT_FALSE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that SignRsaPkcs1Raw can successfully create a signature.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  ObjectHandle expected_key_handle(10);

  std::vector<ObjectHandle> result_object_list({expected_key_handle});
  uint32_t result_code = chromeos::PKCS11_CKR_OK;
  std::vector<uint8_t> result_signature_bytes({11, 12, 13, 14, 15});
  Signature result_signature(result_signature_bytes);

  chaps::AttributeList find_objects_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
                      RunOnceCallback<2>(result_object_list, result_code)));
  EXPECT_CALL(
      chaps_client_,
      Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS,
           std::vector<uint8_t>(), expected_key_handle, digest.value(), _))
      .WillOnce(DoAll(RunOnceCallback<5>(result_signature_bytes, result_code)));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
                         sign_waiter.GetCallback());

  ASSERT_TRUE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().value(), result_signature);
  chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
      chromeos::PKCS11_CKO_PRIVATE_KEY;
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
                            MakeSpan(&priv_key_class)));
  EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
}

// Test that SignRsaPkcs1Raw correctly fails when it fails to find the key.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawFailToSearch) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
                         sign_waiter.GetCallback());

  ASSERT_FALSE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().error(), Error::kFailedToSearchForObjects);
}

// Test that SignRsaPkcs1Raw retries several times when the search for the key
// fails with a session error.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawRetrySearchOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
                         sign_waiter.GetCallback());

  ASSERT_FALSE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that SignRsaPkcs1Raw correctly fails when Chaps fails to sign.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawFailToSign) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(
          RunOnceCallback<2>(std::vector<ObjectHandle>({ObjectHandle(10)}),
                             chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, Sign)
      .WillOnce(RunOnceCallback<5>(std::vector<uint8_t>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
                         sign_waiter.GetCallback());

  ASSERT_FALSE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().error(), Error::kFailedToSign);
}

// Test that SignRsaPkcs1Raw retries several times when Chaps fails to sign with
// a session error.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawRetrySignOnSessionError) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>({ObjectHandle(10)}),
          chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, Sign)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(
          std::vector<uint8_t>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
                         sign_waiter.GetCallback());

  ASSERT_FALSE(sign_waiter.Get().has_value());
  EXPECT_EQ(sign_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

TEST_F(KcerTokenImplTest, GetTokenInfoForUserToken) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<base::expected<TokenInfo, Error>> info_waiter;
  token_.GetTokenInfo(info_waiter.GetCallback());

  ASSERT_TRUE(info_waiter.Get().has_value());
  const TokenInfo& info = info_waiter.Get().value();
  EXPECT_EQ(info.pkcs11_id, pkcs11_slot_id_.value());
  EXPECT_EQ(info.token_name, "User Token");
  EXPECT_EQ(info.module_name, "Chaps");
}

TEST_F(KcerTokenImplTest, GetTokenInfoForDeviceToken) {
  KcerTokenImpl device_token(Token::kDevice, &chaps_client_);
  device_token.InitializeWithoutNss(pkcs11_slot_id_);

  base::test::TestFuture<base::expected<TokenInfo, Error>> info_waiter;
  device_token.GetTokenInfo(info_waiter.GetCallback());

  ASSERT_TRUE(info_waiter.Get().has_value());
  const TokenInfo& info = info_waiter.Get().value();
  EXPECT_EQ(info.pkcs11_id, pkcs11_slot_id_.value());
  EXPECT_EQ(info.token_name, "Device Token");
  EXPECT_EQ(info.module_name, "Chaps");
}

// Test that GetKeyInfo can successfully get key info for RSA keys when PSS is
// supported.
TEST_F(KcerTokenImplTest, GetKeyInfoRsaPssSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};
  bool expected_is_hw_backed = true;
  std::string expected_nickname = "new_label";

  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .WillOnce(RunOnceCallback<1>(
          std::vector<uint64_t>{chromeos::PKCS11_CKM_RSA_PKCS_PSS},
          chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(
          GetFakeKeyInfoAttrs(/*is_in_software=*/!expected_is_hw_backed,
                              chromeos::PKCS11_CKK_RSA, expected_nickname),
          chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_TRUE(info_waiter.Get().has_value());
  const KeyInfo& key_info = info_waiter.Get().value();
  EXPECT_EQ(key_info.is_hardware_backed, expected_is_hw_backed);
  EXPECT_EQ(key_info.key_type, KeyType::kRsa);
  EXPECT_EQ(key_info.nickname, expected_nickname);
  EXPECT_THAT(
      key_info.supported_signing_schemes,
      UnorderedElementsAre(
          SigningScheme::kRsaPkcs1Sha1, SigningScheme::kRsaPkcs1Sha256,
          SigningScheme::kRsaPkcs1Sha384, SigningScheme::kRsaPkcs1Sha512,
          SigningScheme::kRsaPssRsaeSha256, SigningScheme::kRsaPssRsaeSha384,
          SigningScheme::kRsaPssRsaeSha512));
}

// Test that GetKeyInfo can successfully get key info for RSA keys when PSS is
// not supported.
TEST_F(KcerTokenImplTest, GetKeyInfoRsaNoPssSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};
  // Also try a different value for is_hardware_backed.
  bool expected_is_hw_backed = false;
  std::string expected_nickname = "new_label";

  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .WillOnce(
          RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(
          GetFakeKeyInfoAttrs(/*is_in_software=*/!expected_is_hw_backed,
                              chromeos::PKCS11_CKK_RSA, expected_nickname),
          chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_TRUE(info_waiter.Get().has_value());
  const KeyInfo& key_info = info_waiter.Get().value();
  EXPECT_EQ(key_info.is_hardware_backed, expected_is_hw_backed);
  EXPECT_EQ(key_info.key_type, KeyType::kRsa);
  EXPECT_EQ(key_info.nickname, expected_nickname);
  EXPECT_THAT(key_info.supported_signing_schemes,
              UnorderedElementsAre(SigningScheme::kRsaPkcs1Sha1,
                                   SigningScheme::kRsaPkcs1Sha256,
                                   SigningScheme::kRsaPkcs1Sha384,
                                   SigningScheme::kRsaPkcs1Sha512));
}

// Test that GetKeyInfo can successfully get key info for EC keys.
TEST_F(KcerTokenImplTest, GetKeyInfoEcSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};
  bool expected_is_hw_backed = false;
  std::string expected_nickname = "new_label";

  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .WillOnce(
          RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(
          GetFakeKeyInfoAttrs(/*is_in_software=*/!expected_is_hw_backed,
                              chromeos::PKCS11_CKK_EC, expected_nickname),
          chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_TRUE(info_waiter.Get().has_value());
  const KeyInfo& key_info = info_waiter.Get().value();
  EXPECT_EQ(key_info.is_hardware_backed, expected_is_hw_backed);
  EXPECT_EQ(key_info.key_type, KeyType::kEcc);
  EXPECT_EQ(key_info.nickname, expected_nickname);
  EXPECT_THAT(key_info.supported_signing_schemes,
              UnorderedElementsAre(SigningScheme::kEcdsaSecp256r1Sha256,
                                   SigningScheme::kEcdsaSecp384r1Sha384,
                                   SigningScheme::kEcdsaSecp521r1Sha512));
}

// Test that GetKeyInfo correctly fails when it fails to fetch supported
// mechanisms.
TEST_F(KcerTokenImplTest, GetKeyInfoFailToGetMechanisms) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .WillOnce(RunOnceCallback<1>(std::vector<uint64_t>{},
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_FALSE(info_waiter.Get().has_value());
  EXPECT_EQ(info_waiter.Get().error(), Error::kFailedToRetrieveMechanismList);
}

// Test that GetKeyInfo correctly fails when it fails to find the key.
TEST_F(KcerTokenImplTest, GetKeyInfoFailToFindKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .WillOnce(
          RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_FALSE(info_waiter.Get().has_value());
  EXPECT_EQ(info_waiter.Get().error(), Error::kKeyNotFound);
}

// Test that GetKeyInfo correctly fails when it fails to read attributes.
TEST_F(KcerTokenImplTest, GetKeyInfoFailToReadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .WillOnce(
          RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_FALSE(info_waiter.Get().has_value());
  EXPECT_EQ(info_waiter.Get().error(), Error::kFailedToReadAttribute);
}

// Test that GetKeyInfo correctly fails when retrieved attributes are
// invalid.
TEST_F(KcerTokenImplTest, GetKeyInfoBadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};
  const uint32_t kInvalidKeyType = 9999;

  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .WillOnce(
          RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  // Imitate bad key type in the response.
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(
          GetFakeKeyInfoAttrs(/*is_in_software=*/true,
                              /*pkcs_key_type=*/kInvalidKeyType, "nickname"),
          chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_FALSE(info_waiter.Get().has_value());
  EXPECT_EQ(info_waiter.Get().error(), Error::kFailedToDecodeKeyAttributes);
}

// Test that GetKeyInfo retries several times when Chaps fails to get supported
// mechanisms with a session error.
TEST_F(KcerTokenImplTest, GetKeyInfoRetryToGetMechanisms) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<1>(
          std::vector<uint64_t>{}, chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_FALSE(info_waiter.Get().has_value());
  EXPECT_EQ(info_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GetKeyInfo retries several times when Chaps fails to find the key
// with a session error.
TEST_F(KcerTokenImplTest, GetKeyInfoRetryToFindKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  // GetMechanismList's result should be cached and not repeated.
  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .Times(1)
      .WillOnce(
          RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_FALSE(info_waiter.Get().has_value());
  EXPECT_EQ(info_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GetKeyInfo retries several times when Chaps fails to read
// attributes with a session error.
TEST_F(KcerTokenImplTest, GetKeyInfoRetryToReadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  // GetMechanismList's result should be cached and not repeated.
  EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
      .Times(1)
      .WillOnce(
          RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(
          chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());

  ASSERT_FALSE(info_waiter.Get().has_value());
  EXPECT_EQ(info_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GetKeyPermissions can successfully get key permissions.
TEST_F(KcerTokenImplTest, GetKeyPermissionsSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  chaps::KeyPermissions expected_key_permissions;
  expected_key_permissions.mutable_key_usages()->set_corporate(true);
  expected_key_permissions.mutable_key_usages()->set_arc(true);
  chaps::AttributeList key_perm_attrs;
  AddAttribute(
      key_perm_attrs, pkcs11_custom_attributes::kCkaChromeOsKeyPermissions,
      base::as_byte_span(expected_key_permissions.SerializeAsString()));

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(key_perm_attrs, chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<
      base::expected<std::optional<chaps::KeyPermissions>, Error>>
      result_waiter;
  token_.GetKeyPermissions(PrivateKeyHandle(public_key),
                           result_waiter.GetCallback());

  ASSERT_TRUE(result_waiter.Get().has_value());
  const std::optional<chaps::KeyPermissions>& received_key_permissions =
      result_waiter.Get().value();
  EXPECT_TRUE(ExpectKeyPermissionsEqual(expected_key_permissions,
                                        received_key_permissions));
}

// Test that GetKeyPermissions correctly fails when it fails to find the key.
TEST_F(KcerTokenImplTest, GetKeyPermissionsFailToFindKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<
      base::expected<std::optional<chaps::KeyPermissions>, Error>>
      result_waiter;
  token_.GetKeyPermissions(PrivateKeyHandle(public_key),
                           result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kKeyNotFound);
}

// Test that GetKeyPermissions correctly fails when it fails to read attributes.
TEST_F(KcerTokenImplTest, GetKeyPermissionsFailToReadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<
      base::expected<std::optional<chaps::KeyPermissions>, Error>>
      result_waiter;
  token_.GetKeyPermissions(PrivateKeyHandle(public_key),
                           result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kFailedToReadAttribute);
}

// Test that GetKeyPermissions correctly fails when retrieved attributes are
// invalid.
TEST_F(KcerTokenImplTest, GetKeyPermissionsBadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  // Imitate bad key type in the response.
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(
          RunOnceCallback<3>(chaps::AttributeList(), chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<
      base::expected<std::optional<chaps::KeyPermissions>, Error>>
      result_waiter;
  token_.GetKeyPermissions(PrivateKeyHandle(public_key),
                           result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kFailedToDecodeKeyAttributes);
}

// Test that GetKeyPermissions retries several times when Chaps fails to find
// the key with a session error.
TEST_F(KcerTokenImplTest, GetKeyPermissionsRetryToFindKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<
      base::expected<std::optional<chaps::KeyPermissions>, Error>>
      result_waiter;
  token_.GetKeyPermissions(PrivateKeyHandle(public_key),
                           result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GetKeyPermissions retries several times when Chaps fails to read
// attributes with a session error.
TEST_F(KcerTokenImplTest, GetKeyPermissionsRetryToReadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(
          chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<
      base::expected<std::optional<chaps::KeyPermissions>, Error>>
      result_waiter;
  token_.GetKeyPermissions(PrivateKeyHandle(public_key),
                           result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GetCertProvisioningProfileId can successfully get cert provisioning
// profile id.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  std::string expected_cert_prov_id = "new_cert_prov_id";
  chaps::AttributeList cert_prov_id_attrs;
  AddAttribute(
      cert_prov_id_attrs,
      pkcs11_custom_attributes::kCkaChromeOsBuiltinProvisioningProfileId,
      base::as_byte_span(expected_cert_prov_id));

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(
          RunOnceCallback<3>(cert_prov_id_attrs, chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
      result_waiter;
  token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
                                      result_waiter.GetCallback());

  ASSERT_TRUE(result_waiter.Get().has_value());
  EXPECT_EQ(expected_cert_prov_id, result_waiter.Get().value());
}

// Test that GetCertProvisioningProfileId correctly fails when it fails to find
// the key.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdFailToFindKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
      result_waiter;
  token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
                                      result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kKeyNotFound);
}

// Test that GetCertProvisioningProfileId correctly fails when it fails to read
// attributes.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdFailToReadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
      result_waiter;
  token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
                                      result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kFailedToReadAttribute);
}

// Test that GetCertProvisioningProfileId correctly fails when retrieved
// attributes are invalid.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdBadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(
          RunOnceCallback<3>(chaps::AttributeList(), chromeos::PKCS11_CKR_OK));

  base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
      result_waiter;
  token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
                                      result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kFailedToDecodeKeyAttributes);
}

// Test that GetCertProvisioningProfileId retries several times when Chaps fails
// to find the key with a session error.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdRetryToFindKey) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
      result_waiter;
  token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
                                      result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that GetCertProvisioningProfileId retries several times when Chaps fails
// to read attributes with a session error.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdRetryToReadAttributes) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
  ObjectHandle key_handle(1);
  std::vector<ObjectHandle> key_handles{key_handle};

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<2>(key_handles, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_,
              GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<3>(
          chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
      result_waiter;
  token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
                                      result_waiter.GetCallback());

  ASSERT_FALSE(result_waiter.Get().has_value());
  EXPECT_EQ(result_waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that SetKeyNickname can successfully set a nickname.
TEST_F(KcerTokenImplTest, SetKeyNicknameSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  ObjectHandle key_handle{1};
  std::vector<ObjectHandle> key_handles{key_handle};
  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(
          DoAll(MoveArg<1>(&find_key_attrs),
                RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK)));

  chaps::AttributeList nickname_attrs;
  EXPECT_CALL(chaps_client_,
              SetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(DoAll(MoveArg<2>(&nickname_attrs),
                      RunOnceCallback<3>(chromeos::PKCS11_CKR_OK)));

  std::string new_nickname = "new_nickname";
  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetKeyNickname(PrivateKeyHandle(public_key), new_nickname,
                        waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  EXPECT_TRUE(FindAttribute(nickname_attrs, chromeos::PKCS11_CKA_LABEL,
                            base::as_byte_span(new_nickname)));
  EXPECT_TRUE(waiter.Get().has_value());
}

// Test that SetKeyNickname can successfully set a nickname when the key is
// specified by SPKI.
TEST_F(KcerTokenImplTest, SetKeyNicknameBySpkiSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  ObjectHandle key_handle{1};
  std::vector<ObjectHandle> key_handles{key_handle};
  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(
          DoAll(MoveArg<1>(&find_key_attrs),
                RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK)));

  chaps::AttributeList nickname_attrs;
  EXPECT_CALL(chaps_client_,
              SetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(DoAll(MoveArg<2>(&nickname_attrs),
                      RunOnceCallback<3>(chromeos::PKCS11_CKR_OK)));

  std::string new_nickname = "new_nickname";
  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetKeyNickname(PrivateKeyHandle(Token::kUser, GetRsaSpki()),
                        new_nickname, waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  EXPECT_TRUE(FindAttribute(nickname_attrs, chromeos::PKCS11_CKA_LABEL,
                            base::as_byte_span(new_nickname)));
  EXPECT_TRUE(waiter.Get().has_value());
}

// Test that SetKeyNickname correctly fails when the key is specified by an
// invalid SPKI.
TEST_F(KcerTokenImplTest, SetKeyNicknameBySpkiFail) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);

  std::vector<uint8_t> bad_spki = GetRsaSpki().value();
  bad_spki.pop_back();
  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetKeyNickname(PrivateKeyHandle(PublicKeySpki(bad_spki)), "",
                        waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToGetPkcs11Id);
}

// Test that SetKeyNickname correctly fails when the key cannot be found.
TEST_F(KcerTokenImplTest, SetKeyNicknameFailToFind) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
                                   chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetKeyNickname(PrivateKeyHandle(public_key), "", waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kKeyNotFound);
}

// Test that SetKeyNickname correctly fails when Chaps fails to set the
// attribute.
TEST_F(KcerTokenImplTest, SetKeyNicknameFailToSet) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  ObjectHandle key_handle(1);
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>{key_handle},
                                   chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handle, _, _))
      .WillOnce(RunOnceCallback<3>(chromeos::PKCS11_CKR_GENERAL_ERROR));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetKeyNickname(PrivateKeyHandle(public_key), "", waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kFailedToWriteAttribute);
}

// Test that SetKeyNickname retries several times when Chaps fails to find the
// key with a session error.
TEST_F(KcerTokenImplTest, SetKeyNicknameRetryToFind) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetKeyNickname(PrivateKeyHandle(public_key), "", waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that SetKeyNickname retries several times when Chaps fails to set an
// attribute with a session error.
TEST_F(KcerTokenImplTest, SetKeyNicknameRetryToSet) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  ObjectHandle key_handle(1);
  EXPECT_CALL(chaps_client_, FindObjects)
      .Times(kDefaultAttempts)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>{key_handle}, chromeos::PKCS11_CKR_OK));
  EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handle, _, _))
      .Times(kDefaultAttempts)
      .WillRepeatedly(
          RunOnceCallbackRepeatedly<3>(chromeos::PKCS11_CKR_SESSION_CLOSED));

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetKeyNickname(PrivateKeyHandle(public_key), "", waiter.GetCallback());

  ASSERT_FALSE(waiter.Get().has_value());
  EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}

// Test that SetKeyPermissions can successfully set a new value. The
// implementation is largely shared with SetKeyNickname and the tests for it
// cover the fail cases.
TEST_F(KcerTokenImplTest, SetKeyPermissionsSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  ObjectHandle key_handle{1};
  std::vector<ObjectHandle> key_handles{key_handle};
  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(
          DoAll(MoveArg<1>(&find_key_attrs),
                RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK)));

  chaps::AttributeList key_permissions_attrs;
  EXPECT_CALL(chaps_client_,
              SetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(DoAll(MoveArg<2>(&key_permissions_attrs),
                      RunOnceCallback<3>(chromeos::PKCS11_CKR_OK)));

  chaps::KeyPermissions new_key_permissions;
  new_key_permissions.mutable_key_usages()->set_corporate(true);
  new_key_permissions.mutable_key_usages()->set_arc(true);

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetKeyPermissions(PrivateKeyHandle(public_key), new_key_permissions,
                           waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  std::string serialized_key_permissions =
      new_key_permissions.SerializeAsString();
  EXPECT_TRUE(FindAttribute(key_permissions_attrs,
                            static_cast<uint32_t>(AttributeId::kKeyPermissions),
                            base::as_byte_span(serialized_key_permissions)));
  EXPECT_TRUE(waiter.Get().has_value());
}

// Test that SetCertProvisioningProfileId can successfully set a new value. The
// implementation is largely shared with SetKeyNickname and the tests for it
// cover the fail cases.
TEST_F(KcerTokenImplTest, SetCertProvisioningProfileIdSuccess) {
  token_.InitializeWithoutNss(pkcs11_slot_id_);
  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  ObjectHandle key_handle{1};
  std::vector<ObjectHandle> key_handles{key_handle};
  chaps::AttributeList find_key_attrs;
  EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
      .WillOnce(
          DoAll(MoveArg<1>(&find_key_attrs),
                RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK)));

  chaps::AttributeList cert_prov_id_attrs;
  EXPECT_CALL(chaps_client_,
              SetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
      .WillOnce(DoAll(MoveArg<2>(&cert_prov_id_attrs),
                      RunOnceCallback<3>(chromeos::PKCS11_CKR_OK)));

  std::string new_profile_id = "new_profile_id";

  base::test::TestFuture<base::expected<void, Error>> waiter;
  token_.SetCertProvisioningProfileId(PrivateKeyHandle(public_key),
                                      new_profile_id, waiter.GetCallback());

  EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
                            GetRsaPkcs11Id().value()));
  EXPECT_TRUE(
      FindAttribute(cert_prov_id_attrs,
                    static_cast<uint32_t>(AttributeId::kCertProvisioningId),
                    base::as_byte_span(new_profile_id)));
  EXPECT_TRUE(waiter.Get().has_value());
}

// Test that all methods are queued until the token is initialized and unblocked
// after that. In this scenario fail all the methods for simplicity.
TEST_F(KcerTokenImplTest, AllMethodsAreBlockedUntilTokenInitialization) {
  EXPECT_CALL(chaps_client_, GenerateKeyPair)
      .WillRepeatedly(RunOnceCallbackRepeatedly<5>(
          ObjectHandle(), ObjectHandle(), chromeos::PKCS11_CKR_GENERAL_ERROR));
  EXPECT_CALL(chaps_client_, FindObjects)
      .WillRepeatedly(RunOnceCallbackRepeatedly<2>(
          std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_GENERAL_ERROR));
  EXPECT_CALL(chaps_client_, GetMechanismList)
      .WillRepeatedly(RunOnceCallbackRepeatedly<1>(
          std::vector<uint64>(), chromeos::PKCS11_CKR_GENERAL_ERROR));

  PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());

  base::test::TestFuture<base::expected<PublicKey, Error>> generate_rsa_waiter;
  token_.GenerateRsaKey(RsaModulusLength::k2048, true,
                        generate_rsa_waiter.GetCallback());
  base::test::TestFuture<base::expected<PublicKey, Error>> generate_ec_waiter;
  token_.GenerateEcKey(EllipticCurve::kP256, true,
                       generate_ec_waiter.GetCallback());
  base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
  token_.ImportKey(Pkcs8PrivateKeyInfoDer(), import_key_waiter.GetCallback());
  base::test::TestFuture<base::expected<void, Error>> import_cert_waiter;
  token_.ImportCertFromBytes(CertDer(), import_cert_waiter.GetCallback());
  base::test::TestFuture<base::expected<void, Error>> remove_key_waiter;
  token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key),
                           remove_key_waiter.GetCallback());
  base::test::TestFuture<base::expected<void, Error>> remove_cert_waiter;
  token_.RemoveCert(nullptr, remove_cert_waiter.GetCallback());
  base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>>
      list_keys_waiter;
  token_.ListKeys(list_keys_waiter.GetCallback());
  base::test::TestFuture<
      base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
      list_certs_waiter;
  token_.ListCerts(list_certs_waiter.GetCallback());
  base::test::TestFuture<base::expected<bool, Error>> key_exists_waiter;
  token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
                             key_exists_waiter.GetCallback());
  base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
  token_.Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPkcs1Sha1,
              DataToSign({1, 2, 3}), sign_waiter.GetCallback());
  base::test::TestFuture<base::expected<Signature, Error>> sign_raw_waiter;
  token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key),
                         DigestWithPrefix({1, 2, 3}),
                         sign_raw_waiter.GetCallback());
  base::test::TestFuture<base::expected<TokenInfo, Error>> token_info_waiter;
  token_.GetTokenInfo(token_info_waiter.GetCallback());
  base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
  token_.GetKeyInfo(PrivateKeyHandle(public_key),
                    key_info_waiter.GetCallback());
  base::test::TestFuture<
      base::expected<std::optional<chaps::KeyPermissions>, Error>>
      get_key_permissions_waiter;
  token_.GetKeyPermissions(PrivateKeyHandle(public_key),
                           get_key_permissions_waiter.GetCallback());
  base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
      get_cert_prov_id_waiter;
  token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
                                      get_cert_prov_id_waiter.GetCallback());
  base::test::TestFuture<base::expected<void, Error>> set_nickname_waiter;
  token_.SetKeyNickname(PrivateKeyHandle(public_key), "",
                        set_nickname_waiter.GetCallback());
  base::test::TestFuture<base::expected<void, Error>>
      set_key_permissions_waiter;
  token_.SetKeyPermissions(PrivateKeyHandle(public_key),
                           chaps::KeyPermissions(),
                           set_key_permissions_waiter.GetCallback());
  base::test::TestFuture<base::expected<void, Error>> set_cert_prov_id_waiter;
  token_.SetCertProvisioningProfileId(PrivateKeyHandle(public_key), "",
                                      set_cert_prov_id_waiter.GetCallback());
  // Run GenerateRsaKey again to test that SetCertProvisioningProfileId will
  // trigger it after initialization.
  base::test::TestFuture<base::expected<PublicKey, Error>>
      generate_rsa_waiter_2;
  token_.GenerateRsaKey(RsaModulusLength::k2048, true,
                        generate_rsa_waiter_2.GetCallback());

  task_environment_.RunUntilIdle();

  EXPECT_FALSE(generate_rsa_waiter.IsReady());
  EXPECT_FALSE(generate_ec_waiter.IsReady());
  EXPECT_FALSE(import_key_waiter.IsReady());
  EXPECT_FALSE(import_cert_waiter.IsReady());
  EXPECT_FALSE(remove_key_waiter.IsReady());
  EXPECT_FALSE(remove_cert_waiter.IsReady());
  EXPECT_FALSE(list_keys_waiter.IsReady());
  EXPECT_FALSE(list_certs_waiter.IsReady());
  EXPECT_FALSE(key_exists_waiter.IsReady());
  EXPECT_FALSE(sign_waiter.IsReady());
  EXPECT_FALSE(sign_raw_waiter.IsReady());
  EXPECT_FALSE(token_info_waiter.IsReady());
  EXPECT_FALSE(key_info_waiter.IsReady());
  EXPECT_FALSE(get_key_permissions_waiter.IsReady());
  EXPECT_FALSE(get_cert_prov_id_waiter.IsReady());
  EXPECT_FALSE(set_nickname_waiter.IsReady());
  EXPECT_FALSE(set_key_permissions_waiter.IsReady());
  EXPECT_FALSE(set_cert_prov_id_waiter.IsReady());
  EXPECT_FALSE(generate_rsa_waiter_2.IsReady());

  token_.InitializeWithoutNss(pkcs11_slot_id_);

  EXPECT_FALSE(generate_rsa_waiter.Get().has_value());
  EXPECT_FALSE(generate_ec_waiter.Get().has_value());
  EXPECT_FALSE(import_key_waiter.Get().has_value());
  EXPECT_FALSE(import_cert_waiter.Get().has_value());
  EXPECT_FALSE(remove_key_waiter.Get().has_value());
  EXPECT_FALSE(remove_cert_waiter.Get().has_value());
  EXPECT_FALSE(list_keys_waiter.Get().has_value());
  EXPECT_TRUE(list_certs_waiter.Get().has_value());
  EXPECT_FALSE(key_exists_waiter.Get().has_value());
  EXPECT_FALSE(sign_waiter.Get().has_value());
  EXPECT_FALSE(sign_raw_waiter.Get().has_value());
  EXPECT_TRUE(token_info_waiter.Get().has_value());
  EXPECT_FALSE(key_info_waiter.Get().has_value());
  EXPECT_FALSE(get_key_permissions_waiter.Get().has_value());
  EXPECT_FALSE(get_cert_prov_id_waiter.Get().has_value());
  EXPECT_FALSE(set_nickname_waiter.Get().has_value());
  EXPECT_FALSE(set_key_permissions_waiter.Get().has_value());
  EXPECT_FALSE(set_cert_prov_id_waiter.Get().has_value());
  EXPECT_FALSE(generate_rsa_waiter_2.Get().has_value());
}

}  // namespace
}  // namespace kcer::internal